Konstruktion av Led-snöre-motor i FPGA

Berätta om dina pågående projekt.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Hej på er!

Under semestern började jag att försöka designa en egen kontroller till LED-slingorna vi har
(se tråd http://elektronikforumet.com/forum/view ... 11&t=93421)

Till att börja med så ska det sägas att jag ser mig själv som nybörjare inom FPGA'er/VHDL trots att jag pysslat med det i några år nu.

Eftersom att vi saknar databladet till GW6205 är det ganska mycket gissande och trial'n'ärror.
Jag började med att analysera hur FastLED driver GW6205-kretsarna. Efter en del mätande och räknande visade det sig att protokollet är nästintill identiskt med WS28xx-kretsar. Enda som skiljer verkar vara färdjupet, 8b för ws28xx och 12b för GW6205.
När data skrivs till en pixel skickas färgdata i en serialiserad form där klocka och data är integrerat i en tråd. Efter 36 pulser kommer en paus på 5µS sen kommer nästa paket. Det gör att man skriver ledstrippen baklänges. Sista pixeln först alltså. Sedan reducerar man adressen med 1.

Jag laddade ner en ws2812 kärna från OpenCores som jag har haft som försökskanin och grund för den nya motorn. Allt, i princip, bygger på den så all credd till snubben som gjorde den från början.
https://opencores.org/projects/ws2812

Den är uppbyggd av tre olika filer.
PHY - Den krets som sköter själva serialiseringen av inkommande data och gör om den till 1-w signal.
Gamma - en 8b gammakorrektor som bygger på en "lookup"-tabell.
Controller - Gör att man kan skriva till flera pixlar. Tar hand om adresser och minne för buffring av data.

Hittills har jag gort om den här koden så att den funkar med 36b-paket istället för 24b och skickar detta till slingan. Adresseringen är för närvarande en 8b räknare så jag får hela slingan tänd när jag skickar in data. All data kommer från en atmega16 och ett supersimpelt serieprotokoll som skickar 3 x 8b för färgdata som buffras asynkront. Det var för att kunna jobba i två klockfrekvenser mellan uC och FPGA.

Här kommer 12b varianten:

Kod: Markera allt

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

entity ws2812b_controller is
	generic(
		length : integer := 4;         
		f_clk  : natural := 50000000;
		T0H    : real    := 0.00000035;
		T1H    : real    := 0.0000009;
		T0L    : real    := 0.0000009;
		T1L    : real    := 0.00000035;
		DEL    : real    := 0.0000001;  
		RES    : real    := 0.0000050
	);
	port(
		clk           : in  std_logic;
		rst           : in  std_logic;
		-- Hardware Connection
		so            : out std_logic;  
		-- Data Link
		address       : in  std_logic_vector(7 downto 0);
		data_red      : in  std_logic_vector(7 downto 0);
		data_green    : in  std_logic_vector(7 downto 0);
		data_blue     : in  std_logic_vector(7 downto 0);
		dataOut_red   : out std_logic_vector(7 downto 0);
		dataOut_green : out std_logic_vector(7 downto 0);
		dataOut_blue  : out std_logic_vector(7 downto 0);
		we            : in  std_logic;  -- Write to RAM
		render        : in  std_logic;  -- Send data to LEDs
		vsync         : out std_logic   -- Finished sending data out 
	);
end entity ws2812b_controller;

architecture RTL of ws2812b_controller is
	type memory_t is array (length - 1 downto 0) of std_logic_vector(23 downto 0);
	signal memory : memory_t;
	signal rdaddr : std_logic_vector(integer(ceil(log2(real(length - 1)))) downto 0);
	type state_t is (IDLE, PRESENT, WAITEMPTY);
	signal state         : state_t;
	signal pixData_red   : std_logic_vector(7 downto 0);
	signal pixData_green : std_logic_vector(7 downto 0);
	signal pixData_blue  : std_logic_vector(7 downto 0);
	signal pixData_valid : std_logic;
	signal pixData_next  : std_logic;
begin
	-- -----------------------
	-- Bit Timing Driver
	-- -----------------------
	ws2812b_phy_inst : entity work.ws2812b_phy
		generic map(
			f_clk => f_clk,
			T0H   => T0H,
			T1H   => T1H,
			T0L   => T0L,
			T1L   => T1L,
			DEL   => DEL,
			RES   => RES
		)
		port map(
			clk           => clk,
			rst           => rst,
			so            => so,
			pixData_red   => pixData_red,
			pixData_green => pixData_green,
			pixData_blue  => pixData_blue,
			pixData_valid => pixData_valid,
			pixData_next  => pixData_next
		);

	-- -----------------------
	-- Memory Interface
	-- -----------------------
	mem_writer : process(rst, clk) is
	begin
		if rst = '1' then
			dataOut_red   <= (others => '0');
			dataOut_green <= (others => '0');
			dataOut_blue  <= (others => '0');
		elsif rising_edge(clk) then
			dataOut_red   <= memory(to_integer(unsigned(address)))(23 downto 16);
			dataOut_green <= memory(to_integer(unsigned(address)))(15 downto 8);
			dataOut_blue  <= memory(to_integer(unsigned(address)))(7 downto 0);
			if we = '1' then
				memory(to_integer(unsigned(address))) <= data_red & data_green & data_blue;
			end if;
		end if;
	end process mem_writer;

	-- -----------------------
	-- Main Controller FSM
	-- -----------------------
	main : process(rst, clk) is
	begin
		if rst = '1' then
			rdaddr <= (others => '0');
			state  <= IDLE;
			vsync  <= '0';
		elsif rising_edge(clk) then
			vsync <= '0';
			case state is
				when IDLE =>
					rdaddr <= (others => '0');
					if render = '1' then
					
						state <= PRESENT;
					end if;
				when PRESENT =>
					if pixData_next = '1' then
						if to_integer(unsigned(rdaddr)) = length - 1 then
							rdaddr <= (others => '0');
							state  <= WAITEMPTY;
							vsync  <= '1';
						else
							rdaddr <= std_logic_vector(unsigned(rdaddr) + 1);
						end if;
					end if;
				when WAITEMPTY =>
					rdaddr <= (others => '0');
					if pixData_next = '1' then
						state <= IDLE;
					end if;
			end case;
		end if;
	end process main;

	pixData_valid <= '1' when state = PRESENT else '0';

	pixData_red   <= memory(to_integer(unsigned(rdaddr)))(23 downto 16);
	pixData_green <= memory(to_integer(unsigned(rdaddr)))(15 downto 8);
	pixData_blue  <= memory(to_integer(unsigned(rdaddr)))(7 downto 0);

end architecture RTL;

Kod: Markera allt

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ws2812b_phy is
	generic(
		f_clk : natural := 50000000;
		T0H   : real    := 0.00000035;
		T1H   : real    := 0.0000009;
		T0L   : real    := 0.0000009;
		T1L   : real    := 0.00000035;
		DEL   : real    := 0.0000001;  
		RES   : real    := 0.0000050  
	);
	port(
		-- Global Signals
		clk           : in  std_logic;  -- System clock @ f_clk
		rst           : in  std_logic;  -- Asynchronous reset
		-- Hardware Connection
		so            : out std_logic;  -- Serial output to WS2812B
		-- Data Link
		pixData_red   : in  std_logic_vector(7 downto 0);
		pixData_green : in  std_logic_vector(7 downto 0);
		pixData_blue  : in  std_logic_vector(7 downto 0);
		pixData_valid : in  std_logic;
		pixData_next  : out std_logic
	);
end entity ws2812b_phy;

architecture RTL of ws2812b_phy is

	constant CYC_T0H : natural := natural(T0H / (real(1) / real(f_clk))) - 1;
	constant CYC_T1H : natural := natural(T1H / (real(1) / real(f_clk))) - 1;
	constant CYC_T0L : natural := natural(T0L / (real(1) / real(f_clk))) - 1;
	constant CYC_T1L : natural := natural(T1L / (real(1) / real(f_clk))) - 1;
	constant CYC_DEL : natural := natural(DEL / (real(1) / real(f_clk))) - 1;
	constant CYC_RES : natural := natural(RES / (real(1) / real(f_clk))) - 1;

	type state_t is (HIGH, LOW);
	signal bitState : state_t;

	signal bitCnt        : integer range 0 to CYC_RES; -- Timing counter
	signal bitData_i     : std_logic;
	signal bitData       : std_logic_vector(1 downto 0); -- 00: send 0 <br> 01: send 1 <br> 10: send reset <br> 11: send led-separator 
	signal bitData_valid : std_logic;   -- Applied data is valid -> TX request (keep valid until data_next)
	signal bitData_next  : std_logic;   -- Apply next bit or release valid to terminate transmission

	-- Serializer Signals and Definitions
	signal shiftreg : std_logic_vector(35 downto 0);
	signal pixCnt   : integer range 0 to 37;
begin
	-- -----------------------
	-- GW6205 Bit Encoder
	-- -----------------------
	bitEncoder : process(rst, clk) is
	begin
		if rst = '1' then
			bitCnt       <= 0;
			bitState     <= LOW;
			bitData_next <= '0';
		elsif rising_edge(clk) then
			bitData_next <= '0';
			if bitCnt /= 0 then
				bitCnt <= bitCnt - 1;
			end if;
			case bitState is
				when HIGH =>
					if bitCnt = 0 then
						bitState <= LOW;
						if bitData_i = '0' then
							bitCnt <= CYC_T0L;
						else
							bitCnt <= CYC_T1L;
						end if;
					end if;
				when LOW =>
					if bitCnt = 0 then
						if bitData_valid = '1' then
							bitData_next <= '1';
							bitData_i    <= bitData(0);
							if bitData(0) = '0' then
								bitCnt <= CYC_T0H;
							else
								bitCnt <= CYC_T1H;
							end if;
							if bitData(1) = '0' then
								bitState <= HIGH;
							else
								if bitData(0) = '0' then
									bitCnt <= CYC_RES;
								else
									bitCnt <= CYC_DEL;
								end if;
								bitState <= LOW;
							end if;
						end if;
					end if;
			end case;
		end if;
	end process bitEncoder;

	so <= '1' when bitState = HIGH else '0';

	-- -----------------------
	-- Pixel Data Serializer
	-- -----------------------
	pixSerializer : process(rst, clk) is
	begin
		if rst = '1' then
			bitData_valid <= '0';
			pixData_next  <= '0';
			pixCnt        <= 0;
		elsif rising_edge(clk) then
			pixData_next <= '0';
			if bitData_next = '1' then
				pixCnt <= pixCnt - 1;
				if pixCnt = 2 then      -- End of data
					bitData(1) <= '1';  -- Control sequence
					if pixData_valid = '1' then
						shiftreg(35) <= '1'; -- Trigger DEL sequence
						
					else
						shiftreg(35) <= '0'; -- Trigger RES sequence
						
						pixData_next <= '1'; -- Acknowledge that the reset has been latched
					end if;
				elsif pixCnt = 1 then   -- End of control
					bitData_valid <= '0';
				else
					shiftreg <= shiftreg(34 downto 0) & '0';
				end if;
			end if;
			if pixCnt = 0 then          -- End of DEL
				pixCnt <= pixCnt;
				if pixData_valid = '1' then

					pixData_next  <= '1';
					shiftreg      <= (pixData_green & '0'& '0'& '0'& '0') & (pixData_red & '0'& '0'& '0'& '0') & (pixData_blue & '0'& '0'& '0'& '0');
					bitData_valid <= '1';
					pixCnt        <= 37;
					bitData(1)    <= '0'; -- Data bit
				--						else
				--							bitData(1)   <= '1'; -- Control sequence
				--							shiftreg(23) <= '1'; -- Trigger RES sequence
				end if;
			end if;

		end if;
	end process pixSerializer;

	bitData(0) <= shiftreg(35);

end architecture RTL;
Är det någon som har ett tips på hur man gör den här raden snyggare.

Kod: Markera allt

shiftreg      <= (pixData_green & '0'& '0'& '0'& '0') & (pixData_red & '0'& '0'& '0'& '0') & (pixData_blue & '0'& '0'& '0'& '0');
Jag använde "& '0'" för att resarvera de sista 4b i varje färg. Går det att skriva kompaktare på något vis?

Bildbevis på att det verkligen fungerar:
20190116_204320.jpg
P.s Jag har inte lagt upp slingorna ännu. Tiden är infernot vi alla brinner i....
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Såhär såg det ut innan jag började på 12b-varianten.

Räknaren "adresscnt" var det jag labbade med sist.
Skärmklipp.PNG
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Personligen tycker jag att det är väldigt smidigt att man kan göra om sin kod till schematiska symboler som man kan använda i sin top-fil för att få en bra överblick. Det tog tyvärr ganska lång tid innan jag fattade att man kunde göra det.
Mr Andersson
Inlägg: 1394
Blev medlem: 29 januari 2011, 21:06:30
Ort: Lapplandet

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av Mr Andersson »

cosmox skrev:Är det någon som har ett tips på hur man gör den här raden snyggare.

Kod: Markera allt

shiftreg      <= (pixData_green & '0'& '0'& '0'& '0') & (pixData_red & '0'& '0'& '0'& '0') & (pixData_blue & '0'& '0'& '0'& '0');
Jag använde "& '0'" för att resarvera de sista 4b i varje färg. Går det att skriva kompaktare på något vis?
Beror på vad du menar med kompaktare. Det här är mindre tecken och mer lättläst, men exakt samma resursanvändning.
shiftreg <= pixData_green & "0000" & pixData_red & "0000" & pixData_blue & "0000";
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Nu är v2 av 12b nästan klar. Så här ser det ut. Jag lurar på att ändra sh-registren så man kan ladda de också så man kan ansluta lite intressanta signaler från olika källor. Det primära blir en ambilight (?) för composite (det sitter en sån in-krets på det gamla DEII Cyclone II-kortet jag har) :)
Fortfarande 8b adressrymd.

