XMEGA & WS2812B

 
    Для управления отдельным диодом необходимо передать ему 24 бита, содержащие информацию о цветовых компонентах RGB . Для этой цели диодный контроллер использует простой последовательный протокол, в котором биты со значениями 0 и 1 кодируются путем изменения ширины положительного импульса. Давайте посмотрим на заметку, как она выглядит:

    Как мы видим, длительность всего бита составляет 1250 нс ± 300 нс , бит 1 задается как положительный импульс с шириной 800 нс ± 150 нс с последующим отрицательным импульсом с шириной 450 нс ± 150 нс , а бит со значением 0 кодируется положительным импульсом ширина 400ns 150ns ± с последующим отрицательным импульсом шириной 850ns ± 150ns , как показано ниже:
    Сигналом для фиксации данных является сигнал RESET, который длится не менее 50 мкс :
    Это правда, что RESET связан с обнулением системы, но в случае с диодами WS2812B его имя несколько неудачно и не отражает его работу до конца. Он заставляет данные фиксироваться, в результате чего светодиод изменяет цвет и также является сигналом, который инициирует передачу новых данных.
    Каждый светодиод  имеет ввод данных ( DIN) и вывод данных ( DOUT ), которые мы соединяем с входом DIN следующего диода, создавая таким образом цепочку:

    Благодаря этому каждый диод получает 24 бита данных (по 8 бит для каждого цветового компонента ), после чего следующие данные просто пересылаются с входа DIN на выход DOT , откуда получает их другой диод и т. д.
    Таким образом, мы видим, что первые 24 бита будут удалены к первому диоду в цепи, еще 24 бита к другому диоду и т. д. Такой способ передачи данных имеет большое преимущество - каждый диод не только передает их, но также воспроизводит передаваемый сигнал, тем самым избегая его искажения. Поскольку каждый диод получает большой ток и меняет его состояние (цвет), ток изменяется поэтапно, с каждым диодом мы должны поставить конденсатор со значением 10-100нФ- обычно изготовляют ленточные производители, содержащие эти диоды, благодаря чему мы можем использовать ленты непосредственно в строящейся системе.

    Как мы видим, данные отправляются довольно необычно в формате GRB , а не RGB , но передача начинается с наиболее значимого бита ( MSB ), определяющего зеленый цвет. Трудно сказать, почему именно производитель решил так, но к счастью, это не большая проблема.
    Для процессора с тактовой частотой 4 МГц один цикл длится до 250 нс , следовательно, время, равное примерно 1200 нс или длительность одного бита, составляет всего 5 ассемблерных инструкций (при условии, что каждый выполняется в одной мере). Для более высоких тактовых частот ситуация выглядит немного лучше, но для поддержания допустимого значения времени в диапазоне ± 150 нс требуется тактовая частота микроконтроллера не менее 8 МГц и ... возможно, ассемблер.
    К счастью, это не так уж плохо, продолжительность бит может быть увеличена незначительно (но ее не следует сокращать ниже 1100ns ), также можно немного изменить длительность высоких фронтов - в частности, мы можем значительно сократить длительность положительного импульса при отправке бита значения 0. мы можем легко генерировать соответствующие времена с помощью команд задержки в C.
    В нашей программе мы определяем вывод, с которым будет выполняться передача:
#define WS2812B_PORT PORTB // Порт, по которому отправляются данные
#define WS2812B_PIN (1 << 0) // Нет использованного булавки
Начнем с написания функции, которая отправляет бит 1:
void  WS2812B_sendOne ()  
{  
  WS2812B_PORT.OUTSET = WS2812B_PIN;  // Установите контрольный вывод  
  _delay_loop_2 (8 * F_CPU / 10000000UL / 3);  
  WS2812B_PORT.OUTCLR = WS2812B_PIN;  // Сброс контрольного вывода  
  _delay_loop_1 (1);  
}  

   Функция, которая отправляет бит со значением 0, выглядит аналогично:
void WS2812B_sendZero () 
{ 
  WS2812B_PORT.OUTSET = WS2812B_PIN; // Установите контрольный вывод 
  asm volatile ( "nop" ); asm volatile ( "nop" ); 
  asm volatile ( "nop" ); asm volatile ( "nop" ); 
  WS2812B_PORT.OUTCLR = WS2812B_PIN; // Сброс контрольного вывода 
  _delay_loop_2 (8 * F_CPU / 10000000UL / 3); 
} 
    Имея две вышеуказанные функции, мы легко напишем функцию, которая отправляет байты:
void  WS2812B_send (uint8_t byte)  
{  
 uint8_t cnt = 8;  
 while (cnt--)  
  {  
  if (byte & 0x80) WS2812B_sendOne ();  
     else  WS2812B_sendZero ();  
     << byte= 1;  
  }  
}  
    Как мы видим, в зависимости от состояния самого старого бита (помните, что WS2812B ждет бит, начиная с самого старшего), вызывается функция, которая отправляет бит 0 или 1, а затем все биты сдвигаются на одну позицию вправо.

    Нам нужна еще одна функция - она ​​генерирует сигнал защелки данных и сброс по связи:
void  WS2812B_reset ()  
{  
  WS2812B_PORT.OUTCLR = WS2812B_PIN;  // Сброс контрольного вывода  
  _delay_us (50);  
} 
    Мы забыли про один очень важный вопрос - инициализацию выводов, которые мы будем использовать для связи:
void  WS2812B_init ()  
{  
  WS2812B_PORT.OUTCLR = WS2812B_PIN;  // Сброс контрольного вывода  
  WS2812B_PORT.DIRSET = WS2812B_PIN;  // И изменим его на вывод  
} 
    Как известно, в XMEGA у нас есть специальные регистры OUTCLR - установка одного из 8 бит в нем сбрасывает соответствующие выводы IO, как и в регистре DIRSET - установка бит приводит к тому, что бит соответствующего регистра DIR устанавливается
    Таким образом, мы можем изменить состояние вывода IO и его направления, не влияя на другие контакты порта. Стоит отметить, что для других моделей AVR для этой цели обычно требуется операция чтения-изменения-записи, которая длиннее и даже хуже - она ​​не выполняется атомным способом.
    Не забудьте про настройку тактовой частоты синхронизации XMEGA, для этой цели с помощью PLL будет умножать базовые часы с частотой 2 МГц в 16 раз , что даст нам часы с частотой 32 МГц , а затем заставит микроконтроллер использовать его:
    Теперь мы будем инициировать вывод ввода-вывода, используемый для связи, и сбросить шину:
WS2812B_init ();
WS2812B_reset ();
Пример:
uint8_t cnt = LEDNO; 
uint8_t offset = 0; 
uint8_t delta = 256 / LEDNO; 
while (1) 
{ 
  WS2812B_reset (); 
  while (cnt--) 
  { 
   WS2812B_send (CNT * offset + delta); // Отправить компонент G 
   WS2812B_send (0); // Отправить компонент R 
   WS2812B_send (0); // Отправить компонент B 
  } 
  offset + = delta; 
 _delay_ms (10); 
} 

    Blogger Comment
    Facebook Comment

0 коммент.:

Отправить комментарий