МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus II для платы Марсоход

Реверс инжиниринг микросхемы RGB светодиода WS2812B

Приближается Новый Год! Мы поздравляем читателей нашего сайта с этим праздником! НО, сегодня нам нужен Новогодний FPGA Проект. У нас уже чего только не было на сайте: и двигалось и светило и моргало и показывало. Но, что поделать, самое новогоднее, что можно придумать - это гирлянды, бегущие огни, светодиодная лента. Их тоже мы уже подключали: был проект простого управления светодиодной лентой и мы ее устанавливали в виде импровизированной елочки и потом мы даже сделали цветомузыку на светодиодной ленте.

Теперь пришла идея сделать.. саму светодиодную ленту. Простая светодиодная лента состоит из последовательно соединенных RGB микросхем, например, вот таких: WS2812B. Соединяются они тремя проводами: питание, земля и информационный сигнал, по которому передается код цвета для каждого пикселя индивидуально.

Идея проекта состоит в следующем. Я попытаюсь сделать "реверс инжиниринг" микросхемы WS2812B и реализовать всю ее логику внутри простой ПЛИС платы Марсоход с Altera MAX II. Потом я соединяю последовательно несколько плат Марсоход и подключаю их прямо к светодиодной ленте, таким образом, делаю ленту длиннее на несколько пикселей. Мои ПЛИСы становятся продолжением ленты и светодиоды, подключенные к ПЛИС, должны светиться точно так же, как сама лента. Вообще-то платы Марсоход пришлось немного модернизировать: у них штатно стоят 8 желтых светодиодов, а мы заменяем 3 из них на цветные R, G, B. Это не очень большая переделка.

Наверное кто-то скажет, что это бессмысленный проект, зачем делать то, что уже кем-то сделано. Но с другой стороны - а почему бы и нет? У нас же образовательный сайт. Вот поучимся копировать логику существующих микросхем в FPGA, благо логика очень простая. Ну и в конце концов, может какой нибудь светодиодный завод в России захочет производить подобные чипы где нибудь в Зеленограде? Мне даже кажется, что в этом есть какой-то смысл: для изготовления светодиодных лент и видео стен требуются сотни, тысячи и миллионы таких микросхем... Для одного FullHD светодиодного экрана, если бы его изготавливали из светодиодной ленты, нужно 2 миллиона RGB микросхем. Оптовое изготовление всегда снижает итоговую стоимость производства.

Конечно я немного упрощаю. Микросхема WS2812B возможно не такая простая, как я тут расписываю. В ней есть встроенный генератор, видимо RC-генератор, и он должен быть весьма стабильным вне зависимости от внешней температуры. Но я про это сейчас даже думать не буду. Мой проект немного упрощенный, я буду эмулировать микросхему WS2812B в нашей простой плате Марсоход и на плате есть кварцевый генератор 100МГц. Его и буду использовать для измерения интервалов времени.

Итак, поехали.

Описание микросхемы WS2812B можно взять здесь:


Микросхема работает довольно просто. Микросхемы в ленте соединены цепочкой и биты передаются последовательным кодом. Биты ноль в потоке - это узкие импульсы, а биты единицы в потоке - широкие.

WS2812B

Алгоритм работы может быть такой:

  1. Находим перепад с нуля на единицу.
  2. Измеряем временной интервал в половину длины бита, смотрим, что в этот момент зафиксировалось - ноль, значит приняли ноль, а единица - значит приняли единицу.
  3. Принятый бит задвигается в приемный сдвиговый регистр.
  4. Каждый раз при фиксации нового принятого бита увеличиваем счетчик принятых бит.
  5. Как только принято 24 бита в этом чипе WS2812B прием здесь завершается и входной битовый поток переключается на вывод к следующей микросхеме в цепочке. До этого она не получала никакого кода, на выходе предыдущего чипа был ноль.
  6. Если линия находится в нуле достаточно долго, то из сдвигового регистра значения яркостей RGB переписываются в регистр ШИМ, из него формируются Широтно-Импульсно Модулированные сигналы для светодиодов R-G-B.

Код Verilog HDL, который описывает всю эту логику может выглядеть вот так:

