{"id":226,"date":"2013-06-08T00:54:30","date_gmt":"2013-06-08T05:54:30","guid":{"rendered":"http:\/\/kratzindustries.com\/CodeRedBlog\/?p=226"},"modified":"2014-03-27T04:33:39","modified_gmt":"2014-03-27T04:33:39","slug":"learn-with-me-fpga-led-matrix-part-3","status":"publish","type":"post","link":"https:\/\/burnt-traces.com\/?p=226","title":{"rendered":"FPGA + LED Matrix, Part 3"},"content":{"rendered":"<p>Continuing with my learning experiments with the LED Matrix attached to the Papilio dev board, today I am adding <a href=\"http:\/\/en.wikipedia.org\/wiki\/Pulse-width_modulation\">PWM<\/a> output to generate different light intensities. If your not familiar with PWM (pulse width modulation), it&#8217;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&#8217;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. <a href=\"http:\/\/stackoverflow.com\/questions\/15485749\/vhdl-variable-vs-signal\">See this question about signals versus variables<\/a>. 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&#8217;s the code for that part. I deleted out some of the automatic comments the wizard inserted in the file.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n\r\n\r\nlibrary IEEE;\r\nuse IEEE.STD_LOGIC_1164.ALL;\r\n\r\n\r\nentity PWM is\r\n    Port ( Value_In : in  integer range 0 to 255 ; --input for pwm value\r\n           Bit_Out : out  STD_LOGIC;\t\t\t\t\t--pwm output\r\n\t\t\t  clk : in STD_LOGIC);\t\t\t\t\t\t\t--system clock\r\nend PWM;\r\n\r\narchitecture Behavioral of PWM is\r\n\r\nbegin\r\n\r\n\tpwm: process(clk)\r\n\t\tvariable count: integer range 0 to 255 := 0; --variable, internal to process, 8 bit counter\r\n\tbegin\r\n\t\tif rising_edge(clk) then\r\n\t\t\t--increment count variable (note variable uses := not &lt;=\r\n\t\t\tcount := count + 1;\r\n\r\n\t\t\tif count &lt; Value_In then\r\n\t\t\t   --while the counter is less than value, set bit\r\n\t\t\t\t--this ensures duty cycle is proportional to value\r\n\t\t\t\tBit_Out &lt;= '1';\r\n\t\t\telse\r\n\t\t\t\t--off during other half of cycle, while counter is greater than value\r\n\t\t\t\tBit_Out &lt;= '0';\r\n\t\t\tend if;\r\n\r\n\t\tend if;\r\n\r\n\tend process;\r\n\r\n\r\n\r\nend Behavioral;\r\n\r\n<\/pre>\n<p>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.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n\r\n 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\r\n signal display : display_t := ((1, 1, 1, 1, 1, 1, 1, 1 ),\r\n\t\t\t\t\t\t\t\t\t\t\t(3, 3, 3, 3, 3, 3, 3, 3) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(7, 7, 7, 7, 7, 7, 7, 7) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(15,15,15,15,15,15,15,15) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(31,31,31,31,31,31,31,31) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(63,63,63,63,63,63,63,63) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(127,127,127,127,127,127,127,127) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(255,255,255,255,255,255,255,255 ));\r\n\r\n<\/pre>\n<p>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 &#8220;00000000&#8221; 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.<\/p>\n<p>Now to attach the PWM together with the main process here, I learned about the &#8216;port map&#8217; and &#8216;component&#8217; structures, component defined under the architecture and port map defined after the architecture&#8217;s begin statement.  The component I believe acts like a port definition for the referenced entity, and the port map links the signals together.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n--component definition for the PWM\r\ncomponent PWM is\r\n    Port ( Value_In : in  integer range 0 to 255 ;\r\n           Bit_Out : out  STD_LOGIC;\r\n\t\t\t  clk : in STD_LOGIC);\r\nend component;\r\n\r\n\t--port map for linking to the PWM\r\n\tPW1 : PWM port map(Value_In =&gt; col_value,\r\n\t\t\t\t\t\t\t Bit_out =&gt; pwm_bit,\r\n\t\t\t\t\t\t\t clk =&gt; clk);\r\n<\/pre>\n<p>Looking at the port map, the signal col_value is mapped to PWM&#8217;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&#8217;s Bit_out.<\/p>\n<p>Here is the complete code.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n\r\n--Standard includes\r\nlibrary IEEE;\r\nuse IEEE.STD_LOGIC_1164.ALL;\r\nuse IEEE.STD_LOGIC_ARITH.ALL;\r\nuse IEEE.STD_LOGIC_UNSIGNED.ALL;\r\n\r\n--Entity definiton\r\nentity First is\r\n    Port ( A : out  STD_LOGIC_VECTOR (16 downto 0); -- maps to pins 0 to 16 of port A\r\n\t\t\t  clk : in STD_LOGIC); --clock signal\r\nend First;\r\n\r\n--architecture definition\r\narchitecture Behavioral of First is\r\n\r\n--component definition for the PWM\r\ncomponent PWM is\r\n    Port ( Value_In : in  integer range 0 to 255 ;\r\n           Bit_Out : out  STD_LOGIC;\r\n\t\t\t  clk : in STD_LOGIC);\r\nend component;\r\n\r\n --Interial signals\r\n signal counter : STD_LOGIC_VECTOR(8 downto 0) := (others =&gt; '0'); --counter to control state and delay\r\n signal row : STD_LOGIC_VECTOR(7 downto 0) := &quot;11111110&quot;;\t\t\t\t--signal for LED rows\r\n signal col : STD_LOGIC_VECTOR(7 downto 0) := &quot;00000001&quot;;\t\t\t\t--signal for LED columns\r\n 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\r\n signal display : display_t := ((1, 1, 1, 1, 1, 1, 1, 1 ),\r\n\t\t\t\t\t\t\t\t\t\t\t(3, 3, 3, 3, 3, 3, 3, 3) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(7, 7, 7, 7, 7, 7, 7, 7) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(15,15,15,15,15,15,15,15) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(31,31,31,31,31,31,31,31) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(63,63,63,63,63,63,63,63) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(127,127,127,127,127,127,127,127) ,\r\n\t\t\t\t\t\t\t\t\t\t\t(255,255,255,255,255,255,255,255 ));\r\n\r\n signal col_value : integer range 0 to 255 := 0;\t\t\t\t\t--used to retrieve value from array, mapped to PWM input\r\n signal row_count : integer range 0 to 7 := 1;\t\t\t\t\t\t--pointer for array\r\n signal col_count : integer range 0 to 7 := 1;\t\t\t\t\t\t--pointer for array\r\n signal pwm_bit : STD_LOGIC;\t\t\t\t\t\t\t\t\t\t\t\t--signal to be mapped to PWM output\r\n\r\nbegin\r\n\r\n\t--port map for linking to the PWM\r\n\tPW1 : PWM port map(Value_In =&gt; col_value,\r\n\t\t\t\t\t\t\t Bit_out =&gt; pwm_bit,\r\n\t\t\t\t\t\t\t clk =&gt; clk);\r\n\r\n\t--Process Definition\r\n\tcount: process(clk)\r\n   begin\r\n\t\t-- triggers action on rising edge of clock signal\r\n     if rising_edge(clk) then\r\n\t    --increment counter\r\n       counter &lt;= counter+1;\r\n\t\t --clock period is 31.25ns, counter is 9 bits, should scan whole matrix in &lt; 1ms\r\n\t\t\t--trigger each time counter rolls over back to zero\r\n\t\t\tif counter = 0 then\r\n\t\t\t\t-- Left Rotate col\r\n\t\t\t\tcol &lt;= col(6 downto 0) &amp; col(7);\r\n\t\t\t\tcol_count &lt;= col_count + 1;\r\n\t\t\t\t-- Trigger when last column becomes active\r\n\t\t\t\tif col = &quot;10000000&quot; then\r\n\t\t\t\t\t-- Left rotate row\r\n\t\t\t\t\trow &lt;= row(6 downto 0) &amp; row(7);\r\n\t\t\t\t\t-- increment row count\r\n\t\t\t\t\trow_count &lt;= row_count + 1;\r\n\t\t\t\t\t-- get column mask from current position in array\r\n\t\t\t\t\tcol_value &lt;=  display(row_count, col_count);\r\n\r\n\t\t\t\tend if;\r\n\t\t\tend if;\r\n\t\t\t--copy signals to outputs\r\n\t\t\tA(7 downto 0) &lt;= row;\r\n\t\t\t--combine column with mask to only display selected pixels\r\n\t\t\tif pwm_bit = '1' then\r\n\t\t\t\tA(15 downto 8) &lt;= (col);\r\n\t\t\telse\r\n\t\t\t\tA(15 downto 8) &lt;= &quot;00000000&quot;;\r\n\t\t\tend if;\r\n\r\n     end if;\r\n   end process;\r\n\r\n\r\n\r\n\r\nend Behavioral;\r\n\r\n\r\n<\/pre>\n<p>And of course, the working example.<\/p>\n<p><a href=\"http:\/\/burnt-traces.com\/wp-content\/uploads\/2013\/06\/2013-06-08-00.50.37.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/burnt-traces.com\/wp-content\/uploads\/2013\/06\/2013-06-08-00.50.37-169x300.jpg\" alt=\"\" title=\"FPGA LED Matrix PWM\" width=\"169\" height=\"300\" class=\"aligncenter size-medium wp-image-227\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;s just a simple digital to analog conversion technique where the pin is switched on and off very fast, so&hellip;&nbsp;<a href=\"https:\/\/burnt-traces.com\/?p=226\" class=\"\" rel=\"bookmark\">Read More &raquo;<span class=\"screen-reader-text\">FPGA + LED Matrix, Part 3<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[3],"tags":[29,32,33,34,36,38],"class_list":["post-226","post","type-post","status-publish","format-standard","hentry","category-electronics","tag-fpga","tag-led","tag-matrix","tag-papilio","tag-pwm","tag-vhdl"],"_links":{"self":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/226","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=226"}],"version-history":[{"count":2,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/226\/revisions"}],"predecessor-version":[{"id":323,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=\/wp\/v2\/posts\/226\/revisions\/323"}],"wp:attachment":[{"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=226"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=226"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/burnt-traces.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}