本帖最后由 zhangyuhua 于 2017-3-19 15:22 编辑
今天接着写倒计时系统的最后一个子模块之数码管显示模块。现在一般使用的是七段的数码管,再加上一个小数点位,共八段。
这个模块比较简单,直接看下代码。
(1)数码管模块框图
(2)源代码
//`define SIM
`define SMG_NUM 6 //six smg
module smg_display(
//System Signals
input sclk ,
input rst_n ,
//Others
input [`SMG_NUM*4-1:0] bcd_din , //[23:0]
//Interface
output reg [2:0] smg_sel ,
output reg [7:0] smg_seg
);
//========================================================================\
// **********Define Parameter and Internal Signals*******************
//========================================================================/
//light is 0
parameter NUM_0 = 8'b1100_0000 ; //c0 //dp,g,f,e,d,c,b,a
parameter NUM_1 = 8'b1111_1001 ; //f9
parameter NUM_2 = 8'b1010_0100 ; //a4
parameter NUM_3 = 8'b1011_0000 ; //b0
parameter NUM_4 = 8'b1001_1001 ; //99
parameter NUM_5 = 8'b1001_0010 ; //92
parameter NUM_6 = 8'b1000_0010 ; //82
parameter NUM_7 = 8'b1111_1000 ; //f8
parameter NUM_8 = 8'b1000_0000 ; //80
parameter NUM_9 = 8'b1001_0000 ; //90
parameter NUM_ERR = 8'b1111_1111 ; //ff
//`ifndef SIM
parameter TIME_20US = 1000 ;
//`else
//parameter TIME_20US = 20 ;
//`endif
//time 20us
reg [9:0] cnt_20us ;
wire add_cnt_20us ;
wire end_cnt_20us ;
//smg sel
reg [2:0] cnt_sel ;
wire add_cnt_sel ;
wire end_cnt_sel ;
reg [2:0] smg_sel_r ;
reg [3:0] smg_seg_temp ;
//========================================================================\
// **********Main Code*******************
//========================================================================/
//time 20us
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
cnt_20us<='d0;
else if(add_cnt_20us)
if(end_cnt_20us)
cnt_20us<='d0;
else cnt_20us<=cnt_20us+1'b1;
end
assign add_cnt_20us=1'b1;
assign end_cnt_20us=add_cnt_20us && cnt_20us==TIME_20US-1;
//smg sel
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
cnt_sel<=3'd1;
else if(add_cnt_sel)
if(end_cnt_sel)
cnt_sel<='d0;
else cnt_sel<=cnt_sel+1'b1;
end
assign add_cnt_sel=end_cnt_20us;
assign end_cnt_sel=add_cnt_sel && cnt_sel==3'd7;
always @(posedge sclk or negedge rst_n)
begin
if(!rst_n)
begin
smg_sel_r<=3'd0;
smg_sel<=3'd0;
end
else if(end_cnt_20us)
begin
smg_sel_r<=cnt_sel;
smg_sel<=~cnt_sel;
end
end
//smg_seg
always @(posedge sclk or negedge rst_n )
begin
if(!rst_n)
smg_seg_temp<='d0;
else if(end_cnt_20us)
begin
case(smg_sel_r)
'd1:smg_seg_temp<=bcd_din[3:0];
'd2:smg_seg_temp<=bcd_din[7:4];
'd3:smg_seg_temp<=bcd_din[11:8];
'd4:smg_seg_temp<=bcd_din[15:12];
'd5:smg_seg_temp<=bcd_din[19:16];
'd6:smg_seg_temp<=bcd_din[23:20];
default:smg_seg_temp<=smg_seg_temp;
endcase
end
end
always@(posedge sclk or negedge rst_n)
begin
if(!rst_n)
smg_seg<=NUM_0;
else case (smg_seg_temp)
0 : smg_seg <= NUM_0;
1 : smg_seg <= NUM_1;
2 : smg_seg <= NUM_2;
3 : smg_seg <= NUM_3;
4 : smg_seg <= NUM_4;
5 : smg_seg <= NUM_5;
6 : smg_seg <= NUM_6;
7 : smg_seg <= NUM_7;
8 : smg_seg <= NUM_8;
9 : smg_seg <= NUM_9;
default : smg_seg <= NUM_ERR;
endcase
end
endmodule
(3)源代码讲解
这个模块的重点有两部分。一是,6个数码如何实现同时亮多个数码管;二是,如何实现某个特定数码管,来显示输入数据bcd_din[23:0]的某4位数。下面详细介绍这两部分内容。
A. 6个独立的数码管如何同时多个被点亮
本质上6个数码管是一个一个被点亮的,但由于人眼的余光效应,扫描数码管的频率足够快,这样人眼就会看到多个数码管同时被点亮。查阅资料可知,当扫描周期T=20us时,人眼余光开始起作用。由于使用的晶振为50MHZ,所以这里使用了一个计满1000的计数器。(即是,每20us选中一个数码管)
B. 输入数据bcd_din[23:0]如何每4位一组被分别显示
由于数码管从左至右的编号依次是smg_sel[2:0]=0,1,2,3,4,5.而要求smg_sel[2:0]=5显示bcd_din[3:0]、smg_sel[2:0]=4显示bcd_din[7:4]、smg_sel[2:0]=3显示bcd_din[11:8]、smg_sel[2:0]=2显示bcd_din[15:12]、smg_sel[2:0]=1显示bcd_din[19:16]、smg_sel[2:0]=0显示bcd_din[23:20].为了实现上述功能,使用了一次翻转操作。具体看源代码。
(4)顶层模块
一般顶层模块中,只包含例化模块,不需要复杂的代码编写。例化模块的关键是,将各个端口正确的连接。module top(
//System Signals
input sclk ,
input rst_n ,
//Interface
input [1:0] key ,
output [2:0] smg_sel ,
output [7:0] smg_seg
);
//========================================================================\
// **********Define Parameter and Internal Signals*******************
//========================================================================/
wire dout_vld2bcd_trig ;
wire [7:0] cnt_ms2din ;
wire [7:0] cnt_s2din ;
wire [7:0] bcd_cnt_s2smg ;
wire [7:0] bcd_cnt_ms2smg ;
//========================================================================\
// **********Main Code*******************
//========================================================================/
//-------------Inst------------
cnt inst_cnt(
//System Signals
.sclk (sclk ),
.rst_n (rst_n ),
//Others
.key (key ), //key[1]---Start/Stop
//key=1----The key is not pressed;
//key=0----The key is pressed
//key[0]---Rst
.dout_vld (dout_vld2bcd_trig ), //to tell next module start work
.cnt_s (cnt_s2din ),
.cnt_ms (cnt_ms2din )
);
bcd inst_bcd_cnt_ms(
//System Signals
.sclk (sclk ),
.rst_n (rst_n ),
//Others
.bcd_trig (dout_vld2bcd_trig ),
.din (cnt_ms2din ), //max is 99
//cnt_ms[7:0],cnt_s[7:0]
.bcd_data (bcd_cnt_ms2smg ), //two smg:4bit+4bit
//bcd_cnt_ms[7:0],bcd_cnt_s[7:0]
.bcd_data_vld (bcd_data_vld )
);
bcd inst_bcd_cnt_s(
//System Signals
.sclk (sclk ),
.rst_n (rst_n ),
//Others
.bcd_trig (dout_vld2bcd_trig ),
.din (cnt_s2din ), //max is 99
//cnt_ms[7:0],cnt_s[7:0]
.bcd_data (bcd_cnt_s2smg ), //two smg:4bit+4bit
//bcd_cnt_ms[7:0],bcd_cnt_s[7:0]
.bcd_data_vld (bcd_data_vld )
);
smg_display inst_smg_display(
//System Signals
.sclk (sclk ),
.rst_n (rst_n ),
//Others
.bcd_din ({8'h00,bcd_cnt_s2smg,bcd_cnt_ms2smg}), //[23:0]
//Interface
.smg_sel (smg_sel ),
.smg_seg (smg_seg )
);
endmodule
(5)总结
到这,整个倒计时系统算设计完成,下板调试成功。
良好的代码编写风格,在设计复杂系统中,体现的优势越明显。所以为了以后再次修改或使用以前编写的代码。一定要有良好的编写代码风格。
|