Реализация протокола ws2812b на ATmega
Светодиоды ws2812b весьма интересная штука. О реализации протокола их работы я и хочу сейчас поведать. Как и в прошлой статье, код написан в среде IAR под микроконтроллер ATmega32 c 16МГц кварцем. Хочу сразу уточнить, что кварца менее 16МГц скорее всего не хватит, данный протокол рассчитан на весьма жесткие тайминги. Ноль выставляется временным интервалом 0.4 мкс, единица 0.8 мкс.
Первым делом напишем функции выставления 0 и 1 в линию. Количество nop«ов в функциях подобрано экспериментальным путем через логический анализатор. Скриншоты прилагаются немного ниже.
#define ClearOutBit PORTC &= ~(1<<1) //0 на выход #define SetOutBit PORTC |= 1<<1 //1 на выход
void Set0(void) //Выставляем в правую линию ноль ~0.4 мкс { SetOutBit; asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); ClearOutBit; //После этого временной интервал немного увеличен, в связи с выполнением циклов, но диоды сигнал ловят исправно }
void Set1(void) //Выставляем в правую линию единицу ~0.85 мкс { SetOutBit; asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); asm («nop»); ClearOutBit; //После этого временной интервал немного увеличен, в связи с выполнением циклов, но диоды сигнал ловят исправно } Далее у нас есть массив значений. Я создал 32 битный массив из 30 значений (да, понимаю, что это излишество, но мне показалось это решение допустимо). Для каждого светодиода нам необходимо загрузить 24 бита, причем первым выгружается в линию значение для зеленого цвета, далее красного и потом синего.
На выставление в линию значений для всех 30 светодиодов у контроллера уходит не более 4 мс, что достаточно быстро. Конечно, если увеличить тактовую частоту, можно выиграть немного времени между битами. В идеале, если все временные интервалы будут соответствовать, для 30 светодиодов загрузка будет длиться менее 1 мс.
unsigned long int mas[30]; //32 битный массив из 30 значений
void setMas (void) //выставление всего массива в линию { unsigned long int a; unsigned int j, i;
for (j=0; j<30; j++) { //количество светодиодов 30 a = 0x1000000; //первым выставляется G (Hi->Low), потом R и B for (i=0; i<24;i++){ //три байта G,R,B a=a>>1; if ((mas[j]&a)==0×00000000) { Set0(); //ноль } else { Set1(); //единица } } } } По факту получается, что выдерживаются только временные интервалы «high voltage time» 0.4 и 0.8 мкс, а вот «low voltage time» программой не контролируются — они получаются за счет работы микроконтроллера между выставлением значений в линию. Ему ведь надо время, чтобы проверить условие и прокрутить цикл. Но тем не менее, светодиоды ловят посылку исправно. И конечно это время не должно превышать 50 мкс, иначе светодиод воспримет это как Reset и загрузит полученное «неполное» значение себе на индикацию.
В конце статьи видео работы этих 30 светодиодов с круговым сдвигом исходного массива по таймеру и управлением через ИК пульт с NEC протоколом. Получилось весело.
[embedded content]