通过 7 段 LED 循环字母的有效方法

Efficient way to loop letters through 7-seg LEDs

我正在尝试在我的 FPGA 板上的四个 7 段 LED 上滚动 'Happy Bday' 字样。在每个循环中,单词会向左移动一个字母。到目前为止,我已经能够通过为每个组合定义一个状态并循环遍历这些状态来做到这一点。我想知道是否有更好的方法来做到这一点,因为如果我想滚动更多的单词,我将不得不有很多状态。

module bday (
    input rst_b, clk, start,
    output logic [6:0] led [3:0]
);
    logic clk_d;
    logic [25:0] count;
    state_t state; // state enum

    always @(posedge clk) begin
       count <= count + 1;
       if (count == 10000000)
       begin
          count <= '0;
          clk_d <= !clk_d; // divide clock down from 50MHz source
       end
    end

    always_ff @ (posedge clk_d, negedge rst_b)
    begin
        if (!rst_b)
        begin
            state <= S_idle;
            for (int i = 0; i < 4; i++) led[i] <= '1;
        end
        else // letters below are 7-bit parameters to light up the 7-seg LED correctly
            case (state)
                S_idle : if (start) state <= S_1; else state <= S_idle;
                S_1 : begin state <= S_2; led[0] = H; led[1] = A; led[2] = P; led[3] = P; end
                S_2 : begin state <= S_3; led[0] = A; led[1] = P; led[2] = P; led[3] = Y; end
                S_3 : begin state <= S_4; led[0] = P; led[1] = P; led[2] = Y; led[3] = BLANK; end 
                S_4 : begin state <= S_5; led[0] = P; led[1] = Y; led[2] = BLANK; led[3] = B; end
                S_5 : begin state <= S_6; led[0] = Y; led[1] = BLANK; led[2] = B; led[3] = D; end
                S_6 : begin state <= S_7; led[0] = BLANK; led[1] = B; led[2] = D; led[3] = A; end
                S_7 : begin state <= S_8; led[0] = B; led[1] = D; led[2] = A; led[3] = Y; end
                S_8 : begin state <= S_9; led[0] = D; led[1] = A; led[2] = Y; led[3] = BLANK; end
                S_9 : begin state <= S_10; led[0] = A; led[1] = Y; led[2] = BLANK; led[3] = H; end
                S_10 : begin state <= S_11; led[0] = Y; led[1] = BLANK; led[2] = H; led[3] = A; end
                S_11 : begin state <= S_1; led[0] = BLANK; led[1] = H; led[2] = A; led[3] = P; end
                default : state <= S_idle;
            endcase
    end
endmodule

您可以做两件事。

将滚动行为与要显示的消息分开

always_ff @ (posedge clk_d, negedge rst_b)
        if (!rst_b)
            for (int i = 0; i < 3; i++) led[i] <= '1;
        else
            for (int i = 0; i < 3; i++) led[i] <= led[i+1];

然后使用一个数组和一个计数器从您的消息中挑选字母

parameter logic [6:0] message[15] = {H,A,P,P,Y,BLANK,B,I,R,T,H,D,A,Y,BLANK};
logic [7:0] counter;
always_ff @ (posedge clk_d, negedge rst_b)
        if (!rst_b)
        begin
            counter <=0;
            led[3] <= '1;
        end
        else
            led[3] <= message[counter];
            if (counter < 15) 
               counter <= counter+1;
            else
               counter <= 0;

我会使用的解决方案是制作 "ROM"。你可以做一个同步的,也可以做一个异步的。

module async_rom64(
   input logic [5:0] address,
   output logic [6:0] led [3:0]
);
    always @( * ) 
       case (address)
       6'd0 : {led[0] , led[1] , led[2] , led[3] } = {H,A,p,P};
       6'd1 : {led[0] , led[1] , led[2] , led[3] } = {A,P,P,Y};
       6'd2 : {led[0] , led[1] , led[2] , led[3] } = {P,P,Y,BLANK};
       // ...
       // 6'd63 : ...
       endcase

endmodule

现在你只要改变ROM的地址,就会出现一组新的LED图案。

从这里您可以根据需要将其复杂化。

  • 例如,您可以添加一个 'last' 位,它告诉地址计数器它必须再次从零开始计数。
  • 您可以为每个州添加延迟。
  • 您将重复字段添加到例如重复一个模式,然后转到下一个。