LeHack / DE0Nano_VHDL

A set of VHDL examples for DE0 Nano
Other
7 stars 4 forks source link

Why is virtual clock toggling every 43 times CLOCK_50 does? #1

Closed juanjzv closed 6 years ago

juanjzv commented 6 years ago

This is my first time working with the max7219, and I can't figure out why virtual clock toggles every 43 times the system clock does. I would really aprrecciate any help. Thanks in advance.

LeHack commented 6 years ago

Well it's just an arbitrary value I chose when I was first designing this, as I needed to slow down the 50MHz system clock (you could also use a PLL for this). The max7219 specs say it has a 10MHz serial interface, so you could possibly set the clk_div to 2 (to get division by 6 and as a result: a 8.33MHz clock) and everything should work fine.

Just remember that usually to get the high-clock rates you also have to feed it around 5V (VDD), so if you intend to use it with smaller voltages, make sure to also slow the clock down (I guess 1MHz should be ok for 3.3V).

Also note that I'll be revising this code soon, since it was written a long time ago and while it works, it could be made a lot simpler. It was one of my first practical projects in VHDL, so there are some rookie mistakes and confusing choices (like the clock divider).

LeHack commented 6 years ago

A more recent version of the "virtual clock" module looks like this: clock_divider.vhdl

library ieee;
    use ieee.std_logic_1164.all;
    use ieee.numeric_std.all;

entity clock_divider is
    GENERIC (
        denominator : UNSIGNED(25 downto 0) := to_unsigned(42, 26) -- 50 MHz -> 1.19 MHz
    );
    PORT (
        clk_in  : IN  STD_LOGIC;
        clk_out : OUT STD_LOGIC := '0'
    );
END entity;

clock_divider_a.vhdl

architecture arch of clock_divider is
begin
    CLK_DIVIDER: process(clk_in)
        constant div_cnt : unsigned(24 downto 0) := denominator(25 downto 1);
        variable cnt : unsigned(24 downto 0) := (others => '0');
    begin
        if rising_edge(clk_in) then
            cnt := cnt + 1;
            if cnt = div_cnt then
                cnt := (others => '0');
                clk_out <= not clk_out;
            end if;
        end if;
    end process;
end arch;

and you can use it like this (provided you put the above in the "utils" library):

khz_clock : entity utils.clock_divider -- 500kHz
        GENERIC MAP (denominator => to_unsigned(100, 26))
        PORT MAP (clk_in => CLOCK_50, clk_out => CLK_0M5);
juanjzv commented 6 years ago

Thank you, I should've supposed that clock_50 meant a 50MHz clock. I'm a rookie in vhdl and fpga's and your answer was very helpful. Sorry if I'm making obviuos questions, but although I've almost understood all the project, it seems that in spi_master when a transaction is ended, busy (that goes directly to LED_CS) is then unset in order to latch the data at the shift register in the max7219, but the data sheet says data is latchen on rising edge of CS ( called LOAD for max7219 and !CS for max7221), but 'busy' was set to 0. I don't know if it's me the one that is not understanding the workflow of the architecture or if the LED matrix module with hasthe matrix7219 and the LED matrix in a circuit board also has an inverter for the CS pin. Again, thanks in advance.

LeHack commented 6 years ago

Ok, scrap what I wrote previously about LOAD. I forgot that I was using a breakout board (this one), so possibly that is why the signal is inverted. Just define an intermediate signal, and use the inverted value of the busy signal from spi_master like so:

...
    signal spi_busy : std_logic := '0';
begin
    CS <= not spi_busy;
    spi_driver : entity work.spi_master GENERIC MAP (slaves => 1, d_width => 16) PORT MAP (
        clock => CLOCK_50, enable => enable, busy => spi_busy, cont => '0',
        reset_n => '1', cpol => '0', cpha => '0', addr => 0,
        tx_data => data, miso => 'Z', mosi => DIN, sclk => CLK, clk_div => 5
    );
...
juanjzv commented 6 years ago

Thaks for the answer, sorry about delaying mine. I used a breakout board too, when I tested your code, it worked perfectly. After that I was coding a version which allowed me to display a marquee on more than one matrix, and I assigned inverted values to the busy signal and it's working too. Kind of weird, maybe it was just because I dind't have much time to complete it and missed some details. Anyway, the true is that your code works and it was very helpful for me to understand how the Max7219 works.

LeHack commented 6 years ago

Well that's great to hear. I don't have any ideas why the inverted value was required for a multi-display setup. I tested it that way as well, and I believe it worked without any changes other than the bus width obviously. Anyway I'm glad I could help :)