Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
products:laboratory_iot_c:exp50 [2024/12/13 15:32] – [Эксперимент 50. Класс секундомера, логика работы] labuser30 | products:laboratory_iot_c:exp50 [2024/12/13 17:12] (текущий) – [Программный код эксперимента] labuser30 | ||
---|---|---|---|
Строка 20: | Строка 20: | ||
==== Класс Stopwatch==== | ==== Класс Stopwatch==== | ||
- | < | + | < |
- | class Stopwatch: | + | class Stopwatch |
- | | + | private: |
- | self.hours = 0 # Значение часов | + | |
- | self.mins = 0 # Значение минут | + | |
- | self.secs = 0 # Значение секунд | + | |
- | self.start_time = 0 # Время запуска | + | |
- | self.state = 0 # Состояние. 0 - секундомер не запущен, | + | |
- | self.backcolor = tft.rgbcolor(255, | + | |
- | self.fontcolor = tft.rgbcolor(39, | + | |
- | self.draw_init() # Вызов функции отображения начального значения- нулей | + | |
- | # Функция вывода начального значения | + | public: |
- | | + | // Метод инициализации |
- | self.draw_value(98, 132, 2, 0) # Выводим на дисплей | + | // т.к. нам нужен уже инициализированный объект дисплея |
- | self.draw_value(55, 125, 3, 0) # Выводим | + | |
- | | + | tft.fillScreen(backcolor); // Закрашиваем дисплей |
+ | | ||
+ | | ||
+ | } | ||
- | # Выводим разделитель часов | + | // Метод вывода |
- | | + | void drawInit() { |
- | # Выводим | + | drawValue(8, |
- | tft.text(90, | + | |
+ | drawValue(98, | ||
+ | tft.setTextColor(fontcolor); | ||
+ | | ||
+ | tft.setCursor(41, | ||
+ | tft.setTextSize(3); | ||
+ | tft.print(":" | ||
- | # Основная функция секундомера, отсчет времени | + | tft.setCursor(88, |
- | def tick(self): | + | |
- | 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) # Находим | + | |
- | 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, | + | tft.fillRect(x, y, 6 * size * 2, 8 * size -2, backcolor); |
- | + | ||
- | # Если сохраненное | + | |
- | if mins != self.mins: | + | |
- | self.mins = mins | + | |
- | self.draw_value(55, 125, 3, mins) | + | |
- | # Если сохраненное ранее значение часов отличается от вычисленных- обновляем и выводим на дисплей | + | tft.setCursor(x, |
- | if hours != self.hours: | + | tft.setTextColor(fontcolor); |
- | self.hours = hours | + | tft.setTextSize(size); // Задаем размер текста |
- | self.draw_value(5, 125, 3, hours) | + | |
- | | + | |
- | # Функция запуска | + | // Если значение для вывода менее 10, то добавляем ведущий ноль, чтобы |
- | 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, | + | |
- | # Перед выводом на дисплей новых данных закрашиваем старые прямоугольником | + | hours = hours_cur; |
- | | + | drawValue(8, 125, 3, hours); |
+ | } | ||
- | | + | |
- | # конвертируем значение из числа в строку | + | if (mins_cur != mins) { |
- | if value < 10: | + | mins = mins_cur; |
- | | + | drawValue(55, 125, 3, mins); |
- | | + | } |
- | value = str(value) | + | |
+ | // Если сохраненное ранее значение | ||
+ | if (secs_cur != secs) { | ||
+ | | ||
+ | drawValue(98, 132, 2, secs); | ||
+ | | ||
+ | } | ||
+ | } | ||
- | # Выводим значение в виде строки на дисплей в нужном месте нужным цветом и размером | + | // Метод запуска и остановки секундомера |
- | | + | void startStop() { |
+ | state = !state; // Инвертируем состояние секундомера | ||
+ | if (state) start_time = millis(); // Если запущен сохраняем время запуска секундомера | ||
+ | } | ||
+ | }; | ||
</ | </ | ||
- | Класс содержит конструктор '' | + | Класс содержит |
- | * '' | + | * '' |
- | * '' | + | * '' |
- | * '' | + | * '' |
+ | * '' | ||
* '' | * '' | ||
Код класса снабжен комментариями, | Код класса снабжен комментариями, | ||
- | |||
- | Функция '' | ||
Заострим внимание на вычислении количества часов: | Заострим внимание на вычислении количества часов: | ||
- | < | + | < |
- | hours = diff // (1000 * 60 * 60) | + | int hours_cur |
- | diff = diff % (1000 * 60 * 60) | + | diff = diff % (1000 * 60 * 60); |
</ | </ | ||
- | Оператор '' | + | Оператор '' |
А оператор '' | А оператор '' | ||
Сначала мы вычисляем целое количество часов, а потом мы вычисляем по сути сколько осталось после выделения целого количества часов. После этого там останется значение меньше одного часа. Его мы уже будем делить на минуты. А после вычитания минут будем определять сколько осталось секунд. | Сначала мы вычисляем целое количество часов, а потом мы вычисляем по сути сколько осталось после выделения целого количества часов. После этого там останется значение меньше одного часа. Его мы уже будем делить на минуты. А после вычитания минут будем определять сколько осталось секунд. | ||
- | Теперь сделаем программу с использованием данного класса | + | Теперь сделаем программу с использованием данного класса. |
==== Программный код эксперимента ==== | ==== Программный код эксперимента ==== | ||
- | < | + | < |
- | from machine import Pin, SPI | + | #include <SPI.h> |
- | from tft import TFT_GREEN | + | #include < |
- | import font | + | #include " |
- | import time | + | |
- | _init() | + | |
- | machine.freq(160000000) | + | |
+ | #define PIN_CS | ||
+ | #define PIN_DC | ||
+ | #define PIN_RST 5 | ||
- | dc | + | Adafruit_ST7735 tft = Adafruit_ST7735(PIN_CS, |
- | cs = Pin(2, Pin.OUT) | + | LittleFS_ImageReader reader; |
- | rst = Pin(5, Pin.OUT) | + | |
- | spi = SPI(1, baudrate=40000000, polarity=0, phase=0) | + | class Stopwatch { |
- | tft = TFT_GREEN(128, 160, spi, dc, cs, rst, rotate=0) | + | private: |
+ | unsigned int hours = 0; | ||
+ | unsigned int mins = 0; | ||
+ | unsigned int secs = 0; | ||
+ | unsigned long start_time | ||
+ | bool state = 0; | ||
+ | unsigned int backcolor = tft.color565(255, 251, 240); | ||
+ | unsigned int fontcolor = tft.color565(39, 40, 51); | ||
- | tft.initr(tft.BGR) # tft.initr(tft.RGB) #Если вместо синего цвета отображается красный, | + | public: |
- | tft.clear(tft.rgbcolor(255,251,240)) | + | void init() { |
- | tft.draw_bmp(0,0,' | + | |
+ | | ||
+ | drawInit(); | ||
+ | } | ||
+ | void drawInit() { | ||
+ | drawValue(8, | ||
+ | drawValue(55, | ||
+ | drawValue(98, | ||
- | class Stopwatch: | + | tft.setTextColor(fontcolor); |
- | def __init__(self): | + | |
- | | + | tft.setCursor(41, 125); |
- | | + | tft.setTextSize(3); |
- | self.secs = 0 | + | tft.print(":" |
- | self.start_time = 0 | + | |
- | self.state = 0 | + | |
- | self.backcolor = tft.rgbcolor(255,251,240) | + | |
- | | + | |
- | self.draw_init() | + | |
+ | tft.setCursor(88, | ||
+ | tft.setTextSize(2); | ||
+ | tft.print(":" | ||
+ | } | ||
- | | + | |
- | self.draw_value(98, 132, 2, 0) | + | tft.fillRect(x, y, 6 * size * 2, 8 * size -2, backcolor); |
- | self.draw_value(55, 125, 3, 0) | + | |
- | self.draw_value(5, | + | |
- | | + | |
- | tft.text(90, 130, ":", | + | tft.setTextColor(fontcolor); |
+ | tft.setTextSize(size); | ||
+ | if (value < 10) tft.print(0); | ||
+ | tft.print(value); | ||
+ | } | ||
- | | + | |
- | if self.state: | + | if (state) { |
- | diff = time.ticks_diff(time.ticks_ms(), self.start_time) | + | |
- | | + | int hours_cur |
- | diff = diff % (1000 * 60 * 60) | + | diff = diff % (1000 * 60 * 60); |
- | | + | int mins_cur |
- | diff = diff % (1000 * 60) | + | diff = diff % (1000 * 60); |
- | | + | int secs_cur |
- | | + | |
- | | + | |
- | | + | |
+ | } | ||
- | | + | |
- | self.mins = mins | + | mins = mins_cur; |
- | | + | |
- | + | } | |
- | if hours != self.hours: | + | |
- | self.hours = hours | + | |
- | self.draw_value(5, | + | |
| | ||
+ | if (secs_cur != secs) { | ||
+ | secs = secs_cur; | ||
+ | drawValue(98, | ||
+ | } | ||
+ | } | ||
+ | } | ||
- | | + | |
- | if self.state: | + | state = !state; |
- | self.state = 0 | + | if (state) start_time |
- | else: | + | |
- | self.state = 1 | + | }; |
- | self.start_time = time.ticks_ms() | + | |
- | + | ||
- | + | ||
- | def draw_value(self, | + | |
- | tft.rect(x+2, | + | |
- | + | ||
- | if value < 10: | + | |
- | value = ' | + | |
- | else: | + | |
- | value = str(value) | + | |
- | + | ||
- | tft.text(x, y, value, font.terminalfont, | + | |
- | + | ||
- | + | ||
- | class Button: | + | |
- | | + | |
- | 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 | + | |
- | | + | |
+ | Stopwatch stopwatch; | ||
- | stopwatch | + | void setup() { |
- | stopwatch.start_stop() | + | os_update_cpu_frequency(160); |
+ | LittleFS.begin(); | ||
+ | tft.initR(INITR_BLACKTAB); | ||
+ | tft.setRotation(2); | ||
+ | |||
+ | | ||
+ | stopwatch.startStop(); | ||
+ | } | ||
- | while True: | + | void loop() { |
- | stopwatch.tick() | + | stopwatch.tick(); |
+ | } | ||
</ | </ | ||
- | Код эксперимента должен быть понятен. Сначала как обычно подключаем библиотеки, настраиваем SPI и дисплей. Потом код класса секундомера и в конце мы создаем объект секундомера | + | Код эксперимента должен быть понятен. Сначала как обычно подключаем библиотеки и дисплей. Потом код класса секундомера и в конце мы создаем объект секундомера. |
- | < | + | < |
- | stopwatch | + | Stopwatch |
</ | </ | ||
- | Запускаем его | + | Инициализируем и запускаем его. |
- | < | + | < |
- | stopwatch.start_stop() | + | |
+ | | ||
</ | </ | ||
- | И в бесконечном цикле вызываем его метод '' | + | И в бесконечном цикле вызываем его метод '' |
- | < | + | < |
- | while True: | + | stopwatch.tick(); |
- | | + | |
</ | </ | ||
Теперь наш секундомер умеет считать время. Но еще не слушается управления. В следующем эксперименте мы добавим управление с помощью кнопки и получим полноценный секундомер. | Теперь наш секундомер умеет считать время. Но еще не слушается управления. В следующем эксперименте мы добавим управление с помощью кнопки и получим полноценный секундомер. |