FPGA基础入门【5】数码管仿真及实现

您所在的位置:网站首页 74ls47接数码管 FPGA基础入门【5】数码管仿真及实现

FPGA基础入门【5】数码管仿真及实现

2023-12-13 13:27| 来源: 网络整理| 查看: 265

上一篇博文介绍了NEXYS 4的第一个工程blink闪烁,初步了解了FPGA使用的流程,从这一篇开始以难度从低到高的层次逐个介绍开发板上的接口。这次就用上按钮和数码管。

每次把过程写出来都担心写的不清楚,如果有看不懂请随时留言,我会尽快回复并添加内容。

FPGA基础入门【5】数码管 数码管介绍功能设计代码按键消抖十位计数器数码管译码器 仿真编译及烧写

数码管介绍

细节可以参考 NEXYS 4 DDR manual

数码管是用来显示数字的led阵列,由7个LED显示数字并加上一个LED显示小数点。数码管分共阴极和共阳极,共阳极的意思是这八个LED的阳极是连到同一个信号上的,共阴极则是八个LED的阴极是连到同一个信号上的。之所以这么做是因为控制8个LED原本需要16根线,如此可以减少控制线。 segment1 并且共阳极和共阴极的信号还可以作为使能端,只有共阳极的信号为高或者共阴极信号为低时那一个数码管才会显示。再加上人眼只能识别24Hz的闪烁,因此只要以一定的频率持续扫描各个数码管,就不需要保持它们持续点亮,于是另外8个LED的控制信号也可以在数码管中共享,更减少了控制信号。这种控制方式叫做分时复用,典型例子就是红绿灯 segment2 查阅手册,可以看到NEXYS 4中的数码管相关引脚。由于8个AN引脚是由NPN型三极管接到高电平的,而NPN型三极管在低电平是触发联通,因此要想数码管内特定LED亮起,比如最低位的D,则要AN0和CD都输出低电平 segment3 另外,我们这次只使用低4位的数码管,要求高4位不亮。但CA-CG端口是八位共用的,我们需要把AN4到AN7接高电平。

功能设计

这次希望完成的功能是计数器,每次按键按下就加一,而开关可以将其清零。

这里需要按键消抖模块,每次按钮按下时输出一个脉冲。由于人手按下按键相对于电路不是很快,会造成几个毫秒的不稳定信号,而按住的时间往往在百毫秒级以上,因此不能仅仅靠按钮信号上升沿来计算,不然按一次会上升几个数。采取的做法是每5ms读取一次按钮输入,并且保存上一次按钮输入状态,仅仅当这两个状态显示由低到高的时候才输出高电平,否则输出低电平。

十进制的计数器的输入是按钮脉冲,输出是4个4-bit数,分别代表4位数码管要输出的数。每个4位数共有11个状态,数字0-9以及4’ha用来表示空白,这样数码管翻译器可以在这个状态时输出空白。初始状态为全空白,随着每次按钮按下而加一,在进位时会将原来空白的高位填充上。

数码管翻译器输入是来自十进制计数器的4个4位数,输出是8位数码管CA-CG、DP,以及8位AN0-AN7。此翻译器需要每1ms切换到下一个数码管并输出,4个数码管总共是4ms,刷新频率250Hz,高于人眼识别频率。

设计如下:

design

代码

顶层模块的设计较为简单,主要按照设计将输入输出以及各个内部组成相互连接。

// Verilog code for segment project module segment( input clock, input reset, input button, output [7:0] segment_out, output [7:0] digit ); // Pushdown detection wire pushdown; pushdown_detect pd( .clock(clock), .button(button), .pushdown(pushdown) ); // Decimal counter digit0-3 wire [3:0] counter0; wire [3:0] counter1; wire [3:0] counter2; wire [3:0] counter3; wire [2:0] carry; decimal_counter d0( .clock (clock), .reset (reset), .carryin (pushdown), .carryout (carry[0]), .result (counter0) ); decimal_counter d1( .clock (clock), .reset (reset), .carryin (carry[0]), .carryout (carry[1]), .result (counter1) ); decimal_counter d2( .clock (clock), .reset (reset), .carryin (carry[1]), .carryout (carry[2]), .result (counter2) ); decimal_counter d3( .clock (clock), .reset (reset), .carryin (carry[2]), .carryout (), .result (counter3) ); // Segment translation segment_trans trans( .clock (clock), .reset (reset), .counter0 (counter0), .counter1 (counter1), .counter2 (counter2), .counter3 (counter3), .digit (digit[3:0]), .segment_out (segment_out) ); assign digit[7:4] = 4'b1111; endmodule

主框架如下,输入为时钟、复位和按钮信号,输出为数码管AN0-AN7以及CA-CG、DP。

module segment( input clock, input reset, input button, output [7:0] segment_out, output [7:0] digit ); endmodule

下面调用了按钮消抖模块,pushdown作为内部信号输入到十进制计数器。调用子模块的方法和testbench里面调用的方式一样。

// Pushdown detection wire pushdown; pushdown_detect pd( .clock(clock), .button(button), .pushdown(pushdown) );

十进制计数器模块做成了每一位都可以拆分的形式,每一位除了时钟和复位信号之外,会在每次carryin端口收到脉冲信号时加一,并在需要进位时在carryout输出高电平传输到更高位的十进制计数器。在复位时默认输出4’hA,在后面的数码管译码器会转成空白输出。

这样设计的原因是以后也有可能用到十进制计数器,位数未知,这样写方便将来调用。

// Decimal counter digit0-3 wire [3:0] counter0; wire [3:0] counter1; wire [3:0] counter2; wire [3:0] counter3; wire [2:0] carry; decimal_counter d0( .clock (clock), .reset (reset), .carryin (pushdown), .carryout (carry[0]), .result (counter0) ); decimal_counter d1( .clock (clock), .reset (reset), .carryin (carry[0]), .carryout (carry[1]), .result (counter1) ); decimal_counter d2( .clock (clock), .reset (reset), .carryin (carry[1]), .carryout (carry[2]), .result (counter2) ); decimal_counter d3( .clock (clock), .reset (reset), .carryin (carry[2]), .carryout (), .result (counter3) );

下面调用数码管译码器,counter0-3是十进制计数器发送的4位数,数码管译码器可以分时复用,以每4ms刷新一次的速度将其翻译成数码管信号输出。由于CA-CG信号共用,需要调用assign语句将AN4-7拉高以防止高4位显示。

前面说过寄存器reg是用always模块来调用赋值的,这里展示的是wire线网的赋值方式,由于它可以看做是一根导线,因此它没有延时概念。

// Segment translation segment_trans trans( .clock (clock), .reset (reset), .counter0 (counter0), .counter1 (counter1), .counter2 (counter2), .counter3 (counter3), .digit (digit[3:0]), .segment_out (segment_out) ); assign digit[7:4] = 4'b1111; 按键消抖 // Detect button pushdown module pushdown_detect( input clock, // 100MHz clock input input button, output reg pushdown ); reg [31:0] counter; reg [1:0] button_p; // previous button status // Count to 500k // detect button status every 5ms always @(posedge clock) begin if(counter < 32'd500000) begin counter


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3