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;
Kod: Markera allt
shiftreg <= (pixData_green & '0'& '0'& '0'& '0') & (pixData_red & '0'& '0'& '0'& '0') & (pixData_blue & '0'& '0'& '0'& '0');
Bildbevis på att det verkligen fungerar: P.s Jag har inte lagt upp slingorna ännu. Tiden är infernot vi alla brinner i....