ctrl_v2.PNG
Jag byter uC till en mega328p nu istället. Mest för att jag har en som passar i sockeln på DECA-kortet så det blir mindre trådar överallt. Egentligen är det en arduino micro (?) som jag väljer att programmera i Basic eller Bascom AVR. :vissla:

EDIT - Ignorera kortisen på schemat :)
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Senast redigerad av cosmox 19 januari 2019, 19:32:16, redigerad totalt 1 gång.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Hmm, den klagade när jag gjorde det.
Jag får prova igen. Jag har för mig att man använder en annan symbol än (') när man gör det.
Det står i boken jag har bredvid mig antagligen......
Mr Andersson
Inlägg: 1394
Blev medlem: 29 januari 2011, 21:06:30
Ort: Lapplandet

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av Mr Andersson »

' för individuella std_ulogic och subtyper.
" för vektorer.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Aha, det var så det var. Tack!

Jag ska prova som du skrev en gång på kul senare.
Nu behöver jag inte använda det längre hoppas jag. :)
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Fixat schemat:
ctrl_v2.PNG
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Det gick inte så bra som jag hade hoppats igår. Jag fick ganska omgående problem med interfacet mellan uC och FPGA. Jag började med att lägga till en CLR-ingång till shiftregistren. Det funkade betydligt bättre att rensa registret och sedan skriva data, 4x12b (B,G,R, Adress). Men trots det fick jag störningar i slingan som jag inte riktigt kan förklara. Jag gissar att det har med timingen i protokollet som uc'n skickar. Ibland verkar det som den skickar en bit för mycket vilket gör att allt blir skräp i princip.

