基于FPGA的遥控数字时钟设计报告
Author:张宏宇
摘要
数字时钟是一种通过数字显示时间的计时装置,本次项目采用Cyclone Ⅳ系列芯片,使用QuartusII开发环境,使用VerilogHDL硬件描述语音,通过遥控进行数字时钟控制显示,本项目具备日期显示,时间显示以及闹钟等功能,同时可远程控制,使用更加便捷。
关键词:遥控数字时钟;VerilogHDL;FPGA
目录
一、问题描述及硬件介绍
1、问题描述
①基于FPGA的复杂数字始终设计
-
正常显示功能:
正常显示时,六位数码管显示日期,时间以及闹钟。对于日期来说,前两位显示年份的后两位数,中间两位显示月份,最后两位显示日。对于时间来说,前两位显示小时,中间两位显示分钟,最后两位显示秒钟,每两位之间用小数点隔开,最后两位小数点不用亮。
-
切换功能:
复位时,数码管显示时间;“切换”按键按下时,数码管显示日期;再次按下“切换”键时,数码管显示闹钟时间;再次按下“切换”键时,数码管恢复显示时间。
-
调整功能:
当显示时间时,按下“调整”键时,时间的分钟两位闪动,按下“第二个”键或“第三个”键可以加减分钟。按下“切换”键时,小时、日、月、年依次闪动,调节方法与上同。再次按下“调整”键时,即为确定调整。如果对上述调整有疑问,可以再次调整。如果在调整期间,按下“调整”键,所做调整也会生效。调整状态 20秒无操作,自动退出调整状态,正常显示时间。
-
调整功能显示
功能进行调整时,所对应调整的部分应该亮 0.5s,熄灭 0.5s。
-
闹钟
闹钟的小时可以调整到 24,用以关闭闹钟。其他时间打开闹钟,闹钟的时间与日期无关,即每日闹钟都会有作用。当闹钟时间和时间相同时,以闹钟作为指示(闹钟即两只老虎音乐播放器),或者直到第四个按键按下,终止闹钟。年月日不区分大小月、瑞年等,每月按 30 天设计。
②音乐播放器
- 输入不同频率的信号,蜂鸣器可以发出不同音色的声音,“两只老虎”的频率和曲谱已经给出,我们把 PWM 信号输入给 FPGA,用 FPGA 来控制蜂鸣器,就可以把曲子实现出来了。
2、硬件介绍
①板卡介绍
- 本次项目我们小组使用的FPGA芯片是英特尔公司的Cyclone Ⅳ系列芯片,型号为EP4CE6F17C8,具有6272个逻辑单元,270Kbits内存,2个全局锁相环,最大可用IO为179个。
-
我们使用的板卡上板载50MHz的有源晶振,可以提供50MHz的时钟频率,无论是在进行时钟显示时,还是在进行音乐播放时,时钟频率都起着决定性的作用
-
板载六位一体的八段数码管,可以显示包括小数点在内的0-f十六位数字,数码管为共阳极数码管,当某一字段对应的引脚为低电平时,相应字段就点亮,当某一字段对应的引脚为高电平时,相应字段就不亮(这对于设计时间调整时的数码管0.5s闪烁设计具有重大意义)。六位一体数码管采用动态显示,由于人的视觉暂留现象及发光二极管的余辉效应,只要扫描速度足够快,给人的印象就是一组稳定的显示数据,不会有闪烁感。
②外接红外遥控介绍
红外遥控发射部分由遥控按键、编码以及调制电路、红外发光二极管等组成。红外遥控接收部分由光敏二极管、解调电路等组成。最后将解调的信号输入FPGA 内进行解码输出。
我们小组使用的是一体化红外接收头HS0038B
以及红外遥控器
此红外遥控采用的NEC协议是PPM(脉冲位置调制)进行编码,9ms 的高电平脉冲和之后的 4.5ms 的低电平组成了引导码。接着后面是地址码及数据码,低位在前,高位在后。最后是 562.5um 脉冲突发以表示消息传输的结束。其中地址码加上地址反码的时间和数据码加上数据反码的时间是固定不变的(如数据码为 00000111,则数据反码则为 11111000),总是由 8 个“1”和 8个“0”组成。地址码和数据码都是由“0”、“1”组成的。逻辑“1”和逻辑“0”是根据脉冲之间的时间间隔来区分的。逻辑“1”由 560us 的高脉冲加上 1.69ms 的低电平组成,而逻辑“0”由 560us 的高脉冲加上 560us 的低电平组成。
二、开发环境
本组使用的硬件开发环境是英特尔公司的Cyclone Ⅳ系列芯片,型号为EP4CE6F17C8。
软件开发环境是Quartus Prime 17.1 版本编译软件和Modelsim SE 10.1 仿真软件。
三、软件设计与功能实现
1、基础功能
①时钟功能实现
- 时钟进行正常时间计时:
本组使用的开发板的板载有源晶振产生50MHz的时钟频率,一个时钟周期为20ns,计数器计满一秒,即秒钟个位跳转一次的计数次数为CNT_1S = 50_000_000。
将时间分为六个数据进行逐个判断,分别为:秒钟个位,秒钟十位,分钟个位,分钟十位,时钟个位,时钟十位。
parameter CNT_1S = 50_000_000 //计数器技术一秒需要的次数
reg [3:0] sec_unit;
reg [3:0] sec_ten; //秒钟
reg [3:0] min_unit;
reg [3:0] min_ten; //分钟
reg [3:0] hour_unit;
reg [3:0] hour_ten; //时钟
reg [25:0] cnt;
在秒钟个位运行时,逢十进一,在秒钟个位数记到九并且计数器记到cnt == CNT_1S - 1’b1 时,秒钟个位清零,否则秒钟个位自加一。
//计数器计时一秒钟
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else if(cnt == CNT_1S)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
//秒钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_unit <= 1'b0;
else if(nstate_adjust == RUN)begin
if((sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_unit <= 1'b0;
else if(cnt == CNT_1S - 1'b1)
sec_unit <= sec_unit + 1'b1;
end
在秒钟十位运行时,逢6进一,在秒钟个位记到9并且计数器计满一秒时自加一。
//秒钟十位计时,逢6进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_ten <= 1'b0;
else if((sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_ten <= sec_ten + 1'b1;
end
分钟个位、分钟十位、时钟个位、时钟十位的判定条件是在秒钟的计数基础上运行的,代码如下。
//分钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_unit <= 1'b0;
else if(nstate_adjust == RUN)begin
if((min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_unit <= 1'b0;
else if((sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_unit <= min_unit + 1'b1;
end
//分钟十位计时,逢6进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_ten <= 1'b0;
else if((min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_ten <= min_ten + 1'b1;
end
//时钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_unit <= 4'd2;
else if(nstate_adjust == RUN)begin
if((hour_unit == 4'd9) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_unit <= 1'b0;
else if((min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_unit <= hour_unit + 1'b1;
end
//时钟十位计时,逢2进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_ten <= 1'b1;
else if(nstate_adjust == RUN)begin
if((hour_ten == 4'd2) && (hour_unit == 4'd3) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_ten <= 1'b0;
else if((hour_unit == 4'd9) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_ten <= hour_ten + 1'b1;
end
②日期功能实现
- 日期进行正常计时:
日期的显示有两种方式,一种是读取时钟的数据,在时钟计时到24点时日期天数加一,另一种是日期独立计时,在计满一天时自加一。在这里我们采用的是第二种方式。计数器计满一天,即天数个位跳转一次的计数次数为CNT_1d = 42’d4_320_000_000_000。(在这里有一个注意事项,那就是系统自动分配给没有标志位数的数字的最大位数为32位二进制数,而一天的计数器的值是42位的,因此必须在数字前面加上42’d进行约束)
将时间分为六个数据进行逐个判断,分别为:天数个位,天数十位,月份个位,月份十位,年份个位,年份十位。
parameter CNT_1d = 42'd4_320_000_000_000 //计数满一天、
reg [3:0] day_unit;
reg [3:0] day_ten; //天数
reg [3:0] mou_unit;
reg [3:0] mou_ten; //月份
reg [3:0] year_unit;
reg [3:0] year_ten; //年份
在天数个位运行时,逢十进一,在天数个位数记到九并且计数器记到cnt == CNT_1d - 1’b1 时,秒钟个位清零,否则秒钟个位自加一。
//计数器计时一天
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else if(cnt == CNT_1d)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
//天数个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_unit <= 4'd4;
else if(nstate_adjust == RUN)begin
if((day_unit == 4'd9) && (cnt == CNT_1d - 1'b1))
day_unit <= 1'b0;
else if(cnt == CNT_1d - 1'b1)
day_unit <= day_unit + 1'b1;
end
天数十位、月份个位、月份十位、年份个位、年份十位均和时间计时的类似,只不过时间的秒钟分钟十位是满6进一,天数是满30进一,月份是满12进一,下面给出对应代码
//天数十位计时,逢3进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_ten <= 1'b1;
else if(nstate_adjust == RUN)begin
if((day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
day_ten <= 1'b0;
else if((day_unit == 4'd9) && (cnt == CNT_1d - 1'b1))
day_ten <= day_ten + 1'b1;
end
//月份个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_unit <= 4'd9;
else if(nstate_adjust == RUN)begin
if((mou_unit == 4'd9) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_unit <= 1'b0;
else if((day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_unit <= mou_unit + 1'b1;
end
//月份十位计时,逢1进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_ten <= 1'b0;
else if((mou_unit == 4'd9) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_ten <= mou_ten + 1'b1;
end
//年份个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_unit <= 4'd2;
else if(nstate_adjust == RUN)begin
if((year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_unit <= 1'b0;
else if((mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_unit <= year_unit + 1'b1;
end
//年份十位计时,逢2进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_ten <= 4'd2;
else if(nstate_adjust == RUN)begin
if((year_ten == 4'd9) && (year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_ten <= 1'b0;
else if((year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_ten <= year_ten + 1'b1;
end
③闹钟功能实现
闹钟分为闹钟时间设置和响铃设置,其中闹钟时间设置会在后面调整功能实现模块讲解,响铃设置功能在后面音乐播放功能模块讲解。
2、调整功能实现
时钟的运行分别为正常运行模式和调整模式。下面讲解调整模式。
①模式切换状态机
使用一个模式切换按键进行模式切换,总共为两种模式,分别命名为RUN和CHANGE,在模式切换按键按下时,状态机跳转。我们使用的是二段式状态机。代码如下:
parameter RUN = 2'b00, //正常运行
CHANGE = 2'b01; //更改时间
reg [1:0]cstate_adjust;
reg [1:0]nstate_adjust;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_adjust <= RUN;
else
cstate_adjust <= nstate_adjust;
//更改时间和正常显示状态机跳转
always @(*)
case(cstate_adjust)
RUN :
if(adjust_alarm_ctrl)
nstate_adjust = CHANGE;
else
nstate_adjust = RUN;
CHANGE :
if(adjust_alarm_ctrl)
nstate_adjust = RUN;
else
nstate_adjust = CHANGE;
endcase
②数字更改状态机
使用一个左移位选择的按键,实现秒钟、分钟、时钟或者天数、月份、年份的待选择跳转。同样是选择状态机的形式,有三个状态进行跳转,分别命名为STATE_SEC、STATE_MIN、STATE_HOUR(时间和闹钟)或者是STATE_DAY、STATE_MOU、STATE_YEAR(日期),代码如下:
parameter STATE_SEC = 2'b01,
STATE_MIN = 2'b10,
STATE_HOUR = 2'b11;
reg [1:0] cstate;
reg [1:0] nstate;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate <= STATE_SEC;
else
cstate <= nstate;
//状态机跳转
always @(*)
case(cstate)
STATE_SEC :
if(left_ctrl)
nstate <= STATE_MIN;
else
nstate <= STATE_SEC;
STATE_MIN :
if(left_ctrl)
nstate <= STATE_HOUR;
else
nstate <= STATE_MIN;
STATE_HOUR :
if(left_ctrl)
nstate <= STATE_SEC;
else
nstate <= STATE_HOUR;
default : nstate <= STATE_SEC;
endcase
③调整功能实现
对时间和日期的调整都可以通过遥控器上的加和减按键来实现,秒钟和分钟的范围是0-30,时钟范围是0-23,天数范围是0-30,月份范围是0-12。
input wire up_ctrl, //控制加时间
input wire down_ctrl, //控制减时间
input wire left_ctrl, //按键左移位控制
input wire adjust_time_ctrl, //更改时间启动、结束
- 时间调整代码实现如下:
//秒钟个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_unit <= 1'b0;
else if(nstate_adjust == CHANGE)begin
//增加
if(nstate_u_d == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_unit <= 4'd0;
else if(nstate_u_d == STATE_SEC && up_ctrl)
sec_unit <= sec_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_unit <= 4'd9;
else if(nstate_u_d == STATE_SEC && down_ctrl)
sec_unit <= sec_unit - 1'b1;
else
sec_unit <= sec_unit;
end
//秒钟十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_ten <= 1'b0;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_SEC && up_ctrl && sec_ten == 4'd5 && sec_unit == 4'd9)
sec_ten <= 4'd0;
else if(nstate_u_d == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_ten <= sec_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_ten == 4'd0 && sec_unit == 4'd0)
sec_ten <= 4'd5;
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_ten <= sec_ten - 1'b1;
end
//分钟个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_unit <= 1'b0;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_unit <= 4'd0;
else if(nstate_u_d == STATE_MIN && up_ctrl)
min_unit <= min_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate_u_d == STATE_MIN && down_ctrl)
min_unit <= min_unit - 1'b1;
end
//分钟十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_ten <= 1'b0;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MIN && up_ctrl && min_ten == 4'd5 && min_unit == 4'd9)
min_ten <= 4'd0;
else if(nstate_u_d == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_ten <= min_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_MIN && down_ctrl && min_ten == 4'd0 && min_unit == 4'd0)
min_ten <= 4'd5;
else if(nstate_u_d == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_ten <= min_ten - 1'b1;
end
//时钟个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_unit <= 4'd2;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_unit <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_unit <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl)
hour_unit <= hour_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_unit <= 4'd3;
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_unit <= 4'd9;
else if(nstate_u_d == STATE_HOUR && down_ctrl)
hour_unit <= hour_unit - 1'b1;
end
//时钟十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_ten <= 1'b1;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_ten <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_ten <= hour_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_ten <= 4'd2;
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_ten <= hour_ten - 1'b1;
end
- 日期调整代码如下:
//天数个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_unit <= 4'd4;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_DAY && up_ctrl && day_unit == 4'd9)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl && day_ten == 4'd3 && day_unit == 4'd0)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl)
day_unit <= day_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_DAY && down_ctrl && day_ten == 4'd0 && day_unit == 4'd0)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && down_ctrl && day_unit == 4'd0)
day_unit <= 4'd9;
else if(nstate_u_d == STATE_DAY && down_ctrl)
day_unit <= day_unit - 1'b1;
else
day_unit <= day_unit;
end
//天数十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_ten <= 1'b1;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_DAY && up_ctrl && day_ten == 4'd3 && day_unit == 4'd0)
day_ten <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl && day_unit == 4'd9)
day_ten <= day_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_DAY && down_ctrl && day_ten == 4'd0 && day_unit == 4'd0)
day_ten <= 4'd3;
else if(nstate_u_d == STATE_DAY && down_ctrl && day_unit == 4'd0)
day_ten <= day_ten - 1'b1;
end
//月份个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_unit <= 4'd9;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MOU && up_ctrl && mou_unit == 4'd9)
mou_unit <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl && mou_ten == 4'd1 && mou_unit == 4'd2)
mou_unit <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl)
mou_unit <= mou_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_MOU && down_ctrl)
if(mou_ten == 4'd0 && mou_unit == 4'd0)
mou_unit <= 4'd2;
else if(mou_unit == 4'd0)
mou_unit <= 4'd9;
else
mou_unit <= mou_unit - 1'b1;
end
//月份十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_ten <= 1'b0;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MOU && up_ctrl && mou_ten == 4'd1 && mou_unit == 4'd2)
mou_ten <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl && mou_unit == 4'd9)
mou_ten <= mou_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_MOU && down_ctrl && mou_ten == 4'd0 && mou_unit == 4'd0)
mou_ten <= 4'd1;
else if(nstate_u_d == STATE_MOU && down_ctrl && mou_unit == 4'd0)
mou_ten <= mou_ten - 1'b1;
end
//年份个位调整
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_unit <= 4'd2;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_YEAR && up_ctrl && year_unit == 4'd9)
year_unit <= 4'd0;
else if(nstate_u_d == STATE_YEAR && up_ctrl)
year_unit <= year_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_unit == 4'd0)
year_unit <= 4'd9;
else if(nstate_u_d == STATE_YEAR && down_ctrl)
year_unit <= year_unit - 1'b1;
end
//年份十位调整,依据个位的进位和借位判断加减
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_ten <= 4'd2;
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_YEAR && up_ctrl && year_ten == 4'd9 && year_unit == 4'd9)
year_ten <= 4'd0;
else if(nstate_u_d == STATE_YEAR && up_ctrl && year_unit == 4'd9)
year_ten <= year_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_ten == 4'd0 && year_unit == 4'd0)
year_ten <= 4'd9;
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_unit == 4'd0)
year_ten <= year_ten - 1'b1;
end
- 闹钟调整代码如下:
//增加秒钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_unit <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_unit <= 4'd0;
else if(nstate == STATE_SEC && up_ctrl)
sec_unit <= sec_unit + 1'b1;
//减少
else if(nstate == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_unit <= 4'd9;
else if(nstate == STATE_SEC && down_ctrl)
sec_unit <= sec_unit - 1'b1;
end
//增加秒钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_ten <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_SEC && up_ctrl && sec_ten == 4'd5 && sec_unit == 4'd9)
sec_ten <= 4'd0;
else if(nstate == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_ten <= sec_ten + 1'b1;
//减少
else if(nstate == STATE_SEC && down_ctrl && sec_ten == 4'd0 && sec_unit == 4'd0)
sec_ten <= 4'd5;
else if(nstate == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_ten <= sec_ten - 1'b1;
end
//增加分钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_unit <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_unit <= 4'd0;
else if(nstate == STATE_MIN && up_ctrl)
min_unit <= min_unit + 1'b1;
//减少
else if(nstate == STATE_MIN && down_ctrl && min_ten == 4'd0 &&min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate == STATE_MIN && down_ctrl)
min_unit <= min_unit - 1'b1;
end
//增加分钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_ten <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_MIN && up_ctrl && min_ten == 4'd5 && min_unit == 4'd9)
min_ten <= 4'd0;
else if(nstate == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_ten <= min_ten + 1'b1;
//减少
else if(nstate == STATE_MIN && down_ctrl && min_ten == 4'd0 && min_unit == 4'd0)
min_ten <= 4'd5;
else if(nstate == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_ten <= min_ten - 1'b1;
end
//增加时钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_unit <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_unit <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_unit <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl)
hour_unit <= hour_unit + 1'b1;
//减少
else if(nstate == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_unit <= 4'd3;
else if(nstate == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_unit <= 4'd9;
else if(nstate == STATE_HOUR && down_ctrl)
hour_unit <= hour_unit - 1'b1;
end
//增加时钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_ten <= 4'd0;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_ten <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_ten <= hour_ten + 1'b1;
//减少
else if(nstate == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_ten <= 4'd2;
else if(nstate == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_ten <= hour_ten - 1'b1;
end
3、数码管显示模块
①数码管动态显示工作原理
-
人眼的视觉暂留原理:人眼在观察景物时,光信号传入大脑神经,须经过一段短暂的时间,光的作用结束后,视觉影像并不立即消失,这种残留的视觉称”后像“,视觉的这一现象称为”视觉暂留“。
-
数码管的余辉效应:当停止向发光二极管供电时发光二极管亮度仍能维持一段时间。
-
当我们让数码管亮的间隔为1ms,即扫描间隔为1ms时,让六个数码管轮流显示,点亮相应数码管的时候给其显示相应的值,这样就可以使六个数码管显示不同的值了。
②数码管显示代码
module seg(
input wire clk,
input wire rst_n,
input wire [23:0]disp_data,
input wire en,
input wire [5:0]point,
output reg[7:0]seg,
output wire[5:0]sel
);
reg [14:0]divder_cnt;
reg clk_1K;
reg [5:0]sel_r;
reg [2:0]cnt_sel;
//待显示数据缓存
reg [3:0]data_tmp;
reg dot_disp;
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter dispa=8'b1000_1000;
parameter dispb=8'b1000_0011;
parameter dispc=8'b1010_0111;
parameter dispd=8'b1010_0001;
parameter dispe=8'b1000_0110;
parameter dispf=8'b1000_0011;
//分频计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
divder_cnt<=15'd0;
else if(!en)
divder_cnt<=15'd0;
else if(divder_cnt==15'd24999)
divder_cnt<=15'd0;
else
divder_cnt<=divder_cnt+15'd1;
//1KHz时钟产生模块
always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_1K<=1'b0;
else if(divder_cnt==15'd24999)
clk_1K<=~clk_1K;
else
clk_1K<=clk_1K;
//6位循环移位寄存器
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
sel_r<=6'b00_0001;
else if(sel_r==6'b10_0000)
sel_r<=6'b00_0001;
else
sel_r<=sel_r<<1;
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
cnt_sel <= 1'b0;
else if(cnt_sel == 3'b101)
cnt_sel <= 1'b0;
else
cnt_sel <= cnt_sel + 1'b1;
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
dot_disp <= 1'b1;
else
dot_disp <= ~point[cnt_sel];
always @(*)
seg[7] = dot_disp;
//六选一多路器
always @(*)
case(sel_r)
6'b00_0001:data_tmp=disp_data[3:0];
6'b00_0010:data_tmp=disp_data[7:4];
6'b00_0100:data_tmp=disp_data[11:8];
6'b00_1000:data_tmp=disp_data[15:12];
6'b01_0000:data_tmp=disp_data[19:16];
6'b10_0000:data_tmp=disp_data[23:20];
default:data_tmp=4'b0000;
endcase
always@(*)
case(data_tmp)
4'h0:seg[6:0]=7'b100_0000;
4'h1:seg[6:0]=7'b111_1001;
4'h2:seg[6:0]=7'b010_0100;
4'h3:seg[6:0]=7'b011_0000;
4'h4:seg[6:0]=7'b001_1001;
4'h5:seg[6:0]=7'b001_0010;
4'h6:seg[6:0]=7'b000_0010;
4'h7:seg[6:0]=7'b111_1000;
4'h8:seg[6:0]=7'b000_0000;
4'h9:seg[6:0]=7'b001_0000;
4'ha:seg[6:0]=7'b000_1000;
4'hb:seg[6:0]=7'b000_0011;
4'hc:seg[6:0]=7'b100_0110;
4'hd:seg[6:0]=7'b010_0001;
4'he:seg[6:0]=7'b000_0110;
4'hf:seg[6:0]=7'b111_1111;
endcase
assign sel=~sel_r;
endmodule
③显示时间和日期的数据选择输入代码
采用位拼接的方法,分别用六个数据来驱动对应的六个数码管
//显示闹钟
assign alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
//显示日期
assign date_data <= {year_ten,year_unit,mou_ten,mou_unit,day_ten,day_unit};
//显示时间
assign time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
④调整功能下数码管进行0.5s亮灭闪烁模块
我们使用的是共阳极数码管,当输入数码管的电平全为高电平时,数码管熄灭,因此定义一个0.5s的计数器,每当计数0.5s时,将输入数码管的数据全置为1,0.5s后再次显示正常运行的数值。这里以时间显示数码管亮灭代码为例。
parameter CNT_0_5 = 25'd25_000_000; //0.5s计时
reg [24:0] cnt_0_5;
reg cnt_0_5_flag;
//数码管亮灭0.5s计时
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5 <= 1'b0;
else if(cnt_0_5 == CNT_0_5)
cnt_0_5 <= 1'b0;
else
cnt_0_5 <= cnt_0_5 + 1'b1;
//数码管亮灭0.5s分频
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5_flag <= 1'b0;
else if(cnt_0_5 == CNT_0_5 - 1'b1)
cnt_0_5_flag <= ~cnt_0_5_flag;
else
cnt_0_5_flag <= cnt_0_5_flag;
//时间输出显示
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_data <= 1'b0;
else if(nstate_adjust == RUN)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_SEC)begin
if(cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,8'hff};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_MIN)begin
if(cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,8'hff,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_HOUR)begin
if(cnt_0_5_flag)
time_data <= {8'hff,min_ten,min_unit,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
4、音乐播放模块
①频率选择模块
每个简谱名对应不同的频率,可以用系统时钟生成对应的频率,在Hz选择的模块里放入低音、中音、高音、超高音的一共28个频率
module hz(
input wire [7:0]hz_sel,
output reg [19:0]cycle
);
parameter CLK_FRE = 50 ;
always @(*)
begin
case(hz_sel)
8'h01 : cycle <= CLK_FRE*1000000/261 ; //low 1 261Hz
8'h02 : cycle <= CLK_FRE*1000000/293 ; //low 2 293Hz
8'h03 : cycle <= CLK_FRE*1000000/329 ; //low 3 329Hz
8'h04 : cycle <= CLK_FRE*1000000/349 ; //low 4 349Hz
8'h05 : cycle <= CLK_FRE*1000000/392 ; //low 5 392Hz
8'h06 : cycle <= CLK_FRE*1000000/440 ; //low 6 440Hz
8'h07 : cycle <= CLK_FRE*1000000/499 ; //low 7 499Hz
8'h11 : cycle <= CLK_FRE*1000000/523 ; //middle 1 523Hz
8'h12 : cycle <= CLK_FRE*1000000/587 ; //middle 2 587Hz
8'h13 : cycle <= CLK_FRE*1000000/659 ; //middle 3 659Hz
8'h14 : cycle <= CLK_FRE*1000000/698 ; //middle 4 698Hz
8'h15 : cycle <= CLK_FRE*1000000/784 ; //middle 5 784Hz
8'h16 : cycle <= CLK_FRE*1000000/880 ; //middle 6 880Hz
8'h17 : cycle <= CLK_FRE*1000000/998 ; //middle 7 998Hz
8'h21 : cycle <= CLK_FRE*1000000/1046 ; //high 1 1046Hz
8'h22 : cycle <= CLK_FRE*1000000/1174 ; //high 2 1174Hz
8'h23 : cycle <= CLK_FRE*1000000/1318 ; //high 3 1318Hz
8'h24 : cycle <= CLK_FRE*1000000/1396 ; //high 4 1396Hz
8'h25 : cycle <= CLK_FRE*1000000/1568 ; //high 5 1568Hz
8'h26 : cycle <= CLK_FRE*1000000/1760 ; //high 6 1760Hz
8'h27 : cycle <= CLK_FRE*1000000/1976 ; //high 7 1976Hz
8'h31 : cycle <= CLK_FRE*1000000/2093 ; //super high 1 2093Hz
8'h32 : cycle <= CLK_FRE*1000000/2349 ; //super high 2 2349Hz
8'h33 : cycle <= CLK_FRE*1000000/2637 ; //super high 3 2637Hz
8'h34 : cycle <= CLK_FRE*1000000/2794 ; //super high 4 2794Hz
8'h35 : cycle <= CLK_FRE*1000000/3136 ; //super high 5 3136Hz
8'h36 : cycle <= CLK_FRE*1000000/3520 ; //super high 6 3520Hz
8'h37 : cycle <= CLK_FRE*1000000/3951 ; //super high 7 3951Hz
default:cycle<=20'd0;
endcase
end
endmodule
②频率计算器
reg [19:0]cnt_cycle;
//频率计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_cycle<=1'b0;
else if(cnt_cycle==cycle || cnt==time_cycle)
cnt_cycle<=1'b0;
else
cnt_cycle<=cnt_cycle+1'b1;
③单个音符的时间计算
parameter CLK_FRE = 50 ;
wire [7:0]time_music;
//单个音符时间计数值
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_cycle<=32'd0;
else
time_cycle<=time_music*(CLK_FRE*1000000/8) ; //1000000
④读取ROM数据
//计时器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=1'b0;
else if(cnt==time_cycle)
cnt<=1'b0;
else if(beep_en)
cnt<=cnt+1'b1;
//频率rom计数器加一
always @(posedge clk or negedge rst_n)
if(!rst_n)
address<=1'b0;
else if(address==8'd34 && cnt==time_cycle)
address<=1'b0;
else if(!beep_en)
address <= 1'b0;
else if(cnt==time_cycle && beep_en)
address<=address+1'b1;
⑤音乐播放
在beep_en为1时使能,在按下停止按键或者播放20s后自动停止。
wire [19:0]duty_data;
wire [19:0]cycle;
reg [19:0]cnt_cycle;
//频率计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_cycle<=1'b0;
else if(cnt_cycle==cycle || cnt==time_cycle)
cnt_cycle<=1'b0;
else
cnt_cycle<=cnt_cycle+1'b1;
//占空比
assign duty_data=cycle/8;
//蜂鸣器输出PWM
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep_t=1'b1;
else if(cnt_cycle>=duty_data && beep_en)
beep_t=1'b0;
else
beep_t=1'b1;
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep <= 1'b1;
else if(beep_en)
beep <= ~beep_t;
else
beep <= 1'b1;
5、红外控制模块
①红外解码模块
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt<=1'b0;
else
case(state)
IDLE:
cnt<=12'b0;
S_T9:
if(ifr_in_rise && flag_9ms)
cnt<=1'b0;
else
cnt<=cnt+1'b1;
S_JUDGE:
if(ifr_in_fall && (flag_4_5ms || flag_2_25ms))
cnt<=1'b0;
else
cnt<=cnt+1'b1;
S_IFR_DATA:
if(flag_0_56ms && ifr_in_rise)
cnt<=1'b0;
else if((flag_0_56ms || flag_1_69ms) && ifr_in_fall)
cnt<=1'b0;
else
cnt<=cnt+1'b1;
default:
cnt<=1'b0;
endcase
//计数到0.56ms范围拉高标志信号
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_0_56ms<=1'b0;
else if(state==S_IFR_DATA && cnt>=CNT_0_56MS_L && cnt<=CNT_0_56MS_H)
flag_0_56ms<=1'b1;
else
flag_0_56ms<=1'b0;
//计数到1.69ms范围拉高标志信号
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_1_69ms<=1'b0;
else if(state==S_IFR_DATA && cnt>=CNT_1_69MS_L && cnt<=CNT_1_69MS_H)
flag_1_69ms<=1'b1;
else
flag_1_69ms<=1'b0;
//计数2.25ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_2_25ms<=1'b0;
else if(state==S_JUDGE && cnt>=CNT_2_25MS_L && cnt<=CNT_2_25MS_H)
flag_2_25ms<=1'b1;
else
flag_2_25ms<=1'b0;
//计数4.5ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_4_5ms<=1'b0;
else if(state==S_JUDGE && cnt>=CNT_4_5MS_L && cnt<=CNT_4_5MS_H)
flag_4_5ms<=1'b1;
else
flag_4_5ms<=1'b0;
//计数9ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_9ms<=1'b0;
else if(state==S_T9 && cnt>=CNT_9MS_L && cnt<=CNT_9MS_H)
flag_9ms<=1'b1;
else
flag_9ms<=1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
state<=IDLE;
else
case(state)
IDLE:
if(ifr_in_fall)
state<=S_T9;
else
state<=IDLE;
S_T9:
if(ifr_in_rise && flag_9ms)
state<=S_JUDGE;
else if(ifr_in_rise && !flag_9ms)
state<=IDLE;
else
state<=S_T9;
S_JUDGE:
if(ifr_in_fall && flag_2_25ms)
state<=S_REPEAT;
else if(ifr_in_fall && flag_4_5ms)
state<=S_IFR_DATA;
else if(ifr_in_fall && !flag_2_25ms && !flag_4_5ms)
state<=IDLE;
else
state<=S_JUDGE;
S_IFR_DATA:
if(ifr_in_rise && !flag_0_56ms)
state<=IDLE;
else if(ifr_in_fall && !flag_0_56ms && !flag_1_69ms)
state<=IDLE;
else if(ifr_in_rise && data_cnt==6'd32)
state<=IDLE;
S_REPEAT:
if(ifr_in_rise)
state<=IDLE;
else
state<=S_REPEAT;
default:
state<=IDLE;
endcase
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data_tmp<=1'b0;
else if(state==S_IFR_DATA && ifr_in_fall && flag_0_56ms)
data_tmp[data_cnt]<=1'b0;
else if(state==S_IFR_DATA && ifr_in_fall && flag_1_69ms)
data_tmp[data_cnt]<=1'b1;
else
data_tmp<=data_tmp;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data_cnt<=1'b0;
else if(ifr_in_rise && data_cnt==6'd32)
data_cnt<=1'b0;
else if(ifr_in_fall && state==S_IFR_DATA)
data_cnt<=data_cnt+1'b1;
else
data_cnt<=data_cnt;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
repeat_en<=1'b0;
else if(state==S_REPEAT && data_tmp[23:16]==~data_tmp[31:24])
repeat_en<=1'b1;
else
repeat_en<=1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data<=1'b0;
else if(data_tmp[23:16]==~data_tmp[31:24] && data_tmp[7:0]==~data_tmp[15:8] && data_cnt==6'd32)
data<={12'd0,data_tmp[23:16]};
else
data <= 1'b0;
②按键选择模块,以一个按键为代表
reg infrared_68;
reg infrared_68_t;
//检测到红外遥控按键68
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_68 <= 1'b0;
else if(infrared_data == 20'd68)
infrared_68 <= 1'b1;
else
infrared_68 <= 1'b0;
//将按键68打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_68_t <= 1'b0;
else
infrared_68_t <= infrared_68;
assign infrared_68_flag = (infrared_68 && !infrared_68_t)? 1'b1:1'b0;
6、整体显示模块
①三种显示的切换状态机
//状态机状态
parameter TIME = 3'b000,
DATE = 3'b001,
ALARM = 3'b010;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate <= TIME;
else
cstate <= nstate;
//按键状态机状态跳转
always @(*)
case(cstate)
TIME :
if(infrared_68_flag)
nstate <= DATE;
else
nstate <= TIME;
DATE :
if(infrared_68_flag)
nstate <= ALARM;
else
nstate <= DATE;
ALARM :
if(infrared_68_flag)
nstate <= TIME;
else
nstate <= ALARM;
default : nstate <= TIME;
endcase
//显示选择
always @(posedge clk or negedge rst_n)
if(!rst_n)
data <= time_data;
else if(nstate == TIME)
data <= time_data;
else if(nstate == DATE)
data <= date_data;
else if(nstate == ALARM)
data <= alarm_data;
四、系统功能调试与分析
1、时间正常显示和调整显示仿真
- 仿真激励测试模块代码:
`timescale 1ns/1ns
`define clk_period 20
module time_display_tb;
reg clk;
reg rst_n;
reg up_ctrl;
reg down_ctrl;
reg adjust_time_ctrl;
reg left_ctrl;
wire [23:0] time_data;
initial clk = 1'b0;
always #(`clk_period/2) clk = ~clk;
initial begin
rst_n = 1'b0;
up_ctrl = 1'b0;
down_ctrl = 1'b0;
left_ctrl = 1'b0;
adjust_time_ctrl = 1'b0;
#20;
rst_n = 1'b1;
#2000;
adjust_time_ctrl = 1'b1;
#20;
adjust_time_ctrl = 1'b0;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
adjust_time_ctrl = 1'b1;
#20;
adjust_time_ctrl = 1'b0;
end
time_display
#(
.CNT_1S (2)
)
time_display_inst(
.clk(clk),
.rst_n(rst_n),
.up_ctrl(up_ctrl), //控制加时间
.down_ctrl(down_ctrl), //控制减时间
.left_ctrl(left_ctrl), //按键左移位控制
.adjust_time_ctrl(adjust_time_ctrl), //
.time_data(time_data)
);
endmodule
- 时间正常显示仿真波形:
- 时间调整仿真波形图:
- 可以看到,时间在显示功能的时候是正常地在运行,正确的进位;当进入到调整功能时,每按下一个增或减按键就可以实现增减,并且在恢复到运行模式后会根据更改后的时间运行。
2、日期正常显示和调整显示仿真
- 仿真激励测试模块代码
`timescale 1ns/1ns
`define clk_period 20
module date_display_tb;
reg clk;
reg rst_n;
reg up_ctrl; //控制加日期
reg down_ctrl; //控制减日期
reg left_ctrl; //按键左移位控制
reg adjust_date_ctrl; //控制更改日期开始、启动
wire [23:0] date_data;
initial clk = 1'b0;
always #(`clk_period/2) clk = ~clk;
initial begin
rst_n = 1'b0;
up_ctrl = 1'b0;
down_ctrl = 1'b0;
left_ctrl = 1'b0;
adjust_date_ctrl = 1'b0;
#20;
rst_n = 1'b1;
#2000;
adjust_date_ctrl = 1'b1;
#20;
adjust_date_ctrl = 1'b0;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
adjust_date_ctrl = 1'b1;
#20;
adjust_date_ctrl = 1'b0;
end
date_display
#(
.CNT_1d (2)
)
date_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (up_ctrl), //控制加日期
.down_ctrl (down_ctrl), //控制减日期
.left_ctrl (left_ctrl), //按键左移位控制
.adjust_date_ctrl (adjust_date_ctrl), //
.date_data (date_data)
);
endmodule
- 日期正常显示波形:
- 日期调整显示波形:
- 可以看到,日期在显示功能的时候是正常地在运行,正确的进位;当进入到调整功能时,每按下一个增或减按键就可以实现增减,并且在恢复到运行模式后会根据更改后的日期运行。
3、闹钟正常显示和调整显示仿真
- 仿真激励测试模块代码
`timescale 1ns/1ns
`define clk_period 20
module alarm_display_tb;
reg clk;
reg rst_n;
reg up_ctrl;
reg down_ctrl;
reg left_ctrl;
reg adjust_alarm_ctrl;
wire [23:0]alarm_data;
initial clk = 1'b0;
always #(`clk_period/2) clk = ~clk;
initial begin
rst_n = 1'b0;
up_ctrl = 1'b0;
left_ctrl = 1'b0;
down_ctrl = 1'b0;
adjust_alarm_ctrl = 1'b0;
#40;
rst_n = 1'b1;
#200;
adjust_alarm_ctrl = 1'b1;
#100;
adjust_alarm_ctrl = 1'b0;
left_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
left_ctrl = 1'b1;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
left_ctrl = 1'b1;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
adjust_alarm_ctrl = 1'b1;
#100;
adjust_alarm_ctrl = 1'b0;
end
alarm_display alarm_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (up_ctrl),
.down_ctrl (down_ctrl),
.left_ctrl (left_ctrl),
.adjust_alarm_ctrl (adjust_alarm_ctrl),
.alarm_data (alarm_data)
);
endmodule
- 闹钟正常显示波形:持续显示设置的闹钟。
- 闹钟调整显示波形:
- 闹钟在正常显示时固定时间,在调整时随按键加减调整
4、音乐播放电路仿真波形
- 音乐播放激励测试模块代码
`timescale 1ns/1ns
`define clk_period 20
module beep_music_tb;
reg clk;
reg rst_n;
reg beep_en;
wire beep;
initial clk = 1'b0;
always #(`clk_period/2) clk = ~clk;
initial begin
rst_n = 1'b0;
beep_en = 1'b0;
#20;
rst_n = 1'b1;
#200;
beep_en = 1'b1;
end
beep_music beep_music_inst(
.clk (clk),
.rst_n (rst_n),
.beep_en (beep_en),
.beep (beep)
);
endmodule
- 音乐正常播放,顺序读地址,读取每个音符的播放时间和频率
- 一个音符的波形显示一个中音do,占空比控制声音强度,我们设置占空比为%80。
5、数码管显示仿真
- 数码管显示仿真激励测试模块代码(以时间显示为例):
`timescale 1ns/1ns
`define clk_period 20
module time_display_top_tb;
reg clk;
reg rst_n;
reg up_ctrl;
reg down_ctrl;
reg left_ctrl;
reg adjust_time_ctrl;
wire [5:0]sel;
wire [7:0]seg;
initial clk = 1'b0;
always #(`clk_period/2) clk = ~clk;
initial begin
rst_n = 1'b0;
up_ctrl = 1'b0;
down_ctrl = 1'b0;
left_ctrl = 1'b0;
adjust_time_ctrl = 1'b0;
#20;
rst_n = 1'b1;
#2000;
adjust_time_ctrl = 1'b1;
#20;
adjust_time_ctrl = 1'b0;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
up_ctrl = 1'b1;
#20;
up_ctrl = 1'b0;
#20;
adjust_time_ctrl = 1'b1;
#20;
adjust_time_ctrl = 1'b0;
end
defparam time_display_top_inst.time_display_inst.CNT_1S = 2;
//defparam time_display_top_inst.seg_inst.CNT_MAX = 49;
time_display_top time_display_top_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (up_ctrl),
.down_ctrl (down_ctrl),
.left_ctrl (left_ctrl),
.adjust_time_ctrl (adjust_time_ctrl),
.sel (sel),
.seg (seg)
);
endmodule
- 数码管正常显示数字波形图
- 调整功能时数码管闪烁波形图:当seg为全1时数码管熄灭,其余时刻亮。
五、结论与展望:
这是我们第一次进行复杂逻辑电路代码设计,很多东西都是摸索着前进,通过几人的合同协作,最终从最底层的代码,到仿真,再到实物调测,我们收获到了很多,最终完成了这个可以使用红外遥控器控制的复杂的数字时钟的设计。
通过这次比赛,我们对FPGA产生了浓厚的兴趣,并将在以后的大学生活中继续学习,我们希望可以进一步完善我们的数字时钟,将通过使用VGA或LCD屏幕实现一个频幕显示的数字时钟,也希望可以在英特尔以后的活动中提升自己,完善自己。
六、附录:
1、time_display.v
module time_display
#(
parameter CNT_1S = 50_000_000, //50_000_000
parameter CNT_0_5 = 25'd25_000_000 //0.5s计时
)
(
input wire clk,
input wire rst_n,
input wire up_ctrl, //控制加时间
input wire down_ctrl, //控制减时间
input wire left_ctrl, //按键左移位控制
input wire adjust_time_ctrl, //更改时间启动、结束
output reg [23:0]time_data
);
reg [3:0] sec_unit;
reg [3:0] sec_ten; //秒钟
reg [3:0] min_unit;
reg [3:0] min_ten; //分钟
reg [3:0] hour_unit;
reg [3:0] hour_ten; //时钟
reg [25:0] cnt;
parameter RUN = 2'b00, //正常运行
CHANGE = 2'b01; //更改时间
reg [1:0]cstate_adjust;
reg [1:0]nstate_adjust;
parameter STATE_SEC = 2'b00,
STATE_MIN = 2'b01,
STATE_HOUR = 2'b10;
reg [1:0]cstate_u_d;
reg [1:0]nstate_u_d;
reg [24:0] cnt_0_5;
reg cnt_0_5_flag;
//数码管亮灭0.5s计时
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5 <= 1'b0;
else if(cnt_0_5 == CNT_0_5)
cnt_0_5 <= 1'b0;
else
cnt_0_5 <= cnt_0_5 + 1'b1;
//数码管亮灭0.5s分频
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5_flag <= 1'b0;
else if(cnt_0_5 == CNT_0_5 - 1'b1)
cnt_0_5_flag <= ~cnt_0_5_flag;
else
cnt_0_5_flag <= cnt_0_5_flag;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_adjust <= RUN;
else
cstate_adjust <= nstate_adjust;
//更改时间和正常显示状态机跳转
always @(*)
case(cstate_adjust)
RUN :
if(adjust_time_ctrl)
nstate_adjust = CHANGE;
else
nstate_adjust = RUN;
CHANGE :
if(adjust_time_ctrl)
nstate_adjust = RUN;
else
nstate_adjust = CHANGE;
endcase
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_u_d <= STATE_SEC;
else
cstate_u_d <= nstate_u_d;
//更改秒、分、时状态跳转
always @(*)
case(cstate_u_d)
STATE_SEC :
if(left_ctrl)
nstate_u_d = STATE_MIN;
else
nstate_u_d = STATE_SEC;
STATE_MIN :
if(left_ctrl)
nstate_u_d = STATE_HOUR;
else
nstate_u_d = STATE_MIN;
STATE_HOUR :
if(left_ctrl)
nstate_u_d = STATE_SEC;
else
nstate_u_d = STATE_HOUR;
default :
nstate_u_d = STATE_SEC;
endcase
//计数器计时一秒钟
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else if(cnt == CNT_1S)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
//秒钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_unit <= 1'b0;
else if(nstate_adjust == RUN)begin
if((sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_unit <= 1'b0;
else if(cnt == CNT_1S - 1'b1)
sec_unit <= sec_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
//增加
if(nstate_u_d == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_unit <= 4'd0;
else if(nstate_u_d == STATE_SEC && up_ctrl)
sec_unit <= sec_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_unit <= 4'd9;
else if(nstate_u_d == STATE_SEC && down_ctrl)
sec_unit <= sec_unit - 1'b1;
else
sec_unit <= sec_unit;
end
//秒钟十位计时,逢6进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_ten <= 1'b0;
else if((sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
sec_ten <= sec_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_SEC && up_ctrl && sec_ten == 4'd5 && sec_unit == 4'd9)
sec_ten <= 4'd0;
else if(nstate_u_d == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_ten <= sec_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_ten == 4'd0 && sec_unit == 4'd0)
sec_ten <= 4'd5;
else if(nstate_u_d == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_ten <= sec_ten - 1'b1;
end
//分钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_unit <= 1'b0;
else if(nstate_adjust == RUN)begin
if((min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_unit <= 1'b0;
else if((sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_unit <= min_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_unit <= 4'd0;
else if(nstate_u_d == STATE_MIN && up_ctrl)
min_unit <= min_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate_u_d == STATE_MIN && down_ctrl)
min_unit <= min_unit - 1'b1;
end
//分钟十位计时,逢6进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_ten <= 1'b0;
else if((min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
min_ten <= min_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MIN && up_ctrl && min_ten == 4'd5 && min_unit == 4'd9)
min_ten <= 4'd0;
else if(nstate_u_d == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_ten <= min_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_MIN && down_ctrl && min_ten == 4'd0 && min_unit == 4'd0)
min_ten <= 4'd5;
else if(nstate_u_d == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_ten <= min_ten - 1'b1;
end
//时钟个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_unit <= 4'd2;
else if(nstate_adjust == RUN)begin
if((hour_unit == 4'd9) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_unit <= 1'b0;
else if((min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_unit <= hour_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_unit <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_unit <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl)
hour_unit <= hour_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_unit <= 4'd3;
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_unit <= 4'd9;
else if(nstate_u_d == STATE_HOUR && down_ctrl)
hour_unit <= hour_unit - 1'b1;
end
//时钟十位计时,逢2进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_ten <= 1'b1;
else if(nstate_adjust == RUN)begin
if((hour_ten == 4'd2) && (hour_unit == 4'd3) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_ten <= 1'b0;
else if((hour_unit == 4'd9) && (min_ten == 4'd5) && (min_unit == 4'd9) && (sec_ten == 4'd5) && (sec_unit == 4'd9) && (cnt == CNT_1S - 1'b1))
hour_ten <= hour_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_ten <= 4'd0;
else if(nstate_u_d == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_ten <= hour_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_ten <= 4'd2;
else if(nstate_u_d == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_ten <= hour_ten - 1'b1;
end
//时间输出显示
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_data <= 1'b0;
else if(nstate_adjust == RUN)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_SEC)begin
if(cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,8'hff};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_MIN)begin
if(cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,8'hff,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_HOUR)begin
if(cnt_0_5_flag)
time_data <= {8'hff,min_ten,min_unit,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
time_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
endmodule
2、date_display.v
module date_display
#(
parameter CNT_1d = 42'd4_320_000_000_000 //4_320_000_000_000
)
(
input wire clk,
input wire rst_n,
input wire up_ctrl, //控制加日期
input wire down_ctrl, //控制减日期
input wire left_ctrl, //按键左移位控制
input wire adjust_date_ctrl, //控制更改日期开始、启动
output reg [23:0]date_data
);
reg [41:0] cnt;
reg [3:0] day_unit;
reg [3:0] day_ten; //天数
reg [3:0] mou_unit;
reg [3:0] mou_ten; //月份
reg [3:0] year_unit;
reg [3:0] year_ten; //年份
parameter CNT_0_5 = 25'd25_000_000; //0.5s计时
parameter RUN = 2'b00, //正常运行
CHANGE = 2'b01; //更改时间
reg [1:0]cstate_adjust;
reg [1:0]nstate_adjust;
parameter STATE_DAY = 2'b00,
STATE_MOU = 2'b01,
STATE_YEAR = 2'b10;
reg [1:0]cstate_u_d;
reg [1:0]nstate_u_d;
reg [24:0] cnt_0_5;
reg cnt_0_5_flag;
//数码管亮灭0.5s计时
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5 <= 1'b0;
else if(cnt_0_5 == CNT_0_5)
cnt_0_5 <= 1'b0;
else
cnt_0_5 <= cnt_0_5 + 1'b1;
//数码管亮灭0.5s分频
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5_flag <= 1'b0;
else if(cnt_0_5 == CNT_0_5 - 1'b1)
cnt_0_5_flag <= ~cnt_0_5_flag;
else
cnt_0_5_flag <= cnt_0_5_flag;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_adjust <= RUN;
else
cstate_adjust <= nstate_adjust;
//更改时间和正常显示状态机跳转
always @(*)
case(cstate_adjust)
RUN :
if(adjust_date_ctrl)
nstate_adjust = CHANGE;
else
nstate_adjust = RUN;
CHANGE :
if(adjust_date_ctrl)
nstate_adjust = RUN;
else
nstate_adjust = CHANGE;
endcase
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_u_d <= STATE_DAY;
else
cstate_u_d <= nstate_u_d;
//更改秒、分、时状态跳转
always @(*)
case(cstate_u_d)
STATE_DAY :
if(left_ctrl)
nstate_u_d = STATE_MOU;
else
nstate_u_d = STATE_DAY;
STATE_MOU :
if(left_ctrl)
nstate_u_d = STATE_YEAR;
else
nstate_u_d = STATE_MOU;
STATE_YEAR :
if(left_ctrl)
nstate_u_d = STATE_DAY;
else
nstate_u_d = STATE_YEAR;
default :
nstate_u_d = STATE_DAY;
endcase
//计数器计时一天
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt <= 1'b0;
else if(cnt == CNT_1d)
cnt <= 1'b0;
else
cnt <= cnt + 1'b1;
//天数个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_unit <= 4'd4;
else if(nstate_adjust == RUN)begin
if((day_unit == 4'd9) && (cnt == CNT_1d - 1'b1))
day_unit <= 1'b0;
else if(cnt == CNT_1d - 1'b1)
day_unit <= day_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_DAY && up_ctrl && day_unit == 4'd9)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl && day_ten == 4'd3 && day_unit == 4'd0)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl)
day_unit <= day_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_DAY && down_ctrl && day_ten == 4'd0 && day_unit == 4'd0)
day_unit <= 4'd0;
else if(nstate_u_d == STATE_DAY && down_ctrl && day_unit == 4'd0)
day_unit <= 4'd9;
else if(nstate_u_d == STATE_DAY && down_ctrl)
day_unit <= day_unit - 1'b1;
else
day_unit <= day_unit;
end
//天数十位计时,逢3进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
day_ten <= 1'b1;
else if(nstate_adjust == RUN)begin
if((day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
day_ten <= 1'b0;
else if((day_unit == 4'd9) && (cnt == CNT_1d - 1'b1))
day_ten <= day_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_DAY && up_ctrl && day_ten == 4'd3 && day_unit == 4'd0)
day_ten <= 4'd0;
else if(nstate_u_d == STATE_DAY && up_ctrl && day_unit == 4'd9)
day_ten <= day_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_DAY && down_ctrl && day_ten == 4'd0 && day_unit == 4'd0)
day_ten <= 4'd3;
else if(nstate_u_d == STATE_DAY && down_ctrl && day_unit == 4'd0)
day_ten <= day_ten - 1'b1;
end
//月份个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_unit <= 4'd9;
else if(nstate_adjust == RUN)begin
if((mou_unit == 4'd9) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_unit <= 1'b0;
else if((day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_unit <= mou_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MOU && up_ctrl && mou_unit == 4'd9)
mou_unit <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl && mou_ten == 4'd1 && mou_unit == 4'd2)
mou_unit <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl)
mou_unit <= mou_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_MOU && down_ctrl)
if(mou_ten == 4'd0 && mou_unit == 4'd0)
mou_unit <= 4'd2;
else if(mou_unit == 4'd0)
mou_unit <= 4'd9;
else
mou_unit <= mou_unit - 1'b1;
end
//月份十位计时,逢1进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
mou_ten <= 1'b0;
else if(nstate_adjust == RUN)begin
if((mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_ten <= 1'b0;
else if((mou_unit == 4'd9) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
mou_ten <= mou_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_MOU && up_ctrl && mou_ten == 4'd1 && mou_unit == 4'd2)
mou_ten <= 4'd0;
else if(nstate_u_d == STATE_MOU && up_ctrl && mou_unit == 4'd9)
mou_ten <= mou_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_MOU && down_ctrl && mou_ten == 4'd0 && mou_unit == 4'd0)
mou_ten <= 4'd1;
else if(nstate_u_d == STATE_MOU && down_ctrl && mou_unit == 4'd0)
mou_ten <= mou_ten - 1'b1;
end
//年份个位计时,逢10进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_unit <= 4'd2;
else if(nstate_adjust == RUN)begin
if((year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_unit <= 1'b0;
else if((mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_unit <= year_unit + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_YEAR && up_ctrl && year_unit == 4'd9)
year_unit <= 4'd0;
else if(nstate_u_d == STATE_YEAR && up_ctrl)
year_unit <= year_unit + 1'b1;
//减少
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_unit == 4'd0)
year_unit <= 4'd9;
else if(nstate_u_d == STATE_YEAR && down_ctrl)
year_unit <= year_unit - 1'b1;
end
//年份十位计时,逢2进一
always @(posedge clk or negedge rst_n)
if(!rst_n)
year_ten <= 4'd2;
else if(nstate_adjust == RUN)begin
if((year_ten == 4'd9) && (year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_ten <= 1'b0;
else if((year_unit == 4'd9) && (mou_ten == 4'd1) && (mou_unit == 4'd2) && (day_ten == 4'd3) && (day_unit == 4'd0) && (cnt == CNT_1d - 1'b1))
year_ten <= year_ten + 1'b1;
end
else if(nstate_adjust == CHANGE)begin
if(nstate_u_d == STATE_YEAR && up_ctrl && year_ten == 4'd9 && year_unit == 4'd9)
year_ten <= 4'd0;
else if(nstate_u_d == STATE_YEAR && up_ctrl && year_unit == 4'd9)
year_ten <= year_ten + 1'b1;
//减少
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_ten == 4'd0 && year_unit == 4'd0)
year_ten <= 4'd9;
else if(nstate_u_d == STATE_YEAR && down_ctrl && year_unit == 4'd0)
year_ten <= year_ten - 1'b1;
end
//时间输出显示
always @(posedge clk or negedge rst_n)
if(!rst_n)
date_data <= 1'b0;
else if(nstate_adjust == RUN)
date_data <= {year_ten,year_unit,mou_ten,mou_unit,day_ten,day_unit};
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_DAY)begin
if(cnt_0_5_flag)
date_data <= {year_ten,year_unit,mou_ten,mou_unit,8'hff};
else if(!cnt_0_5_flag)
date_data <= {year_ten,year_unit,mou_ten,mou_unit,day_ten,day_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_MOU)begin
if(cnt_0_5_flag)
date_data <= {year_ten,year_unit,8'hff,day_ten,day_unit};
else if(!cnt_0_5_flag)
date_data <= {year_ten,year_unit,mou_ten,mou_unit,day_ten,day_unit};
end
else if(nstate_adjust == CHANGE && nstate_u_d == STATE_YEAR)begin
if(cnt_0_5_flag)
date_data <= {8'hff,mou_ten,mou_unit,day_ten,day_unit};
else if(!cnt_0_5_flag)
date_data <= {year_ten,year_unit,mou_ten,mou_unit,day_ten,day_unit};
end
endmodule
3、alarm_display.v
module alarm_display(
input wire clk,
input wire rst_n,
input wire up_ctrl,
input wire down_ctrl,
input wire left_ctrl,
input wire adjust_alarm_ctrl,
output reg [23:0] alarm_data
);
parameter STATE_SEC = 2'b01,
STATE_MIN = 2'b10,
STATE_HOUR = 2'b11;
reg [1:0] cstate;
reg [1:0] nstate;
reg [3:0] sec_unit;
reg [3:0] sec_ten;
reg [3:0] min_unit;
reg [3:0] min_ten;
reg [3:0] hour_unit;
reg [3:0] hour_ten;
parameter CNT_0_5 = 25'd25_000_000; //0.5s计时
parameter RUN = 2'b00, //正常运行
CHANGE = 2'b01; //更改时间
reg [1:0]cstate_adjust;
reg [1:0]nstate_adjust;
reg [24:0] cnt_0_5;
reg cnt_0_5_flag;
//数码管亮灭0.5s计时
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5 <= 1'b0;
else if(cnt_0_5 == CNT_0_5)
cnt_0_5 <= 1'b0;
else
cnt_0_5 <= cnt_0_5 + 1'b1;
//数码管亮灭0.5s分频
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_0_5_flag <= 1'b0;
else if(cnt_0_5 == CNT_0_5 - 1'b1)
cnt_0_5_flag <= ~cnt_0_5_flag;
else
cnt_0_5_flag <= cnt_0_5_flag;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate_adjust <= RUN;
else
cstate_adjust <= nstate_adjust;
//更改时间和正常显示状态机跳转
always @(*)
case(cstate_adjust)
RUN :
if(adjust_alarm_ctrl)
nstate_adjust = CHANGE;
else
nstate_adjust = RUN;
CHANGE :
if(adjust_alarm_ctrl)
nstate_adjust = RUN;
else
nstate_adjust = CHANGE;
endcase
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate <= STATE_SEC;
else
cstate <= nstate;
//状态机跳转
always @(*)
case(cstate)
STATE_SEC :
if(left_ctrl)
nstate <= STATE_MIN;
else
nstate <= STATE_SEC;
STATE_MIN :
if(left_ctrl)
nstate <= STATE_HOUR;
else
nstate <= STATE_MIN;
STATE_HOUR :
if(left_ctrl)
nstate <= STATE_SEC;
else
nstate <= STATE_HOUR;
default : nstate <= STATE_SEC;
endcase
//增加秒钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_unit <= 4'd0;
else if(nstate_adjust == RUN)
sec_unit <= sec_unit;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_unit <= 4'd0;
else if(nstate == STATE_SEC && up_ctrl)
sec_unit <= sec_unit + 1'b1;
//减少
else if(nstate == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_unit <= 4'd9;
else if(nstate == STATE_SEC && down_ctrl)
sec_unit <= sec_unit - 1'b1;
end
//增加秒钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
sec_ten <= 4'd0;
else if(nstate_adjust == RUN)
sec_ten <= sec_ten;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_SEC && up_ctrl && sec_ten == 4'd5 && sec_unit == 4'd9)
sec_ten <= 4'd0;
else if(nstate == STATE_SEC && up_ctrl && sec_unit == 4'd9)
sec_ten <= sec_ten + 1'b1;
//减少
else if(nstate == STATE_SEC && down_ctrl && sec_ten == 4'd0 && sec_unit == 4'd0)
sec_ten <= 4'd5;
else if(nstate == STATE_SEC && down_ctrl && sec_unit == 4'd0)
sec_ten <= sec_ten - 1'b1;
end
//增加分钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_unit <= 4'd0;
else if(nstate_adjust == RUN)
min_unit <= min_unit;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_unit <= 4'd0;
else if(nstate == STATE_MIN && up_ctrl)
min_unit <= min_unit + 1'b1;
//减少
else if(nstate == STATE_MIN && down_ctrl && min_ten == 4'd0 &&min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_unit <= 4'd9;
else if(nstate == STATE_MIN && down_ctrl)
min_unit <= min_unit - 1'b1;
end
//增加分钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
min_ten <= 4'd0;
else if(nstate_adjust == RUN)
min_ten <= min_ten;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_MIN && up_ctrl && min_ten == 4'd5 && min_unit == 4'd9)
min_ten <= 4'd0;
else if(nstate == STATE_MIN && up_ctrl && min_unit == 4'd9)
min_ten <= min_ten + 1'b1;
//减少
else if(nstate == STATE_MIN && down_ctrl && min_ten == 4'd0 && min_unit == 4'd0)
min_ten <= 4'd5;
else if(nstate == STATE_MIN && down_ctrl && min_unit == 4'd0)
min_ten <= min_ten - 1'b1;
end
//增加时钟个位
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_unit <= 4'd0;
else if(nstate_adjust == RUN)
hour_unit <= hour_unit;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_unit <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_unit <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl)
hour_unit <= hour_unit + 1'b1;
//减少
else if(nstate == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_unit <= 4'd3;
else if(nstate == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_unit <= 4'd9;
else if(nstate == STATE_HOUR && down_ctrl)
hour_unit <= hour_unit - 1'b1;
end
//增加时钟十位
always @(posedge clk or negedge rst_n)
if(!rst_n)
hour_ten <= 4'd0;
else if(nstate_adjust == RUN)
hour_ten <= hour_ten;
else if(nstate_adjust == CHANGE)begin
if(nstate == STATE_HOUR && up_ctrl && hour_ten == 4'd2 && hour_unit == 4'd3)
hour_ten <= 4'd0;
else if(nstate == STATE_HOUR && up_ctrl && hour_unit == 4'd9)
hour_ten <= hour_ten + 1'b1;
//减少
else if(nstate == STATE_HOUR && down_ctrl && hour_ten == 4'd0 && hour_unit == 4'd0)
hour_ten <= 4'd2;
else if(nstate == STATE_HOUR && down_ctrl && hour_unit == 4'd0)
hour_ten <= hour_ten - 1'b1;
end
//assign alarm_data = {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_data <= 1'b0;
else if(nstate_adjust == RUN)
alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
else if(nstate_adjust == CHANGE && nstate == STATE_SEC)begin
if(cnt_0_5_flag)
alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,8'hff};
else if(!cnt_0_5_flag)
alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate == STATE_MIN)begin
if(cnt_0_5_flag)
alarm_data <= {hour_ten,hour_unit,8'hff,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
else if(nstate_adjust == CHANGE && nstate == STATE_HOUR)begin
if(cnt_0_5_flag)
alarm_data <= {8'hff,min_ten,min_unit,sec_ten,sec_unit};
else if(!cnt_0_5_flag)
alarm_data <= {hour_ten,hour_unit,min_ten,min_unit,sec_ten,sec_unit};
end
endmodule
4、beep_music.v
module beep_music(
input wire clk,
input wire rst_n,
input wire beep_en,
output reg beep
);
parameter CLK_FRE = 50 ;
reg beep_t;
reg [31:0]cnt;
wire [19:0]duty_data;
reg [6:0]address;
wire [7:0]time_music;
reg [31:0]time_cycle;
wire [7:0]hz_sel;
wire [19:0]cycle;
reg [19:0]cnt_cycle;
//单个音符时间计数值
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_cycle<=32'd0;
else
time_cycle<=time_music*(CLK_FRE*1000000/8) ; //1000000
//计时器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=1'b0;
else if(cnt==time_cycle)
cnt<=1'b0;
else if(beep_en)
cnt<=cnt+1'b1;
//频率rom计数器加一
always @(posedge clk or negedge rst_n)
if(!rst_n)
address<=1'b0;
else if(address==8'd34 && cnt==time_cycle)
address<=1'b0;
else if(!beep_en)
address <= 1'b0;
else if(cnt==time_cycle && beep_en)
address<=address+1'b1;
//频率计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_cycle<=1'b0;
else if(cnt_cycle==cycle || cnt==time_cycle)
cnt_cycle<=1'b0;
else
cnt_cycle<=cnt_cycle+1'b1;
//占空比
assign duty_data=cycle/8;
//蜂鸣器输出PWM
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep_t=1'b1;
else if(cnt_cycle>=duty_data && beep_en)
beep_t=1'b0;
else
beep_t=1'b1;
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep <= 1'b1;
else if(beep_en)
beep <= ~beep_t;
else
beep <= 1'b1;
time_music time_music_inst(
.address(address),
.clock(clk),
.q(time_music)
);
hz_sel hz_sel_inst(
.address(address),
.clock(clk),
.q(hz_sel)
);
hz hz_inst(
.hz_sel(hz_sel),
.cycle(cycle)
);
endmodule
5、infrared_rcv.v
module infrared_rcv(
input wire sys_clk,
input wire sys_rst_n,
input wire infrared_in,
output reg [19:0]data,
output reg repeat_en
);
parameter CNT_0_56MS_L=20000,
CNT_0_56MS_H=35000,
CNT_1_69MS_L=80000,
CNT_1_69MS_H=90000,
CNT_2_25MS_L=100000,
CNT_2_25MS_H=125000,
CNT_4_5MS_L=175000,
CNT_4_5MS_H=275000,
CNT_9MS_L=400000,
CNT_9MS_H=490000;
parameter IDLE=5'b0_0001,
S_T9=5'b0_0010,
S_JUDGE=5'b0_0100,
S_IFR_DATA=5'b0_1000,
S_REPEAT=5'b1_0000;
wire ifr_in_rise;//检测红外信号上升沿
wire ifr_in_fall;
reg infrared_in_d1;
reg infrared_in_d2;
reg [18:0]cnt;
reg flag_0_56ms;
reg flag_1_69ms;
reg flag_2_25ms;
reg flag_4_5ms;
reg flag_9ms;
reg [4:0]state;
reg [5:0]data_cnt;
reg [31:0]data_tmp;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
infrared_in_d1<=1'b0;
else
infrared_in_d1<=infrared_in;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
infrared_in_d2<=1'b0;
else
infrared_in_d2<=infrared_in_d1;
//红外信号上升和下降沿
assign ifr_in_rise=infrared_in_d1 && ~infrared_in_d2;
assign ifr_in_fall=infrared_in_d2 && ~infrared_in_d1;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
cnt<=1'b0;
else
case(state)
IDLE:
cnt<=12'b0;
S_T9:
if(ifr_in_rise && flag_9ms)
cnt<=1'b0;
else
cnt<=cnt+1'b1;
S_JUDGE:
if(ifr_in_fall && (flag_4_5ms || flag_2_25ms))
cnt<=1'b0;
else
cnt<=cnt+1'b1;
S_IFR_DATA:
if(flag_0_56ms && ifr_in_rise)
cnt<=1'b0;
else if((flag_0_56ms || flag_1_69ms) && ifr_in_fall)
cnt<=1'b0;
else
cnt<=cnt+1'b1;
default:
cnt<=1'b0;
endcase
//计数到0.56ms范围拉高标志信号
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_0_56ms<=1'b0;
else if(state==S_IFR_DATA && cnt>=CNT_0_56MS_L && cnt<=CNT_0_56MS_H)
flag_0_56ms<=1'b1;
else
flag_0_56ms<=1'b0;
//计数到1.69ms范围拉高标志信号
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_1_69ms<=1'b0;
else if(state==S_IFR_DATA && cnt>=CNT_1_69MS_L && cnt<=CNT_1_69MS_H)
flag_1_69ms<=1'b1;
else
flag_1_69ms<=1'b0;
//计数2.25ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_2_25ms<=1'b0;
else if(state==S_JUDGE && cnt>=CNT_2_25MS_L && cnt<=CNT_2_25MS_H)
flag_2_25ms<=1'b1;
else
flag_2_25ms<=1'b0;
//计数4.5ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_4_5ms<=1'b0;
else if(state==S_JUDGE && cnt>=CNT_4_5MS_L && cnt<=CNT_4_5MS_H)
flag_4_5ms<=1'b1;
else
flag_4_5ms<=1'b0;
//计数9ms
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
flag_9ms<=1'b0;
else if(state==S_T9 && cnt>=CNT_9MS_L && cnt<=CNT_9MS_H)
flag_9ms<=1'b1;
else
flag_9ms<=1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
state<=IDLE;
else
case(state)
IDLE:
if(ifr_in_fall)
state<=S_T9;
else
state<=IDLE;
S_T9:
if(ifr_in_rise && flag_9ms)
state<=S_JUDGE;
else if(ifr_in_rise && !flag_9ms)
state<=IDLE;
else
state<=S_T9;
S_JUDGE:
if(ifr_in_fall && flag_2_25ms)
state<=S_REPEAT;
else if(ifr_in_fall && flag_4_5ms)
state<=S_IFR_DATA;
else if(ifr_in_fall && !flag_2_25ms && !flag_4_5ms)
state<=IDLE;
else
state<=S_JUDGE;
S_IFR_DATA:
if(ifr_in_rise && !flag_0_56ms)
state<=IDLE;
else if(ifr_in_fall && !flag_0_56ms && !flag_1_69ms)
state<=IDLE;
else if(ifr_in_rise && data_cnt==6'd32)
state<=IDLE;
S_REPEAT:
if(ifr_in_rise)
state<=IDLE;
else
state<=S_REPEAT;
default:
state<=IDLE;
endcase
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data_tmp<=1'b0;
else if(state==S_IFR_DATA && ifr_in_fall && flag_0_56ms)
data_tmp[data_cnt]<=1'b0;
else if(state==S_IFR_DATA && ifr_in_fall && flag_1_69ms)
data_tmp[data_cnt]<=1'b1;
else
data_tmp<=data_tmp;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data_cnt<=1'b0;
else if(ifr_in_rise && data_cnt==6'd32)
data_cnt<=1'b0;
else if(ifr_in_fall && state==S_IFR_DATA)
data_cnt<=data_cnt+1'b1;
else
data_cnt<=data_cnt;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
repeat_en<=1'b0;
else if(state==S_REPEAT && data_tmp[23:16]==~data_tmp[31:24])
repeat_en<=1'b1;
else
repeat_en<=1'b0;
always @(posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n)
data<=1'b0;
else if(data_tmp[23:16]==~data_tmp[31:24] && data_tmp[7:0]==~data_tmp[15:8] && data_cnt==6'd32)
data<={12'd0,data_tmp[23:16]};
else
data <= 1'b0;
endmodule
6、infrared_key_adjust.v
module infrared_key_adjust(
input wire clk,
input wire rst_n,
input wire [19:0] infrared_data,
output wire infrared_68_flag,
output wire infrared_7_flag,
output wire infrared_25_flag,
output wire infrared_71_flag,
output wire infrared_69_flag,
output wire infrared_21_flag,
output wire infrared_64_flag
);
wire infrared_in;
reg infrared_68;
reg infrared_68_t;
reg infrared_64;
reg infrared_64_t;
reg infrared_7;
reg infrared_7_t;
reg infrared_25;
reg infrared_25_t;
reg infrared_71;
reg infrared_71_t;
reg infrared_69;
reg infrared_69_t;
reg infrared_21;
reg infrared_21_t;
//检测到红外遥控按键68
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_68 <= 1'b0;
else if(infrared_data == 20'd68)
infrared_68 <= 1'b1;
else
infrared_68 <= 1'b0;
//将按键68打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_68_t <= 1'b0;
else
infrared_68_t <= infrared_68;
assign infrared_68_flag = (infrared_68 && !infrared_68_t)? 1'b1:1'b0;
//检测到红外遥控按键64
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_64 <= 1'b0;
else if(infrared_data == 20'd64)
infrared_64 <= 1'b1;
else
infrared_64 <= 1'b0;
//将按键64打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_64_t <= 1'b0;
else
infrared_64_t <= infrared_64;
assign infrared_64_flag = (infrared_64 && !infrared_64_t)? 1'b1:1'b0;
//检测到红外遥控按键7
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_7 <= 1'b0;
else if(infrared_data == 20'd7)
infrared_7 <= 1'b1;
else
infrared_7 <= 1'b0;
//将按键7打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_7_t <= 1'b0;
else
infrared_7_t <= infrared_7;
assign infrared_7_flag = (infrared_7 && !infrared_7_t)? 1'b1:1'b0;
//检测到红外遥控按键25
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_25 <= 1'b0;
else if(infrared_data == 20'd25)
infrared_25 <= 1'b1;
else
infrared_25 <= 1'b0;
//将按键25打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_25_t <= 1'b0;
else
infrared_25_t <= infrared_25;
assign infrared_25_flag = (infrared_25 && !infrared_25_t)? 1'b1:1'b0;
//检测到红外遥控按键71
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_71 <= 1'b0;
else if(infrared_data == 20'd71)
infrared_71 <= 1'b1;
else
infrared_71 <= 1'b0;
//将按键71打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_71_t <= 1'b0;
else
infrared_71_t <= infrared_71;
assign infrared_71_flag = (infrared_71 && !infrared_71_t)? 1'b1:1'b0;
//检测到红外遥控按键69
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_69 <= 1'b0;
else if(infrared_data == 20'd69)
infrared_69 <= 1'b1;
else
infrared_69 <= 1'b0;
//将按键69打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_69_t <= 1'b0;
else
infrared_69_t <= infrared_69;
assign infrared_69_flag = (infrared_69 && !infrared_69_t)? 1'b1:1'b0;
//检测到红外遥控按键21
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_21 <= 1'b0;
else if(infrared_data == 20'd21)
infrared_21 <= 1'b1;
else
infrared_21 <= 1'b0;
//将按键21打一拍
always @(posedge clk or negedge rst_n)
if(!rst_n)
infrared_21_t <= 1'b0;
else
infrared_21_t <= infrared_21;
assign infrared_21_flag = (infrared_21 && !infrared_21_t)? 1'b1:1'b0;
endmodule
7、seg.v
module seg(
input wire clk,
input wire rst_n,
input wire [23:0]disp_data,
input wire en,
input wire [5:0]point,
output reg[7:0]seg,
output wire[5:0]sel
);
reg [14:0]divder_cnt;
reg clk_1K;
reg [5:0]sel_r;
reg [2:0]cnt_sel;
//待显示数据缓存
reg [3:0]data_tmp;
reg dot_disp;
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter dispa=8'b1000_1000;
parameter dispb=8'b1000_0011;
parameter dispc=8'b1010_0111;
parameter dispd=8'b1010_0001;
parameter dispe=8'b1000_0110;
parameter dispf=8'b1000_0011;
//分频计数器
always @(posedge clk or negedge rst_n)
if(!rst_n)
divder_cnt<=15'd0;
else if(!en)
divder_cnt<=15'd0;
else if(divder_cnt==15'd24999)
divder_cnt<=15'd0;
else
divder_cnt<=divder_cnt+15'd1;
//1KHz时钟产生模块
always @(posedge clk or negedge rst_n)
if(!rst_n)
clk_1K<=1'b0;
else if(divder_cnt==15'd24999)
clk_1K<=~clk_1K;
else
clk_1K<=clk_1K;
//6位循环移位寄存器
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
sel_r<=6'b00_0001;
else if(sel_r==6'b10_0000)
sel_r<=6'b00_0001;
else
sel_r<=sel_r<<1;
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
cnt_sel <= 1'b0;
else if(cnt_sel == 3'b101)
cnt_sel <= 1'b0;
else
cnt_sel <= cnt_sel + 1'b1;
always @(posedge clk_1K or negedge rst_n)
if(!rst_n)
dot_disp <= 1'b1;
else
dot_disp <= ~point[cnt_sel];
always @(*)
seg[7] = dot_disp;
//六选一多路器
always @(*)
case(sel_r)
6'b00_0001:data_tmp=disp_data[3:0];
6'b00_0010:data_tmp=disp_data[7:4];
6'b00_0100:data_tmp=disp_data[11:8];
6'b00_1000:data_tmp=disp_data[15:12];
6'b01_0000:data_tmp=disp_data[19:16];
6'b10_0000:data_tmp=disp_data[23:20];
default:data_tmp=4'b0000;
endcase
always@(*)
case(data_tmp)
4'h0:seg[6:0]=7'b100_0000;
4'h1:seg[6:0]=7'b111_1001;
4'h2:seg[6:0]=7'b010_0100;
4'h3:seg[6:0]=7'b011_0000;
4'h4:seg[6:0]=7'b001_1001;
4'h5:seg[6:0]=7'b001_0010;
4'h6:seg[6:0]=7'b000_0010;
4'h7:seg[6:0]=7'b111_1000;
4'h8:seg[6:0]=7'b000_0000;
4'h9:seg[6:0]=7'b001_0000;
4'ha:seg[6:0]=7'b000_1000;
4'hb:seg[6:0]=7'b000_0011;
4'hc:seg[6:0]=7'b100_0110;
4'hd:seg[6:0]=7'b010_0001;
4'he:seg[6:0]=7'b000_0110;
4'hf:seg[6:0]=7'b111_1111;
endcase
assign sel=~sel_r;
endmodule
8、all_display.v
module all_display(
input wire clk,
input wire rst_n,
input wire infrared_in,
output wire beep,
output wire [5:0]sel,
output wire [7:0]seg
);
parameter CNT_BEEP_20 = 1_000_000_000; //1_000_000_000
//状态机状态
parameter TIME = 3'b000,
DATE = 3'b001,
ALARM = 3'b010;
wire [5:0] point;
//wire repeat_en;
wire [23:0] alarm_data;
wire [23:0] time_data;
wire [23:0] date_data;
reg beep_en;
reg alarm_flag;
reg [29:0] cnt_beep;
reg [23:0] data;
reg [2:0] nstate;
reg [2:0] cstate;
wire [19:0] infrared_data;
wire infrared_68_flag;
wire infrared_21_flag;
wire infrared_64_flag;
wire infrared_25_flag;
wire infrared_7_flag;
wire infrared_71_flag;
reg time_up_ctrl;
reg time_down_ctrl;
reg time_left_ctrl;
reg adjust_time_ctrl;
reg date_up_ctrl;
reg date_down_ctrl;
reg date_left_ctrl;
reg adjust_date_ctrl;
reg alarm_up_ctrl;
reg alarm_down_ctrl;
reg alarm_left_ctrl;
reg adjust_alarm_ctrl;
//时钟控制键
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_up_ctrl <= 1'b0;
else if(nstate == TIME)
time_up_ctrl <= infrared_64_flag;
else
time_up_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_down_ctrl <= 1'b0;
else if(nstate == TIME)
time_down_ctrl <= infrared_25_flag;
else
time_down_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
time_left_ctrl <= 1'b0;
else if(nstate == TIME)
time_left_ctrl <= infrared_7_flag;
else
time_left_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
adjust_time_ctrl <= 1'b0;
else if(nstate == TIME)
adjust_time_ctrl <= infrared_71_flag;
else
adjust_time_ctrl <= 1'b0;
//日期控制键
always @(posedge clk or negedge rst_n)
if(!rst_n)
date_up_ctrl <= 1'b0;
else if(nstate == DATE)
date_up_ctrl <= infrared_64_flag;
else
date_up_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
date_down_ctrl <= 1'b0;
else if(nstate == DATE)
date_down_ctrl <= infrared_25_flag;
else
date_down_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
date_left_ctrl <= 1'b0;
else if(nstate == DATE)
date_left_ctrl <= infrared_7_flag;
else
date_left_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
adjust_date_ctrl <= 1'b0;
else if(nstate == DATE)
adjust_date_ctrl <= infrared_71_flag;
else
adjust_date_ctrl <= 1'b0;
//闹钟控制键
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_up_ctrl <= 1'b0;
else if(nstate == ALARM)
alarm_up_ctrl <= infrared_64_flag;
else
alarm_up_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_down_ctrl <= 1'b0;
else if(nstate == ALARM)
alarm_down_ctrl <= infrared_25_flag;
else
alarm_down_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_left_ctrl <= 1'b0;
else if(nstate == ALARM)
alarm_left_ctrl <= infrared_7_flag;
else
alarm_left_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
adjust_alarm_ctrl <= 1'b0;
else if(nstate == ALARM)
adjust_alarm_ctrl <= infrared_71_flag;
else
adjust_alarm_ctrl <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
cstate <= TIME;
else
cstate <= nstate;
//按键状态机状态跳转
always @(*)
case(cstate)
TIME :
if(infrared_68_flag)
nstate <= DATE;
else
nstate <= TIME;
DATE :
if(infrared_68_flag)
nstate <= ALARM;
else
nstate <= DATE;
ALARM :
if(infrared_68_flag)
nstate <= TIME;
else
nstate <= ALARM;
default : nstate <= TIME;
endcase
//显示选择
always @(posedge clk or negedge rst_n)
if(!rst_n)
data <= time_data;
else if(nstate == TIME)
data <= time_data;
else if(nstate == DATE)
data <= date_data;
else if(nstate == ALARM)
data <= alarm_data;
//闹钟计时20s
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_beep <= 1'b0;
else if(cnt_beep == CNT_BEEP_20 && alarm_flag)
cnt_beep <= 1'b0;
else if(alarm_flag)
cnt_beep <= cnt_beep + 1'b1;
//闹钟标志信号
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_flag <= 1'b0;
else if(time_data == alarm_data)
alarm_flag <= 1'b1;
else if(infrared_21_flag)
alarm_flag <= 1'b0;
else if(cnt_beep == CNT_BEEP_20 - 1'b1)
alarm_flag <= 1'b0;
else
alarm_flag <= alarm_flag;
//蜂鸣器使能信号
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep_en <= 1'b0;
else if(alarm_flag)
beep_en <= 1'b1;
else
beep_en <= 1'b0;
time_display
#(
.CNT_1S (50_000_000) //50_000_000
)
time_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (time_up_ctrl),
.down_ctrl (time_down_ctrl),
.left_ctrl (time_left_ctrl),
.adjust_time_ctrl (adjust_time_ctrl),
.time_data (time_data)
);
date_display
#(
.CNT_1d (42'd4_320_000_000_000) //42'd4_320_000_000_000
)
date_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (date_up_ctrl),
.down_ctrl (date_down_ctrl),
.left_ctrl (date_left_ctrl),
.adjust_date_ctrl (adjust_date_ctrl),
.date_data (date_data)
);
alarm_display alarm_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (alarm_up_ctrl),
.down_ctrl (alarm_down_ctrl),
.left_ctrl (alarm_left_ctrl),
.adjust_alarm_ctrl (adjust_alarm_ctrl),
.alarm_data (alarm_data)
);
seg seg_inst(
.clk (clk),
.rst_n (rst_n),
.disp_data (data),
.en (!infrared_69_flag),
.point (6'b001010),
.seg (seg),
.sel (sel)
);
beep_music beep_music_inst(
.clk (clk),
.rst_n (rst_n),
.beep_en (beep_en),
.beep (beep)
);
//红外遥控模块
infrared_rcv infrared_rcv_isnt(
.sys_clk (clk),
.sys_rst_n (rst_n),
.infrared_in(infrared_in),
.data (infrared_data),
.repeat_en ()
);
infrared_key_adjust infrared_key_adjust_inst(
.clk (clk),
.rst_n (rst_n),
.infrared_data (infrared_data),
.infrared_68_flag (infrared_68_flag),
.infrared_7_flag (infrared_7_flag),
.infrared_25_flag (infrared_25_flag),
.infrared_71_flag (infrared_71_flag),
.infrared_69_flag (infrared_69_flag),
.infrared_21_flag (infrared_21_flag),
.infrared_64_flag (infrared_64_flag)
);
endmodule
ate <= ALARM;
else
nstate <= DATE;
ALARM :
if(infrared_68_flag)
nstate <= TIME;
else
nstate <= ALARM;
default : nstate <= TIME;
endcase
//显示选择
always @(posedge clk or negedge rst_n)
if(!rst_n)
data <= time_data;
else if(nstate == TIME)
data <= time_data;
else if(nstate == DATE)
data <= date_data;
else if(nstate == ALARM)
data <= alarm_data;
//闹钟计时20s
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_beep <= 1'b0;
else if(cnt_beep == CNT_BEEP_20 && alarm_flag)
cnt_beep <= 1'b0;
else if(alarm_flag)
cnt_beep <= cnt_beep + 1'b1;
//闹钟标志信号
always @(posedge clk or negedge rst_n)
if(!rst_n)
alarm_flag <= 1'b0;
else if(time_data == alarm_data)
alarm_flag <= 1'b1;
else if(infrared_21_flag)
alarm_flag <= 1'b0;
else if(cnt_beep == CNT_BEEP_20 - 1'b1)
alarm_flag <= 1'b0;
else
alarm_flag <= alarm_flag;
//蜂鸣器使能信号
always @(posedge clk or negedge rst_n)
if(!rst_n)
beep_en <= 1'b0;
else if(alarm_flag)
beep_en <= 1'b1;
else
beep_en <= 1'b0;
time_display
#(
.CNT_1S (50_000_000) //50_000_000
)
time_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (time_up_ctrl),
.down_ctrl (time_down_ctrl),
.left_ctrl (time_left_ctrl),
.adjust_time_ctrl (adjust_time_ctrl),
.time_data (time_data)
);
date_display
#(
.CNT_1d (42'd4_320_000_000_000) //42'd4_320_000_000_000
)
date_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (date_up_ctrl),
.down_ctrl (date_down_ctrl),
.left_ctrl (date_left_ctrl),
.adjust_date_ctrl (adjust_date_ctrl),
.date_data (date_data)
);
alarm_display alarm_display_inst(
.clk (clk),
.rst_n (rst_n),
.up_ctrl (alarm_up_ctrl),
.down_ctrl (alarm_down_ctrl),
.left_ctrl (alarm_left_ctrl),
.adjust_alarm_ctrl (adjust_alarm_ctrl),
.alarm_data (alarm_data)
);
seg seg_inst(
.clk (clk),
.rst_n (rst_n),
.disp_data (data),
.en (!infrared_69_flag),
.point (6'b001010),
.seg (seg),
.sel (sel)
);
beep_music beep_music_inst(
.clk (clk),
.rst_n (rst_n),
.beep_en (beep_en),
.beep (beep)
);
//红外遥控模块
infrared_rcv infrared_rcv_isnt(
.sys_clk (clk),
.sys_rst_n (rst_n),
.infrared_in(infrared_in),
.data (infrared_data),
.repeat_en ()
);
infrared_key_adjust infrared_key_adjust_inst(
.clk (clk),
.rst_n (rst_n),
.infrared_data (infrared_data),
.infrared_68_flag (infrared_68_flag),
.infrared_7_flag (infrared_7_flag),
.infrared_25_flag (infrared_25_flag),
.infrared_71_flag (infrared_71_flag),
.infrared_69_flag (infrared_69_flag),
.infrared_21_flag (infrared_21_flag),
.infrared_64_flag (infrared_64_flag)
);
endmodule