Как работает адресная светодиодная лента?

vo6rd2jupdaxgzun1zl3_iyp-k4.png
Наверное этот вопрос «как работает» очень многим покажется глупым. Ответ почти очевиден: адресная светодиодная лента состоит из множества последовательно соединенных «умных светодиодов». Это можно увидеть просто рассматривая устройство ленты. Видны отдельные микросхемы, припаянные к гибкому шлейфу, видны соединения: микросхемы соединены последовательно всего тремя проводами, при этом два из них это питание и земля. Только один провод передает данные о цвете пикселей. Как же это? Что такое «умный светодиод»?

Дальше я расскажу о протоколе передачи данных, используемом в светодиодной ленте на базе WS2812B, и, более того, я почти создам свою «микросхему светодиодной ленты» в микросхеме ПЛИС.
Итак, в ленте используется последовательная передача через один единственный сигнал данных.

Бит ноль передается, как короткий положительный импульс и пауза, которая примерно в два раза шире импульса. Бит единица передается как широкий положительный импульс и короткая пауза:

6ne9d-mfj_ah8zurj1i6fnrevgy.png

При отсутствии передачи более 50 микросекунд лента переходит в исходное состояние, готова принимать пиксели начиная с первого.

Каждые 24 бита в последовательности — это 3 байта для трех цветов RGB. Причем на самом деле последовательность будет G-R-B. Старший бит G7 идет первым.

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

pv8drz6goxvaujnutaw38e0tvbi.png

Заполнится первый, потом второй, потом третий и так все по очереди.

Таким образом, я считаю, что с протоколом передачи разобрались.

Можно ли попробовать самому спроектировать такой «умный светодиод»? Практического смысла в этом конечно мало, но для самообразования и расширения кругозора — задача интересная. Попробуем описать логику чипа на языке проектирования аппраратуры 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. Платы соединил последовательно и более того подключил их к настоящей светодиодной ленте. Таким образом, я удлинил настоящую ленту своими самодельными светодиодами.

Получилось вот такое соединение:

-u5kshz_kj4zwuj1uewj7wstrdk.png

В реальности это выглядит вот так:

v1rmqtb1_pbswak3fy8ewtl0-k0.jpeg

Теперь, подавая на ленту некоторое изображение я вижу, что мои «умные светодиоды» ведут себе точно так же, как и настоящие из ленты:


Получается, что реализованная мною в ПЛИС логика вполне работоспособна! Я смог в первом приближении сделать нечто похожее на реальный чип «умного светодиода».

Вообще, мне нравятся светодиодные ленты. На их основе каждый может изобрести что-то свое: интеллектуальное освещение, экраны, амбилайт эффекты. Однажды я даже реализовал цветомузыку на светодионой ленте под управлением FPGA. Но это уже другая история.

© Habrahabr.ru