FPGA上的2位BCD计数器

时间:2017-02-11 05:31:05

标签: verilog fpga lcd

我试图制作一个2位数的BCD计数器,计数从0到99.我面临的问题是,电路板上的7段表示同时都在改变数字。 scenairio就像这样 - 00,11,22 ...... 99。

这是主要的逻辑代码:

module PartD(
output reg[0:6] lcd, //for one particular 7 segment LCD.
input clock,
output reg[0:7] sel // for selecting which LCD to be used
);
integer count=0;
//integer i=0, j=0;

reg[3:0] i, j;  //4 bit reg for counting

always@(posedge clock)
begin
    if(count==100000000)  //(100MHz) count till 100M cycles...(1 sec delay)
    begin
        count = 0;

        sel=00000001;  //selecting the most significant digit
        case (i)
              0: lcd = 7'b0000001;
              1: lcd = 7'b1001111;
              2: lcd = 7'b0010010;
              3: lcd = 7'b0000110;
              4: lcd = 7'b1001100;
              5: lcd = 7'b0100100;
              6: lcd = 7'b0100000;
              7: lcd = 7'b0001111;
              8: lcd = 7'b0000000;
              9: lcd = 7'b0000100;
        endcase


        sel=00000010; //selecting the least significant digit
        case (j)
              0: lcd = 7'b0000001;
              1: lcd = 7'b1001111;
              2: lcd = 7'b0010010;
              3: lcd = 7'b0000110;
              4: lcd = 7'b1001100;
              5: lcd = 7'b0100100;
              6: lcd = 7'b0100000;
              7: lcd = 7'b0001111;
              8: lcd = 7'b0000000;
              9: lcd = 7'b0000100;
        endcase
        j = j+1;
        if(j>9)
        begin
            j=0;
            i = i+1;    //increment i only when j overflows.
            if(i>9)
            i = 0;
        end  
    end
    else count = count + 1;
end
endmodule

由于两个显示器都在同时更改,因此似乎存在一些逻辑错误。会是什么呢。我是否犯了不考虑硬件的错误?

2 个答案:

答案 0 :(得分:3)

您的代码存在的主要问题是,当您的计数器为100000000时,您试图在同一周期内激活两个LCD段。逻辑上,您使用阻塞分配'=',第二个分配有效地覆盖第一个分配。

您需要决定何时开启每个细分。

根据您的要求,我会找到一种方法在50%的时间内驱动每个细分。假设10 ^ 8的最大2 ^ n除数是256,我选择使用count [7]来决定段1和段2之间,以便在移动到另一段之前刷新每个段128个周期。

