首页 > 解决方案 > DE0 Nano LED 连续亮灭

问题描述

请理解我的代码技能非常低。我正在努力学习变得更好。

使用 DE0 Nano 板,我正在尝试编写 VHDL 来模拟板上所有可用的 LED(其中 8 个)

我将它们标记为 LED0 ~ LED7。使用 50 MHZ 和 1/2 秒计数器,我想按顺序操作各个 LED。

例如,如果这些单独的位代表 LED 的开和关。1|0|0|0|0|0|0|0 -> 0|1|0|0|0|0|0|0 -> 0|0|1|0|0|0|0|0 和很快。最后,计数器将重置为 0 以再次重复该序列。

请在下面查看我的代码以及这些问题/问题。

1)我在第 8 个 LED 后暂停了 1/2 秒。为什么?我该如何解决?

2)即使我将变量计数器设置为 8,它也会重复为 16,因此我必须将计数器重置为 0。(在代码中标记为问题 2)

3)有没有更好的方法来编写这些代码?这完全是一团糟。您能否就我可以用来缩短此代码的任何其他功能或方法提供提示?

如果有任何问题,请告诉我!

非常感谢您的帮助。

entity ledtest is
port(
    clk_50mhz : in std_logic ;
    reset_btn : in std_logic;
    green_led : out std_logic_vector(7 downto 0)
);
end entity;


architecture behave of ledtest is

signal clk_1hz : std_logic ;
signal scaler : integer range 0 to 25000000 ;
signal counter : integer range 0 to 8;
signal LED  : std_logic_vector(7 downto 0);

begin

clk_1hz_process : process( clk_50mhz , reset_btn )
begin
    if (reset_btn = '0') then 
        clk_1hz <= '0';
        scaler <= 0;
        counter <= 0;
    elsif(rising_edge(clk_50mhz)) then 
        if (scaler < 25000000) then 
            scaler <= scaler + 1 ;
            clk_1hz <= '0';
        else
            scaler <= 0;
            clk_1hz <= '1';
            counter <= counter + 1;
            if (counter >= 8) then  --------question 2
                counter <= 0;
            end if;
        end if;
    end if;
end process clk_1hz_process;

blinking_process : process (clk_1hz,reset_btn)
begin
    if (reset_btn = '0') then 
        LED(0) <= '0';

    elsif rising_edge(clk_1hz) AND counter = 1 then
        LED(0) <= not LED(0) ;
        LED(7) <=  '0' ; 
    elsif rising_edge(clk_1hz) AND counter = 2 then
        LED(1) <= not LED(1) ;
        LED(0) <= not LED(0) ;
    elsif rising_edge(clk_1hz) AND counter = 3 then
        LED(2) <= not LED(2) ;
        LED(1) <= not LED(1) ;
    elsif rising_edge(clk_1hz) AND counter = 4 then
        LED(3) <= not LED(3) ;
        LED(2) <= not LED(2) ;
    elsif rising_edge(clk_1hz) AND counter = 5 then
        LED(4) <= not LED(4) ;
        LED(3) <= not LED(3) ;
    elsif rising_edge(clk_1hz) AND counter = 6 then
        LED(5) <= not LED(5) ;
        LED(4) <= not LED(4) ;
    elsif rising_edge(clk_1hz) AND counter = 7 then
        LED(6) <= not LED(6) ;
        LED(5) <= not LED(5) ;
    elsif rising_edge(clk_1hz) AND counter = 8 then
        LED(7) <= not LED(7) ;
        LED(6) <= not LED(6) ;
    end if;

end process blinking_process;

green_led(0) <= LED(0);
green_led(1) <= LED(1);
green_led(2) <= LED(2);
green_led(3) <= LED(3);
green_led(4) <= LED(4);
green_led(5) <= LED(5);
green_led(6) <= LED(6);
green_led(7) <= LED(7);
end behave;

标签: vhdlfpgaquartus

解决方案


如果您的读者真的眯着眼睛,他们可以将原始帖子视为一个问题和两个问题。(问题是单数。)

请在下面查看我的代码以及这些问题/问题。

1)我在第 8 个 LED 后暂停了 1/2 秒。为什么?我该如何解决?

有 9 个计数器值(0 到 8),只有 8 个 LED(7 到 0)。在将 counter 分配为 0 和再次递增 1 之间的半秒内没有分配发生。

2)即使我将变量计数器设置为 8,它也会重复为 16,因此我必须将计数器重置为 0。(在代码中标记为问题 2)

这个问题与 1) 有关。评估计数器大于或等于 8 的要求是由将计数器分配给 8 引起的,同样有 9 个值并且只有 8 个 LED。请注意,这是同步加载到 0 而不是异步复位。

3)有没有更好的方法来编写这些代码?这完全是一团糟。您能否就我可以用来缩短此代码的任何其他功能或方法提供提示?

因为您试图直接进入 FPGA 而不是模拟,所以重点应该与问题 1) 以及如何解决它有关。还有一些综合问题,这里通过在 if 语句elsif替代条件中添加使能来控制时钟。还有设计规范复杂性和与代码行数相关的调试工作量的问题。

首先,所有 LED 元件都有触发器和计数器。我们可以通过使用环形计数器(不要与 Johnson 计数器混淆)将每个 LED 元件的触发器数量减少到一个。

(std_logic_vector) 的分配green_led可以来自 LED (std_logic_vector),而不是逐个元素。赋值两侧的元素索引之间存在一对一的对应关系。

