Skocz do zawartości

Sterowanie diodami RGB - WS2812B


Elvis

Pomocna odpowiedź

Jakiś czas temu na forum pojawiła się dyskucja o obsłudze inteligentnych diod WS2812B - problem który był omawiany dotyczył zakłóceń związanych z obsługą przerwań.

Diody WS2812B wymagają bardzo ścisłego przestrzegania czasów, więc obsługa przerwania podczas komunikacji powoduje błędy. Natomiast wyłączanie przerwań powoduje czasem problemy z innymi modułami.

Jest to ciekawy przykład, aby zastosować FPGA. Dzięki temu sterowanie może odbywać się sprzętowo i bez problemu zapewniać odpowiednie przebiegi czasowe.

Na szybko napisałem następujący program, który chociaż nie jest piękny, ale działa na Maximatorze (płytka rozszerzeń ma 2 diody WS2812B na pokładzie):

module top(
input wire clk,
input wire rst,
output reg rgb_led
);

parameter [1:0] IDLE = 2'b00;
parameter [1:0] RESET = 2'b01;
parameter [1:0] DATA = 2'b10;

reg [1:0] state;
reg [15:0] counter;
reg [47:0] led_value;
reg [5:0] bit_counter;

always @(posedge clk)
begin
if (rst == 0) begin
	state <= IDLE;
	counter <= 0;
	// LED 1
	led_value[7:0] = 8'he0; 	// green
	led_value[15:8] = 8'h00; 	// red
	led_value[23:16] = 8'h00; 	// blue
	// LED 2
	led_value[31:24] = 8'h00; 	// green
	led_value[39:32] = 8'hf0; 	// red
	led_value[47:40] = 8'h00; 	// blue
end else begin
	case (state)
	IDLE:
		if (counter < 10000)
			counter <= counter + 1;
		else begin
			state <= RESET;
			counter <= 0;
		end
	RESET:
		if (counter < 500)
			counter <= counter + 1;
		else begin
			state <= DATA;
			counter <= 0;
			bit_counter <= 0;
		end
	DATA:
		if (counter < 12) begin
			counter <= counter + 1;
		end else begin
			counter <= 0;
			if (bit_counter < 47)
				bit_counter <= bit_counter + 1;					
			else
				state <= IDLE;
		end
	default:
		state <= IDLE;
	endcase
end
end

always @(*)
begin
rgb_led = 1;
case (state)
RESET:
	rgb_led = 0;
DATA:
	if (led_value[bit_counter])
	begin
		if (counter >= 8)
			rgb_led = 0;
	end else begin
		if (counter >= 4)
			rgb_led = 0;
	end
endcase
end

endmodule

Teraz planuję dodać rdzeń Nios2 i spróbować wysterować diody z programu. Jeśli coś z tego wyjdzie, postaram się opisać wyniki 🙂

[ Dodano: 12-05-2018, 22:10 ]

Update:

Przerobiłem poprzednio opisywany program, tak żeby można go było zastosować jako komponent. Wybrałem magistralę Avalon - czyli wynalazek Altery, ale łatwiejszy niż ARM-owe AXI.

Kod komponentu jest brzydki, ale całość robiłem "na kolanie" czekając na samolot...

