文章目录
- 前言
- 实验手册
- 一、实验目的
- 二、实验原理
- 三、系统架构设计
- 四、模块说明
- 1.模块端口信号列表
- 2.状态转移图
- 3.时序图
- 五、仿真波形图
- 六、代码编写
- 七、引脚分配
- 八、板级验证效果(拍照或录制视频)
前言
利用动态数码管的原理对秒表计时器的实现
实验手册
一、实验目的
用按键控制秒表的启动、停止和重置 2.用两个数码管显示秒表的分; 用两个数码管显示秒表的秒; 用两个数码管显示秒表的毫秒的百位和十位数值。
二、实验原理
1.理论原理
在数码管中由于有位选信号和段选信号的存在,我们无法做到将数码管同时进行变换,所以我们通过快速的切换和刷新,使得我们看到的是连续的多个数码管显示结果。通过控制位选信号和段选信号,可以实现不同的数字、字母或符号的动态显示。
用已经消抖的按键控制秒表计数器的停止、启动、复位。
2.硬件原理
开发板上的数码管数码管位选和段选信号都是低电平有效。
位选信号根据需要的显示位数进行生成(该实验所用的开发板可用6个位选信号),它们用于选择要显示的数码管。
段选信号根据需要的显示内容进行生成,它们用于控制数码管的七段LED灯以及小数点LED灯的亮灭状态(该实验所用的开发板可用8个段选信号)。
三、系统架构设计
由于数码管无法同时改变状态,我在此处采用了0.1ms的计数器对数码管进行刷新,人眼不会察觉到闪烁。
在取seg的值时我采用了两个计数器,一个是作为数码管的最小数值10ms,一个是最大数值60min,然后对60min计数器中的计数值取余、取整,分离出分、秒、毫秒的个位和十位。
我还设置两个按键控制该秒表的启动、停止、以及复位,按键1控制启动,再次按下则停止,按键4控制秒表复位。
四、模块说明
1.模块端口信号列表
dig_driver(数码管驱动模块)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | [1:0]key_in | 消抖后稳定按键信号 |
output | reg | [5:0]sel | 6位位选信号 |
output | reg | [7:0]seg | 8位段选信号 |
key(按键消抖模块)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | [1:0]key_in | 2位输入按键信号 |
output | reg | key_out | 输出稳定按键信号 |
top(顶层模块)
端口信号 | 信号类型 | 信号名称 | 信号作用 |
---|---|---|---|
input | wire | clk | 时钟信号 |
input | wire | rst_n | 复位信号 |
input | wire | [1:0]key_in | 2位输入按键信号 |
output | reg | [5:0]sel | 6位位选信号 |
output | reg | [7:0]seg | 8位段选信号 |
2.状态转移图
无
3.时序图
五、仿真波形图
仿真代码
c
`timescale 1ns/1ns
`define CYCLE 20
module tb (
);
//激励信号
/* input */ reg clk ;
/* input */ reg rst_n ;
/* input */ reg [3:0] key_in ;
//响应信号
/* output */ wire [7:0] seg ; //段选信号
/* output */ wire [5:0] sel ; //位选信号
//重定义参数
defparam
top.u_dig_driver.MAX_10ms = 5,
top.u_dig_driver.MAX_0_1ms = 1,
top.u_dig_driver.MAX_60min = 36000,
top.u_key.CNT_MAX = 1,
top.u_dig_driver.sec_unit = 600,
top.u_dig_driver.ms_unit = 10;
//时钟信号
always #(`CYCLE/2) clk = ~clk;
//激励开始
initial begin
clk = 1'b0;
rst_n = 1'b0;
key_in = 4'b1111;
#3
rst_n = 1'b1;
#10
key_in = 4'b1110;
#(`CYCLE*100)
key_in = 4'b1111;
#(top.u_dig_driver.MAX_60min*`CYCLE)
key_in = 4'b1110;
#(`CYCLE*100)
key_in = 4'b1111;
#(top.u_dig_driver.MAX_60min/2)
key_in = 4'b1111;
#(`CYCLE)
key_in = 4'b0111;
#(`CYCLE*100)
key_in = 4'b1111;
#(`CYCLE*100)
$stop(2);
end
//实例化
top
top (
/* input */ .clk (clk ) ,
/* input */ .rst_n(rst_n) ,
/* output reg [7:0] */ .seg (seg ) , //段选信号
/* output reg [5:0] */ .sel (sel ) //位选信号
);
endmodule
在这个仿真波形图中我们可以看到位选信号在我设置的频率中不停来回切换,在切换中计数器10ms每计数一次前两位位选信号加一,直到加到第二位位选信号9为止,而中间两位的位选信号则是加到第四位位选信号6为止,与预期效果一致。
六、代码编写
dig_driver(数码管驱动模块)
c
module dig_driver (
input clk ,
input rst_n ,
input [3:0] key_out ,
output reg [7:0] seg , //段选信号
output reg [5:0] sel //位选信号
);
wire [10:0] min;
wire [10:0] sec;
wire [10:0] ms ;
parameter MAX_10ms = 32'd499_999;
reg [31:0] cnt_10ms;
wire add_10ms;
wire end_10ms;
parameter MAX_0_1ms = 32'd4_999;
reg [31:0] cnt_0_1ms;
parameter MAX_60min = 32'd359_999;
reg [31:0] cnt_60min;
parameter sec_unit = 10'd6000,
ms_unit = 10'd100;
reg [4:0] seg_value;//seg的状态寄存器
parameter ZERO = 8'b1100_0000 ,
ONE = 8'b1111_1001 ,
TWO = 8'b1010_0100 ,
THREE = 8'b1011_0000 ,
FOUR = 8'b1001_1001 ,
FIVE = 8'b1001_0010 ,
SIX = 8'b1000_0010 ,
SEVEN = 8'b1111_1000 ,
EIGHT = 8'b1000_0000 ,
NINE = 8'b1001_0000 ;
//parameter sel_0 = 6'b111110,
// sel_1 = 6'b111101,
// sel_2 = 6'b111011,
// sel_3 = 6'b110111,
// sel_4 = 6'b101111,
// sel_5 = 6'b011111;
//1ms计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_10ms <= 1'd0;
end
else if (cnt_10ms == MAX_10ms) begin
cnt_10ms <= 1'd0;
end
else if (key_out[0]) begin
cnt_10ms <= cnt_10ms + 1;
end
else if (key_out[1]) begin
cnt_10ms <= cnt_10ms ;
end
end
assign add_10ms = 1'b1;
assign end_10ms = cnt_10ms == MAX_10ms;
//60min计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_60min <= 1'd0;
end
else if (end_10ms) begin
cnt_60min <= cnt_60min + 1;
end
else if (cnt_60min == MAX_60min) begin
cnt_60min <= 1'd0;
end
else begin
cnt_60min <= cnt_60min;
end
end
//对计数值进行除和取余获得相应的值
assign min = cnt_60min/sec_unit;
assign sec = cnt_60min%sec_unit/ms_unit;
assign ms = cnt_60min%sec_unit%ms_unit;
//0.1ms计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt_0_1ms <= 1'd0;
end
else if (cnt_0_1ms == MAX_0_1ms) begin
cnt_0_1ms <= 1'd0;
end
else begin
cnt_0_1ms <= cnt_0_1ms + 1;
end
end
//该板块无法同时变化,只能依次闪烁,该方法为依次闪烁的间隔,人肉眼无法判断 0.1ms
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sel <= 6'b011_111;
end
else if (cnt_0_1ms == MAX_0_1ms) begin
sel <= {sel[0],sel[5:1]};
end
else begin
sel <= sel ;
end
end
//每位位选信号的段选信号的值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
seg_value <= 5'd0;
end
case (sel)
6'b011111: seg_value <= ms%10;
6'b101111: seg_value <= ms/10;
6'b110111: seg_value <= sec%10;
6'b111011: seg_value <= sec/10;
6'b111101: seg_value <= min%10;
6'b111110: seg_value <= min/10;
default: seg_value <= 5'd0;
endcase
end
//根据seg_value显示数码管
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
seg <= 8'b11111111;
end
case (seg_value)
5'd0:begin seg <= ZERO ; end
5'd1:begin seg <= ONE ; end
5'd2:begin seg <= TWO ; end
5'd3:begin seg <= THREE ; end
5'd4:begin seg <= FOUR ; end
5'd5:begin seg <= FIVE ; end
5'd6:begin seg <= SIX ; end
5'd7:begin seg <= SEVEN ; end
5'd8:begin seg <= EIGHT ; end
5'd9:begin seg <= NINE ; end
default: seg <= seg;
endcase
end
endmodule
key(按键消抖模块)
c
module key
(
input wire clk,
input wire rst_n,
input wire key_in,
output reg key_out
);
parameter CNT_MAX =20'd999_999; //20ms计数
reg [19:0] cnt_20ms;
reg key_flag;
//20ms消抖
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt_20ms<=20'b0;
else if(key_in==1'b1)
cnt_20ms<=20'd0;
else if(cnt_20ms==CNT_MAX)
cnt_20ms<=CNT_MAX;
else
cnt_20ms<=cnt_20ms+20'd1;
//取单个脉冲信号
always@(posedge clk or negedge rst_n)
if(!rst_n)
key_flag<=1'b0;
else if(cnt_20ms==(CNT_MAX-20'd1))
key_flag<=1'b1;
else
key_flag<=1'b0;
//有效信号
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out <= 1'b0;
end
else if(key_flag == 1'b1)
key_out <= ~key_out;
else
key_out <= key_out;
end
endmodule
top(顶层模块)
c
module top (
input clk,
input rst_n,
input [3:0] key_in,
output [7:0] seg , //段选信号
output [5:0] sel //位选信号
);
wire [3:0]key_flag;
wire [3:0]key_out ;
key u_key//key_fsm
(
/* input wire */ .clk (clk ) ,
/* input wire */ .rst_n (rst_n ) ,
/* input wire */ .key_in (key_in[0]) ,
/* output reg */ .key_out (key_out[0])
);
key u_key1
(
/* input wire */ .clk (clk ) ,
/* input wire */ .rst_n (rst_n ) ,
/* input wire */ .key_in (key_in[1]) ,
/* output reg */ .key_out (key_out[1])
);
key u_key2
(
/* input wire */ .clk (clk ) ,
/* input wire */ .rst_n (rst_n ) ,
/* input wire */ .key_in (key_in[2]) ,
/* output reg */ .key_out (key_out[2])
);
key u_key3
(
/* input wire */ .clk (clk ) ,
/* input wire */ .rst_n (rst_n ) ,
/* input wire */ .key_in (key_in[3]) ,
/* output reg */ .key_out (key_out[3])
);
dig_driver u_dig_driver
(
/* input */ .clk (clk ) ,
/* input */ .rst_n (rst_n ) ,
/* input [3:0] */ .key_out (key_out) ,
/* output reg [7:0] */ .seg (seg) , //段选信号
/* output reg [5:0] */ .sel (sel) //位选信号
);
endmodule