Ну, в общем я кратко скажу суть/идею решения, кому надо - будет иметь в виду, а кому обязательно готовый код на тарелочке - всё равно этой микросхемой не воспользуется, не так она проста.
Как я уже писал, общая схема работы этого драйвера такая:
- В память микросхемы посредством SPI закачивается 16 (по одному на ШИМ-канал) 12-тиразрядных числа;
- Защёлкивается «защёлка» (импульсом на пине
XLAT
)
- Подаётся сигнал «начало нового периода» (импульс на пине
BLANK
)
- При первом после этого восходящем фронте тактирующего сигнала (пин
GSCLK
), все каналы, для которых закачан не 0 включаются в HIGH и из их чисел вычитается 1;
- При каждом последующем восходящем фронте тактирующего сигнала, все каналы для которых число стало нулём, переводятся в
LOW
, а из чисел всех остальных каналов вычитается 1;
- И так 4096 раз. После этого всё затухает, если не придёт новый сигнал «начало нового периода». Если же он придёт, то, веселье продолжается с п.4
Итак, таймеры в библиотеке нужны для того, чтобы (А) обеспечить тактирующий сигнал на пине GSCLK
и (Б) сигнал начала периода – короткие иглы на пине BLANK
с частотой в 4096 раз меньшей, чем частота тактирующего сигнала. Собственно, частота игл на пине BLANK
и есть частота ШИМ на светодиодах.
Хочу обойтись одним таймером. Решение такое.
Тактирующий сигнал:
В ATmega328P программируем фьюз CKOUT
. На пине PB0
(он же пин 8 в Ардуино) появляется тактирующий сигнал контроллера. Если это Ардуино Uno/Nano, то частота этого сигнала – 16МГц. Этот пин (PB0
) соединяем с пином GSCLK
драйвера. Задача (А) решена – TLС-шка нормально тактируется.
Сигнал начала периода:
просто настраиваем таймер, чтобы он выдавал короткий пик один раз в 4096 тактов котроллера. Например, вот так (сигнал будет на 9-ом пине ардуино):
static constexpr uint8_t prescalerMask = bitMask(CS10); // делитель - 1
inline void TLCTimerStart(void) { TCCR1B |= prescalerMask; } //запустим таймер
inline void TLCTimerStop(void) { TCCR1B &= ~ bitMask(CS10, CS11, CS12); } // остановим таймер
inline void TLCTimerInit(void) {
TLCTimerStop();
TCCR1A = bitMask(COM1A1, WGM11);
TCCR1B = bitMask(WGM12, WGM13);
ICR1 = 4096;
TCNT1 = 0;
OCR1A = 0;
OCR1B = 0;
TIMSK1 = 0;
}
Вот и второй сигнал сделан. Причём работает всё без участия прошивки - само. Нам даже прерываний никаких обрабатывать не надо.
Единственное о чём осталось позаботиться, это чтобы сигнал на пине BLANK
не прилетел в тот момент, когда мы защёлкиваем данных (когда на XLAT
сидит HIGH
). Чтобы не допустить такого, я на момент защёлкивания просто выключаю таймер функцией TLCTimerStop
, а потом снова включаю функцией TLCTimerStart
.
Собственно, всё. Решение работающее, проверено в железе, все функции драйвера работают, ни от чего не пришлось отказываться.
Важное дополнение:
Тщательное тестирование показало, что при таком использовании, микросхема работает нестабильно. Нет-нет, да вылазят всякие артефакты. Скрупулёзная проверка кода ничего не дала. Зато повторное, более внимательное, прочтение даташита дало результат.
Дело в том, что GSCLK у нас тактируется от контроллера его тактовой частотой, т.е. имеем восходящий фронт на каждом такте контроллера. Таким образом, любой другой сигнал у нас может подаваться только одновременно с тактовым сигналом на GSCLK и никак иначе. В то же время, даташит требует разносить по времени некоторые сигналы с GSCLK на 10-30 наносекунд:
Обойти это мы не можем никак. То, что GSCLK срабатывает не каждом такте, здесь принципиально.
Таким образом, вынужден констатировать, что идея провалилась. А жаль