Skip navigation

Tag Archives: LED

I just got in my new boards for the scoreboard project, and I was super excited to test out my plain old, unmodified toaster oven for some old fashioned reflow soldering. So I taped down the stencil and jig I had ordered from OSHStencils, and applied the solder paste to the board.

2015-04-03 21.32.22

One of my first concerns when settings up this first go at reflow soldering, was how expensive the solder paste was. Well, just as i had thought, it takes very little paste to cover all the little pads. I was concerned about waste, and while there was some paste left on the stencil and the spreader tool (a plastic card that also came with the OSHStencil order), I was able to collect most of the excess paste with the tool and place it back in the container. Since this was a large board, I started with a small gob of paste and worked the board in sections, alternating between thick spreading, and scraping it back up with the edge of the card.

2015-04-03 22.26.22

Placing the parts wasn’t too bad, I’d say it was easier than normal soldering with the 0603 resistors. I did expect the paste to be tackier, and while the parts did stick in place, it took very little effort to move them, so a stead hand is still needed to place and position. On the SOIC parts, the paste did seem to mingle between pads, but that wasn’t an issue at all. Once all the parts were down, it was into the oven.

2015-04-03 22.29.37

I tried to keep with the same methodology as the last post, although this time around I had black PCBs (since its an LED display, a dark background is preferred). While I’m only speculating, these boards reached much higher temperature much faster, I believe due to extra IR absorption. So this time I set the oven to 300F/150C on the convection setting (fan on). I let it heat until the thermostat clicked off, and waited until it clicked back on to turn the oven up to 400F/200C. This is were I was suprised. The black board started to flow as it approached this temperature setting, where the red board I had tested earlier flowed on its way to the 450F setting. Using a bright light, I was able to see the metallic shine of melted solder on all the joints. So once the thermostat clicked off, I shut the oven down and opened the door to let it cool. The board looked great, all the joints looked solid. There were a few were I could see that there possibly wasn’t enough paste on the pad, but the connections were still solid. The shift registers looked good, with no solder bridges.

2015-04-03 22.41.16

2015-04-03 22.41.07

2015-04-03 22.40.17

And the best part is, it worked!

2015-04-03 23.26.30

Even the digit chaining worked as expected.

2015-04-04 01.08.31

I think a proper diffusing sheet would make the final product very easy to read.

2015-04-03 23.25.51

And lastly, the Arduino code I used for the testing the digits.

/*
|***F***|
A       E
|***G***|
B       D
|***C***|

*/
const int D1 = 2;
const int CLK = 4;
const int STROBE = 3;

byte digitMap[10] =
{
	B11111100,
	B00011000,
	B01101110,
	B00111110,
	B10011010,
	B10110110,
	B11110110,
	B00011100,
	B11111110,
	B10011110
};


void setup()
{

	/* add setup code here */
	pinMode(D1, OUTPUT);
	pinMode(CLK, OUTPUT);
	pinMode(STROBE, OUTPUT);

	digitalWrite(STROBE, LOW);
	digitalWrite(CLK, LOW);
	digitalWrite(D1, LOW);


}

int state = HIGH;
int count = 0;
void loop()
{

	for (int i = 0; i < 100; i++)
	{
		byte Data2 = digitMap[i / 10];
		byte Data1 = digitMap[i % 10];

		//Write to display 
		for (int j = 0; j < 8; j++)
		{
			digitalWrite(CLK, LOW);
			digitalWrite(D1, Data1 & 0x01);
			Data1 = Data1 >> 1;
			digitalWrite(CLK, HIGH);
		}
		//Write to display 
		for (int j = 0; j < 8; j++)
		{
			digitalWrite(CLK, LOW);
			digitalWrite(D1, Data2 & 0x01);
			Data2 = Data2 >> 1;
			digitalWrite(CLK, HIGH);
		}
		delayMicroseconds(10);
		digitalWrite(STROBE, HIGH);
		digitalWrite(STROBE, LOW);

		delay(1000);
	}
}


I was just recently aquired an 8×8 RGB LED matrix, similar to this one. I though it would be good practice to wire up the Papilio board to it an write up some VHDL to drive the display.

2013-10-02 23.55.48

