【问题标题】:VHDL counter/timerVHDL 计数器/定时器
【发布时间】:2014-11-16 02:03:50
【问题描述】:

我是 VHDL 新手,我正在为以下想法而苦苦挣扎。我想我仍然误解了 VHDL 中计数器和计时器的概念。我将用一个简单的闪烁 LED 二极管来解释它。 (顺便说一句,我正在学习 Spartan-3E FPGA 套件。)
这是我的代码(我还没有使用重置)

entity top_level is
  Port( clk : in STD_LOGIC;
        led1 : out STD_LOGIC);
end top_level;

architecture Behavioral of top_level is
    signal timer : STD_LOGIC_VECTOR(25 downto 0) := "00000000000000000000000000"; 
    signal reset: boolean:= false;

begin 

    process (clk) 
    begin
        led1 <= '1';
        if clk='1' and clk'event then
            if reset <= true then
                timer <= (others => '0');
            end if;
            timer <= timer + 1;
        end if;

        if timer <= "11100100111000011100000000" then
            led1 <= '0';
        end if;
    end process;
end Behavioral;

振荡器频率为 50 MHz,因此一个周期为 20 ns。如果我想用 LED 闪烁 1.2 秒(产生 60 000 000 个周期),我需要创建一个 26 位向量。所以我创建了一个由 clk 更改触发的进程。
我认为应该做什么: 第一行代码是对 led1 的逻辑 1 赋值。所以 led1 应该点亮,直到计数器不会计算 60 000 000 个周期。当计数器计数 60 000 000 个周期时,LED 逻辑状态应切换到 0,这意味着没有光。由于 26 位数的最大值为 67 108 863,因此 LED 应点亮 60 000 000 个周期,并在剩余的 7 108 863 个周期内关闭。不是吗?
它的作用:我的印象是,它有点颠倒了。 LED 大部分时间是关闭的(67 108 063 次循环)并点亮 7 108 863 次循环。为什么会这样?我真的不明白。

其他问题:我怎样才能实现只运行一次/两次/...?例如。我想用我的 LED 闪烁 3 次然后将其关闭。因为据我所知,在达到最大 26 位数后,时间向量将从头开始计数(从 0 开始)。以此类推,无穷无尽。

【问题讨论】:

  • 您确定 LED 的接线方式与您的想法一致吗?如果它是有线的(阴极到引脚,阳极到 +V,通过电阻),那么它会在“0”时点亮,在“1”时熄灭。

标签: count vhdl counter fpga timing


【解决方案1】:

首先,您应该将您的设计分成几个部分,可以是实体,也可以是多个流程。我将使用流程或一些单行。您还可以将呈现的函数提取到单独的包中。

  • 第 1 部分:从 50 MHz 系统时钟生成 0.833 Hz 信号
  • 第 2 部分:控制 LED 是开还是关
  • 第 3 部分:计算开启周期并在 3 后禁用 LED

第 1 部分

我建议使用 UNSIGNED 类型作为计数器,因此为这种类型定义了“+”运算符。 我将它命名为timer_us,所以很容易看出这个信号是无符号的。 此外,您也可以在声明中使用(others =&gt; '0')

signal reset      : STD_LOGIC             := '0'; -- disabled reset; TODO move this signal to port

signal timer_us   : UNSIGNED(25 downto 0) := (others => '0');
signal timer_tick : STD_LOGIC;


process(clk)
begin
  if rising_edge(clk) then
    if (reset = '1') then
      timer_us <= (others => '0');
    else
      timer_us <= timer_us + 1;
    end if;
  end if;
end process;

timer_tick <= '1' when (timer_us = to_unsigned(60 * 1000 * 1000), timer_us'length) else '0';

最后一行描述了一个三元运算符(如 C 中的z = a ? x : y)。因此,如果计时器达到 60,000,000,则刻度信号为“1”。但是有一个错误:您的计数器从 0 开始,所以在 max - 1 已经达到了 60,000,000 个周期。

对更通用的计数器模板的一些改进:

  • 在大多数情况下,需要独立于主重置来重置计数器 -> timer_rst
  • 为最大计数器值定义一个常数 -> TIMER_MAX
  • 根据最大计数器值自动计算计数器值->log2ceil
  • 实现一个函数将布尔表达式转换为 STD_LOGIC -> to_sl
  • 因为定时器是无符号类型的,所以可以直接比较整数值

这里是扩展计数器代码:

signal reset        : STD_LOGIC     := '0'; -- disabled reset; TODO move this signal to port

function log2ceil(arg : positive) return natural is
    variable tmp : positive     := 1;
    variable log : natural      := 0;
begin
    if arg = 1 then return 0; end if;
    while arg > tmp loop
        tmp := tmp * 2;
        log := log + 1;
    end loop;
    return log;
end function;

function to_sl(condition : BOOLEAN) return STD_LOGIC is
begin
    if condition then
        return '1';
    else
        return '0';
    end if;
end function;

constant TIMER_MAX  : POSITIVE      := 60 * 1000 * 1000 - 1;
constant TIMER_BITS : POSITIVE      := log2ceil(TIMER_MAX);

signal timer_rst    : STD_LOGIC;
signal timer_us     : UNSIGNED(TIMER_BITS - 1 downto 0) := (others => '0');
signal timer_tick   : STD_LOGIC;


timer_rst  <= reset or timer_tick;

process(clk)
begin
  if rising_edge(clk) then
    if (timer_rst = '1') then
      timer <= (others => '0');
    else
      timer <= timer + 1;
    end if;
  end if;
end process;

timer_tick <= to_sl(timer = TIMER_MAX - 1));

如果您对编码函数感兴趣,您还可以发明一个函数,将时间(0.833 Hz 信号的周期)转换为给定频率 50 MHz 的循环计数;)

第 2 部分

闪烁的 LED 是一个简单的 T-FF:

signal blink : STD_LOGIC := '0';    -- blink will be mapped to a FF, so initialize it


process(clk)
begin
  if rising_edge(clk) then
    if (timer_tick = '1') then
      blink <= not blink;
    end if;
  end if;
end process;

LED <= blink;

或作为单行(不需要进程块):

blink <= blink xor timer_tick when rising_edge(clk);
LED   <= blink;

第三部分

所以现在您拥有构建计数器的所有工具。您可以实现第二个计数器从 0 计数到 2。如果达到 2,您可以设置控制信号 counter2_ctrl,然后将其反馈到闪烁过程以停止此过程的切换。

这里是扩展的单行:

blink_enable <= not counter2_ctrl;
blink        <= blink xor (timer_tick and blink_enable) when rising_edge(clk);

你的问题

反向 LED 输出可能是由测试板上的低有源电路引起的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-27
    • 2015-04-04
    • 2015-04-25
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-14
    相关资源
    最近更新 更多