Today, I’m learning about the magical and mystical world of FPGAs. FPGAs can be a challenging subject to get into for some of us, especially those of us who spent their days in college learning about programming computers, and not electrical engineering. Like many in this subject, I really had no idea where to start. So, I’ve setup a simple experiment using everyone’s favorite low cost FPGA dev board, the Papilio, a standard bread board, and a cheap red LED matrix from EBay.
If you go to the Papilio website like I did to start gathering knowledge, they seem to want to push you towards starting by loading an implementation of an AVR soft processor. Personally, I think it kinda defeats the purpose of trying to learn about FPGAs and VHDL, if you just load up a pre-built processor and go back to writing C code. Other than that, if you look around you will find nice step by step instructions for setting up a simple project. This includes the links and instructions for installing the Xilinx tools required to synthesize the VHDL into a bitstream that can be loaded on Papilio’s Xilinx Spartan 3 FPGA.
Once you’ve finished with setting up your first project from the tutorial on the Papilio site, let’s setup the bread board for our experiments. The idea here is simple, using 16 of the IO pins from the FPGA, we will drive the 8 rows and 8 columns of the LED matrix. With the IO pins set to output, it should be as simple as putting one column high, and one row low, thus creating a current through the diode in the correct direction. The diode action prevents the low column outputs from sinking current from the high rows. Now, a little bit of care is noted here, as I don’t know the maximum current source and sink rating of the IO pins, or if they contain any short kind of protection. Potentially, as I can only assume, connecting a high output directly to a low output would create a short circuit and damage your FPGA. Also, I should assume using too low of a value resistor could potentially draw too much current and damage the IO pins as well, but I could be completely wrong about that as well. If you followed the tutorial above to setup your project, all the output pins should be configured as LVCMOS33, which means a logic 1 will put 3.3 volts on the pin. In my circuit, I have 330 ohm resistor, so taking V = IR, that gives 10mA. That should be plenty to power the LED and doesn’t seem like it should be too much for the FPGA IO pins to handle. Again, I couldn’t find the spec for that, so its just a guess. You could use a higher value resistor as well, I just had those 330s in my parts bin. A 1K would probably work as well.
Here is the circuit. Basically, we have all the rows of the LED matrix attached to IO pins A0 to A7 on the Papilio board. The pinout and polarity of the matrix I figured out mostly from other sources online using similar parts, and a little probing with the multimeter. Im not sure why, but the most trouble I had with understanding this matrix is that the lower left hand corner in the picture is row 1, column 1, with the rows and columns increasing across and up. The actuall FPGA pin mapping to the logical Papilio ports is defined in that .UCF file you added in the above tutorial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | NET A(0) LOC="P18" | IOSTANDARD=LVCMOS33; # A0 NET A(1) LOC="P23" | IOSTANDARD=LVCMOS33; # A1 NET A(2) LOC="P26" | IOSTANDARD=LVCMOS33; # A2 NET A(3) LOC="P33" | IOSTANDARD=LVCMOS33; # A3 NET A(4) LOC="P35" | IOSTANDARD=LVCMOS33; # A4 NET A(5) LOC="P40" | IOSTANDARD=LVCMOS33; # A5 NET A(6) LOC="P53" | IOSTANDARD=LVCMOS33; # A6 NET A(7) LOC="P57" | IOSTANDARD=LVCMOS33; # A7 NET A(8) LOC="P60" | IOSTANDARD=LVCMOS33; # A8 NET A(9) LOC="P62" | IOSTANDARD=LVCMOS33; # A9 NET A(10) LOC="P65" | IOSTANDARD=LVCMOS33; # A10 NET A(11) LOC="P67" | IOSTANDARD=LVCMOS33; # A11 NET A(12) LOC="P70" | IOSTANDARD=LVCMOS33; # A12 NET A(13) LOC="P79" | IOSTANDARD=LVCMOS33; # A13 NET A(14) LOC="P84" | IOSTANDARD=LVCMOS33; # A14 NET A(15) LOC="P86" | IOSTANDARD=LVCMOS33; # A15 |
To create an image, we can turn the LEDs on and off, one at a time, really fast, so it makes it look like an image appears. This eliminates the need to have 64 IO lines to control each led. So the first experiment we can do is to generate a slow scan of all the LEDs to verify that everything is hooked up correctly and our VHDL code works. After a bit of experimentation, modifying the code from the Papilio tutorial, and a little Googling, I came up with this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | --Standard includes library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; --Entity definiton entity First is Port ( A : out STD_LOGIC_VECTOR (16 downto 0); -- maps to pins 0 to 16 of port A clk : in STD_LOGIC); --clock signal end First; --architecture definition architecture Behavioral of First is --Interial signals signal counter : STD_LOGIC_VECTOR(22 downto 0) := (others => '0'); --counter to control state and delay signal row : STD_LOGIC_VECTOR(7 downto 0) := "11111110"; --signal for LED rows signal col : STD_LOGIC_VECTOR(7 downto 0) := "00000001"; --signal for LED columns begin --Process Definition count: process(clk) begin -- triggers action on rising edge of clock signal if rising_edge(clk) then --increment counter counter <= counter+1; --clock period is 31.25ns, counter is 22 bits, should roll over every 260ms --trigger each time counter rolls over back to zero if counter = 0 then -- Left Rotate col col <= col(6 downto 0) & col(7); -- Trigger when last column becomes active if col = "10000000" then -- Left rotate row row <= row(6 downto 0) & row(7); end if; end if; --copy signals to outputs A(7 downto 0) <= row; A(15 downto 8 ) <= col; end if; end process; end Behavioral; |
I’ve commented the code to try and make it easy to follow. The important thing to remember is that VHDL code defines hardware, it DOES NOT RUN. As a programmer this is a difficult concept to grasp, as I am so ingrained in the idea of procedural code, running instructions one at a time. For the most part, everything in VHDL happens simultaneously, with the IF statements basically defining when each block of stuff can happen. For our current experiment, we want things to run in a procedural manner, lighting one LED at a time in order. To do this, we have to implement a primitive state machine, using a counter to create a delay and the current row and col values as the state, triggering off of the clk signal. Staring at the beginning, we have our counter signal defined as 22 down to 0 (23 bits), defaulted to all zeros. The size of the counter was chosen so that at our clock period of 31.25ns, the counter would roll over back to zero in about 260ms, giving a nice slow visible scan of the martix. Row is an 8 bit value we are defaulting to “11111110” and col is an 8 bit value defaulting to “00000001”. As you see in the defaults, row 1 and column 1 are defaulted to be on. To create the scan, these two values will be rotated, so that the row that is set to 0 and the column that is set to 1 will light the correct LED. The first if statement detects and activates on the rising edge of the clock signal. Again, not a procedural language, when we hit the rising edge of the clock, everything in the block will be run. Two basic assignments happen in this block, first we add one to the counter signal, second we copy the row and col values to the outputs of port A. The nested if activates when the counter rolls over and hits zero again. When that happens we rotate the col signal to the left. We do that by taking the right most 6 bits and appending the 7th bit to the end of it. Another nested if detects when col reaches the value “10000000”, meaning its on the last column, we rotate the row signal.
If you generate the bit file for the code above and load it into your board you should see the LED scroll through each row and column on the display. After we’ve verified that it in fact scans the matrix in the correct direction and that all the LEDs light up, we can just decrease the size of the counter signal to increase the scroll speed. If we take it down to 9 bits, it should scan through the entire matrix in less than 1ms. This will give the impression that all the LEDs are on at once.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | --Standard includes library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; --Entity definiton entity First is Port ( A : out STD_LOGIC_VECTOR (16 downto 0); -- maps to pins 0 to 16 of port A clk : in STD_LOGIC); --clock signal end First; --architecture definition architecture Behavioral of First is --Interial signals signal counter : STD_LOGIC_VECTOR(8 downto 0) := (others => '0'); --counter to control state and delay signal row : STD_LOGIC_VECTOR(7 downto 0) := "11111110"; --signal for LED rows signal col : STD_LOGIC_VECTOR(7 downto 0) := "00000001"; --signal for LED columns begin --Process Definition count: process(clk) begin -- triggers action on rising edge of clock signal if rising_edge(clk) then --increment counter counter <= counter+1; --clock period is 31.25ns, counter is 9 bits, should scan whole matrix in < 1ms --trigger each time counter rolls over back to zero if counter = 0 then -- Left Rotate col col <= col(6 downto 0) & col(7); -- Trigger when last column becomes active if col = "10000000" then -- Left rotate row row <= row(6 downto 0) & row(7); end if; end if; --copy signals to outputs A(7 downto 0) <= row; A(15 downto 8 ) <= col; end if; end process; end Behavioral; |
Again, all that changed is the counter size, but running it now shows a fully lit matrix.
OK! We’ve learned a bit, and managed to generate a signal that can drive an LED matrix. Next time, I’ll be attempting to modify this code to read some simple images from memory and display them on the matrix.
Tnx for these exelent tutorials…pls go ahead about how to driving rgb led panel dotmatrix,for example 16*32 rgb panels…tnx
Hello man, I’m developing a digital systems project with an 8×8 led matrix, I followed your code as a basis to form a Mario Bros, but I need the mario led to move, could you give me some idea?