Управление светодиодной лентой

Поскольку я делаю новогодний проект "Цветомузыка", то мне нужны для нее цветные управляемые огни / лампы / светодиоды. Тут я вспомнил, что у нас есть светодиодная лента. Сперва нужно научиться управлять ею и я это сделал! На этом видео выше показано, как плата Марсоход3bis управляет светодиодами на ленте.
Описание этого маленького отдельного проекта читайте далее.

Светодиодная лента состоит из множества последовательно соединенных микросхем 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

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

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


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