As you can see on the datasheet, this is a common anode display, meaning that the positive voltage will be supplied to the 8 common terminals to operate. The pinout is pretty simple, 1 through 8 are the blue cathodes, which I connected to pins B0-B7 on the board. 9 through 16 are the green cathodes, which are connected to pins A8-A15 board. Pins 21 through 28, are the red cathodes and are connected to the boards B15-B8 pins. Pins 17,18,19,20,29,30,31,32 are the anodes and are connected to pins A0-A7 on the board. I included some small 360 ohm resistors on the anodes, just to limit the current. However the LEDs are not very bright as such. Its much brighter, and I *think* it’s safe to drive the outputs directly from the pins. Otherwise some simple buffer drivers I’m sure would produce a much brighter result.

As for the code, I started off with some of the same stuff from the last LED matrix. The PWM drive is the same, but I changed the counter from 8 bit to 4 bit. It simply counts up, and as long as the counter is less than the set value, the output bit is on.


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity PWM is
    Port ( Value_In : in  integer range 0 to 15 ; --input for pwm value
           Bit_Out : out  STD_LOGIC;					--pwm output
			  clk : in STD_LOGIC);							--system clock
end PWM;

architecture Behavioral of PWM is

begin

	pwm: process(clk)
		variable count: integer range 0 to 15 := 0; --variable, internal to process, 8 bit counter
	begin
		if rising_edge(clk) then 
			--increment count variable (note variable uses := not <=
			count := count + 1;
			
			if count < Value_In then 
			   --while the counter is less than value, set bit
				--this ensures duty cycle is proportional to value
				Bit_Out <= '1';
			else
				--off during other half of cycle, while counter is greater than value
				Bit_Out <= '0';
			end if;		
				
		end if;
	
	end process;
	


end Behavioral;

For the main code, I decided to just have it display all one color, where the RGB values are cycled through at different rates. I also tried to pull out as much as I could from the main process block, to avoid some of the confusion of values being set after the process completes, which caused actions to be one step behind where I thought they would be. The way I understand it, statements in the architecture block are “wired up” so that changes propagate almost instantly. I added three processes to handle the cycling of the RGB values. The signal ‘counter’ just serves as a delay to time the scanning process. The signal ‘row’ is a 24 bit mask for the cathode pins, where the 0 indicates the pin that is active. The signal ‘col’ is an 8 bit mask for the anode pins, where 1 indicates the active pin. The signal ‘rgb’ is a 3 bit flag which is rotated to keep track of which value, red, green, or blue, should be applied to the currently active cathode pin. ‘col’ is rotated left whenever ‘counter’ rolls back to zero, ‘row’ and ‘rgb’ are rotated left when after ‘col’ is rotated back to its original value. A WITH … SELECT syntax is used to map the different color values into ‘value’ depending on the value of ‘rgb’. ‘value’ is mapped into the PW generator, and ‘pwm_bit’ is mapped as its output. A WHEN … ELSE style assignment is used to enable or disable the ‘col’ flags on the output, which is what ties the PWM drive to the output.


--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 (15 downto 0); -- maps to pins 0 to 16 of port A
				B : out STD_LOGIC_VECTOR (15 downto 0); --
			  clk : in STD_LOGIC); --clock signal
end First;

--architecture definition
architecture Behavioral of First is

--component definition for the PWM
component PWM is
    Port ( Value_In : in  integer range 0 to 15 ;
           Bit_Out : out  STD_LOGIC;
			  clk : in STD_LOGIC);
end component;
			  
 --Interial signals
 --counter to control state and delay
 signal counter : STD_LOGIC_VECTOR(8 downto 0) := (others => '0'); 
 --signal for LED "rows", anodes. 0 provides ground for selected row
 signal row : STD_LOGIC_VECTOR(23 downto 0) := "111111111111111111111110";				
 --signal for LED "columns", common cathodes. 1 provides + volts for selected column.
 signal col : STD_LOGIC_VECTOR(7 downto 0) := "00000001";				--signal for LED columns
 
 --rgb flag, for selecting R,G,B values to send to selected output
 signal rgb : STD_LOGIC_VECTOR(2 downto 0) := "001";

 --rolling color values																	
 signal red_value : integer range 0 to 15 := 0;		 
 signal green_value : integer range 0 to 15 := 0;					
 signal blue_value : integer range 0 to 15 := 0;					
 --used to retrieve value from array, mapped to PWM input
 signal value : integer range 0 to 15 := 0;	
 
 --counters for timing color changes
 signal red_counter : STD_LOGIC_VECTOR(20 downto 0) := (others => '0');
 signal green_counter : STD_LOGIC_VECTOR(19 downto 0) := (others => '0');
 signal blue_counter : STD_LOGIC_VECTOR(18 downto 0) := (others => '0');
	
 --signal to be mapped to PWM output	
 signal pwm_bit : STD_LOGIC;												

