Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
products:laboratory_iot_c:exp50 [2024/12/13 15:32] – [Эксперимент 50. Класс секундомера, логика работы] labuser30products:laboratory_iot_c:exp50 [2024/12/13 17:12] (текущий) – [Программный код эксперимента] labuser30
Строка 20: Строка 20:
 ==== Класс Stopwatch==== ==== Класс Stopwatch====
  
-<code python [enable_line_numbers="2", start_line_numbers_at="1"]> +<code arduino [enable_line_numbers="2", start_line_numbers_at="1"]> 
-class Stopwatch: +class Stopwatch 
-    def __init__(self): +  private
-        self.hours = 0 Значение часов +    unsigned int hours = 0; // Значение часов 
-        self.mins = 0 Значение минут +    unsigned int mins = 0;  // Значение минут 
-        self.secs = 0 Значение секунд +    unsigned int secs = 0;  // Значение секунд 
-        self.start_time = 0 Время запуска +    unsigned long start_time = 0; // Время запуска 
-        self.state = 0 Состояние0 - секундомер не запущен, 1 - запущен +    bool state = 0; // Состояние0 - секундомер не запущен, 1 - запущен 
-        self.backcolor = tft.rgbcolor(255,251,240) Цвет фона +    unsigned int backcolor = tft.color565(255, 251, 240); // Цвет фона 
-        self.fontcolor = tft.rgbcolor(39,40,51) Цвет шрифта +    unsigned int fontcolor = tft.color565(39, 40, 51);    // Цвет шрифта
-        self.draw_init() # Вызов функции отображения начального значения- нулей+
  