此外,您遇到语法错误,忘记在sel分配上使用二进制前缀(0000010而不是'b000010)。

这是使其正常工作的最低要求。

在实践中,我会进一步改变这一点并将其分解为几个“总是”的块:
一个用于柜台
一个用于显示器
一个用于递增i和j。

我还选择将计数器从整数类型替换为寄存器类型。保留非硬件结构的整数(用于循环计数器等)

为case语句添加了一个默认值,将x驱动为'lcd'。这有助于优化意外的i,j值(10..15)的合成以及传播x以进行模拟。

我还添加了一个复位输入并移动了时钟&将信号复位为模块接口上的第一个信号。没有重置,你的计数,i,j从随机值开始。在你的情况下,他们最终会合法,但它会使模拟变得更难,因为它们以“x”值开头。

另外,请记住阻止('=')和非阻塞('< =')分配之间的区别。尝试避免像使用时钟一样阻止使用阻塞分配。

module PartD(
    input clock,
    input reset,
    output reg[0:6] lcd, //for one particular 7 segment LCD.
    output reg[0:7] sel // for selecting which LCD to be used
);
reg [26:0]count;
reg[3:0] i, j;  //4 bit reg for counting

always@(posedge clock)
begin
    count <= reset ? 'd0 : (count == 100000000) ? 'd0 : (count + 1);
end

always@(posedge clock)
begin
    j <= reset ? 'b0 : (j < 9) ? j+1 : 'b0;
    i <= reset ? 'b0 : (j < 9) ? i : (i < 9) ? (i + 1) : 'b0;
end

always@(posedge clock)
begin
    if (count & 128)
    begin
        sel <= 'b00000001;  //selecting the most significant digit
        case (i)
              0: lcd <= 7'b0000001;
              1: lcd <= 7'b1001111;
              2: lcd <= 7'b0010010;
              3: lcd <= 7'b0000110;
              4: lcd <= 7'b1001100;
              5: lcd <= 7'b0100100;
              6: lcd <= 7'b0100000;
              7: lcd <= 7'b0001111;
              8: lcd <= 7'b0000000;
              9: lcd <= 7'b0000100;
              default:
                 lcd <= 'bx;
        endcase
    end
    else
    begin
        sel <= 'b00000010; //selecting the least significant digit
        case (j)
              0: lcd <= 7'b0000001;
              1: lcd <= 7'b1001111;
              2: lcd <= 7'b0010010;
              3: lcd <= 7'b0000110;
              4: lcd <= 7'b1001100;
              5: lcd <= 7'b0100100;
              6: lcd <= 7'b0100000;
              7: lcd <= 7'b0001111;
              8: lcd <= 7'b0000000;
              9: lcd <= 7'b0000100;
              default:
                 lcd <= 'bx;
        endcase
    end
end

endmodule

答案 1 :(得分:0)

我面临的问题是,板上的7段赔付都在同时改变数字

  1. 该代码缺少控制8位显示屏上两位数字之间切换机制的时序约束。您必须考虑 刷新率 ;这使您能够连续更新显示。按照惯例,刷新率可以在 1 kHz到60 Hz 之间的任意值。如果我们选择的频率值太低,则显示将开始闪烁,这意味着 切换速度不够快。

假设您选择的刷新周期为 1 ms 。这意味着每个数字在周期的1/8内保持活动状态(对于8位数显示)。换句话说,数字周期为1/8 ms。这个周期连续发生,从而呈现出独立且持续点亮的数字,这是产生2位数BCD计数器的目的之一。

我们可以将现有的FPGA时钟转换为一个计数器,该计数器的值递增到与1到16ms之间的刷新周期相对应的值。如果将内部时钟频率除以刷新频率,这将为我们提供整个刷新周期的计数器值。

  • 例如,使用 100MHz 时钟的 1 ms 刷新周期将生成 10 * 10 ^的计数器值4 。因此,为了使寄存器保存计数器值,它必须至少为17位宽。

接下来,我们必须决定如何触发正确的数字,同时使用数字句点从0到99进行计数。在下面的代码中,对于第一种情况,我们利用寄存器“ refresh_counter”的宽度

  • 我们只有两位数字可以控制,因此刷新周期被除以2。假设有一个1 ms的刷新周期,那么我们有一个0.5 ms的数字周期。基于此,我们可以找到寄存器“ refresh_counter”中的哪个位位置将在0.5 ms之后触发。

BCD计数器代码

module PartD(
             input MHZ,
             output reg DP = 1'b1,
             output reg [6:0] A_TO_G,
             output reg [7:0] AN
             );

localparam divisor = 50000000;
reg [25:0] counter = 0;
reg clock = 0;

always@(posedge MHZ) begin
if (counter < divisor) begin
    counter <= counter + 1;
    clock <= clock; 
end

else begin
    counter <= 0;
    clock <= ~clock; end
 end

reg [16:0] refresh_counter = 0; //17 bits wide
always@(posedge MHZ) begin
    refresh_counter <= refresh_counter + 1;
end

reg [3:0] num_one, num_two;
always@(posedge clock) begin

        if(num_one == 4'd9) begin
               num_one <= 0;
        
               if (num_two == 4'd9)
                      num_two <= 0;
             
               else
                      num_two <= num_two + 1; 
         end
                    
         else 
            num_one <= num_one + 1; 
 end                          

 reg [3:0] NUMBER;
 always@(refresh_counter or num_one or num_two) begin

 // digit_per = 0.5 ms ---> digit_freq = 2000 Hz

 // mhz_freq = 100*10^6 Hz

 // bit = log(mhz_freq/digit_freq)/log(2)

 // bit_position = bit - 1

 // refresh_counter[bit_position]

 case(refresh_counter[15]) 
    1'b0: begin
         AN = 8'b11111110;
         NUMBER  = num_one;
    end
    1'b1: begin
         AN = 8'b11111101;
         NUMBER = num_two;
    end
endcase   
end

always@(NUMBER) begin
case(NUMBER)
    0: A_TO_G = 7'b0000001;
    1: A_TO_G = 7'b1001111;
    2: A_TO_G = 7'b0010010;
    3: A_TO_G = 7'b0000110;
    4: A_TO_G = 7'b1001100;
    5: A_TO_G = 7'b0100100;
    6: A_TO_G = 7'b0100000;
    7: A_TO_G = 7'b0001111;
    8: A_TO_G = 7'b0000000;
    9: A_TO_G = 7'b0000100;
    default: A_TO_G = 7'b0000001;
endcase
end


endmodule