征途Pro开发板上使用的数码管型号为FJ3661BH,以及使用了两片74HC595芯片。
一. 硬件原理图
数码管为共阳极数码管,即段选为低电平点亮。
如图 所示开发板上搭载了两片74HC595芯片用于输出数码管驱动信号,其中我们接到了VCC防止数据清零,所以这两片74HC595芯片我们只用四个IO口控制即可。我们将两片74HC595进行级联,一片的Q7S输出端接到另一片的数据输入端DS,这样我们输入的14位串行输入数码管信号的前六位就会在 第二片就行输出。(注意:因为是移位寄存器,如果一次共输入14位数据,那么第一位输入的串行数据会在第二片74HC595芯片的Q5输出)。
74HC595芯片的使用:
-
首先把要传输的数据通过引脚DS输入到74HC595中。
-
产生SHCP时钟,将DS上的数据串行移入移位寄存器。
-
产生STCP时钟,将移位寄存器里的数据送入存储寄存器。
-
将引脚置为低电平,存储寄存器的数据会在Q0—Q7并行输出,同时并行输出的数据会被锁存起来。
二. Verilog代码设计实现595控制模块
1. 模块框图设计
输入段选信号,位选信号,系统时钟以及复位信号,输出ds,oe,stcp和shcp。
2. Verilog代码实现
module hc595_ctrl
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [5:0] sel ,
input wire [7:0] seg ,
output reg stcp ,
output reg shcp ,
output reg ds ,
output wire oe
);
reg [1:0] cnt_4 ;
reg [3:0] cnt_bit ;
wire [13:0] data ;
assign data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};
assign oe = ~sys_rst_n;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_4 <= 2'd0;
else if(cnt_4 == 2'd3)
cnt_4 <= 2'd0;
else
cnt_4 <= cnt_4 + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_bit <= 4'd0;
else if(cnt_4 == 2'd3 && cnt_bit == 4'd13)
cnt_bit <= 4'd0;
else if(cnt_4 == 2'd3)
cnt_bit <= cnt_bit + 1'b1;
else
cnt_bit <= cnt_bit;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
stcp <= 1'b0;
else if(cnt_bit == 4'd13 && cnt_4 == 2'd3)
stcp <= 1'b1;
else
stcp <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
shcp <= 1'b0;
else if(cnt_4 >= 4'd2)
shcp <= 1'b1;
else
shcp <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
ds <= 1'b0;
else if(cnt_4 == 2'd0)
ds <= data[cnt_bit];
else
ds <= ds;
endmodule
三. 数码管显示实例
( 以实现模60计数器为例,计数时间间隔为1s)
1. 总体模块设计
模60计数器的实现包括计数模块timer,数据显示模块display,595控制模块hc595_ctrl以及顶层模块counter60。模块display_595包含595控制模块以及数据显示模块。
整体RTL视图如下:
display_595模块如下:
2. 计时模块timer:
实现数据从00-59的循环计数,时间间隔为1s。Verilog代码描述如下:
module timer
(
input wire sys_clk ,
input wire sys_rst_n ,
output reg [7:0] timer ,
output wire [5:0] point ,
output reg seg_en ,
output wire sign
);
parameter CNT_MAX = 26'd24_999_999;
reg [25:0] cnt_1s ;
reg clk1 ;
assign point = 6'b000_000;
assign sign = 1'b0 ;
always@( posedge sys_clk or negedge sys_rst_n )
if( sys_rst_n == 1'b0 )
cnt_1s <= 26'd0 ;
else if( cnt_1s == CNT_MAX )
cnt_1s <= 26'd0 ;
else
cnt_1s <= cnt_1s + 1'b1 ;
always@( posedge sys_clk or negedge sys_rst_n )
if( sys_rst_n == 1'b0 )
clk1 <= 1'b0 ;
else if( cnt_1s == CNT_MAX )
clk1 <= ~ clk1 ;
else
clk1 <= clk1 ;
always@( posedge clk1 or negedge sys_rst_n )
if( sys_rst_n == 1'b0 )
timer <= 8'b0000_0000;
else if( timer[7:4] == 4'd5 && timer[3:0] == 4'd9 )
timer <= 8'd0000_0000;
else if( timer[3:0] == 4'd9 )
begin
timer[7:4] <= timer[7:4] + 1'b1 ;
timer[3:0] <= 4'd0 ;
end
else
timer[3:0] <= timer[3:0] + 1'b1 ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
seg_en <= 1'b0;
else
seg_en <= 1'b1;
endmodule
3. 数据显示模块display
六位八段数码管扫描显示。由于数码管的余辉效应以及人眼的视觉残留,动态扫描的方式能实现数码管的动态显示。Verilog代码如下:
module display
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] timer ,
input wire [5:0] point ,
input wire seg_en ,
input wire sign ,
output reg [5:0] sel ,
output reg [7:0] seg
);
parameter CNT_MAX = 16'd49_999;
reg [15:0] cnt_1ms ;
reg flag_1ms ;
reg [2:0] cnt_sel ;
reg [3:0] data ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_1ms <= 16'd0;
else if(cnt_1ms == CNT_MAX)
cnt_1ms <= 16'd0;
else
cnt_1ms <= cnt_1ms + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
flag_1ms <= 1'b0;
else if(cnt_1ms == CNT_MAX )
flag_1ms <= 1'b1;
else
flag_1ms <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_sel <= 3'd0;
else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
cnt_sel <= 3'd0;
else if(flag_1ms == 1'b1)
cnt_sel <= cnt_sel + 1'b1;
else
cnt_sel <= cnt_sel;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
sel <= 6'b000_000;
else if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
sel <= 6'b000_001;
else if(flag_1ms == 1'b1)
sel <= sel << 1;
else
sel <= sel;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
data <= 4'b0 ;
else if( seg_en == 1'b1 && flag_1ms == 1'b1 )
case( cnt_sel )
3'd0: data <= timer[3:0] ;
3'd1: data <= timer[7:4] ;
3'd2: data <= 4'd11 ;
3'd3: data <= 4'd11 ;
3'd4: data <= 4'd11 ;
3'd5: data <= 4'd11 ;
default:data <= 4'b0 ;
endcase
else
data <= data ;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
seg <= 8'b1111_1111 ;
else
case(data)
4'd0 : seg <= 8'b1100_0000 ;
4'd1 : seg <= 8'b1111_1001 ;
4'd2 : seg <= 8'b1010_0100 ;
4'd3 : seg <= 8'b1011_0000 ;
4'd4 : seg <= 8'b1001_1001 ;
4'd5 : seg <= 8'b1001_0010 ;
4'd6 : seg <= 8'b1000_0010 ;
4'd7 : seg <= 8'b1111_1000 ;
4'd8 : seg <= 8'b1000_0000 ;
4'd9 : seg <= 8'b1001_0000 ;
4'd10 : seg <= 8'b1111_1111 ;
4'd11 : seg <= 8'b1111_1111 ;
default:seg <= 8'b1100_0000 ;
endcase
endmodule
4. display_595模块
该模块中实现对display模块以及hc595_ctrl模块的例化。
module display_595
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [7:0] timer ,
input wire [5:0] point ,
input wire seg_en ,
input wire sign ,
output wire stcp ,
output wire shcp ,
output wire ds ,
output wire oe
);
wire [7:0] seg ;
wire [5:0] sel ;
display display_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.timer (timer ),
.point (point ),
.seg_en (seg_en ),
.sign (sign ),
.sel (sel ),
.seg (seg )
);
hc595_ctrl hc595_ctrl_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.sel (sel ),
.seg (seg ),
.stcp (stcp ),
.shcp (shcp ),
.ds (ds ),
.oe (oe )
);
endmodule
5. 顶层模块counter60
该模块中实现对所有底层模块的例化。
module counter60
(
input wire sys_clk ,
input wire sys_rst_n ,
output wire stcp ,
output wire shcp ,
output wire ds ,
output wire oe
);
wire [7:0] timer ;
wire [5:0] point ;
wire seg_en ;
wire sign ;
timer timer_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.timer (timer ),
.point (point ),
.seg_en (seg_en ),
.sign (sign )
);
display_595 display_595_inst
(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n ),
.timer (timer ),
.point (point ),
.seg_en (seg_en ),
.sign (sign ),
.stcp (stcp ),
.shcp (shcp ),
.ds (ds ),
.oe (oe )
);
endmodule
6. counter60模块对应的开发板引脚配置
7. 程序烧录结果
开发板六位数码管最后两位显示数据00-59的循环计数,时间间隔为1s。