-    # Функция вывода начального значения секундомеранулей +  public: 
-    def draw_init(self): +    // Метод инициализации вместо конструктора, которым мы не можем воспользоваться, 
-        self.draw_value(98, 132, 2, 0# Выводим на дисплей 0 секунд +    // т.кнам нужен уже инициализированный объект дисплея 
-        self.draw_value(551253, 0) Выводим на дисплей 0 минут +    void init() {   
-        self.draw_value(5, 125, 3, 0Выводим на дисплей 0 часов+      tft.fillScreen(backcolor); // Закрашиваем дисплей цветом фона 
 +      reader.drawBMP("/time.bmp"tft0, 0); // Выводим изображение секундомера 
 +      drawInit(); // Вызов функции отображения начального значения- нулей 
 +    } 
  
-        # Выводим разделитель часов и минут +    // Метод вывода начального значения секундомера — нулей 
-        tft.text(41, 125, ":"font.terminalfont, self.fontcolor, 3 +    void drawInit() { 
-        # Выводим разделитель минут и секунд +      drawValue(8, 125, 3, 0);  // Выводим на дисплей 0 часов 
-        tft.text(90, 130, ":", font.terminalfont, self.fontcolor, 2)+      drawValue(55, 125, 30); // Выводим на дисплей 0 минут 
 +      drawValue(98, 132, 2, 0); // Выводим на дисплей 0 секунд
  
 +      tft.setTextColor(fontcolor); // Задаем цвет текста
 +      
 +      tft.setCursor(41, 125);  // Задаем координаты вывода
 +      tft.setTextSize(3); // Задаем размер текста
 +      tft.print(":"); // Выводим разделитель часов и минут
  
-    # Основная функция секундомера, отсчет времени +      tft.setCursor(88, 132);  // Задаем координаты вывода 
-    def tick(self): +      tft.setTextSize(2); // Задаем размер текста 
-        if self.state: # отсчет производим только, если секундомер запущен +      tft.print(":")// Выводим разделитель минут и секунд 
-            # Находим разницу в миллисекундах между текущим временем и временем запуска +    }
-            diff = time.ticks_diff(time.ticks_ms(), self.start_time) +
-            hours = diff // (1000 * 60 * 60) # Деление без остатка. Находим сколько целых часов прошло +
-            diff = diff % (1000 * 60 * 60) # Находим остаток после деления на часы и сохраняем в diff +
-            mins = diff // (1000 * 60) # Деление без остатка. Находим сколько целых минут прошло +
-            diff = diff % (1000 * 60) # Находим остаток после деления на минуты и сохраняем в diff +
-            secs = diff // (1000) # Деление без остатка. Находим сколько целых секунд прошло+
  
-            # Если сохраненное ранее значение секунд отличается от вычисленных- обновляем и выводим на дисплей +    // Метод отображения на дисплее значений 
-            if secs != self.secs: +    void drawValue(int x, int y, int size, int value) { 
-                self.secs = secs +      // Перед выводом на дисплей новых данных закрашиваем старые прямоугольником с цветом фона 
-                self.draw_value(98, 132, 2, secs) +      tft.fillRect(xy6 * size * 2, 8 * size -2backcolor);
-            +
-            # Если сохраненное ранее значение минут отличается от вычисленных- обновляем и выводим на дисплей +
-            if mins != self.mins: +
-                self.mins = mins +
-                self.draw_value(551253mins)+
  
-            # Если сохраненное ранее значение часов отличается от вычисленных- обновляем и выводим на дисплей +      tft.setCursor(x, y);  // Задаем координаты вывода 
-            if hours != self.hours: +      tft.setTextColor(fontcolor); // Задаем цвет текста 
-                self.hours = hours +      tft.setTextSize(size); // Задаем размер текста
-                self.draw_value(5, 125, 3, hours) +
-        +
  
-    # Функция запуска и остановки секундомера +      // Если значение для вывода менее 10, то добавляем ведущий ноль, чтобы всегда было 2 знака 
-    def start_stop(self): +      if (value < 10) tft.print(0); 
-        if self.state: # Если секундомер запущеностанавливаем +      tft.print(value); // Выводим значение 
-            self.state = 0 +    }
-        else: # Иначе- запускаем +
-            self.state = 1 +
-            self.start_time = time.ticks_ms() # Сохраняем время запуска секундомера+
  
 +    // Основной метод секундомера, отсчет времени
 +    void tick() {
 +      if (state) { // отсчет производим только, если секундомер запущен
 +        // Находим разницу в миллисекундах между текущим временем и временем запуска
 +        unsigned long diff = millis() - start_time;
 +        int hours_cur = diff / (1000 * 60 * 60); // Деление без остатка. Находим сколько целых часов прошло
 +        diff = diff % (1000 * 60 * 60); // Находим остаток после деления на часы и сохраняем в diff
 +        int mins_cur = diff / (1000 * 60); // Деление без остатка. Находим сколько целых минут прошло
 +        diff = diff % (1000 * 60); // Находим остаток после деления на минуты и сохраняем в diff
 +        int secs_cur = diff / (1000); // Деление без остатка. Находим сколько целых секунд прошло
  
-    # Функция отображения на дисплее значений +        // Если сохраненное ранее значение часов отличается от вычисленных — обновляем и выводим на дисплей 
-    def draw_value(self, x, y, size, value): +        if (hours_cur != hours) { 
-        # Перед выводом на дисплей новых данных закрашиваем старые прямоугольником с цветом фона +            hours = hours_cur;  
-        tft.rect(x+2y6 * size * 28 * size -2, self.backcolor)+            drawValue(81253hours)
 +        }
  
-        Если значение для вывода менее 10, то добавляем ведущий нольчтобы всегда было 2 знака +        // Если сохраненное ранее значение минут отличается от вычисленных — обновляем и выводим на дисплей 
-        # конвертируем значение из числа в строку +        if (mins_cur != mins) { 
-        if value < 10: +          mins = mins_cur; 
-            value '0' + str(value+          drawValue(55125, 3, mins); 
-        else: +        } 
-            value = str(value)+         
 +        // Если сохраненное ранее значение секунд отличается от вычисленных — обновляем и выводим на дисплей 
 +        if (secs_cur != secs) { 
 +          secs secs_cur; 
 +          drawValue(98, 132, 2, secs); 
 +        } 
 +      } 
 +    }
  
-        # Выводим значение в виде строки на дисплей в нужном месте нужным цветом и размером +     // Метод запуска и остановки секундомера 
-        tft.text(x, y, value, font.terminalfont, self.fontcolor, size)+    void startStop() { 
 +      state = !state; // Инвертируем состояние секундомера 
 +      if (state) start_time = millis(); // Если запущен сохраняем время запуска секундомера   
 +    } 
 +};
 </code> </code>
  
-Класс содержит конструктор ''__init__'' и методы: +Класс содержит методы: 
-  * ''draw_init'' — отображает начальное значение таймера, нули, как в прошлом эксперименте. +  * ''init'' — используется вместо конструктора и вызывается в функции  ''setup()''. 
-  * ''start_stop'' — запускает и останавливает секундомер.  +  * ''drawInit'' — отображает начальное значение таймера, нули, как в прошлом эксперименте. 
-  * ''draw_value'' — выводит значение часов минут или секунд. В качестве параметров принимает координаты, размер шрифта и значение.+  * ''startStop'' — запускает и останавливает секундомер.  
 +  * ''drawValue'' — выводит значение часов минут или секунд. В качестве параметров принимает координаты, размер шрифта и значение.
   * ''tick'' — системная функция секундомера. Она определяет сколько часов, минут и секунд прошло с момента его запуска. Эту функцию необходимо вызывать постоянно.   * ''tick'' — системная функция секундомера. Она определяет сколько часов, минут и секунд прошло с момента его запуска. Эту функцию необходимо вызывать постоянно.
  
 Код класса снабжен комментариями, подробно описывающими почти каждую строку. Кроме этого дадим несколько пояснений. Когда мы говорим "Сохраняем время запуска секундомера", то имеется в виду, что мы сохраняем значение количества миллисекунд с момента запуска микроконтроллера. И все упоминания времени следует читать в этом ключе. Код класса снабжен комментариями, подробно описывающими почти каждую строку. Кроме этого дадим несколько пояснений. Когда мы говорим "Сохраняем время запуска секундомера", то имеется в виду, что мы сохраняем значение количества миллисекунд с момента запуска микроконтроллера. И все упоминания времени следует читать в этом ключе.
- 
-Функция ''diff = time.ticks_diff(time.ticks_ms(), self.start_time)'' нужна для нахождения разницы между  количеством миллисекунд, прошедших со старта микроконтроллера при запуске секундомера и сейчас. Несмотря на то, что это есть обычное число, которое можно было бы вычесть обычным знаком минус, лучше это делать с помощью данной функции. Дело в том, что это значение может переполниться и обнулиться и начать считать "по второму кругу". Тогда простое вычитание даст ошибку. Только функция ''ticks_diff'' гарантирует правильное значение. 
  
 Заострим внимание на вычислении количества часов: Заострим внимание на вычислении количества часов:
-<code python+<code arduino
-hours = diff // (1000 * 60 * 60) +int hours_cur = diff / (1000 * 60 * 60); 
-diff = diff % (1000 * 60 * 60)+diff = diff % (1000 * 60 * 60);
 </code> </code>
  
-Оператор ''%%//%%'' — это оператор деления без остатка. Например 5 %%//%% 2 = 2. А остаток 0.5 был отброшен.+Оператор ''%%/%%'' — это оператор деления без остатка. Например 5 %%/%% 2 = 2. А остаток 0.5 был отброшен.
 А оператор ''%'' — напротив возвращает только остаток от деления. 5 % 2 = 0.5 А оператор ''%'' — напротив возвращает только остаток от деления. 5 % 2 = 0.5
  
 Сначала мы вычисляем целое количество часов, а потом мы вычисляем по сути сколько осталось после выделения целого количества часов. После этого там останется значение меньше одного часа. Его мы уже будем делить на минуты. А после вычитания минут будем определять сколько осталось секунд. Сначала мы вычисляем целое количество часов, а потом мы вычисляем по сути сколько осталось после выделения целого количества часов. После этого там останется значение меньше одного часа. Его мы уже будем делить на минуты. А после вычитания минут будем определять сколько осталось секунд.
  
-Теперь сделаем программу с использованием данного класса+Теперь сделаем программу с использованием данного класса.
  
 ==== Программный код эксперимента ==== ==== Программный код эксперимента ====
  
-<file python Exp50.py[enable_line_numbers="2", start_line_numbers_at="1"]> +<file arduino Exp50.ino[enable_line_numbers="2", start_line_numbers_at="1"]> 
-from machine import Pin, SPI +#include <SPI.h> 
-from tft import TFT_GREEN +#include <Adafruit_ST7735.h> 
-import font +#include "LittleFS_ImageReader.h" 
-import time +
-_init() +
-machine.freq(160000000)+
  
 +#define PIN_CS  2
 +#define PIN_DC  4
 +#define PIN_RST 5
  
-dc  Pin(4Pin.OUT)  +Adafruit_ST7735 tft Adafruit_ST7735(PIN_CS,  PIN_DCPIN_RST); 
-cs  = Pin(2Pin.OUT+LittleFS_ImageReader reader;
-rst = Pin(5, Pin.OUT)+
  
-spi SPI(1, baudrate=40000000, polarity=0, phase=0) +class Stopwatch { 
-tft TFT_GREEN(128160spidccs, rst, rotate=0)+  private: 
 +    unsigned int hours 0;  
 +    unsigned int mins 0;  
 +    unsigned int secs = 0;   
 +    unsigned long start_time = 0; 
 +    bool state 0;  
 +    unsigned int backcolor = tft.color565(255251240);  
 +    unsigned int fontcolor = tft.color565(394051);   
  
-tft.initr(tft.BGRtft.initr(tft.RGB#Если вместо синего цвета отображается красный, а вместо красного синий +  public: 
-tft.clear(tft.rgbcolor(255,251,240))  +    void init() {   
-tft.draw_bmp(0,0,'time.bmp')+      tft.fillScreen(backcolor) 
 +      reader.drawBMP("/time.bmp"tft, 0, 0)
 +      drawInit();  
 +    } 
  
 +    void drawInit() {
 +      drawValue(8, 125, 3, 0);  
 +      drawValue(55, 125, 3, 0); 
 +      drawValue(98, 132, 2, 0);
  
-class Stopwatch: +      tft.setTextColor(fontcolor) 
-    def __init__(self): +       
-        self.hours = 0 +      tft.setCursor(41125) 
-        self.mins = 0 +      tft.setTextSize(3); 
-        self.secs = 0 +      tft.print(":");
-        self.start_time = 0 +
-        self.state = 0 +
-        self.backcolor = tft.rgbcolor(255,251,240+
-        self.fontcolor = tft.rgbcolor(39,40,51+
-        self.draw_init()+
  
 +      tft.setCursor(88, 132);  
 +      tft.setTextSize(2); 
 +      tft.print(":");
 +    }
  
-    def draw_init(self): +    void drawValue(int xint yint sizeint value{ 
-        self.draw_value(9813220+      tft.fillRect(xy6 * size * 28 * size -2backcolor);
-        self.draw_value(5512530) +
-        self.draw_value(5, 125, 30)+
  
-        tft.text(41125, ":", font.terminalfont, self.fontcolor, 3+      tft.setCursor(xy);   
-        tft.text(90, 130, ":", font.terminalfont, self.fontcolor, 2)+      tft.setTextColor(fontcolor); 
 +      tft.setTextSize(size);
  
 +      if (value < 10) tft.print(0);
 +      tft.print(value); 
 +    }
  
-    def tick(self): +    void tick() { 
-        if self.state: +      if (state) {  
-            diff = time.ticks_diff(time.ticks_ms(), self.start_time) +        unsigned long diff = millis() start_time; 
-            hours = diff // (1000 * 60 * 60) +        int hours_cur = diff / (1000 * 60 * 60) 
-            diff = diff % (1000 * 60 * 60) +        diff = diff % (1000 * 60 * 60) 
-            mins = diff // (1000 * 60) +        int mins_cur = diff / (1000 * 60) 
-            diff = diff % (1000 * 60) +        diff = diff % (1000 * 60) 
-            secs = diff // (1000)+        int secs_cur = diff / (1000)
  
-            if secs != self.secs: +        if (hours_cur != hours) { 
-                self.secs secs +            hours hours_cur;  
-                self.draw_value(981322secs)+            drawValue(81253hours)
 +        }
  
-            if mins != self.mins: +        if (mins_cur != mins) { 
-                self.mins = mins +          mins = mins_cur; 
-                self.draw_value(55, 125, 3, mins) +          drawValue(55, 125, 3, mins); 
- +        }
-            if hours != self.hours: +
-                self.hours = hours +
-                self.draw_value(5, 125, 3, hours)+
                  
 +        if (secs_cur != secs) {
 +          secs = secs_cur;
 +          drawValue(98, 132, 2, secs);
 +        }
 +      }
 +    }
  
-    def start_stop(self): +    void startStop() { 
-        if self.state: +      state = !state 
-            self.state = +      if (statestart_time millis() 
-        else: +    } 
-            self.state = 1 +};
-            self.start_time = time.ticks_ms() +
- +
- +
-    def draw_value(self, x, y, size, value): +
-        tft.rect(x+2, y, 6 * size * 2, 8 * size -2, self.backcolor) +
- +
-        if value < 10: +
-            value = '0' + str(value) +
-        else: +
-            value str(value) +
- +
-        tft.text(x, y, value, font.terminalfont, self.fontcolor, size) +
- +
- +
-class Button: +
-    def __init__(self, p, pressSate): +
-        self.pin = Pin(p, Pin.IN) +
-        self.pressSate = pressSate +
-        self.oldState = not pressSate +
-  +
-    def onPress(self): +
-        state = self.pin.value() +
-        if state != self.oldState: +
-            self.oldState = state +
-            if state == self.pressSate: +
-                return True +
-        return False+
  
 +Stopwatch stopwatch;
  
-stopwatch = Stopwatch() +void setup() { 
-stopwatch.start_stop()+  os_update_cpu_frequency(160); 
 +  LittleFS.begin(); 
 +  tft.initR(INITR_BLACKTAB); 
 +  tft.setRotation(2);  
 +   
 +  stopwatch.init(); 
 +  stopwatch.startStop()
 +}
  
-while True: +void loop() { 
-    stopwatch.tick()+  stopwatch.tick()
 +}
 </file> </file>
  
-Код эксперимента должен быть понятен. Сначала как обычно подключаем библиотеки, настраиваем SPI и дисплей. Потом код класса секундомера и в конце мы создаем объект секундомера +Код эксперимента должен быть понятен. Сначала как обычно подключаем библиотеки и дисплей. Потом код класса секундомера и в конце мы создаем объект секундомера. 
-<code python[enable_line_numbers="2", start_line_numbers_at="98"]> +<code arduino[enable_line_numbers="2", start_line_numbers_at="88"]> 
-stopwatch = Stopwatch()+Stopwatch stopwatch;
 </code> </code>
  
-Запускаем его +Инициализируем и запускаем его. 
-<code python[enable_line_numbers="2", start_line_numbers_at="99"]> +<code arduino[enable_line_numbers="2", start_line_numbers_at="96"]> 
-stopwatch.start_stop()+  stopwatch.init(); 
 +  stopwatch.startStop();
 </code> </code>
  
-И в бесконечном цикле вызываем его метод ''tick()''. В нем он занимается отслеживаем времени и отображением изменений на дисплее +И в бесконечном цикле вызываем его метод ''tick()''. В нем он занимается отслеживаем времени и отображением изменений на дисплее. 
-<code python[enable_line_numbers="2", start_line_numbers_at="101"]> +<code arduino[enable_line_numbers="2", start_line_numbers_at="101"]> 
-while True: +  stopwatch.tick();
-    stopwatch.tick()+
 </code> </code>
  
 Теперь наш секундомер умеет считать время. Но еще не слушается управления. В следующем эксперименте мы добавим управление с помощью кнопки и получим полноценный секундомер. Теперь наш секундомер умеет считать время. Но еще не слушается управления. В следующем эксперименте мы добавим управление с помощью кнопки и получим полноценный секундомер.