`timescale 1ns / 1ns

module WS2812B(
	input wire clk,
	input wire in,
	output wire out,
	output wire [23:0]q,
	output reg r,
	output reg g,
	output reg b
);

localparam reset_level = 3000;
localparam fix_level   = 50;

//reg clk = 0;
//always #26 clk = ~clk;

//capture "in" signal into shift register
reg [1:0]r_in = 0;
always @( posedge clk )
	r_in <= { r_in[0],in };

//detect zero-to-one transition in "in" signal via shift register
wire in_pos_edge; assign in_pos_edge = (r_in==2'b01);

//count how long "in" signal stays in zero
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;

//if "in" signal stays in zero for long time -> reset condition
wire reset; assign reset = (reset_counter==reset_level);

//every zero-to-one signal "in" transition mean start of new bit
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;

//get impulse of bit capture
wire bit_fix; assign bit_fix = (bit_length_cnt==fix_level);

reg pass = 0;
reg [5:0]bits_captured = 0;

//count number of bits captured
always @( posedge clk )
	if( reset )
		bits_captured <= 1'b0;
	else
	if( ~pass && bit_fix )
		bits_captured <= bits_captured+1'b1;

//after capturing 24 bits this chip is locked and pass input to output
always @( posedge clk )
	if( reset )
		pass <= 1'b0;
	else
	if( bits_captured==23 && bit_fix )
		pass <= 1'b1;
		
//actual pass after last bit receive (falling edge)
reg pass_final;
always @( posedge clk )
	if( reset )
		pass_final <= 1'b0;
	else
	if( r_in!=2'b11 )
		pass_final <= pass;
	
//accumulating shift register for RGB bits
reg [23:0]shift_rgb;
always @( posedge clk )
	if( bit_fix )
		shift_rgb <= { in, shift_rgb[23:1] };

//final capture register for RGB bits
reg [23:0]fix_rgb;
always @( posedge clk )
	if( bits_captured==23 && bit_fix )
		fix_rgb <= { in, shift_rgb[23:1] };

//when this chip captured 24 bits of RGB it becomes transparent
//and passes all input to output without change
assign out = pass_final ? in : 1'b0;

assign q = fix_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] };

//pulse-width-modulation
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

endmodule

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

Чтобы было более понятно, я приведу фото соединений плат поближе:

fpga marsohod boards chain

А вот схематичное изображение соединений:

schema

Пин F5 платы марсоход служит входом для последовательных данных, а пин F2 - выход данных.
Вот они и соединены цепочкой [LED TAPE]->[F5,F2]->[F5, F2]->[F5, F2]->..

Несмотря на то, что на моей ленте питание +5В, я не стал подключать платы прямо к этому питанию. Максимальное напряжение для ПЛИС MAX II всего +4,6В, поэтому я питаю всю цепочку плат одним дополнительным источником, аккумулятором. У аккумулятора и ленты сигналы "Земля" соединены вместе.

При желании можно просимулировать всю цепочку "чипов". Для этого нужен Verilog HDL тестбенч:

`timescale 1ns / 1ns

module LED_tape_TB;

// Inputs
reg clk = 0;
always
	begin
		#206;
		clk = ~clk;
	end

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

reg [1:0]r_sync =0;
always @(posedge clk )
	r_sync <= { r_sync[0], w_sync };

reg [7:0]color = 8'h31;
always @(posedge clk )
	if( r_sync==2'b01 )
		color<=color+1;
		
reg [23:0]rgb = 0;
always @(posedge clk )
	if( w_req )
		rgb <= { 8'hA5, 8'hB6, color };

// Instantiate the LED TAPE control Unit
LED_tape #( .NUM_LEDS(7), .NUM_RESET_LEDS(10) )uut (
	.clk(clk), 
	.RGB(rgb), 
	.data(data), 
	.num(w_num), 
	.sync(w_sync),
	.req(w_req)
);

reg clk100 = 0;
always
	begin
		#5;
		clk100 = ~clk100;
	end
	
wire out0;
wire [23:0]q0;
WS2812B WS2812B_0(
	.clk( clk100 ),
	.in( data ),
	.out( out0 ),
	.q( q0 ),
	.r(),
	.g(),
	.b()
);

