Как работает адресная светодиодная лента?
Наверное этот вопрос «как работает» очень многим покажется глупым. Ответ почти очевиден: адресная светодиодная лента состоит из множества последовательно соединенных «умных светодиодов». Это можно увидеть просто рассматривая устройство ленты. Видны отдельные микросхемы, припаянные к гибкому шлейфу, видны соединения: микросхемы соединены последовательно всего тремя проводами, при этом два из них это питание и земля. Только один провод передает данные о цвете пикселей. Как же это? Что такое «умный светодиод»?
Дальше я расскажу о протоколе передачи данных, используемом в светодиодной ленте на базе WS2812B, и, более того, я почти создам свою «микросхему светодиодной ленты» в микросхеме ПЛИС.
Итак, в ленте используется последовательная передача через один единственный сигнал данных.
Бит ноль передается, как короткий положительный импульс и пауза, которая примерно в два раза шире импульса. Бит единица передается как широкий положительный импульс и короткая пауза:
При отсутствии передачи более 50 микросекунд лента переходит в исходное состояние, готова принимать пиксели начиная с первого.
Каждые 24 бита в последовательности — это 3 байта для трех цветов RGB. Причем на самом деле последовательность будет G-R-B. Старший бит G7 идет первым.
Последовательность из первых 24х бит представляет из себя один пиксель, который получит самый первый светодиод в ленте. Пока первый светодиод не насытится он не передает данные дальше к следующему светодиоду. После того, как первый светодиод получит свою порцию из 24х бит RGB он открывает передачу следующему. Примитивно можно последовательность светодиодов представить, как каскад из кувшинов, последовательно наполняемых водой:
Заполнится первый, потом второй, потом третий и так все по очереди.
Таким образом, я считаю, что с протоколом передачи разобрались.
Можно ли попробовать самому спроектировать такой «умный светодиод»? Практического смысла в этом конечно мало, но для самообразования и расширения кругозора — задача интересная. Попробуем описать логику чипа на языке проектирования аппраратуры Verilog HDL. Конечно, это будет не настоящий дизайн микросхемы, будут ограничения. Одно из самых важных ограничений — мне для моей микросхемы будет нужен внешний тактовый генератор. В настоящем умном светодиоде такой генератор тоже есть, но он встроен уже в чип.
Модуль на Verilog начнем вот так:
module WS2812B(
input wire clk,
input wire in,
output wire out,
output reg r,
output reg g,
output reg b
);
Здесь думаю все понятно: тактовая частота clk, входной и выходной сигналы «умного светодиода» in и out, ну и, конечно, выходные сигналы r, g, b через которые я буду управлять реальными внешними светодиодами красным, зеленым и синим.
Входной сигнал я буду захватывать в двухббитный сдвиговый регистр и по текущему состоянию в этих захваченных битах смогу определить начало положительного фронта сигнала in:
reg [1:0]r_in = 0;
always @( posedge clk )
r_in <= { r_in[0],in };
wire in_pos_edge; assign in_pos_edge = (r_in==2'b01);
Кроме этого, важно определить состояние сброса ленты, когда управляющий контроллер выдерживает паузу перед началом новой передачи:
localparam reset_level = 3000;
reg [15:0]reset_counter = 0;
always @( posedge clk )
if( r_in[0] )
reset_counter <= 0;
else
if( reset_counter<reset_level )
reset_counter <= reset_counter+1;
wire reset; assign reset = (reset_counter==reset_level);
Дальше, от положительного фронта in_pos_edge нужно выдержать некоторую паузу, чтобы получить момент фиксации нового бита:
localparam fix_level = 50;
reg [7:0]bit_length_cnt;
always @( posedge clk )
if( in_pos_edge )
bit_length_cnt <= 0;
else
if( bit_length_cnt<(fix_level+1) && !pass )
bit_length_cnt <= bit_length_cnt + 1;
wire bit_fix; assign bit_fix = (bit_length_cnt==fix_level);
Количество уже принятых бит в чипе считаем так:
reg pass = 0;
reg [5:0]bits_captured = 0;
always @( posedge clk )
if( reset )
bits_captured <= 1'b0;
else
if( ~pass && bit_fix )
bits_captured <= bits_captured+1'b1;
Здесь вводится еще важный сигнал pass, который как раз и определяет перенаправление входного потока на выход. После принятия 24х бит пикселя сигнал pass устанавливается в единицу:
always @( posedge clk )
if( reset )
pass <= 1'b0;
else
if( bits_captured==23 && bit_fix )
pass <= 1'b1;
reg pass_final;
always @( posedge clk )
if( reset )
pass_final <= 1'b0;
else
if( r_in!=2'b11 )
pass_final <= pass;
assign out = pass_final ? in : 1'b0;
На выход out мультиплексируются входные данные, когда сигнал pass_final в единице.
Ну и, конечно, нужен сдвиговый регистр, где накапливаются принятые 24 бита пикселя:
reg [23:0]shift_rgb;
always @( posedge clk )
if( bit_fix )
shift_rgb <= { in, shift_rgb[23:1] };
reg [23:0]fix_rgb;
always @( posedge clk )
if( bits_captured==23 && bit_fix )
fix_rgb <= { in, shift_rgb[23:1] };
По приему всех 24х бит они переписываются в итоговый так же 24х битный регистр.
Теперь остается дело за малым. Нужно реализовать ШИМ (Широтно Импульсную Модуляцию) сигнала для передачи яркости реальным внешним светодиодам согласно принятым байтам RGB:
wire [7:0]wgreen; assign wgreen = { fix_rgb[0 ], fix_rgb[1 ], fix_rgb[2 ], fix_rgb[3 ], fix_rgb[4 ], fix_rgb[5 ], fix_rgb[6 ], fix_rgb[7 ] };
wire [7:0]wred; assign wred = { fix_rgb[8 ], fix_rgb[9 ], fix_rgb[10], fix_rgb[11], fix_rgb[12], fix_rgb[13], fix_rgb[14], fix_rgb[15] };
wire [7:0]wblue; assign wblue = { fix_rgb[16], fix_rgb[17], fix_rgb[18], fix_rgb[19], fix_rgb[20], fix_rgb[21], fix_rgb[22], fix_rgb[23] };
reg [7:0]pwm_cnt;
always @( posedge clk )
begin
pwm_cnt <= pwm_cnt+1;
r <= pwm_cnt<wred;
g <= pwm_cnt<wgreen;
b <= pwm_cnt<wblue;
end
Вот кажется и все.
Остается маленькая деталь — как это все испытать?
Я взял несколько простых плат с ПЛИС MAX II (это платы серии Марсоход) и прошил их все проектом с вот этим Verilog кодом. На платах уже было 8 светодиодов, но они были все желтые. На каждой из плат я заменил 3 светодиода на R, G, B. Платы соединил последовательно и более того подключил их к настоящей светодиодной ленте. Таким образом, я удлинил настоящую ленту своими самодельными светодиодами.
Получилось вот такое соединение:
В реальности это выглядит вот так:
Теперь, подавая на ленту некоторое изображение я вижу, что мои «умные светодиоды» ведут себе точно так же, как и настоящие из ленты:
Получается, что реализованная мною в ПЛИС логика вполне работоспособна! Я смог в первом приближении сделать нечто похожее на реальный чип «умного светодиода».
Вообще, мне нравятся светодиодные ленты. На их основе каждый может изобрести что-то свое: интеллектуальное освещение, экраны, амбилайт эффекты. Однажды я даже реализовал цветомузыку на светодионой ленте под управлением FPGA. Но это уже другая история.