数码管的驱动设计与验证
一、数码管驱动原理
其中 8 段数码管的结构图如下图所示
由上图可以看出数码管有两种结构:共阴极与共阳极。这两者的区别在于,公共端是连接到地还是高电平,对于共阴数码管需要给对应段以高电平才会使其点亮,而对于共阳极数码管则需要给低电平才会点亮。本文采用共阳数码管。同时为了显示数字或字符,必须对数字或字符进行编码译码。这里先不考虑小数点也就是简化为 7 段数码管,其编码译码格式如下表所示:
段式数码管工作方式有两种:静态显示方式和动态显示方式。静态显示的特点是每个数码管的段选必须接一个 8 位数据线来保持显示的字形码。当送入一次字形码后,显示字形可一直保持,直到送入新字形码为止。这种方法由于每一个数码管均需要独立的数据线因此硬件电路比较复杂,成本较高,很少使用。
为了节约 IO 以及成本一般采用如下图所示的电路结构,这样 3 个数码管接在一起就比静态的少了 7*2 个 I/O。
这样就实现了另一种显示模式,动态显示。动态显示的特点是将所有位数码管的段选线并联在一起,由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选,利用发光管的余辉和人眼视觉暂留作用,使人的感觉好像各位数码管同时都在显示。
现在举例假设将扫描时间定为 1S,这三个数码管分成 3s,第 1 秒时 sel 数据线上为b100,这时数码管 0 被选中,这时 a=0,数码管 0 的 LED0 就可以点亮;第 2 秒时 sel 数据线上为b010,这时数码管 1 被选中,这时 b=0,数码管 1 的 LED1 就可以点亮;第 3 秒时 sel 数据线上为b001,这时数码管 2 被选中,这时 c=0,数码管 2 的 LED2 就可以点亮。这时的效果就会是数码管 0 的 LED0 亮一秒后数码管 1 的 LED1 亮一秒最后是数码管 2 的 LED2 亮一秒,这样再次循环。这样如果使用 1ms 刷新时间的话由于数码管的余辉效应以及人的视觉暂留这样就会出现数码管 0 的 LED0、数码管 1 的 LED1 以及数码管 2 的 LED2 “同时”亮,并不会有闪烁感。
二、模块设计
三、工程实现
1.设计文件
module hex8(Clk,Reset_n,disp_data,sel,seg);input Clk;input Reset_n;input [31:0] disp_data;output reg [7:0] sel; //片选output reg [7:0] seg; //段选//分频为1kHz 即每1ms刷新一次//使用门控时钟reg[14:0] div_cnt; //分频计数 always@(posedge Clk or negedge Reset_n)if(!Reset_n)div_cnt <= 0;else if(div_cnt >= 24_999)div_cnt <= 0; elsediv_cnt <= div_cnt + 1;reg clk_1k; //此类门控时钟在大部分场合下不被允许always@(posedge Clk or negedge Reset_n)if(!Reset_n)clk_1k <= 0;else if(div_cnt == 24999)clk_1k <= ~clk_1k; //3位计数器 reg [2:0] sel_num;always@(posedge clk_1k or negedge Reset_n)if(!Reset_n)sel_num <= 0;else sel_num <= sel_num + 1; //3-8译码器always@(*)case(sel_num)0: sel = 8'b00000001;1: sel = 8'b00000010;2: sel = 8'b00000100;3: sel = 8'b00001000;4: sel = 8'b00010000;5: sel = 8'b00100000;6: sel = 8'b01000000;7: sel = 8'b10000000;endcase //多路选择器 reg[3:0]disp_tmp;always@(*)case(sel_num)7: disp_tmp = disp_data[31:28];6: disp_tmp = disp_data[27:24];5: disp_tmp = disp_data[23:20];4: disp_tmp = disp_data[19:16];3: disp_tmp = disp_data[15:12];2: disp_tmp = disp_data[11:8];1: disp_tmp = disp_data[7:4];0: disp_tmp = disp_data[3:0];endcase //LUT查找表always@(*)case(disp_tmp)0: seg = 8'hc0;1: seg = 8'hf9;2: seg = 8'ha4;3: seg = 8'hb0;4: seg = 8'h99;5: seg = 8'h92;6: seg = 8'h82;7: seg = 8'hf8;8: seg = 8'h80;9: seg = 8'h90;4'ha: seg= 8'h88;4'hb: seg= 8'h83;4'hc: seg= 8'hc6;4'hd: seg= 8'ha1;4'he: seg= 8'h86;4'hf: seg= 8'h8e;endcase endmodule
2.仿真文件
`timescale 1ns / 1ps
module hex8_tb();reg Clk;reg Reset_n;reg [31:0]disp_data;wire [7:0]sel;wire [7:0]seg;hex8_2 hex8(Clk,Reset_n,disp_data,sel,seg); initial Clk = 1;always#10 Clk = ~Clk;initial beginReset_n = 0;disp_data = 32'h00000000;#201;Reset_n = 1;#2000;disp_data = 32'h12345678;#10000000;disp_data = 32'h9abcdef0;#10000000;$stop;endendmodule
3.仿真结果
四、思考
关于门控时钟和使能时钟:使用门控时钟,将门控时钟直接作为DFF的工作时钟(即如上面第1部分代码所示),使用使能时钟的情况,DFF的工作时钟继续使用全局的高质量时钟,而将使能时钟作为DFF的使能信号使用(如下面代码所示)。关于时钟质量对FPGA设计的重要性,可以参考使能时钟与门控时钟的原理与差异视频学习。
module hex8_2(Clk,Reset_n,disp_data,sel,seg);input Clk;input Reset_n;input [31:0] disp_data;output reg [7:0] sel; //片选output reg [7:0] seg; //段选//分频为1kHz 即每1ms刷新一次//使用使能时钟reg[15:0] div_cnt; //分频计数 always@(posedge Clk or negedge Reset_n)if(!Reset_n)div_cnt <= 0;else if(div_cnt >= 49_999)div_cnt <= 0; elsediv_cnt <= div_cnt + 1;reg clk_1k; always@(posedge Clk or negedge Reset_n)if(!Reset_n)clk_1k <= 0;else if(div_cnt == 49999)clk_1k <= 1; else clk_1k <= 0;//3位计数器 reg [2:0] sel_num;always@(posedge Clk or negedge Reset_n)if(!Reset_n)sel_num <= 0;else if(clk_1k) sel_num <= sel_num + 1; //3-8译码器always@(posedge Clk)case(sel_num)0: sel = 8'b00000001;1: sel = 8'b00000010;2: sel = 8'b00000100;3: sel = 8'b00001000;4: sel = 8'b00010000;5: sel = 8'b00100000;6: sel = 8'b01000000;7: sel = 8'b10000000;endcase //多路选择器 reg[3:0]disp_tmp;always@(posedge Clk)case(sel_num)7: disp_tmp = disp_data[31:28];6: disp_tmp = disp_data[27:24];5: disp_tmp = disp_data[23:20];4: disp_tmp = disp_data[19:16];3: disp_tmp = disp_data[15:12];2: disp_tmp = disp_data[11:8];1: disp_tmp = disp_data[7:4];0: disp_tmp = disp_data[3:0];endcase //LUT查找表always@(posedge Clk)case(disp_tmp)0: seg = 8'hc0;1: seg = 8'hf9;2: seg = 8'ha4;3: seg = 8'hb0;4: seg = 8'h99;5: seg = 8'h92;6: seg = 8'h82;7: seg = 8'hf8;8: seg = 8'h80;9: seg = 8'h90;4'ha: seg= 8'h88;4'hb: seg= 8'h83;4'hc: seg= 8'hc6;4'hd: seg= 8'ha1;4'he: seg= 8'h86;4'hf: seg= 8'h8e;endcase endmodule
仿真结果