begin 

	--port map for linking to the PWM
	PW1 : PWM port map(Value_In => value, 
							 Bit_out => pwm_bit,
							 clk => clk);
							 

	--route row masks to outputs
	A(15 downto 8) <=  row(22) & row(19) & row(16) & row(13) & row(10) & row(7) & row(4) & row(1); -- green
	B(15 downto 8) <= row(21) & row(18) & row(15) & row(12) & row(9) & row(6) & row(3) & row(0); -- red
	B(7 downto 0) <= row(23) & row(20) & row(17) & row(14) & row(11) & row(8) & row(5) & row(2); -- blue
	
	--route column masks to outputs, only if PWM bit is on
	A(7 downto 0) <= col WHEN pwm_bit = '1' 
								ELSE "00000000";
	
	--route proper RGB value to PWM generator
	WITH rgb SELECT
		value <= red_value WHEN "001",
					green_value WHEN "010",
					blue_value WHEN "100",
				   0 WHEN OTHERS;
					
					
	--Process Definition
	scan_matrix: 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(22 downto 0) & row(23);
					-- increment row count
					rgb <= rgb(1 downto 0) & rgb(2);

				end if;
				
			end if;
					
     end if; 	  	  
   end process;
						
		
	--Process Definition
	red: process(clk)
   begin
		-- triggers action on rising edge of clock signal
     if rising_edge(clk) then
			red_counter <= red_counter + 1;
			if red_counter = 0 then 
				red_value <= red_value + 1;
			end if;
	  end if;
	end process;
	  
	  	--Process Definition
	blue: process(clk)
   begin
		-- triggers action on rising edge of clock signal
     if rising_edge(clk) then
			blue_counter <= blue_counter + 1;
			if blue_counter = 0 then 
				blue_value <= blue_value + 1;
			end if;
	  end if;
	end process;
	
		--Process Definition
	green: process(clk)
   begin
		-- triggers action on rising edge of clock signal
     if rising_edge(clk) then
			green_counter <= green_counter + 1;
			if green_counter = 0 then 
				green_value <= green_value + 1;
			end if;
	  end if;
	end process;
	
	
		
	
end Behavioral;

Continuing with my learning experiments with the LED Matrix attached to the Papilio dev board, today I am adding PWM output to generate different light intensities. If your not familiar with PWM (pulse width modulation), it’s just a simple digital to analog conversion technique where the pin is switched on and off very fast, so the output effectively becomes an analog average between the time it’s on and the time is off. This is done by changing the duty cycle, which is the ratio to on time versus off time. In our VHDL project, this is a good opportunity to explore adding a new file to the project, a new entity, and interfacing multiple entities together. I added the new file for the PWM entity through the ISE Project Navigator, and followed the wizard, basically just giving the name of the file, entity, and the ports. The ports for this entity are pretty simple, we just need a value in to set the output of the PWM, the output bit, and the system clock in. I added a single process to the architecture, and this time decided to use a variable for internal counting instead of a signal. See this question about signals versus variables. Also being a little different from the code of parts 1 and 2, I am using the integer type, with the range specification. The operation of the PWM is really simple, each rising clock edge, the count variable is incremented. When the count is greater than the set value, the output is turned on, else its turned off. No other logic is needed since the count will keep rolling over back to zero. The higher the set value, the longer the bit stays on, and the higher the output. Just to note, with our clock period set at 31.25nS, and our count variable declared as 0 to 255, our wave will be exactly 256 clock cycles long, or 8uS. That comes out to be a 125KHz PWM frequency. Here’s the code for that part. I deleted out some of the automatic comments the wizard inserted in the file.



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;


entity PWM is
    Port ( Value_In : in  integer range 0 to 255 ; --input for pwm value
           Bit_Out : out  STD_LOGIC;					--pwm output
			  clk : in STD_LOGIC);							--system clock
end PWM;

architecture Behavioral of PWM is

