FPGA wheelchair fairy lights

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in July 2014.

I quickly built some flashing fairy lights for my son's wheelchair, as we were off to the 2KidFest lantern parade that night.

Chairlights.jpg

The lights used were salvaged from a set of rechargeable LED Christmas lights, that were run over by a lawnmower last year. Opps!

Here's the schematic for the output interface - the buffers shown are the FPGA's output drivers, so it is only really 8 resistors:

Chairlights schematic.jpg

And here is the interface board:

Chairlights interface.jpg

And here is the controller, ready to be put in an antistatic back and taped to the wheelchair:

Chairlights controller.jpg

Source code

chairlights.vhd

----------------------------------------------------
--
-- chairlights.vhd
--
-- Author: Mike Field <hamster@snap.net.nz>
--
-- A simple PWM controller for a string of LEDs for
-- my son's wheelchair.
----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

Library UNISIM;
use UNISIM.vcomponents.all;


entity chairlights is
    Port ( clk32 : in  STD_LOGIC;
           out0 : out  STD_LOGIC_VECTOR (4 downto 0);
           out1 : out  STD_LOGIC_VECTOR (4 downto 0));
end chairlights;

architecture Behavioral of chairlights is
   signal pwm_count : unsigned(3 downto 0);
   signal divider   : unsigned(20 downto 0) := (others => '0');
   constant divider_max : unsigned(20 downto 0) := to_unsigned(32000000/60/30,20);
   signal address : unsigned(12 downto 0) := (others => '0');
   signal address_slv : std_logic_vector(10 downto 0) := (others => '0');
   signal memdata_slv : std_logic_vector(7 downto 0) := "10001000";
   signal pwm_level_0 : unsigned(3 downto 0);
   signal pwm_level_1 : unsigned(3 downto 0);
   signal enable : std_logic := '0';
   signal channel : std_logic := '0';
begin

   address_slv <= std_logic_vector(address(12 downto 2));
   pwm_level_0 <= x"F"; --unsigned(memdata_slv(3 downto 0));
   pwm_level_1 <= x"F"; --unsigned(memdata_slv(7 downto 4));
  
 
