Intelligent LEDs

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in July 2014

A lot of different addressable LED sub-assemblies are available, and I am using the 8x strip from the [SPLixel Started Bundle]. This features five individual LEDs on their own small PCBs and two eight-LED strips.

Ws2812b leds.jpg

(The colours are actually a lot more vivid than they look in the photo)

These LEDs are based around the WS2812B LED parts - you can find a datasheet at http://www.seeedstudio.com/document/pdf/WS2812B%20Datasheet.pdf

It is pretty tricky to control these with a micro-controller as timing needs to be within +/- 0.15us for the entire duration of the data transfer, but is a piece of cake for an FPGA.

The interface is pretty simple - the state of the bus has to be reset by holding the data signal low for 50us, followed with sending out 24 bits for each LED that is connected in the string. The '1' bits are sent by driving the data line high for 0.9us, followed by driving the line low for 0.35ns, The '0' bits are sent by driving the line high for 0.35us, then driving it low for 0.90us. The frame is in 8-bit Green, 8-bit Red then 8-bit Blue format, and within each colour the MSB is sent first.

To allow them to be chained int strings LEDs keep their data output pin low receive receiving the first 24 bits, then sends any subsequent data bits out, to be received by to the next pixel in the string.

A word of caution: These devices seem to be awfully sensitive to ESD damage - I've already fried a couple.

Contents

Source Code

WS2812B_test.vhd

Sorry about how bad this code is - it is just a quick hack to get them working.
----------------------------------------------------------------------------------
-- Engineer:    Mike Field <hamster@snap.net.nz>
-- 
-- Module Name: ws2812b_test - Behavioral 
-- Description: Test driver for a WS2812B intelligent LED
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

use IEEE.NUMERIC_STD.ALL;

entity ws2812b_test is
    Port ( clk32 : in  STD_LOGIC;
           dout : out  STD_LOGIC);
end ws2812b_test;

architecture Behavioral of ws2812b_test is
    -- LED Colours in GGRRBB order.
   signal led1 : STD_LOGIC_VECTOR(23 downto 0) := x"202020";
   signal led2 : STD_LOGIC_VECTOR(23 downto 0) := x"300000";
   signal led3 : STD_LOGIC_VECTOR(23 downto 0) := x"202000";
   signal led4 : STD_LOGIC_VECTOR(23 downto 0) := x"003000";
   signal led5 : STD_LOGIC_VECTOR(23 downto 0) := x"002020";
   signal led6 : STD_LOGIC_VECTOR(23 downto 0) := x"000030";
   signal led7 : STD_LOGIC_VECTOR(23 downto 0) := x"200020";
   signal led8 : STD_LOGIC_VECTOR(23 downto 0) := x"202020";
   
   signal in_data_phase : std_logic := '0';
   signal bit_count     : unsigned(10 downto 0) := (others => '0');
   signal count         : unsigned(11 downto 0) := (others => '0');
   signal data          : std_logic_vector(24*8-1 downto 0) := (others => '0');
begin
   
process(clk32) 
   begin
      if rising_edge(clk32) then
         if in_data_phase = '1' then
            if count < 11 then
               dout <= '1';
               count <= count+1;
            elsif count < 29 then
               if data(data'high) = '1' then
                  dout <= '1';
               else
                  dout <= '0';
               end if;
               count <= count+1;
            elsif(count < 39) then
               dout <= '0';
               count <= count+1;
            else
               dout <= '0';
               count <= (others => '0');
               if bit_count = data'high-1 then
                  in_data_phase <= '0';
               else
                  bit_count <= bit_count + 1;
                  data <= data(data'high-1 downto 0) & '0';
                  bit_count <= bit_count+1;
               end if;
            end if;
         else
            -- send the reset signal (50us of low)
            dout <= '0';
            if count = 1599 then
               data <= led1 & led2 & led3 & led4 & led5 & led6 & led7 & led8;
               in_data_phase <= '1';
               bit_count <= (others => '0');
               count <= (others => '0');
            else
               count <= count + 1;
            end if;
         end if;
      end if;
   end process;
end Behavioral;

WS2812B_test.ucf

These constraints are for the Papilio Plus board, with the LED's data_in connected to pin A0. The 5V line on the Papilio Pro is able to drive a few of these LEDs, but for driving long strings you will need a more powerful power supply.

NET clk32    LOC=P94  | PERIOD=31.25 ns;
NET dout     LOC=P48  | IOSTANDARD=LVTTL;

More source

See this source in action at https://www.youtube.com/watch?v=PI25niFyMjI

----------------------------------------------------------------------------------
-- Engineer:    Mike Field <hamster@snap.net.nz>
-- 
-- Module Name: ws2812b_test - Behavioral 
-- Description: Test driver for a WS2812 inteligent pixel
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: -
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

use IEEE.NUMERIC_STD.ALL;

entity ws2812b_test is
    Port ( clk32 : in  STD_LOGIC;
           dout : out  STD_LOGIC);
end ws2812b_test;

architecture Behavioral of ws2812b_test is

   signal in_data_phase : std_logic := '0';
   signal bit_count     : unsigned(10 downto 0) := (others => '0');
   signal count         : unsigned(11 downto 0) := (others => '0');
   signal data          : std_logic_vector(24*30-1 downto 0) := 
      x"180000" & x"240000" & x"300000" & x"240800" & x"181800" & x"182400" & x"003000" & x"002408" &
      x"001818" & x"000824" & x"000030" & x"000024" & x"000018" & x"000008" & x"000000" & x"000000" &
      x"000000" & x"000000" & x"000000" & x"000000" & x"000000" & x"000000" & x"000000" & x"000000" &
      x"000000" & x"000000" & x"000000" & x"000000" & x"000000" & x"000000";

   signal start_led     : unsigned(10 downto 0) := (others => '0');
begin
   
process(clk32) 
   begin
      if rising_edge(clk32) then
         if in_data_phase = '1' then
            if count < 11 then
               dout <= '1';
               count <= count+1;
            elsif count < 29 then
               dout <= data(data'high);
               count <= count+1;
            elsif(count < 39) then
               dout <= '0';
               count <= count+1;
            else
               dout <= '0';
               data <= data(data'high-1 downto 0) & data(data'high);
               count <= (others => '0');
               if bit_count = 30*24-1 then
                  in_data_phase <= '0';
               else
                  bit_count <= bit_count+1;
               end if;
            end if;
         else
            -- send the reset signal (50us of low)
            dout <= '0';
            if start_led(4 downto 0) = 0 and count < 24  then
                     data <= data(data'high-1 downto 0) & data(data'high);
            end if;
 
            if count = 1599 then
               start_led <= start_led + 1;
               in_data_phase <= '1';
               bit_count <= (others => '0');
               count <= (others => '0');
            else
               count <= count + 1;
            end if;
         end if;
      end if;
   end process;

end Behavioral;


Personal tools