数码管在我们的整个设计中,我们输入的数据与计算的结果都需要用数码管来显示,所以说数码管还是扮演着很重要的角色的。
1.1简析数码管的工作原理关于数码管的工作原理,为了照顾零基础的朋友,Kevin还是再进行下简单的介绍,如果是已经了解了数码管工作原理的朋友可以直接跳过这一节。
下面我们先来一张数码管的图片:
数码管是由多个发光二极管封装在一起组成的“8”字型器件,各发光二极管已经在器件内部完成连线。
对于数码管,有共阴极和共阳极之分,共阳极及共阴极数码管内部间的连线可以简化成下图:
图中的“COM”相当于是一个片选信号,例如,共阳极的位选“COM”为高时,即选中了该数码管,此时数码管才能工作起来。当“COM”选通之后,我们就可以选通相应的段选信号使数码管显示我们想要的字符了。比如说,我们想让数码管显示一个“0”(假设数码管是共阳极的),那我们相应的h~a的十六进制代码应为0xc0。
以下是共阴极与共阳极数码管的0~f编码(顺序是为h~a):
共阳:
0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
共阴:
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
1.2让数码管走起来在这一节中,我们来做这样的一个小练习,Kevin也是想通过这样的一个小练习来让大家基本掌握数码管的工作原理,同时也了解如何使用FPGA来驱动数码管。
先说一下实验要求:数码管从0~9循环计数,变化时间的间隔为1S。
首先,咱们先画一个该设计的简单的逻辑框图:
图中“Div_cnt”模块在1s计时到了之后,就让div_flag这个信号拉高一个时钟周期。“Seg_ctrl”为数码管控制模块,产生位选sel信号及数码管的显示字符信号seg,显然我们的seg信号的变化需要根据div_flag这个信号来控制。
下面可以根据我们的这个逻辑图框图来写代码了。
首先来写div_cnt这个模块:
- /********************************************************************
- * Module Name: div_cnt
- * Engineer : Kevin
- * Function : 1s计数模块
- * Blog Website : http://dengkanwen.com
- * Version : v1.0
- ********************************************************************/
- module div_cnt(
- input wire sclk, //系统时钟为50MHz,即周期为20ns
- input wire s_rst_n,
-
- output reg div_flag
- );
-
- parameter CNT_END = 26'd4999_9999; //1s计时结束
-
- reg [25:0] cnt; //计数器
-
- always @(posedge sclk or negedge s_rst_n)
- if(s_rst_n == 1'b0)
- cnt <= 26'd0;
- else if(cnt == CNT_END)
- cnt <= 26'd0;
- else
- cnt <= cnt + 1'b1;
-
- always @(posedge sclk or negedge s_rst_n)
- if(s_rst_n == 1'b0)
- div_flag <= 1'b0;
- else if(cnt == CNT_END)
- div_flag <= 1'b1;
- else
- div_flag <= 1'b0;
- endmodule
接下来,我们来写数码管的控制模块,在写控制模块之前,我们需要来看一下我们的电路原理图:
咱们先来简单的说一下这个数码管电路。电路图中总共有6位数码管,所以呢就有6个位选控制端,而这6个位选控制端是和三极管连在一块的,看着三极管的连法,应该知道,数码管是属于共阳极的。然而这6个数码管的位选是由3-8译码器来控制的,下面咱们再看一下3-8译码器与数码管位选端的连线图:
连线图已经出来了,可能有些朋友还不太清楚3-8译码器的原理,那咱们再来一张3-8译码器的真值表:
结合电路图及真值表,咱们可以看出,我们可以控制译码器的A、B、C这三个引脚来控制输出端Y电平的高低,从而就可以来控制我们数码管的位选端。例如,我们想让位选信号为S1的那个数码管工作起来,那我们就必须要先使之位选端有效,则需要使Y0的输出为低,所以我们ABC的信号应该为000.
在Kevin讲完之后,相信大家对咱们这个数码管电路的工作原理应该了解的差不多了。
我们只是让一个数码管亮起来,那我们索性也就只让位选信号为s1的那个数码管工作,其余数码管不工作。下面咱们就动手来写数码管控制模块的代码:
- /********************************************************************
- * Module Name : seg_ctrl
- * Engineer : Kevin
- * Function : 数码管显示控制模块
- * Blog Website : http://dengkanwen.com
- * Version : v1.0
- *********************************************************************/
- module seg_ctrl(
- input wire sclk,
- input wire s_rst_n,
- input wire div_flag,
-
- output wire [2:0 sel, //位选控制,连3-8译码器
- output reg [7:0 seg //段选信号
- );
-
- //========================共阳极数码管编码=========================================
- //0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
- //=================================================================================
-
- parameter ZERO = 8'hc0,
- ONE = 8'hf9,
- TWO = 8'ha4,
- THREE = 8'hb0,
- FOUR = 8'h99,
- FIVE = 8'h92,
- SIX = 8'h82,
- SEVEN = 8'hf8,
- EIGHT = 8'h80,
- NINE = 8'h90;
-
- reg [3:0 num_cnt; //用于寄存当前数码管应显示的值
- //num_cnt
- always @(posedge sclk or negedge s_rst_n)
- if(s_rst_n == 1'b0)
- num_cnt <= 4'd0;
- else if(num_cnt == 4'd9 && div_flag == 1'b1)
- num_cnt <= 4'd0;
- else if(div_flag == 1'b1)
- num_cnt <= num_cnt + 1'b1;
- //seg
- always @*
- case(num_cnt)
- 4'd0:
- seg <= ZERO;
- 4'd1:
- seg <= ONE;
- 4'd2:
- seg <= TWO;
- 4'd3:
- seg <= THREE;
- 4'd4:
- seg <= FOUR;
- 4'd5:
- seg <= FIVE;
- 4'd6:
- seg <= SIX;
- 4'd7:
- seg <= SEVEN;
- 4'd8:
- seg <= EIGHT;
- 4'd9:
- seg <= NINE;
- endcase
- //sel
- assign sel = 3'b000;
- endmodule
这两个模块写好了之后,下面咱们再写一个顶层模块,用于连接两个模块之间的连线。
- /********************************************************************
- * Module Name : seg_top
- * Eneigneer : Kevin
- * Function : 数码管0~9变换顶层模块
- * Blog Website : http://dengkanwen.com
- * Version : v1.0
- *********************************************************************/
- module seg_top(
- input wire sclk,
- input wire s_rst_n,
-
- output wire [2:0 sel,
- output wire [7:0 seg
- );
- wire div_flag;
-
- div_cnt div_cnt_inst(
- .sclk (sclk), //系统时钟为50MHz,即周期为20ns
- .s_rst_n (s_rst_n),
- .div_flag (div_flag)
- );
-
- seg_ctrl seg_ctrl_inst(
- .sclk (sclk),
- .s_rst_n (s_rst_n),
- .div_flag (div_flag),
- .sel (sel), //位选控制,连3-8译码器
- .seg (seg) //段选信号
- );
- endmodule
虽然Kevin对于这种简单的设计还是比较有自信的,但是为了教给大家一个良好的设计习惯,咱们还是进行仿真,所以接下来咱们来写一个仿真文件。 当然在仿真的时候,我们可以将div_cnt中的cnt计满的值写小一点。
- `timescale 1ns/1ns
- module tb_seg_top;
- reg sclk;
- reg s_rst_n;
-
- wire [2:0] sel;
- wire [7:0] seg;
-
- initial begin
- sclk = 1;
- s_rst_n <= 0;
- #20
- s_rst_n <= 1;
- end
-
- always #5 sclk = ~sclk;
-
- seg_top seg_top_inst(
- .sclk (sclk),
- .s_rst_n (s_rst_n),
- .sel (sel),
- .seg (seg)
- );
- endmodule
下面是咱们的仿真波形:
根据咱们的波形,可以说暂时是没有发现错误的,当然,Kevin下板子后也是板子也能正常工作。
本章剩余部分,请看评论哦
(备注:如果有发现内容错误,请大家及时指出,Kevin的QQ:1024726016)
转载请注明:邓堪文博客 » 【Kevin原创】《基于FPGA的简易计算器设计》第一章:玩转数码管
|