wire out1;
wire [23:0]q1;
WS2812B WS2812B_1(
	.clk( clk100 ),
	.in( out0 ),
	.out( out1 ),
	.q( q1 ),
	.r(),
	.g(),
	.b()
);

wire out2;
wire [23:0]q2;
WS2812B WS2812B_2(
	.clk( clk100 ),
	.in( out1 ),
	.out( out2 ),
	.q( q2 ),
	.r(),
	.g(),
	.b()
);


wire out3;
wire [23:0]q3;
WS2812B WS2812B_3(
	.clk( clk100 ),
	.in( out2 ),
	.out( out3 ),
	.q( q3 ),
	.r(),
	.g(),
	.b()
);

initial begin
	$dumpfile("out.vcd");
	$dumpvars(0,LED_tape_TB);

	#2000000;
	$finish(0);
end

endmodule

Здесь в тестбенче установлено несколько экземпляров WS2812B_0, WS2812B_1, WS2812B_2, WS2812B_3. Выход первого чипа out0 служит входом для второго, из второго выходит out1 и заходит в третий. Так и получается цепочка. Запустить симулятор Icarus Verilog можно следующими командами:

>iverilog -o qqq WS2812B.v LED_tape_TB.v LED_tape.v
>vvp qqq
>gtkwave out.vcd

На временных диаграммах сигналов можно посмотреть, как происходит передача данных от чипа к чипу:

gtkwave

Исходные данные data поначалу видит только первый в цепочке "чип" WS2812B_0. Остальные видят на входных линиях только ноль. Как только первый чип выберет из входного потока свои положенные 24 R-G-B бита у него внутри взводится регистр pass_final и входные данные перенаправляются на выход. Теперь уже второй "чип" WS2812B_1 видит поток и он может выбрать из него свои собственные 24 RGB бита. Теперь уже второй чип "насытился", у него устанавливается внутренний pass_final регистр и он передает эстафету следующему.

Когда на линии длинная пауза все чипы это видят, защелкивают принятые данные в регистры для ШИМ и возвращаются в исходное состояние. Опять первый чип готов выбирать свои 24 бита. Так и работает настоящая лента.

Видео демонстрация в начале статьи показывает, как все происходит. Задействована плата Марсоход3 в качестве источника сигнала для светодиодной ленты. FPGA проект в плате Марсоход3 сперва зажигает (с мягким нарастанием и затуханием) все синие светодиоды, потом все красные, потом все зеленые, ну и затем чередует цвета. К ленте подключено последовательно еще 4 платы Марсоход, которые являются прототипами микросхемы WS2812B. Видно, что светодиоды моих плат работают так же как светодиоды ленты. Таким образом мне удалось воссоздать логику работы микросхемы WS2812B в простой ПЛИС Altera MAX II платы Марсоход.

Весь проект Intel Quartus Prime можно взять у нас на странице github: https://github.com/marsohod4you/WS2812B-FPGA-Emulation-

Там есть папка max10-led-tape-control  - проект Quartus Prime для платы Марсоход3 (чип MAX10), как источника сигнала для светодиодной ленты, так и проект Quartus для CPLD MAX II платы Марсоход в папке max2.

 

Комментарии  

0 #2 Userfriendly 20.01.2020 12:04
Конденсаторы и резисторы по планарной технологии имеют очень сильный разброс. Скорее всего при определении значения бита использовали счетчик, при единице считали вверх, при нуле - вниз с игнорированием переполнения. Если больше нуля значит 1, нет - 0. Глубину счетчика с запасом от известного разброса частот на RC генераторе.
0 #1 2N7002 11.01.2020 14:10
Офигенно, "отэмулируйте" еще микросхемы, например DS18B20, часто нужно для проверки своих самодельных устройств. И покажите как их сделать сразу много в одной микросхеме ПЛИС. Еще может быть TTP223, сенсоров, эмуляция микросхем памяти I2C и SPIFLASH в RAM, RFID, микросхемы часов и т.д.

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



ВКонтакте  facebook  GitHub  YouTube  Twitter
Вы здесь: Начало Проекты Проект Марсоход Реверс инжиниринг микросхемы RGB светодиода WS2812B