Историй много. Сейчас будет чаще. То, что вспомнится быстрее или помнится лучше.
Жаль, что в своё время не записывал. Были довольно забавные или поучительные эпизоды.
История вторая.
На "морде" одного из разрабатывавшихся устройств были только четыре "информационных" светодиода. Плюс парочка - для индикации включения внешних запитанных от нашего блока подсистем.
Опять возникла ситуация показа сбоя.
Но здесь нужно просто показать, какая из внешних подсистем перестала исправно работать (с возможным кодом причины сбоя).
Простым включением комбинации удаётся показывать 16 кодов ошибки (2^4)
Но простые состояния "светит/погашен" применять нельзя - такой режим использовался в штатной работе для этапов и инициализации подсистем. То есть, они просто показывали активности и готовности блоков.
Значит, остаётся мигание.
Решил, что и мигание можно "растиповать". По частоте. Оптимально получилось две частоты. Если больше, испытуемые в качестве наблюдателей терялись в оценке. Да и до эпилепсии можно было отдельных индивидов довести, невзначай...
Подходящим оказались частоты мигания 1 и 2 герца.
Получалось, что признаком высвечивания ошибки было наличие хотя бы одного (их четырёх )мигающего светодиода. Получается, что можно отображать уже не 16, а 128 кодов ошибок при сбое. Уже жить можно!
Откуда 128? Один светодиод мигает с одной из частот - 2 состояния. Остальные 3 светодиода могут находиться в одном из 4 состояний: 1 - светит, 2 - погашен, 3 - мигает с частотой 1 герц, 4 - мигает с частотой 2 герца (2*4*4*4 = 128).
Как реализовать?
Завёл перечисление режимов и массив массивов "состояний светимости" светодиода.
enum LED_Mode { LED_OFF = 0, LED_ON, LED_1HZ, LED_2HZ, Led_Mode_COUNT };
#define LED_TICK_COUNT 4
#define LED_TICK_MASK 3
uint8_t led_modes[ Led_Mode_COUNT ][ LED_TICK_COUNT ] = {
{ 0, 0, 0, 0 },
{ 1, 1, 1, 1 },
{ 1, 1, 0, 0 },
{ 1, 0, 1, 0 }
};
Дальше, по таймеру (естественно, после делителя), каждые 0.25 сек (4 герца), для каждого из светодиодов, происходила "смена активности":
...
for( Led_t* led = leds; led < leds + LEDS_COUNT; ++led ) {
PORT_WRITE( led->_port, led_modes[ led->_mode ][ led_tick ] );
}
if( ++led_tick == Led_Mode_COUNT ) led_tick = 0; // можно и led_tick = (led_tick + 1) % Led_Mode_COUNT, но техника была уж очень слабовастенькая и с делением там было - на уровне очень длинных подпрограмм, что в обработчике прерываний использовать - неразумно. А можно и просто: led_tick = (led_tick + 1) & LED_TICK_MASK;
...
в дальнейшем, даже, в результате болезненной тяги к оптимизированию по быстродействию и сокращению времени пребывания в обработчике прерывания от "светодиодного" таймера, был даже вариант:
for( Led_t* led = leds; led < leds + LEDS_COUNT; ++led ) {
if( (uint8_t)led->_mode >> 1 ) PORT_WRITE( led->_port, led_modes[ led->_mode ][ led_tick ] );
}
а выставление в порт "светимости" для случая "светит" и "погашен" производить только в местах, где на них переключаются, но потом от этого отказался. Здесь всё - в одном месте, а там - в куче мест будет разбросано. Некрасиво. Да и - происходит смешивания логических уровней системы.
Можно и массив режимов/светимости свести к битам, но там, всё же, операций больше получается (сдвиги, маскирования...).
До следующих встреч.