此外,为了允许模拟,您可以虚拟化加载到scalar. 这具有更少的时钟代表半秒的效果。这个想法是模拟不必每秒进行 1 亿次时钟转换(上升沿和下降沿)的 10 多秒。

将所有这些放在一起,代码将更改为:

library ieee;
use ieee.std_logic_1164.all;

entity ledtest is
    generic (half_second:   integer := 24999999); -- zero identity
    -- the generic allows fewer clocks per second for simulation
    port (
        clk_50mhz:  in std_logic;
        reset_btn:  in std_logic;
        green_led:  out std_logic_vector(7 downto 0)
    );
end entity;

architecture behave of ledtest is
    signal clk_1hz:  std_logic;
    signal scaler:      integer range 0 to half_second;
    -- signal counter:  integer range 0 to 8;               -- DELETED
    signal ring_counter:    std_logic_vector (7 downto 0);  -- ADDED
    signal LED:             std_logic_vector (7 downto 0);
    signal LED0I:           std_logic;                      -- ADDED

begin
    LED0I <= '1' when LED = "00000000" else 
             '0';

clk_1hz_process:  
    process (clk_50mhz, reset_btn)
    begin
        if reset_btn = '0' then 
            clk_1hz <= '0';
            scaler <= 0;
            -- counter <= 0;
        elsif rising_edge(clk_50mhz) then 
            if scaler /= half_second then 
                scaler <= scaler + 1;
                clk_1hz <= '0';
            else
                scaler <= 0;
                clk_1hz <= '1';
                -- counter <= counter + 1;
                -- if counter >= 8 then  --------question 2
                --     counter <= 0;
                -- end if;
            end if;
        end if;
    end process clk_1hz_process;

blinking_process:  
    process (clk_1hz, reset_btn)
    begin
        if reset_btn = '0' then

            LED <= (others => '0');
            -- LED(0) <= '0';
        elsif rising_edge(clk_1hz) then
            LED <= LED(6 downto 0) & (LED(7) or LED0I);
            -- ring counter with a roulette ball lauch after reset

        -- elsif rising_edge(clk_1hz) AND counter = 1 then
        --     LED(0) <= not LED(0);
        --     LED(7) <=  '0';
        -- elsif rising_edge(clk_1hz) AND counter = 2 then
        --     LED(1) <= not LED(1);
        --     LED(0) <= not LED(0);
        -- elsif rising_edge(clk_1hz) AND counter = 3 then
        --     LED(2) <= not LED(2);
        --     LED(1) <= not LED(1);
        -- elsif rising_edge(clk_1hz) AND counter = 4 then
        --     LED(3) <= not LED(3);
        --     LED(2) <= not LED(2);
        -- elsif rising_edge(clk_1hz) AND counter = 5 then
        --     LED(4) <= not LED(4);
        --     LED(3) <= not LED(3);
        -- elsif rising_edge(clk_1hz) AND counter = 6 then
        --     LED(5) <= not LED(5);
        --     LED(4) <= not LED(4);
        -- elsif rising_edge(clk_1hz) AND counter = 7 then
        --     LED(6) <= not LED(6);
        --     LED(5) <= not LED(5);
        -- elsif rising_edge(clk_1hz) AND counter = 8 then
        --     LED(7) <= not LED(7);
        --     LED(6) <= not LED(6);
        end if;

    end process blinking_process;
    green_led <= led;
    -- green_led(0) <= LED(0);
    -- green_led(1) <= LED(1);
    -- green_led(2) <= LED(2);
    -- green_led(3) <= LED(3);
    -- green_led(4) <= LED(4);
    -- green_led(5) <= LED(5);
    -- green_led(6) <= LED(6);
    -- green_led(7) <= LED(7);
end architecture behave;

(另请注意,默认的通用值标量已重置并加载到已递减以包括单位 0 在 250,000,000 个时钟为半秒。相等性测试half_second比使用幅度比较更简单。)

使用环形计数器降低了复杂性,并绕过了计数器范围为 9 的问题。

环形计数器增加了一个繁荣,复位值全是'0',由LED0I用于在复位后启动环形计数器的信号检测。它可以防止所有 LED 在复位期间点亮。

您可以使用时钟数为半秒到小得多的测试台,从而允许使用小型波形转储文件进行快速仿真:

library ieee;
use ieee.std_logic_1164.all;

entity ledtest_tb is
end entity;

architecture foo of ledtest_tb is
    signal clk:         std_logic := '0';
    signal reset_btn:   std_logic := '1';
    signal green_led:   std_logic_vector (7 downto 0);
begin
    DUT:
    entity work.ledtest
        generic map (half_second => 7)
        port map (
            clk_50mhz => clk,
            reset_btn => reset_btn,
            green_led => green_led
        );
CLOCK:
    process
    begin
        wait for 0.5 sec / 7;
        clk <= not clk;
        if now > 19 sec then
            wait;
        end if;
    end process;
STIMULUS:
    process
    begin
        wait for 0.5 sec;
        reset_btn <= '0';
        wait for 0.5 sec;
        reset_btn <= '1';
        wait;
    end process;
end architecture;

这给出了:

ledtest_tb.jpg

您可以消除 ledtest 的测试台实例化中的通用映射,以演示使用 50 MHz 时钟模拟每个时钟转换所固有的仿真时间和转储文件大小的差异。这里的想法是对模拟进行故障排除比从你所看到的(这里是 LED)中猜测更容易。因为简化了代码描述,所以需要使用原始代码作为起点进行调试。它确实依赖于数字电子和 VHDL 的知识。

使用 ghdl 和 gtkwave 进行模拟。


推荐阅读