Светодиодная лента состоит из множества последовательно соединенных микросхем WS2812B. Каждая такая микросхема представляет собой intelligent control LED light source - то есть интеллектуально управляемый светодиодный источник света, в моем вольном переводе. Каждая такая микросхема не только управляемый контроллер, но и три светодиода: красный, зеленый, синий. Каждую микросхему в ленте можно индивидуально зажечь в свой собственный цвет, комбинируя три базовых цвета.

Описание чипа можно взять здесь:

Там ничего сложного. Микросхемы соединены цепочкой всего одним проводом, выход одной микросхемы на вход другой, ее выход на вход следующей, и так далее:

intellectual leds control chip WS2812B

Кодирование сигнала простое: интервал времени "бит" условно можно разделить на три равные части, первая часть всегда единица, третья часть - всегда ноль. Часть по середине - это и есть передаваемый бит. Каждый пиксель передается 24-мя битами ( 8R-8G-8B ), каждый бит по три части. Пиксели передаются один за другим без интервалов.

Когда передано достаточное количество пикселей (в моей в ленте 300 микросхем), то чтобы переданные пиксели загорелись в те цвета, которые им были переданны нужно передать еще небольшую паузу. В смысле передать "паузу" - это значит ничего не передавать некоторое время.

Тактовая частота на ленту 2,4МГц. Соответственно один бит передается 1,25 микросекунды. Пауза по активации пикселей 50 микросекунд или больше.

Сделаю такой Verilog модуль:


`timescale 1ns / 1ps

module LED_tape(
  input wire clk,
  input wire [23:0] RGB,
  output reg data,
  output wire[15:0]num,
  output reg sync,
  output reg req
);

parameter NUM_LEDS = 8;
parameter NUM_RESET_LEDS = 2;
localparam NUM_TOTAL = (NUM_LEDS+NUM_RESET_LEDS);

//3 tick counter
reg [1:0] cnt3 = 2'b0;
always @(posedge clk)
  if (cnt3 == 2'b10)
    cnt3 <= 2'b0;
  else
    cnt3 <= cnt3+1;

//24 tick counter
reg [4:0] cnt24 = 5'b0;
always @(posedge clk)
  if (cnt3 == 2'b10)
    cnt24 <= ( cnt24 == 23 ) ? 5'b0 : cnt24 +1'b1;

reg _req = 1'b0;
reg req_ = 1'b0;
always @(posedge clk)
  begin
    _req <= (cnt3 == 2'b10) & (cnt24 == 22);
    req <= _req;
    req_ <= req;
  end


reg [15:0]cntN = 0;
always @(posedge clk)
  if(_req)
  begin
    if( cntN==(NUM_TOTAL) )
      cntN <= 0;
    else
      cntN <= cntN + 1;
  end

assign num = cntN;

reg [23:0]shift_r24;
always @(posedge clk)
  if(req_)
    shift_r24 <= RGB;
  else
  if( cnt3==2'b10 )
    shift_r24 <= { 1'b0, shift_r24[23:1] };

wire hide; assign hide = (cntN>NUM_LEDS & cntN<=(NUM_TOTAL));
always @(posedge clk)
  sync <= hide;

always @(posedge clk)
  if( hide )
    data <= 1'b0;
  else
    data <= (cnt3==2'b00) ? 1'b1 :
            (cnt3==2'b01) ? shift_r24[0] : 1'b0;

endmodule


В этом модуле два входных сигнала: clk - это тактовая частота, 24 бита RGB - это цвет пикселя, который будет передаваться. Я предлагаю, чтобы модуль выдавал выходной сигнал [15:0]num - это порядковый номер пикселя, который будет передаваться следующим. Выходной сигнал req - запрос на смену пикселя. Таким образом, внешний модуль получает информацию о позиции текущего выводимого пикселя и сигнал, когда требуется смена пикселя на следующий.

Модуль параметризуемый. Можно задавать, сколько светодиодов будут управляться NUM_LEDS и какова будет длительность сигнала reset NUM_RESET_LEDS. Название reset мне не очень нравится, так как обычно это что-то другое, но в документации на микросхему написано именно так. Reset - это пауза между передачами бит пикселей, и эта пауза активирует цвет переданный на пиксели.

Если хотите посмотреть, как может работать этот модуль в симуляторе Verilog, то вот вам тестбенч:


 `timescale 1ns / 1ns

module LED_tape_TB;

reg clk = 0;
always
  begin
    #5;
    clk = ~clk;
  end

wire data;
wire [15:0] w_num;
wire w_req;
wire w_sync;

reg [23:0]rgb = 0;
always @(posedge clk )
  if( w_req )
    rgb <= w_sync ? 0 : { {8{w_num[2]}}, {8{w_num[1]}}, {8{w_num[0]}} };

// Instantiate the Unit Under Test (UUT)
LED_tape #( .NUM_LEDS(7), .NUM_RESET_LEDS(4) )uut (
  .clk(clk),
  .RGB(rgb),
  .data(data),
  .num(w_num),
  .sync(w_sync),
  .req(w_req)
  );

initial
begin
  $dumpfile("out.vcd");
  $dumpvars(0,LED_tape_TB);
  #1000000;
  $finish(0);
end

endmodule


Тестбенч смотрит запрашиваемый номер пикселя, его младшие три бита и на их основании формирует цвета. Результат работы модуля в симуляторе - передача последовательно только восьми пикселей: черный, зеленый, красный, желтый, синий, бирюзовый, фиолетовый, белый.

Вот диаграммы из GtkWave, передача нескольких пикселей и "пауза", активирующая пиксели на ленте:

GtkWave signal diagram LED control

Вот передача одного пикселя:

GtkWave signal diagram LED control

Счетчик cnt3 считает от нуля до 2х - это фазы передачи одного бита. Счетчик cnt24 - номер передаваемого бита пикселя. Счетчик cntN - номер передаваемого пикселя. Сигнал req - запрос внешнему модулю на смену пикселя, RGB - новый пиксель, который нужно передать в ленту.

Этот же модуль Verilog  управления светодиодной лентой я хочу вставить в проект цветомузыки, только там уже цвета будут задаваться согласно спектра сигнала и я надеюсь в такт музыке.

Чтобы испытать модуль в реальной ПЛИС платы Марсоход3bis я сделал отдельный проект в САПР Intel Quartus Prime. Топ модуль этого проекта выглядит вот так:

top module in Intel Quartus Prime

Весь проект можно взять на GitHub: https://github.com/marsohod4you/Led-control-WS2812B

Ну а демонстрация его работы - на видео в начале статьи. 

Следующий этап - это вставить модуль управления светодиодной лентой в проект цветомузыки.

Добавить комментарий