Verilog打造除法器驱动数码管(上)

来源:本站
导读:目前正在解读《Verilog打造除法器驱动数码管(上)》的相关信息,《Verilog打造除法器驱动数码管(上)》是由用户自行发布的知识型内容!下面请观看由(电工技术网 - www.9ddd.net)用户发布《Verilog打造除法器驱动数码管(上)》的详细说明。
简介:记得很久以前,用单片机玩数码管的时候,我们用“%”和“/”将一个长长的数字一一分离出来逐个显示在数码管上。如今已不玩单片机很多年,想用Verilog打造一个数码管接口,用来实时显示一个“较长的”数字。比如前段时间,和一个学长“比赛”看谁用到较少的逻辑先驱动DS18B20,就需要将温度这个数字显示在数码管上。

先看看C语言是如何实现分离一个数字的比如我要将一个三位数分别显示在三个数码管上,那么操作过程如下:

假设这个三位数叫做Temp;

百位=Temp/100;

十位=Temp%100/10;

个位=Temp%10;

这样的话就能将Temp拆分开来,并且分别显示在三个数码管上。但是对于FPGA而言,“%”和“/”是消耗逻辑的恶魔,要想用较少的逻辑门,那么这种方法直接被抛弃~~

很久以前我看到一篇笔记,叫《VerilogHDL那些事儿_建模篇》里面介绍了一个用Verilog打造的除法器。这个除法器,有以下特点:

1、这个除法器有两个输入端口,一个是除数输入,一个是被除数输入;输出端口也有两个,一个输出商,一个输出余数;

2、不管除数被除数如何变化(在变化范围内),所消耗的时间是一样的。

3、它是一个带符号的除法器。

这个除法器,确实十分强大,它的第一个特点就完成了“%”和“/”这两个功能,不是吗?呵呵~~~所以这个强大的除法器就是我们的不二人选。

接下来,就来看看这个除法器,不过恕笔者愚笨,至今它的原理我还是不甚明白,不知道BT的创始人是怎么想出来的,不过用确实好用,所以我只会用而已,代码如下:

module Divider              (       input CLK,       input RSTn,       input Start_Sig,       input [15:0] X1,  //被除数          input [15:0] X2,  //除数            output reg[15:0] Quotient,       output reg[15:0] Reminder,         output reg Done);        reg [4:0] i;        reg [31:0] Diff;        reg [31:0] p;   // 操作空间为操作数宽度的两倍        reg [16:0] S;         always @(posedge CLK or negedge RSTn)        if(!RSTn)         begin          Diff <= 32'd0;          p <= 32'd0;          S <= 17'd0;          i <= 5'd0;          Done <= 1'b0;        end        else if(Start_Sig)            case (i)            0:              begin                  Diff <= 16'd0;                  S <= X2[15] ? { 1'b1, X2 } : { 1'b1, ~X2 + 1'b1 };                  p <= X1[15] ? { 16'd0, ~X1 + 1'b1  } : { 16'd0,  X1};                  i <= i + 1'b1;              end            1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16:            begin                Diff = p + { S, 15'd0 };                if( Diff[31] )  p <= { p[30:0], 1'b0 };                else p <=  { Diff[30:0], 1'b1 };                i <= i + 1'b1;            end            17:            begin                Quotient <=  ( X1[15] ^ X2[15] ) ? ( ~p[15:0] + 1'b1 ) : p[15:0];                 Reminder <= p[31:16];                i <= i + 1'b1;            end            18:             begin                Done <= 1'b1;                i <= i + 1'b1;            end            19:            begin                Done <= 1'b0;                i <= 5'd0;            end        endcase endmodule

以上就是一个,16位的除法器,至于如何打造其他位数的除法器,照葫芦画瓢吧,会用就行呵呵~~若特别想理解,那就去看《VerilogHDL那些事儿_建模篇》这本笔记吧。

简单的分析一下这个除法器,如你所见他的被除数和除数都是16位的,且他是带符号位的,就是说除数,被除数,商都可以是负数,很显然,我们用不到这么多,应为对于除数而言,只可能是10,100,1000之类的数。此外我们还注意到这个模块有一个Start_Sig信号,它是用来启动或是关闭,除法器的。Done信号是表示一个除法的完成。有了这两个信号,那么这个除法器就可以复用了。嘿嘿,这个是《VerilogHDL那些事儿_建模篇》里面的仿顺序操作写法,个人十分喜欢。

好了,准备工作已经做好了,现在开始驱动数码管~~

我的6个数码管是底电平驱动的,于是乎声明参数如下,也就是所谓的段选和为选:

parameter    _0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100,     _3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010,     _6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,    _9 = 8'b1001_0000;       parameter    wei_1 = 6'b111110, wei_2 = 6'b111101, wei_3 = 6'b111011,    wei_4 = 6'b110111, wei_5 = 6'b101111, wei_6 = 6'b011111;

好我们来声明一个“数组”来存储将要分离的数:

以为分离出的数最大为9,最小为0,且一共6个数码管,所以“数组”大小如下:

reg[3:0]Num[0:5]; //内存的宽度4,内存的深度6;

假设长长的数字已经完美的分离出来了,那把它显示出来就好啦:

/**************加码模块*************************/    reg [7:0]SegNum[5:0];    always @(posedge CLK)        case(Num[0])            0:SegNum[0] <= _0;            1:SegNum[0] <= _1;            2:SegNum[0] <= _2;            3:SegNum[0] <= _3;            4:SegNum[0] <= _4;            5:SegNum[0] <= _5;            6:SegNum[0] <= _6;            7:SegNum[0] <= _7;            8:SegNum[0] <= _8;            9:SegNum[0] <= _9;        endcase             always @(posedge CLK)        case(Num[1])            0:SegNum[1] <= _0;            1:SegNum[1] <= _1;            2:SegNum[1] <= _2;            3:SegNum[1] <= _3;            4:SegNum[1] <= _4;            5:SegNum[1] <= _5;            6:SegNum[1] <= _6;            7:SegNum[1] <= _7;            8:SegNum[1] <= _8;            9:SegNum[1] <= _9;        endcase         always @(posedge CLK)        case(Num[2])            0:SegNum[2] <= _0;            1:SegNum[2] <= _1;            2:SegNum[2] <= _2;            3:SegNum[2] <= _3;            4:SegNum[2] <= _4;            5:SegNum[2] <= _5;            6:SegNum[2] <= _6;            7:SegNum[2] <= _7;            8:SegNum[2] <= _8;            9:SegNum[2] <= _9;        endcase

多的我就不写了~~~~待会源码贴在下面。

虽然,FPGA是并行的,但是为了节约引脚,数码管还是需要扫描的~~

//--------------------数码管扫描----------------------------     reg [19:0] C0 = 20'd0;    reg [5:0]Control = 6'b111110;    always @(posedge CLK)        if(C0 == 20'h1ffff)        begin            C0 <= 20'd0;            Control <= {Control[4:0],Control[5]};        end        else           begin            C0 <= C0 + 1'b1;        end         //------------将每一位数显示在对应的数码管上-----------------------            reg [7:0]Seg = 8'hff;    always @(posedge CLK)        case(Control)            wei_1: Seg <= SegNum[0];            wei_2: Seg <= SegNum[1];             wei_3: Seg <= SegNum[2];            wei_4: Seg <= SegNum[3];            wei_5: Seg <= SegNum[4];            wei_6: Seg <= SegNum[5];        endcase

由于FPGA的速度不是单片机可以比的,所以扫描的时候加个延时,才能更好的显示。

接下来,把我们之前的写的除法器例化出来备用:

//-------------------------除法器例化------------------------    reg[15:0] X1;           //被除数       reg[15:0] X2;       //除数         wire[15:0] Quotient;    //商     wire[15:0] Reminder;    //余数    reg Start_Sig = 1'b0;    wire Done;    Divider         Divi        (        .CLK(CLK),        .RSTn(RSTn),        .Start_Sig(Start_Sig),        .X1(X1),  //被除数           .X2(X2),  //除数             .Quotient(Quotient),        .Reminder(Reminder),        .Done(Done)    );

之前都是铺垫,最后的关键是如何将数字逐个分离出来~~

//-------------------------利用除法器将数字分离出来----------------------------     reg [15:0]Display_Num;      //要显示的一个数字    reg [3:0]i = 4'd0;    reg [3:0]Num [0:5];         //内存的宽度4,内存的深度6;    reg [2:0]Num_i = 3'd0;    always @(posedge CLK)        case(i)            0:            begin                Display_Num <= 16'd4321;                i <= i + 1'b1;            end            1:            begin                Start_Sig <= 1'b1;                X1 <= Display_Num;                X2 <= 16'd10;                i <= i + 1'b1;            end            2:            begin                if(Done)                 begin                    Start_Sig <= 1'b0;                    X1 <= Quotient;                    X2 <= 16'd10;                    Num[Num_i] <= Reminder[3:0];                    i <= i + 1'b1;                end                else begin Start_Sig <= 1'b1; i <= i; end            end            3:            begin                    if(Num_i < 5)begin Num_i <= Num_i + 1'b1; i <= i - 1'b1;end                    else    i <= i + 1'b1;            end            4:            begin                i <= i;            end        endcase

Display_Num,这个数是用来做测试的,看看我写的这个数是不是能反映到数码管上~~

接下来,分析这段程序:

步骤0:对Display_Num任意赋一个值。(接下来就是分离Display_Num)

步骤1:打开除法器,并将Display_Num作为除法器的被除数,10作为除法器的除数。

步骤2:当除法器一轮完成之时,很明显此时而余数Reminder=1,

也正是我们要分离的第一个数,于是把它存到了NUM[0],而商Quotient此时等于432,将继续作为下一轮除法的被除数;

步骤3:判断是否分离出所以的数。

步骤4:分离完成,卡死于此。

要注意的是,这用的是一个16位的带符号的除法器,所以显示数的范围,是0到32768而已,当然稍加修改,就能显示负数啦~

到目前为止,这个程序也只是测试程序而已,检测是否能完成共能而已,而不是一个完整的接口。下次再来完成它吧~~

未完待续~~

完整的Digital代码如下:

module Digital (    input CLK,    input RSTn,    //--    output [7:0]Row_Scan_Sig,    output [5:0]Column_Scan_Sig); /***************************************/parameter    _0 = 8'b1100_0000, _1 = 8'b1111_1001, _2 = 8'b1010_0100,     _3 = 8'b1011_0000, _4 = 8'b1001_1001, _5 = 8'b1001_0010,     _6 = 8'b1000_0010, _7 = 8'b1111_1000, _8 = 8'b1000_0000,    _9 = 8'b1001_0000;   parameter    wei_1 = 6'b111110, wei_2 = 6'b111101, wei_3 = 6'b111011,    wei_4 = 6'b110111, wei_5 = 6'b101111, wei_6 = 6'b011111;//-------------------------除法器例化------------------------    reg[15:0] X1;           //被除数       reg[15:0] X2;       //除数         wire[15:0] Quotient;    //商     wire[15:0] Reminder;    //余数    reg Start_Sig = 1'b0;    wire Done;    Divider         Divi        (        .CLK(CLK),        .RSTn(RSTn),        .Start_Sig(Start_Sig),        .X1(X1),  //被除数           .X2(X2),  //除数             .Quotient(Quotient),        .Reminder(Reminder),        .Done(Done)    );//-------------------------利用除法器将数字分离出来----------------------------     reg [15:0]Display_Num;      //要显示的一个数字    reg [3:0]i = 4'd0;    reg [3:0]Num [0:5];         //内存的宽度4,内存的深度6;    reg [2:0]Num_i = 3'd0;    always @(posedge CLK)        case(i)            0:            begin                Display_Num <= 16'd4321;                i <= i + 1'b1;            end            1:            begin                Start_Sig <= 1'b1;                X1 <= Display_Num;                X2 <= 16'd10;                i <= i + 1'b1;            end            2:            begin                if(Done)                 begin                    Start_Sig <= 1'b0;                    X1 <= Quotient;                    X2 <= 16'd10;                    Num[Num_i] <= Reminder[3:0];                    i <= i + 1'b1;                end                else begin Start_Sig <= 1'b1; i <= i; end            end            3:            begin                    if(Num_i < 5)begin Num_i <= Num_i + 1'b1; i <= i - 1'b1;end                    else    i <= i + 1'b1;            end            4:            begin                i <= i;            end        endcase           /**************加码模块*************************/    reg [7:0]SegNum[5:0];    always @(posedge CLK)        case(Num[0])            0:SegNum[0] <= _0;            1:SegNum[0] <= _1;            2:SegNum[0] <= _2;            3:SegNum[0] <= _3;            4:SegNum[0] <= _4;            5:SegNum[0] <= _5;            6:SegNum[0] <= _6;            7:SegNum[0] <= _7;            8:SegNum[0] <= _8;            9:SegNum[0] <= _9;        endcase             always @(posedge CLK)        case(Num[1])            0:SegNum[1] <= _0;            1:SegNum[1] <= _1;            2:SegNum[1] <= _2;            3:SegNum[1] <= _3;            4:SegNum[1] <= _4;            5:SegNum[1] <= _5;            6:SegNum[1] <= _6;            7:SegNum[1] <= _7;            8:SegNum[1] <= _8;            9:SegNum[1] <= _9;        endcase         always @(posedge CLK)        case(Num[2])            0:SegNum[2] <= _0;            1:SegNum[2] <= _1;            2:SegNum[2] <= _2;            3:SegNum[2] <= _3;            4:SegNum[2] <= _4;            5:SegNum[2] <= _5;            6:SegNum[2] <= _6;            7:SegNum[2] <= _7;            8:SegNum[2] <= _8;            9:SegNum[2] <= _9;        endcase                 always @(posedge CLK)        case(Num[3])            0:SegNum[3] <= _0;            1:SegNum[3] <= _1;            2:SegNum[3] <= _2;            3:SegNum[3] <= _3;            4:SegNum[3] <= _4;            5:SegNum[3] <= _5;            6:SegNum[3] <= _6;            7:SegNum[3] <= _7;            8:SegNum[3] <= _8;            9:SegNum[3] <= _9;        endcase         always @(posedge CLK)        case(Num[4])            0:SegNum[4] <= _0;            1:SegNum[4] <= _1;            2:SegNum[4] <= _2;            3:SegNum[4] <= _3;            4:SegNum[4] <= _4;            5:SegNum[4] <= _5;            6:SegNum[4] <= _6;            7:SegNum[4] <= _7;            8:SegNum[4] <= _8;            9:SegNum[4] <= _9;        endcase         always @(posedge CLK)        case(Num[5])            0:SegNum[5] <= _0;            1:SegNum[5] <= _1;            2:SegNum[5] <= _2;            3:SegNum[5] <= _3;            4:SegNum[5] <= _4;            5:SegNum[5] <= _5;            6:SegNum[5] <= _6;            7:SegNum[5] <= _7;            8:SegNum[5] <= _8;            9:SegNum[5] <= _9;        endcase     //--------------------数码管扫描----------------------------     reg [19:0] C0 = 20'd0;    reg [5:0]Control = 6'b111110;    always @(posedge CLK)        if(C0 == 20'h1ffff)        begin            C0 <= 20'd0;            Control <= {Control[4:0],Control[5]};        end        else           begin            C0 <= C0 + 1'b1;        end         //------------将每一位数显示在对应的数码管上-----------------------            reg [7:0]Seg = 8'hff;    always @(posedge CLK)        case(Control)            wei_1: Seg <= SegNum[0];            wei_2: Seg <= SegNum[1];             wei_3: Seg <= SegNum[2];            wei_4: Seg <= SegNum[3];            wei_5: Seg <= SegNum[4];            wei_6: Seg <= SegNum[5];        endcase           assign Column_Scan_Sig = Control;    assign Row_Scan_Sig = Seg;   endmodule

提醒:《Verilog打造除法器驱动数码管(上)》最后刷新时间 2024-03-14 01:09:01,本站为公益型个人网站,仅供个人学习和记录信息,不进行任何商业性质的盈利。如果内容、图片资源失效或内容涉及侵权,请反馈至,我们会及时处理。本站只保证内容的可读性,无法保证真实性,《Verilog打造除法器驱动数码管(上)》该内容的真实性请自行鉴别。