设计要求
1:能实现0到59秒的自动周而复始计时
2:通过键盘能暂停计时
3:通过键盘能倒计时59到0
4: 通过键盘能复位计时从0开始
5: 计时结果通过两位七段码显示
可选拓展设计
1:实现小时、分钟、秒的全部显示
2:能设置初始时间
3:整点报时
4:自行设计其他功能,如闪烁显示等
五、实验步骤与实验结果
1、编写一个程序,实现实验的要求,我们实现的功能如下,可以重置,可以计数(从0开始),可以倒计时(59到0),计时结束蜂鸣器会响,可以当电子表使用,用6个数码位显示,区分小时,分钟和秒数,可以通过按键进行调时间。实现的结果见附件一。
16代表的是小时
.用来分割时分秒
25代表的是分
32代表的是秒
可以根据图2-2给出的变量清单实现不同的操作
图1 测试1运行结果图
2、实验准备。变量声明和引脚声明,我将此系统定义为mysystem,定义了8个输入变量,3个输出变量,端口和变量声明清单见图2-1变量清单,mysystem的原理图见图2-2。
端口 | 引脚 | 作用 |
clkin | 16 | 20mhz时钟的输入 |
rst_n | 51 | 置0 |
zt | 52 | 暂停计数和计时工作,此时可以调时间 |
jishu | 52 | 计数开始 |
jishi | 53 | 倒计时开始 |
thour | 48 | 调小时0-23 |
tmin | 49 | 调分钟0-59 |
tsecond | 50 | 调秒0-59 |
fm | 79 | 蜂鸣器,倒计时结束会响 |
dig_sel_n | 111-114,129-132 | 控制数码位,只用1到6,7-8长暗,1和2代表秒,3和4代表分钟,5和6代表小时,中间有“.”来区分小时,分钟和秒数 |
seg_out | 125-128,119-122 | 控制7段码 |
图2-1 变量清单
图2-2 mysystem的原理图
3、总体的实现过程。我们通过分块解决的方案,逐步实现所有功能,先实现的计数再实现的倒计时,再实现的计数结束后蜂鸣器响,再实现的计数器可以最大显示24小时再实现可以调时间,最后实现用“.“区分秒数分钟和小时。图3-1mysystem总体实现思路
图3-1 mysystem总体实现思路
4、分频器的实现。用到的需要分频成1s和1ms的两个分频器,前者给计数器使用,后者给7段码显示用,7段码显示的原理就是通过人眼的视觉停留快速刷新达到不动的效果。模块实现图4-1
图4-1 两个分频器的实现
5、计数器模块的实现。计数器内部分为计数和倒计时,实现的思路见图5-1。实现的时候会通过jishu和jishi这两个引脚的情况进行选择是倒计时还是计数,倒计时的时候数从59记到0的时候蜂鸣器会响。对于内部计数的时候要注意,计数的时候需要注意秒-分-小时之间的换算,当够60秒时分钟会进1,分钟够60的时候会进一,小时够24会清零,调时间的时候会搭配其余三个小模块进行工作。
图5-1 mysystem的原理图
6、段码显示模块。7段码译码器模块和输出模块并用,实现的思路见图6-1。
图6-1 7段码模块
六、实验讨论与总结
1.做数字系统设计的时候一定要注意系统和原理图设计的不同,本次实验是纯verilog代码,需要有完整的思路,学会的是自顶向下的设计思路,先从一个简单的小模块开始设计,逐步的增加功能
2.代码中的最需要注意的问题一定要设置好变量的宽度,尽量可以大一点但坚决不能小了,完美起见还是用多大设置多大,设计小了错误还不容易找错误
3.命名要符合代码的规范,要声明好输入的变量和输出的变量的,要设置好位数,位数匹配也是需要注意的
4.分频器模块的设置,1s的和1ms的异曲同工,区别在于分出来大小的不同
5.调时间的输入端口要连接在47-50这几个消抖的按钮上,不然调时间输入的时候会一下跳好几个数
6.计数器的设计是本实验的重点。在计数器设计上要明确设计的思路,倒计时和计数功能分开实现的同时要注意两者的共性
7.段码显示也是一个重点,需要用到译码器,附件一的代码通过设置一个译码器功能的函数,将数据的个位和十位分离进行调用处理,我还对段码显示的小点点进行了处理,也就是对小时分钟秒数进行了分割,便于观看
8.实验的优缺点:实验要求的基本功能全部实验,扩展功能也基本实现。后期会对写的Verilog程序进一步优化设计,进一步添加需要的功能,比如说在一个按键重复使用,我本次实验用到的端口比较多,解决这个问题会进一步节省端口。
附件一:程序源代码
1、mysystem程序清单如下:
//定义了一个模块系统,20mhz时钟脉冲,重置,暂停,蜂鸣器,计数(0-59),计时(倒计时),调时间,7段码,数码位
module mysystem (clkin,rst_n,zt,fm,jishu,jishi,thour,tmin,tsecond,seg_out,dig_sel_n);
//输入端口
input clkin,rst_n,zt,jishu,jishi,thour,tmin,tsecond;
//1ms的分频器
reg clk1ms;
//计数用
reg [17:0] cnt2;
//1ms----20MHZ入1000HZ输出
always @(posedge clkin or negedge rst_n)
begin
//如果重置的话
if(!rst_n)
cnt2 <=0;
//如果不重置的话
else
begin
if(cnt2 == 19_999)
cnt2 <=0;
else
cnt2 <= cnt2 + 1;
if (cnt2 <10_000)
clk1ms <= 1'b1;
else
clk1ms <= 1'b0;
end
end
//分频得到1s的输出,七段码更替会用到
reg clk1S;
reg [24:0]cnt1;
//1s----20MHZ入1HZ输出
always @(posedge clkin or negedge rst_n)
begin
if(!rst_n)
cnt1 <= 0;
else
begin
if(cnt1 == 19_999_999)
cnt1 <=0;
else
cnt1 <= cnt1 + 1;
if (cnt1 <10_000_000)
clk1S <= 1'b1;
else
clk1S <= 1'b0;
end
end
//6进制选择器,实现六个数码位的交替
reg [2:0]cnt22;
always @(posedge clk1ms)
begin
if(!rst_n)
cnt22 <= 0;
else
begin
if(cnt22 == 5)
cnt22 <=0;
else
cnt22 <= cnt22 + 1;
end
end
//时间调节
reg [5:0] cnt60_s;
reg [5:0] co1;
reg [4:0] myhour1;
always @(posedge thour or negedge rst_n)
begin
if(!rst_n)
myhour1<=0;
else if(thour)
begin
if(myhour1==23)
myhour1<=0;
else
myhour1<=myhour1+1;
end
end
always @( posedge tmin or negedge rst_n)
begin
if(!rst_n)
co1<=0;
else if(tmin)
begin
if(co1==59)
co1<=0;
else
co1<=co1+1;
end
end
always @(posedge tsecond or negedge rst_n)
begin
if(!rst_n)
cnt60_s<=0;
else if(tsecond)
begin
if(cnt60_s==59)
cnt60_s<=0;
else
cnt60_s<=cnt60_s+1;
end
end
//******************计数器(0-59)秒(重复进行)******倒计时59-0(不重复)
output reg fm;//这是一个蜂鸣器
reg [5:0] rcnt60=6'b11_1011;//定义59
reg [5:0] cnt60;//记录秒数
reg [5:0] co;//记录分钟
reg [4:0] myhour;//记录小时
always @(posedge clk1S or negedge rst_n or posedge zt)
begin
//置零的时候会实现cnt60(0-59计数器)清0,实现rcnt60(计时器)恢复59,而且不响铃
if(!rst_n)
begin cnt60 <= 0;rcnt60<=6'b11_1011;fm<=0;co<=0;myhour<=0;end
//暂停计时和计数
else if(zt)
begin cnt60 <= cnt60; rcnt60<=rcnt60;
if(thour)
myhour<=myhour1;
if(tmin)
co<=co1;
if(tsecond)
cnt60<=cnt60_s;
end
else
//看看是计时还是计数
case({jishu,jishi})
//当是00的时候处于暂停的状态,此时会把蜂鸣器fm设置为0,不响的状态
2'b00:begin fm<=0;end
//倒计时
2'b01:begin
if(rcnt60 == 0)
begin rcnt60<=0;fm<=1;cnt60<=rcnt60; end
else
begin rcnt60 <= rcnt60 - 1;cnt60<=rcnt60; fm<=0;end
end
//计数器
2'b10: begin
if(cnt60 == 59)
begin cnt60<=0;
if(co==59)
begin
co<=0;
if(myhour==23)
myhour<=0;
else
myhour<=myhour+1;
end
else
co<=co+1;
end
else begin cnt60 <= cnt60 + 1; fm<=0; end
end
endcase
end
//7段码译码器,这是定义了一个函数,4位的输入8位的输出
//4位会对应0到9,0000~1001,seg7就是七段码的输出,从后往前对应a,b,c,d,e,f,g,dp,fpga实验板是共阳极,也就是说输入0的时候才是有效的
output reg [7:0] seg_out;
output reg [7:0] dig_sel_n;
function [7:0] seg7;
input [3:0] data;
begin
case(data)
0: seg7= 8'b11000000;
1: seg7= 8'b11111001;
2: seg7= 8'b10100100;
3: seg7= 8'b10110000;
4: seg7= 8'b10011001;
5: seg7= 8'b10010010;
6: seg7= 8'b10000010;
7: seg7= 8'b11111000;
8: seg7= 8'b10000000;
9: seg7= 8'b10010000;
default: seg7= 8'b11111111;
endcase
end
endfunction
//用来显示小点点,便于区分小时,分钟和秒
function [7:0] seg77;
input [3:0] data_2;
begin
case(data_2)
0: seg77= 8'b01000000;
1: seg77= 8'b01111001;
2: seg77= 8'b00100100;
3: seg77= 8'b00110000;
4: seg77= 8'b00011001;
5: seg77= 8'b00010010;
6: seg77= 8'b00000010;
7: seg77= 8'b01111000;
8: seg77= 8'b00000000;
9: seg77= 8'b00010000;
default: seg77= 8'b01111111;
endcase
end
endfunction
always @(cnt22 ,cnt60,rst_n,myhour,co)
begin
if(!rst_n)
begin dig_sel_n <= 8'b11111111; seg_out <=8'b11111111; end
else
begin
case(cnt22)
//对计数器的十位和个位进行分离,交替闪烁
0: begin dig_sel_n <= 8'b11111110; seg_out <= seg7(cnt60%10); end
1: begin dig_sel_n <= 8'b11111101; seg_out <= seg7(cnt60/10);end
2: begin dig_sel_n <= 8'b11111011; seg_out <= seg77(co%10); end
3: begin dig_sel_n <= 8'b11110111; seg_out <= seg7(co/10);end
4: begin dig_sel_n <= 8'b11101111; seg_out <= seg77(myhour%10); end
5: begin dig_sel_n <= 8'b11011111; seg_out <= seg7(myhour/10);end
default:
begin dig_sel_n <= 8'b11111111; seg_out <= 8'b11111111;end
endcase
end
end
endmodule