begin

	pwm: process(clk)
		variable count: integer range 0 to 255 := 0; --variable, internal to process, 8 bit counter
	begin
		if rising_edge(clk) then
			--increment count variable (note variable uses := not <=
			count := count + 1;

			if count < Value_In then
			   --while the counter is less than value, set bit
				--this ensures duty cycle is proportional to value
				Bit_Out <= '1';
			else
				--off during other half of cycle, while counter is greater than value
				Bit_Out <= '0';
			end if;

		end if;

	end process;



end Behavioral;

That seems like it will work. Now the trouble I had after this was trying to figure out how to tie the main entity in with this one so I could use it. I changed quite a bit of other stuff in the code to, since we now have to support multiple values for each LED, not just single bit on and off. I changed the display array to be a 2 dimensional array of 8 bit integers. The declaration and instantiation looked like this.


 type display_t is array(7 downto 0, 7 downto 0) of integer range 0 to 255; --array type for new image data, a 2d array of integers
 signal display : display_t := ((1, 1, 1, 1, 1, 1, 1, 1 ),
											(3, 3, 3, 3, 3, 3, 3, 3) ,
											(7, 7, 7, 7, 7, 7, 7, 7) ,
											(15,15,15,15,15,15,15,15) ,
											(31,31,31,31,31,31,31,31) ,
											(63,63,63,63,63,63,63,63) ,
											(127,127,127,127,127,127,127,127) ,
											(255,255,255,255,255,255,255,255 ));

With the 2D array, i also had to set a row_count and col_count to keep track of the position in the array. I also learned that I can define those as ranged integers, so they can be used to index the array directly without conversion. As for the col output, I added an if on the PWM output that would use the col mask if it was true or “00000000” if false. That statement was triggering on the same clock as the PWM so the output will be kept up to the PWM output. Our counter in this process is 9 bits, so each pixel should be getting about 2 PWM waves per scan.

Now to attach the PWM together with the main process here, I learned about the ‘port map’ and ‘component’ structures, component defined under the architecture and port map defined after the architecture’s begin statement. The component I believe acts like a port definition for the referenced entity, and the port map links the signals together.

--component definition for the PWM
component PWM is
    Port ( Value_In : in  integer range 0 to 255 ;
           Bit_Out : out  STD_LOGIC;
			  clk : in STD_LOGIC);
end component;

	--port map for linking to the PWM
	PW1 : PWM port map(Value_In => col_value,
							 Bit_out => pwm_bit,
							 clk => clk);

Looking at the port map, the signal col_value is mapped to PWM’s Value_In, so when we read the pixel value from the array into col_value, it is transferred to the input of the PWM. Likewise, pwm_bit will always be set to the value of the PWM’s Bit_out.

Here is the complete code.


--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

--component definition for the PWM
component PWM is
    Port ( Value_In : in  integer range 0 to 255 ;
           Bit_Out : out  STD_LOGIC;
			  clk : in STD_LOGIC);
end component;

 --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
 type display_t is array(7 downto 0, 7 downto 0) of integer range 0 to 255; --array type for new image data, a 2d array of integers
 signal display : display_t := ((1, 1, 1, 1, 1, 1, 1, 1 ),
											(3, 3, 3, 3, 3, 3, 3, 3) ,
											(7, 7, 7, 7, 7, 7, 7, 7) ,
											(15,15,15,15,15,15,15,15) ,
											(31,31,31,31,31,31,31,31) ,
											(63,63,63,63,63,63,63,63) ,
											(127,127,127,127,127,127,127,127) ,
											(255,255,255,255,255,255,255,255 ));

 signal col_value : integer range 0 to 255 := 0;					--used to retrieve value from array, mapped to PWM input
 signal row_count : integer range 0 to 7 := 1;						--pointer for array
 signal col_count : integer range 0 to 7 := 1;						--pointer for array
 signal pwm_bit : STD_LOGIC;												--signal to be mapped to PWM output

begin

	--port map for linking to the PWM
	PW1 : PWM port map(Value_In => col_value,
							 Bit_out => pwm_bit,
							 clk => clk);

	--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);
				col_count <= col_count + 1;
				-- Trigger when last column becomes active
				if col = "10000000" then
					-- Left rotate row
					row <= row(6 downto 0) & row(7);
					-- increment row count
					row_count <= row_count + 1;
					-- get column mask from current position in array
					col_value <=  display(row_count, col_count);

				end if;
			end if;
			--copy signals to outputs
			A(7 downto 0) <= row;
			--combine column with mask to only display selected pixels
			if pwm_bit = '1' then
				A(15 downto 8) <= (col);
			else
				A(15 downto 8) <= "00000000";
			end if;

     end if;
   end process;




end Behavioral;


And of course, the working example.