`timescale 1 ps / 1 ps
module ws2812b (
	input  wire        clk_clk,           //    clk.clk
	input  wire        reset_reset,       //  reset.reset
	input  wire [3:0]  avalon_address,    // avalon.address
	input  wire        avalon_read,       //       .read
	output wire [31:0] avalon_readdata,   //       .readdata
	input  wire        avalon_write,      //       .write
	input  wire [31:0] avalon_writedata,  //       .writedata
	input  wire        avalon_chipselect, //       .chipselect
	input  wire [3:0]  avalon_byteenable, //       .byteenable
	output wire        data               //   pins.new_signal
);

assign avalon_readdata = 32'b00000000000000000000000000000000;


parameter [1:0] IDLE = 2'b00;
parameter [1:0] RESET = 2'b01;
parameter [1:0] DATA = 2'b10;

reg rgb_led;
reg [1:0] state;
reg [15:0] counter;
reg [47:0] led_value;
reg [5:0] bit_counter;

always @(posedge clk_clk)
begin
if (avalon_chipselect & avalon_write)
begin
	if (avalon_address == 4'b0000)
	begin
		if (avalon_byteenable[0])
			led_value[7:0] = avalon_writedata[7:0];
		if (avalon_byteenable[1])
			led_value[15:8] = avalon_writedata[15:8];
		if (avalon_byteenable[2])
			led_value[23:16] = avalon_writedata[23:16];
	end else begin
		if (avalon_byteenable[0])
			led_value[31:24] = avalon_writedata[7:0];
		if (avalon_byteenable[1])
			led_value[39:32] = avalon_writedata[15:8];
		if (avalon_byteenable[2])
			led_value[47:40] = avalon_writedata[23:16];
	end
end
end

always @(posedge clk_clk)
begin
if (reset_reset) begin
	state <= IDLE;
	counter <= 0;
end else begin
	case (state)
	IDLE:
		if (counter < 10000)
			counter <= counter + 1;
		else begin
			state <= RESET;
			counter <= 0;
		end
	RESET:
		if (counter < 500)
			counter <= counter + 1;
		else begin
			state <= DATA;
			counter <= 0;
			bit_counter <= 0;
		end
	DATA:
		if (counter < 12) begin
			counter <= counter + 1;
		end else begin
			counter <= 0;
			if (bit_counter < 47)
				bit_counter <= bit_counter + 1;					
			else
				state <= IDLE;
		end
	default:
		state <= IDLE;
	endcase
end
end

always @(*)
begin
rgb_led = 1;
case (state)
RESET:
	rgb_led = 0;
DATA:
	if (led_value[bit_counter])
	begin
		if (counter >= 8)
			rgb_led = 0;
	end else begin
		if (counter >= 4)
			rgb_led = 0;
	end
endcase
end

assign data = rgb_led;

endmodule

Taki komponent dołączyłem do konfiguracji systemu, podobnie jak w opisywałem wcześniej

Dzięki temu rejestry komponentu są dla procesora Nios2 widoczne w przestrzeni adresowej - zupełnie jak rejestry innych układów peryferyjnych, UART, SPI, czy PWM.

Pozostało więc napisać program, który wysteruje taki moduł:

#include "sys/alt_stdio.h"
#include "io.h"
#include "system.h"

int main()
{ 
 alt_putstr("Hello from Nios II!\n");

 IOWR(WS2812_0_BASE, 0, 0x00ff00);
 IOWR(WS2812_0_BASE, 1, 0x0000f0);

 while (1);

 return 0;
}

Jak już wspomniałem pisałem całość w bardzo kiepskich warunkach, ale po powrocie do domu sprawdziłem - i nawet działa 🙂

Oczywiście wypadałoby teraz wszystko przepisać, poprawić itd.

Ale tym co dla mnie było super doświadczeniem jest zrealizowanie w FPGA własnego układu peryferyjnego - dzięki temu mogę sterować diodami RGB typu WS2812 nie zużywając zasobów procesora. Wszystko jest tak proste jak zapis do rejestru wartości na jaką mają być ustawione jasności świecienia diody. Super sprawa, i znikają problemy z przerwaniami.

  • Lubię! 2
Link do komentarza
Share on other sites

WS2812B to standard, który przerabiamy śmiało na uczelni. Jest tak prosty, że w 5 minut w byle HDL-u można go napisać (szkoda tylko, że wzrost mocy się pojawia jak z kilka tysięcy takich diod się połączy, ale coś za coś w końcu - łatwe sterowanie, ale jeszcze do tego scalaki przy każdej diodzie i same diody co jednak mocno świecą 🙂 )

  • Lubię! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.