На AVR мне ни разу больше 6 таймеров не потребовалось, нинаю, как на RP2040 масть пайдёть. А очередь сообщений у меня теперь кольцевым буфером сделана, что на Тини, что на меге, 16 сообщений глубиной. Думаю и для других контроллеров этого будет достат.кол.
У меня уже не раз за десяток переваливало. Конечно, можно что-то упростить, но упрощенное по любому придется переводить на миллис, так какой смысл заводить два списка?
Ну смотри, реальная штука - на работе будильник, что пищит каждые полтора часа ночью, типа, пора на обход
shHandle rtc_guard; // опрос микросхемы RTC по таймеру, чтобы не дергать ее откуда попало; можно и обойтись, но так упорядоченнее
shHandle blink_timer; // блинк, используется всем, что мигает; можно тоже обойтись, но в каждом случае таки придется усложнять код;
shHandle return_to_default_mode; // таймер автовозврата в режим показа времени из любого режима настройки; тут без вариантов
shHandle set_time_mode; // режим настройки времени; тут тоже без вариантов
shHandle display_guard; // вывод данных на экран; проверяет буфер экрана на изменения и выводит только если что изменилось; можно обойтись? можно, но тогда вывод придется дергать отовсюду, где информация выводится
shHandle alarm_guard; // отслеживание будильника; без вариантов
shHandle alarm_buzzer; // пищалка будильника; без вариантов
shHandle show_alarm_setting_mode; // режим показа настроек будильника; без вариантов
#ifdef USE_TEMP_DATA
shHandle show_temp_mode; // режим показа температуры; ежели у нас есть вывод температуры, то без вариантов
#ifdef USE_DS18B20
shHandle ds18b20_guard; // опрос датчика DS18b20; ежели используется этот датчик, то тоже без вариантов
#endif
#endif
#ifdef USE_LIGHT_SENSOR
shHandle light_sensor_guard; // отслеживание показаний датчика света; возможно, можно обойтись, но так регулировать яркость экрана проще
#endif
#ifdef USE_SET_BRIGHTNESS_MODE
shHandle set_brightness_mode; // режим настройки яркости экрана; без вариантов
#endif
Итого до 12 задач.
Вспоминается (не могу найти ссылку на обсуждение на стром форуме, мы с тобой тогда решали вопрос), была у меня еще одна проблема с твоими таймерами - в какой-то момент часы зависали. Оказалось, что проблема в прерываниях, соответственно, пришлось в таймере просто поднимать флаг, а обрабатывать его в лупе.
Да, я сам отказался от выполнения привязанной к таймеру функции в контексте прерывания. Теперь у меня таймер при сработке просто кладет в очередь сообщение msg_TimerEnd и свой номер, а диспетчер потом сам разберется, что ему с этим делать. А сенсоры у меня в loop опрашиваются, без таймеров, каждый сенсор сам знает, когда его опрашивали в последний раз, и просто не читается реально, если интервал опроса меньше заданного. А когда пройдет заданное время, то сенсор реально читается, и если его значение изменилось, в очередь шлёцца сообщение об этом.
Вот, а у меня и сенсоры тоже отдельными задачами опрашиваются. В общем, в лупе обычно только опрос кнопок и опрос списка задач. В принципе и опрос кнопок можно было бы отдельной задачей оформить, но у них тоже опрос желательно делать как можно чаще, потому отдельно
void internalRead() override {
if (ReadData()) { // Здесь читаются 8 байт с CRC из Далласа (true, если CRC сошлась)
int8_t temp = (FDallasMemory[1] << 4) | (FDallasMemory[0] >> 4); // берем тенпературу
if (temp == FTemperature) return; // если она равна предыдущей, уходим, ничо не поменялось
FTemperature = temp; // запоминаем последнее прочитанное
PostMessage(msg_TemperatureChanged, FTemperature); // шлём привет диспеччеру
}
else
Error(err_OneWire_CRCError); // либо шлем ошибку для того, чтобы перечитать правильно
}
Не в тему, но коль уж проскочил Dallas, самому часто приходится разбивать операции для DS18b20 по шагам, с тем что бы не было блокировки в лупе. В принципе это не важно, когда опрашиваешь раз в…, но может быть важно для других процессов, типа индикации и т.п.