Jag tänkte konstruera ett nytt shiftregister med "latch" för data samt "load" idag så det går att nollställa till en given adress/färg vid tex startup.
Zhorts
Inlägg: 217
Blev medlem: 15 augusti 2011, 14:42:03

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av Zhorts »

Det är värt att tänka både en och tre gånger på hur man samplar in data från ett asynkront protokoll. Var i biten som samplingspunkten hamnar är väldigt viktigt, och det finns många strategier för att försöka göra systemet mindre störningskänsligt. Till exempel samplar man ibland tre eller fem gånger och gör en majoritetsomröstning om resultatet. Jag har sett system som samplar både analogt och digitalt (inte analogt på varje bit) för att försöka förstå sin omvärld. Det finns system som dynamiskt flyttar sin samplingspunkt beroende på en hel massa faktorer.

Men för något så pass "enkelt" som detta så är det troligt att du samplar för nära kanten på bitarna, eller att det kommer in någon störning (har du långa ledningar i luften?) någonstans ifrån. Eller naturligtvis en bugg i µC, men om den skickar med en standard UART så är det mycket otroligt.
cosmox
EF Sponsor
Inlägg: 3854
Blev medlem: 6 januari 2004, 02:11:54
Ort: Hjälteby - Tjörn

Re: Konstruktion av Led-snöre-motor i FPGA

Inlägg av cosmox »

Ursäkta, jag missade att du hade svarat.

Jo, jag har upptäckt att det inte var så enkelt som jag inbillade mig.

En grej jag goofade var pull-down motstånd från µC'n samt att jag borde varit noggrannare med hur varje paket markeras för FPGA'n. Det genererade en glitch ibland som gjorde att data-linan lämnades hög mellan två paket. Jag fattade inte varför det blev så himla skumt ibland men det var garanterat en av orsakerna.

Jag ska jobba vidare med det till helgen hoppas jag.
Skriv svar