process(clk32)
   begin
      if rising_edge(clk32) then
         if enable = '1' then
            out0 <= (others => '0');
            out1 <= (others => '0');
            if channel = '0' and pwm_level_0 > pwm_count then
               out0 <= (others => '1');
            end if;
            if channel = '1' and pwm_level_1 > pwm_count then
               out1 <= (others => '1');
            end if;
            
            if pwm_count = 14 then
               pwm_count <= (others => '0');
               channel <= not channel;
               address <= address+1;
            else
               pwm_count <= pwm_count + 1;
            end if;
         end if;
         
         if divider = divider_max then
            enable <= '1';
            divider <= (others => '0');
         else
            enable <= '0';
            divider <= divider+1;
         end if;
      end if;
   end process;


   RAMB16_S9_inst : RAMB16_S9
   generic map (
      INIT => X"000", --  Value of output RAM registers at startup
      SRVAL => X"000", --  Output value upon SSR assertion
      WRITE_MODE => "WRITE_FIRST", --  WRITE_FIRST, READ_FIRST or NO_CHANGE
      -- The following INIT_xx declarations specify the initial contents of the RAM
      -- Address 0 to 511
      INIT_00 => X"0F0F000F0F000000000000000000000000000000000000000000000000000000",
      INIT_01 => X"0F0F000F0F000000000000000000000000000000000000000000000000000000",
      INIT_02 => X"F0F000F0F0000000000000000000000000000000000000000000000000000000",
      INIT_03 => X"F0F000F0F0000000000000000000000000000000000000000000000000000000",
      INIT_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INIT_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INIT_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INIT_07 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INIT_08 => X"1111111111111111111111111111111111111111111111111111111111111111",
      INIT_09 => X"2222222222222222222222222222222222222222222222222222222222222222",
      INIT_0A => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_0B => X"4444444444444444444444444444444444444444444444444444444444444444",
      INIT_0C => X"5555555555555555555555555555555555555555555555555555555555555555",
      INIT_0D => X"6666666666666666666666666666666666666666666666666666666666666666",
      INIT_0E => X"7777777777777777777777777777777777777777777777777777777777777777",
      INIT_0F => X"8888888888888888888888888888888888888888888888888888888888888888",
      -- Address 512 to 1023
      INIT_10 => X"9999999999999999999999999999999999999999999999999999999999999999",
      INIT_11 => X"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      INIT_12 => X"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
      INIT_13 => X"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
      INIT_14 => X"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
      INIT_15 => X"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
      INIT_16 => X"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
      INIT_17 => X"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE",
      INIT_18 => X"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
      INIT_19 => X"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
      INIT_1A => X"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
      INIT_1B => X"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
      INIT_1C => X"9999999999999999999999999999999999999999999999999999999999999999",
      -- Address 1024 to 1535
      INIT_1D => X"8888888888888888888888888888888888888888888888888888888888888888",
      INIT_1E => X"7777777777777777777777777777777777777777777777777777777777777777",
      INIT_1F => X"6666666666666666666666666666666666666666666666666666666666666666",
      INIT_20 => X"5555555555555555555555555555555555555555555555555555555555555555",
      INIT_21 => X"4444444444444444444444444444444444444444444444444444444444444444",
      INIT_22 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_23 => X"2222222222222222222222222222222222222222222222222222222222222222",
      INIT_24 => X"1111111111111111111111111111111111111111111111111111111111111111",

      INIT_25 => X"00F0808040404020200000000000000000000000000000000000000000000000",
      INIT_26 => X"00000F000000000000000000F0000000000F0000000000000000000000000000",
      INIT_27 => X"000000F00000F00000000F08080404040202000F00000000F0000F00000F0000",
      INIT_28 => X"0000000000000000000000000000000000000000F00000000000000000000000",
      INIT_29 => X"0000F00000F00000000000000000F000000000F0808040404020200000000000",
      INIT_2A => X"00000F080804040402020000000000000000000F000000000F0000000000F000",
      INIT_2B => X"000000000F0000000000000F080804040402020000000000F000000000000000",
      INIT_2C => X"0000F000000000F00000000F00000000000000000000F0000000000000000000",
      INIT_2D => X"000000000000000000000000F0808040404020200000000F0000000000000000",
      INIT_2E => X"000000F080804040402020000000000000000000000000000000000000000000",
      INIT_2F => X"0000000000000000000000000000000000000000000F08080404040202000000",
      -- Address 1536 to 2047
      INIT_30 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_31 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_32 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_33 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_34 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_35 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_36 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_37 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_38 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_39 => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3A => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3B => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3C => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3D => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3E => X"3333333333333333333333333333333333333333333333333333333333333333",
      INIT_3F => X"0000000000000000000000000000000000000000000000000000000000000000",
      -- The next set of INITP_xx are for the parity bits
      -- Address 0 to 511
      INITP_00 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INITP_01 => X"0000000000000000000000000000000000000000000000000000000000000000",
      -- Address 512 to 1023
      INITP_02 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INITP_03 => X"0000000000000000000000000000000000000000000000000000000000000000",
      -- Address 1024 to 1535
      INITP_04 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INITP_05 => X"0000000000000000000000000000000000000000000000000000000000000000",
      -- Address 1536 to 2047
      INITP_06 => X"0000000000000000000000000000000000000000000000000000000000000000",
      INITP_07 => X"0000000000000000000000000000000000000000000000000000000000000000")
   port map (
      DO => memdata_slv,      -- 8-bit Data Output
      DOP => open,    -- 1-bit parity Output
      ADDR => address_slv,  -- 11-bit Address Input
      CLK => clk32,    -- Clock
      DI => (others => '0'),      -- 8-bit Data Input
      DIP => (others => '0'),    -- 1-bit parity Input
      EN => '1',      -- RAM Enable Input
      SSR => '0',    -- Synchronous Set/Reset Input
      WE => '0'       -- Write Enable Input
   );
						   
end Behavioral;

chairlights.ucf

NET "clk32" PERIOD=31.25 ns | LOC=P89;
net "out0<3>" LOC=P12;
net "out0<2>" LOC=P15;
net "out0<1>" LOC=P16;
net "out0<0>" LOC=P17;

net "out1<3>" LOC=P34;
net "out1<2>" LOC=P32;
net "out1<1>" LOC=P25;
net "out1<0>" LOC=P22;

Personal tools