您好,欢迎来到小奈知识网。
搜索
您的当前位置:首页基于VerilogHDL语言多功能数字钟设计毕业设计论文

基于VerilogHDL语言多功能数字钟设计毕业设计论文

来源:小奈知识网
多功能数字钟

NJUST

多功能数字钟设计

基于VerilogHDL语言

学院: 电子工程与光电技术学院

学号: ************ 姓名: 指导教师:

2014年11月21日星期五

摘要:基于FPGA平台,运用Verilog语言编写设计一多功能数字钟,包括基本的时钟,校时校分,整点报时功能。扩展闹钟,秒表,万年历,键盘输入功能。 Abstract:FPGA-based platform, using Verilog language to design a

multi-functional digital clock, including basic function of clock, school hours, school minutes,the whole point timekeeping. And extended function of alarm clock, stopwatch, calendar, keyboard input. 关键词:多功能数字钟,可编程逻辑器件,EDA设计,Verilog

Keywords:multi-functional digital clock, FPGA, EDA disign, Verilog

目录

1 设计要求 ....................................................................................................... 2 2 设计方案选择及思路分析 ................................................................................ 2 3 各子模块设计原理和分析 ................................................................................. 3

3.1 分频模块 ............................................................................................... 3 3.2 时分秒模块 ............................................................................................ 5 3.3 时分调整模块 ........................................................................................ 6 3.4 报时模块 ............................................................................................... 7 3.5 扫描显示模块 ........................................................................................ 8 3.6 秒表模块 ............................................................................................... 9 3.7 闹钟模块 ............................................................................................. 10 3.8 万年历模块 .......................................................................................... 12 3.9 键盘扫描模块 ...................................................................................... 13 4 调试仿真 ...................................................................................................... 15 5 编程下载 ...................................................................................................... 16 6 结论 ............................................................................................................. 17 7 参考文献 ...................................................................................................... 17 8 实验感想 ...................................................................................................... 17 9 源代码 ......................................................................................................... 18

1

1 设计要求

基于FPGA可编程逻辑器件,用quatusII软件设计一个多功能数字钟,其基本要求如下:

1.有基础的计时显示功能,即时、分、秒显示在6个七段管上

2.K0,K1,K2,K3分别为系统使能(暂停),时钟清零,校时,校分开关。由于按键是长期处于“1”状态,故在这里采用低电平“0”为有效电平(本人认为原要求中“1”为有效电平不合理)。

3. 使时钟具有整点报时功能(当时钟计到59’53”时开始报时,在59’53”, 59’55”,59’57”时报时频率为500Hz,59’59”时报时频率为1KHz, )。

提高部分要求:

添加按键:K4,K5分别为设置位选择,设置位加一。K6,K7为组合功能选择,当K6K7值:(11)为时钟功能,(10)为秒表功能,(01)为闹钟设置,(00)为万年历功能。

1.闹时功能,按动方式键,使电路工作于预置状态,此时显示器与时钟脱开,而与预置计数器相连,利用前面手动校时,校分方式进行预置,预置后回到正常模式。当计时计至预置的时间时,扬声器发出闹铃信号,时间为半分钟,闹铃信号可以用开关“止闹”,按下此开关后,闹铃声立刻中止,正常情况下应将此开关释放,否则无闹时作用。

2.秒表功能。按start键开始计秒,按stop键停止计秒并保持显示数不变,直到复位信号加入。

3.万年历功能,

4.使用4*4矩阵键盘输入设置信号

2 设计方案选择及思路分析

由于之前参加过华为杯电子设计大赛,当时采用的是VerilogHDL语言,而

且EDA实验一曾经做过用器件搭数字钟的实验,如果再用原理图方法的话没有挑战性,而且VerilogHDL语言更为灵活方便,因此决定采用其完成本次电子设计。 设计的总体部分按照要求可以分为基本模块:分频模块、时钟计时及调整模

2

块、扫描显示。附加模块:万年历、整点报时、闹钟功能和秒表功能。其总体设计框图如下: 秒表 分频 日月年计数 报时 闹钟 扫描显示 嗡鸣器 秒分时计数

3 各子模块设计原理和分析

3.1 分频模块

初步分析后面所需要的信号频率,分频器的功能主要有4个:分别是产生计时用的标准秒脉冲1HZ信号;闹钟及万年历设置时用的2HZ闪烁信号整点报时及显示扫描用的1kHZ高音频信号和500HZ低音频信号。分析系统时钟为48M,经过48K的分频后得到1K信号,再经过2分频可以得到500HZ方波,1K经过5分频得到200HZ信号,最后100分频得到的2HZ信号,再2分频得到1HZ的时钟。原理框图如下图5所示。

200HZ 48M 1K 500HZ 2HZ 1HZ 图5 分频信号框图

Verilog设计分频器很简单,在偶数分频时,在输入脉冲下直接计数到所分频数的一半,然后翻转即可。如:

3

always@(posedge clk) begin

f1k<=(count48k<48000/2)?1'b1:1'b0; if(count48k==48000-1) count48k<=0; else

count48k<=count48k+1; end

但是奇数分频则要复杂得多,若奇数分频不要求占空比为50%,原理同偶数分频,可计数到(N-1)/2翻转,此时占空比接近50%。但如果要求占空比为准确的50%,通过查阅资料得知也可以实现的。原理如下图

always @(posedge f1k) //上升沿计数 if(count5p==4) count5p<=0; else

count5p<=count5p+1;

//posedge wave //上升沿波形 always @(posedge f1k ) begin

if(count5p<2)

f200p <=1; else

f200p <=0; end

//negedge counter

always @(negedge f1k ) //下降沿计数 if(count5n==4) count5n<=0; else

count5n<=count5n+1; //negedge wave

always @(negedge f1k) //下降沿波形 begin

if(count5n<2)

f200n <=1; else

4

f200n <=0; end

assign f200hz=f200n|f200p; //波形相或

仿真波形如下,clk设置频率为4800M,则f1k的周期为10us,仿真结果正确。

3.2 时分秒模块

均用BCD码来保存便于后期显示,时分秒的情况基本相同,现详细分析秒的

进位情况。用4位寄存器来保存秒个位,4位寄存器来保存秒十位。时分秒一共需要24位寄存器。

在1HZ的脉冲下,秒个位计数,当满10时即4’ha时向秒十位进位,秒个位

清零;秒十位满6即4’h6时向分个位进位,秒十位清零……以此类推,特别在时十位处理时有些不同,当小时整体满24即8’h24时,清零,(向天进位,为以后万年历做铺垫)。代码如下,(此处把年月日的计数一起处理) begin

second[3:0]=second[3:0]+1'b1; if(second[3:0]==4'ha) begin

second[3:0]=4'h0;

second[7:4]=second[7:4]+1'b1; if(second[7:4]==4'h6) begin second[7:4]=4'h0; minute[3:0]=minute[3:0]+1'b1; if(minute[3:0]==4'ha) begin minute[3:0]=4'h0; minute[7:4]=minute[7:4]+1'b1; if(minute[7:4]==4'h6) begin minute[7:4]=4'h0; hour[3:0]=hour[3:0]+1'b1; if(hour[3:0]==4'ha) begin hour[3:0]=4'h0; hour[7:4]=hour[7:4]+1'b1;

5

if(hour==8'h24) begin hour[7:4]=4'h0; day[3:0]=day[3:0]+1'b1; if(day[3:0]==4'ha) begin day[3:0]=4'h0; day[7:4]=day[7:4]+1'b1; if(day[7:4]==4'h3) begin day[7:4]=4'h0; month[3:0]=month[3:0]+1'b1; if(month[3:0]==4'ha) begin month[3:0]=4'h0; month[7:4]=month[7:4]+1'b1; if(month[7:0]==8'h12) begin month=8'h0; year=year+1; end end end end end end end end end end end

仿真波形:

仿真结果正确

3.3 时分调整模块

调整模块的功能包括暂停计时,清零,校时,校分。在Verilog中很容易实现, 暂停:在时分秒计数模块前加一个条件,当key[0]不等于0时计时,那么低电平0有效时就不计时,即暂停。

6

清零:

always@(posedge clk_1hz or negedge key[1])//key[1]控制清零 begin

if(key[1]==0)//clear begin

hour=8'b0; minute=8'b0; second=8'b0; end

校时和校分一致,当key[2]=0时,时个位加一,个位满十时,向十位进一位。校时:

else if(key[7:6]==2'b11 && key[2]==0) begin

hour[3:0]=hour[3:0]+1'b1; if(hour[3:0]==4'ha) begin

hour[3:0]=4'h0;

hour[7:4]=hour[7:4]+1; if(hour[7:4]==4'h6) hour[7:4]=4'h0; end end 校分:

else if(key[7:6]==2'b11 && key[3]==0) begin

minute[3:0]=minute[3:0]+1'b1; if(minute[3:0]==4'ha) begin

minute[3:0]=4'h0;

minute[7:4]=minute[7:4]+1; if(minute[7:4]==4'h6) minute[7:4]=4'h0; end end

3.4 报时模块

以分和秒为敏感表,检测当分为59时,秒为53,55,57时,beep_r寄存器放入500hz;秒为59时,beep_r寄存器放入1khz。 always@(minute or second)

if(minute==8'h59) //59分钟时

case(second) //秒为53、55、57低音报时 8'h53, 8'h55,

8'h57:beep_r<=clk_500hz;

7

8'h59:beep_r<=clk_1k; default:beep_r<=1'b0; endcase else

beep_r<=1'b0; 3.5 扫描显示模块

扫描显示模块是一个重点模块,理解其工作原理很重要。先定义一个32位的寄存器装载要显示的内容,方便在功能扩展时直接把想要显示的内容送到该寄存器就行了。采用扫描显示,只要扫描的周期小于10ms,人眼就感觉不到闪烁。通过1k的频率控制扫描,一共扫描8位,则显示的频率为125hz,扫描周期为8ms。这样还可以节省管脚,方便操作。

原理简述,通过快速循环扫描产生8位的位码和段码,循环因子当然也是8.在循环中把8位中对应的当前位的位码置为0,同时把该为对应的数保存到寄存器。 同时把刚才寄存器中的值翻译为对应的七段管码值送到段码中。 always@(posedge clk_1k) begin

//display_function chose case(key[7:6])

2'b11:display={hour,4'ha,minute,4'ha,second};//正常计时 2'b10:display={hour1,4'ha,minute1,4'ha,second1};//设置闹钟 2'b01:display={min,4'ha,sec,4'ha,ms};//秒表 2'b00:display={year,month,day};//万年历 endcase

case(count1)

3'd0:dis_data=display[3:0]; 3'd1:dis_data=display[7:4]; 3'd2:dis_data=display[11:8]; 3'd3:dis_data=display[15:12]; 3'd4:dis_data=display[19:16]; 3'd5:dis_data=display[23:20]; 3'd6:dis_data=display[27:24]; 3'd7:dis_data=display[31:28]; endcase

case(count1)

3'd0:dig_r=8'b11111110; 3'd1:dig_r=8'b11111101; 3'd2:dig_r=8'b11111011; 3'd3:dig_r=8'b11110111; 3'd4:dig_r=8'b11101111; 3'd5:dig_r=8'b11011111; 3'd6:dig_r=8'b10111111;

8

3'd7:dig_r=8'b01111111; endcase

count1<=count1+1;

case(dis_data) 4'h0:seg_r=8'hc0; 4'h1:seg_r=8'hf9; 4'h2:seg_r=8'ha4; 4'h3:seg_r=8'hb0; 4'h4:seg_r=8'h99; 4'h5:seg_r=8'h92; 4'h6:seg_r=8'h82; 4'h7:seg_r=8'hf8; 4'h8:seg_r=8'h80; 4'h9:seg_r=8'h90; 4'ha:seg_r=8'hbf; default:seg_r=8'hff; endcase end

3.6 秒表模块

秒表模块是一个相对的模块,用单独的分、秒、毫秒寄存器计数。通过key[6:7]=10来选择功能,key[4]清零,key[5]暂停。秒表的计数模块与时分秒的大同小异。

//先把1k的信号分频为0.01s的信号

always@(posedge clk_1k) begin

f100hz<=(count10<10/2)?1'b1:1'b0; if(count10==10-1) count10<=0; else

count10<=count10+1; end

always@(posedge f100hz ) begin

if(key[7:4]==4'b0111)//若没有暂停,(key[4]=1) begin

ms[3:0]=ms[3:0]+1'b1; if(ms[3:0]==4'ha) begin ms[3:0]=4'h0; ms[7:4]=ms[7:4]+1'b1; if(ms[7:4]==4'ha) begin

9

ms[7:4]=4'h0; sec[3:0]=sec[3:0]+1'b1; if(sec[3:0]==4'ha) begin sec[3:0]=4'h0; sec[7:4]=sec[7:4]+1'b1; if(sec[7:4]==4'h6) begin sec[7:4]=4'h0; min[3:0]=min[3:0]+1'b1; if(min[3:0]==4'ha) begin min[3:0]=4'h0; min[7:4]=min[7:4]+1'b1; end if(min==8'h60) min=8'h0; end end end end end else

if(key[4]==0)//清零 begin ms=0; sec=0; min=0; end end

3.7 闹钟模块

闹钟模块主要包括存储时分的寄存器,设置闹钟,闹钟音乐,对比是否到达设定时刻几个子模块。其中音乐模块最为复杂,也是本设计的亮点,单独在一个文件中编写。 //设置闹钟

always@(negedge key)

if(key[7:6]==2'b01) //选择闹钟设置 begin

if(!key[2])

hour1=hour+1; else if(!key[3])

minute1=minute1+1; end

10

//对比是否到达设定时刻 always@(minute or second)

if(minute==minute1&&hour==hour1) beep2=beep1; else

beep2=1'b0; //调用音乐模块 cannon music( .clk(clk),

.beep1(beep1));

//音乐模块,采用曲子cannon片段

产生音乐原理:对嗡鸣器而言,每个音调都有其对应的频率。在当前节拍时,给出相应的频率的脉冲即可发出对应的音调。通过查找相关资料得出音调与频率的对应关系如下:

音符 频率/HZ 半周期/us(N) --------------------------------

低1DO 262 1908 #1DO# 277 1805 低2RE 294 1700 #2RE# 311 1608 低3MI 330 1516 #3MI# 340 1470 低4FA 349 1433 #4FA# 370 1350 低5SO 392 1276 #5SO# 415 1205 低6LA 440 1136 #6LA# 466 1072 低7SI 494 1012 #7SI# 524 09

中1DO 523 0956 #1DO# 5 0903 中2RE 578 0842 #2RE# 622 0804 中3MI 659 0759 #3MI# 682 0733 中4FA 698 0716 #4FA# 740 0676 中5SO 784 0638 #5SO# 831 0602 中6LA 880 0568 #6LA# 932 0536 中7SI 988 0506 #7SI# 1046 478

高1DO 1046 478 #1DO# 1109 451 高2RE 1175 426 #2RE# 1245 402 高3MI 1318 372 #3MI# 1356 368 高4FA 1397 358 #4FA# 1480 338 高5SO 1568 319 #5S0# 1661 292 高6LA 1760 284 #6LA# 1865 268 高7SI 1976 253 #7SI# 2066 242

11

(——来自百度知道)

卡农简谱:

然后通过计算定义好每个音阶对应的频率。250ms为一个节拍,把每一个节拍的频率送到beep即可播放音乐。代码太长,放到最后。

3.8 万年历模块

万年历的存储计数模块在前面已经和时分秒一起完成,这里只需要调整设置

模块。

这里采用另外一种设置的方式,移位设置。万年历一共有三组数,共8位,若分别用一个键去设置,太浪费资源,而且如果是年的话要从0到2014一个一个累加设置也不现实。故采用移位设置,每一位分别设置,并控制每一位的最大值。这样只需要两个键即可完成功能,一个移位,一个控制累加。

其中,为了突出当前正在设置的位,将该位闪烁。闪烁的方法是用一个低频

信号(2hz)与原来的高频扫描的位码相或。这样低频信号为“1”时会掩盖该位的位码,使其不显示,只要在低频信号为“0”时才正常显示。 //设置万年历

reg [2:0]setpoint;

always@(negedge key[4])

if(key[7:6]==2'b00 && key[4]==0) setpoint=setpoint+1;

always@(negedge key[5])

if(key[7:6]==2'b00 && key[5]==0) case(setpoint)

3'd0:begin day[3:0]=day[3:0]+1;

12

if(day[3:0]==4'ha)day[3:0]=4'h0; dig_blink[0]=clk_2hz; end

3'd1:begin day[7:4]=day[7:4]+1; if(day[7:4]==4'h3)day[7:4]=4'h0; dig_blink[1]=clk_2hz; end

3'd2:begin month[3:0]=month[3:0]+1; if(month[3:0]==4'ha)month[3:0]=4'h0; dig_blink[2]=clk_2hz; end

3'd3:begin month[7:4]=month[7:4]+1; if(month[7:4]==4'h2)month[7:4]=4'h0; dig_blink[3]=clk_2hz; end

3'd4:begin year[3:0]=year[3:0]+1; if(year[3:0]==4'ha)year[3:0]=4'h0; dig_blink[4]=clk_2hz; end

3'd5:begin year[7:4]=year[7:4]+1;

if(year[7:4]==4'ha)year[7:4]=4'h0; dig_blink[5]=clk_2hz;end 3'd6:begin year[11:8]=year[11:8]+1;

if(year[11:8]==4'ha)year[11:8]=4'h0; dig_blink[6]=clk_2hz;end 3'd7:begin year[15:9]=year[15:9]+1;

if(year[15:9]==4'ha)year[15:9]=4'h0; dig_blink[7]=clk_2hz;end endcase

3.9 键盘扫描模块

通过前面的操作发现单独的操作按键进行数码的设置不太方便,若能够将4*4矩阵键盘利用起来,必将方便的多。 键盘扫描的原理如下:

keyout1——4:key5——key8的管脚 keyin1——4:key1——key4的管脚 (通过跳线进行复用)

13

矩阵键盘的键值读取是通过逐行扫描判断实现的,如上图所示,矩阵键盘的扫描流程是这样的,刚开始在KEYOUT[4:1]输出1110(KEYOUT1输出低电平),延时一段时间后读

取KEYIN的状态,由于KEYIN接有上拉电阻,平时无按键时值为1111,若读取的值不为1111,则说明S1、S5、S9、S13有按键按下,KEYIN1-KEYIN4的每一位对应一个按键(如KEYIN[4:1]的值为1110,说明S1按下);扫描完一行之后继续扫描第二行,KEYOUT[4:1]输出1101(KEYOUT2输出低电平),通读取KEYIN的值可判断S2、S6、S10、S14按键的值。以此类推……

通过定义6个状态,0状态监测有按键按下,1-4扫描是哪一列按下,若检测到有按下,立即转到5状态,在按下时重复保存下当前的行和列。若均无按下,回到0状态等待按下。 主要代码实现:

always @(posedge clk_500khz or negedge reset) if(!reset) begin col<=4'b0000;state<=3'd0;end else begin

case (state) 3'd0: begin

col[3:0]<=4'b0000; key_flag<=1'b0;

if(row[3:0]!=4'b1111) begin state<=3'd1;col[3:0]<=4'b1110;end //有键按下,扫描第一列

else state<=3'd0; end 3'd1: begin

if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第一列 else begin state<=3'd2;col[3:0]<=4'b1101;end //扫描第二列 end 3'd2:

begin

if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第二列 else begin state<=3'd3;col[3:0]<=4'b1011;end //扫描第三列 end 3'd3:

begin

if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第三列 else begin state<=3'd4;col[3:0]<=4'b0111;end //扫描第四列 end 3'd4:

begin

if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第一行 else state<=3'd0; end 3'd5:

14

begin

if(row[3:0]!=4'b1111) begin col_reg<=col; //保存扫描列值 row_reg<=row; //保存扫描行值 state<=3'd5; key_flag<=1'b1; //有键按下 end else begin state<=0; end end

endcase end

always @(clk_500khz or col_reg or row_reg) begin

if(key_flag==1'b1) begin case ({col_reg,row_reg})

8'b1110_1110:key_value<=8'h7; 8'b1110_1101:key_value<=8'h4; 8'b1110_1011:key_value<=8'h1; 8'b1110_0111:key_value<=8'h0;

8'b1101_1110:key_value<=8'h8; 8'b1101_1101:key_value<=8'h5; 8'b1101_1011:key_value<=8'h2; 8'b1101_0111:key_value<=8'ha;

8'b1011_1110:key_value<=8'h9; 8'b1011_1101:key_value<=8'h6; 8'b1011_1011:key_value<=8'h3; 8'b1011_0111:key_value<=8'hb;

8'b0111_1110:key_value<=8'hf; 8'b0111_1101:key_value<=8'he; 8'b0111_1011:key_value<=8'hd;

8'b0111_0111:key_value<=8'hc; endcase end end

4 调试仿真

15

由于之前比赛就有过太多的失败和教训,不过也积累了不少经验,所以总体在软件使用方面不存在障碍。提几点十分重要的地方,当然老师上课也都说过的。

1.软件必须完全破解,否则无法使用高级功能,也不能使用cycloneIII系列FPGA芯片。 2.所有的文件必须在英文路径下面,否则会有意想不到的各种问题。

3.以前我不知道仿真的信号可以自由分组,以方便观察组合值,以前都是默认的总线才有分组的。

总而言之,写好的项目需要编译,然后根据提示的错误进行修改就行。

仿真时需要新建波形文件,选择感兴趣的端口,设置好输入信号,使能,时钟,功能选择等等。仿真,然后查看输出波形。 总的仿真波形如下:

5 编程下载

分配好管脚,注意把没有用到的管脚定义为三态输入。再次编译。 分配好管脚如下:

16

6 结论

成功地运用VerilogHDL设计出多功能数字钟,完成所有基本功能,并且添加了秒表,闹钟,万年历功能。

7 参考文献

《EDA实验与实践》.......................................................................周立功主编 《数字逻辑电路与系统设计》...........................................................蒋立平主编 《Verilog_HDL_华为入门教程》

8 实验感想

遇到了很多的问题,基本上都是Verilog的问题,毕竟没有系统的学习过,下面包括几个典型的:

1. 只能在一个进程中操作寄存器,不然会有冲突。刚开始很不理解,而且觉得了

很多的功能。比如想在多种信号条件去操作同一个寄存器。解决方法是将所有触发

17

信号写入敏感表,在进程中用if语句判断到底是哪一个信号触发的,从而做出相应的操作。

2. 不能多次赋值给网线类型,其实这个问题与前面一个问题有相似之处。解决方法是

先将多个要赋值的信号逻辑运算后再进行赋值,比如或和与运算。(前面一个问题通过if语句进行逻辑运算的)

3. 在赋值时不能偷懒,最好指定好数据的位,如4’h0,不然会出现意想不到的错误。 4. if条件语句(如果包含)必须至少有一个条件指向其中一个敏感事件(边界标识符) 5. 设计流程最好采用并行设计,在多个always@进程中处理时序逻辑或组合逻辑。这

样有利于问题的简化。

通过此次电子设计,再次复习学习了一遍Verilog HDL,对其中数据流的操作又有了更多的理解。当然,还是停留在表面,以后还需要更加深入的学习,这样运用起来才会。 Verilog HDL很强大,既包括低级的逻辑门,又包括高级的数算。其模块化和结构化足以设计大型的数字电路。

个人认为硬件描述语言通过语言主要是直接操作寄存器和网线,从而综合时序逻辑电路和组合逻辑电路,理论上可以设计出任何逻辑功能的电路。

9 源代码

1.

module clock(clk,clk_1hz,clk_2hz,clk_500hz,clk_1k,key_in,dig,seg,beep); input clk,clk_1hz,clk_2hz,clk_500hz,clk_1k; input [7:0]

key_in;//key[0]:en,key[1]:clear,key[2]:jiaoshi,key[3]:jiaofen,key[4]:,key[5],key[6],key[7]:function choose output [7:0]dig; output [7:0]seg; output beep; wire beep1; reg beep2; reg beep_r;

//output [7:0]hour,minute,second; assign dig=dig_r | dig_blink; assign seg=seg_r;

assign beep=beep_r|beep2;//nao zhong,bao shi reg [7:0]key_r1,key_r2,key_r3; wire [7:0]key;

reg [7:0]seg_r;//segment choose

reg [7:0]dig_r,dig_blink;//digit choose reg [7:0]hour,minute,second;

18

reg [7:0]month,day; reg [15:0]year;

reg [7:0]hour1,minute1,second1;//nao zhong reg [7:0]min,sec,ms;//miao biao

reg [3:0]dis_data;//the data you want display now reg [2:0]count1;

reg [32:0]display;//all the data series to display reg f50hz,f100hz; reg [4:0]count20; reg [3:0]count10;

//////////////////////////////////////////////// //去抖动电路/

assign key=(key_r1 | key_r2 | key_r3); //T=20ms

always@(posedge clk_1k) begin

f50hz<=(count20<20/2)?1'b1:1'b0; if(count20==20-1) count20<=0; else count20<=count20+1; end

always@(posedge f50hz) begin key_r1<=key_in; key_r2<=key_r1; key_r3<=key_r2; end

//second->minute->hour///->day->month->year/////////////////////////// //always@(posedge clk_1hz or negedge key[1] or negedge key[2] or negedge key[3]) always@(posedge clk_1hz or negedge key[1]) begin

if(key[1]==0)//clear begin

hour=8'b0; minute=8'b0; second=8'b0; end

else if(key[7:6]==2'b11 && key[2]==0) begin hour[3:0]=hour[3:0]+1'b1; if(hour[3:0]==4'ha) begin hour[3:0]=4'h0; hour[7:4]=hour[7:4]+1; if(hour[7:4]==4'h6) hour[7:4]=4'h0; end end

else if(key[7:6]==2'b11 && key[3]==0) begin

19

minute[3:0]=minute[3:0]+1'b1; if(minute[3:0]==4'ha) begin minute[3:0]=4'h0; minute[7:4]=minute[7:4]+1; if(minute[7:4]==4'h6) minute[7:4]=4'h0; end end

else if(key[0])//no pause begin second[3:0]=second[3:0]+1'b1; if(second[3:0]==4'ha) begin second[3:0]=4'h0; second[7:4]=second[7:4]+1'b1; if(second[7:4]==4'h6) begin second[7:4]=4'h0; minute[3:0]=minute[3:0]+1'b1; if(minute[3:0]==4'ha) begin minute[3:0]=4'h0; minute[7:4]=minute[7:4]+1'b1; if(minute[7:4]==4'h6) begin minute[7:4]=4'h0; hour[3:0]=hour[3:0]+1'b1; if(hour[3:0]==4'ha) begin hour[3:0]=4'h0; hour[7:4]=hour[7:4]+1'b1; if(hour==8'h24) begin hour[7:4]=4'h0; day[3:0]=day[3:0]+1'b1; if(day[3:0]==4'ha) begin day[3:0]=4'h0; day[7:4]=day[7:4]+1'b1; if(day[7:4]==4'h3) begin day[7:4]=4'h0; month[3:0]=month[3:0]+1'b1; if(month[3:0]==4'ha) begin month[3:0]=4'h0; month[7:4]=month[7:4]+1'b1; if(month[7:0]==8'h12) begin month=8'h0; year=year+1; end end

20

end end end end end end end end end end

/////////////////////////////////////////////////////////////////////// //设置闹钟

always@(negedge key) if(key[7:6]==2'b01) begin

if(!key[2])

hour1=hour+1; else if(!key[3])

minute1=minute1+1; end

/////////////////////////////////////////////////////////////////////// //秒表

always@(posedge clk_1k) begin f100hz<=(count10<10/2)?1'b1:1'b0; if(count10==10-1) count10<=0; else count10<=count10+1; end

always@(posedge f100hz ) begin if(key[7:4]==4'b0111) begin ms[3:0]=ms[3:0]+1'b1; if(ms[3:0]==4'ha) begin ms[3:0]=4'h0; ms[7:4]=ms[7:4]+1'b1; if(ms[7:4]==4'ha) begin ms[7:4]=4'h0; sec[3:0]=sec[3:0]+1'b1; if(sec[3:0]==4'ha) begin sec[3:0]=4'h0; sec[7:4]=sec[7:4]+1'b1; if(sec[7:4]==4'h6) begin sec[7:4]=4'h0; min[3:0]=min[3:0]+1'b1; if(min[3:0]==4'ha) begin

21

min[3:0]=4'h0; min[7:4]=min[7:4]+1'b1; end if(min==8'h60) min=8'h0; end end end end end else if(key[4]==0) begin ms=0; sec=0; min=0; end end

/////////////////////////////////////////////////////////////////////// //设置万年历 reg [2:0]setpoint;

always@(negedge key[4])

if(key[7:6]==2'b00 && key[4]==0) setpoint=setpoint+1;

always@(negedge key[5])

if(key[7:6]==2'b00 && key[5]==0) case(setpoint)

3'd0:begin day[3:0]=day[3:0]+1; if(day[3:0]==4'ha)day[3:0]=4'h0; dig_blink[0]=clk_2hz;end

3'd1:begin day[7:4]=day[7:4]+1; if(day[7:4]==4'h3)day[7:4]=4'h0; dig_blink[1]=clk_2hz;end

3'd2:begin month[3:0]=month[3:0]+1; if(month[3:0]==4'ha)month[3:0]=4'h0; dig_blink[2]=clk_2hz;end

3'd3:begin month[7:4]=month[7:4]+1; if(month[7:4]==4'h2)month[7:4]=4'h0; dig_blink[3]=clk_2hz;end

3'd4:begin year[3:0]=year[3:0]+1; if(year[3:0]==4'ha)year[3:0]=4'h0; dig_blink[4]=clk_2hz;end

3'd5:begin year[7:4]=year[7:4]+1; if(year[7:4]==4'ha)year[7:4]=4'h0; dig_blink[5]=clk_2hz;end

3'd6:begin year[11:8]=year[11:8]+1; if(year[11:8]==4'ha)year[11:8]=4'h0; dig_blink[6]=clk_2hz;end

3'd7:begin year[15:9]=year[15:9]+1; if(year[15:9]==4'ha)year[15:9]=4'h0; dig_blink[7]=clk_2hz;end endcase

///////////////////////////////////////////////////////////////////////////// //scan and display/

always@(posedge clk_1k) begin

//display_function chose case(key[7:6])

2'b11:display={hour,4'ha,minute,4'ha,second};//正常计时 2'b10:display={hour1,4'ha,minute1,4'ha,second1};//设置闹钟

22

2'b01:display={min,4'ha,sec,4'ha,ms};//秒表

2'b00:display={year,month,day};//万年历 endcase case(count1) 3'd0:dis_data=display[3:0]; 3'd1:dis_data=display[7:4]; 3'd2:dis_data=display[11:8]; 3'd3:dis_data=display[15:12]; 3'd4:dis_data=display[19:16]; 3'd5:dis_data=display[23:20]; 3'd6:dis_data=display[27:24]; 3'd7:dis_data=display[31:28]; endcase case(count1) 3'd0:dig_r=8'b11111110; 3'd1:dig_r=8'b11111101; 3'd2:dig_r=8'b11111011; 3'd3:dig_r=8'b11110111; 3'd4:dig_r=8'b11101111; 3'd5:dig_r=8'b11011111; 3'd6:dig_r=8'b10111111; 3'd7:dig_r=8'b01111111; endcase count1<=count1+1;

case(dis_data) 4'h0:seg_r=8'hc0; 4'h1:seg_r=8'hf9; 4'h2:seg_r=8'ha4; 4'h3:seg_r=8'hb0; 4'h4:seg_r=8'h99; 4'h5:seg_r=8'h92; 4'h6:seg_r=8'h82; 4'h7:seg_r=8'hf8; 4'h8:seg_r=8'h80; 4'h9:seg_r=8'h90; 4'ha:seg_r=8'hbf; default:seg_r=8'hff; endcase end

//////////////////////////////////////////////////////////// //报时

always@(minute or second) if(minute==8'h59) //59分钟时 case(second) //秒为53、55、57低音报时 8'h53, 8'h55, 8'h57:beep_r<=clk_500hz; 8'h59:beep_r<=clk_1k; default:beep_r<=1'b0; endcase

else

23

beep_r<=1'b0;

/////////////////////////////////////////////////////////// //闹钟

always@(minute or second)

if(minute==minute1&&hour==hour1) beep2=beep1; else

beep2=1'b0; cannon music( .clk(clk), .beep1(beep1));

Endmodule 2.

module cannon(clk,beep1); input clk;

output wire beep1; reg beep_r; reg[7:0]state;

reg[15:0]count,count_end; reg[23:0]count1; parameter l5=16'd61224, l6=16'd5, l7=16'd48593, m1=16'd45863, m2=16'd408, m3=16'd320, m4=16'd34359, m5=16'd30612, m6=16'd27273, m7=16'd24296, h1=16'd22956, h2=16'd20431, h3=16'd18203, h4=16'd17181, h5=16'd15306, h6=16'd13636, h7=16'd12149; parameter TIME=12000000; assign beep1=beep_r; always@(posedge clk) begin count<=count+1'b1; if(count==count_end) begin count<=16'h0; beep_r<=!beep_r; end end

always@(posedge clk) begin

24

if(count1count1=24'd0; if(state==8'd147) state=8'd0; else state=state+1'b1; case(state)

8'd0,8'd1: count_end=h5; 8'd2: count_end=h3; 8'd3: 8'd4,8'd5: 8'd6: 8'd7: 8'd8: 8'd9: 8'd10: 8'd11: 8'd12: 8'd13: 8'd14: 8'd15:

8'd16,8'd17: 8'd18: 8'd19: 8'd20,8'd21: 8'd22: 8'd23: 8'd24: 8'd25: 8'd26: 8'd27: 8'd28: 8'd29: 8'd30: 8'd31:

8'd32,8'd33: 8'd34: 8'd35: 8'd36,8'd37: 8'd38: 8'd39: 8'd40: 8'd41: 8'd42: 8'd43: 8'd44: 8'd45: 8'd46: 8'd47:

count_end=h4; count_end=h5; count_end=h3; count_end=h4; count_end=h5; count_end=m5; count_end=m6; count_end=m7; count_end=h1; count_end=h2; count_end=h3; count_end=h4; count_end=h3; count_end=h1; count_end=h2; count_end=h3; count_end=m3; count_end=m4; count_end=m5; count_end=m6; count_end=m5; count_end=m4; count_end=m5; count_end=m3; count_end=m4; count_end=m5; count_end=m4; count_end=m6; count_end=m5; count_end=m4; count_end=m3; count_end=m2; count_end=m3; count_end=m2; count_end=m1; count_end=m2; count_end=m3; count_end=m4; count_end=m5; count_end=m6; 25

8'd48,8'd49: count_end=m4; 8'd50: count_end=m6; 8'd51: count_end=m5; 8'd52,8'd53: count_end=m6; 8'd: count_end=m7; 8'd55: count_end=h1; 8'd56: count_end=m5; 8'd57: count_end=m6; 8'd58: count_end=m7; 8'd59: count_end=h1; 8'd60: count_end=h2; 8'd61: count_end=h3; 8'd62: 8'd63:

8'd,8'd65: 8'd66: 8'd67: 8'd68,8'd69: 8'd70: 8'd71: 8'd72: 8'd73: 8'd74: 8'd75: 8'd76: 8'd77: 8'd78: 8'd79:

8'd80,8'd81: 8'd82: 8'd83: 8'd84,8'd85: 8'd86: 8'd87: 8'd88: 8'd: 8'd90: 8'd91: 8'd92: 8'd93: 8'd94: 8'd95:

8'd96,8'd97: 8'd98: 8'd99: 8'd100,8'd101: 8'd102: 8'd103: 8'd104: 8'd105: 8'd106: 8'd107:

count_end=h4; count_end=h5; count_end=h3; count_end=h1; count_end=h2; count_end=h3; count_end=h2; count_end=h1; count_end=h2; count_end=m7; count_end=h1; count_end=h2; count_end=h3; count_end=h2; count_end=h1; count_end=m7; count_end=h1; count_end=m6; count_end=m7; count_end=h1; count_end=m1; count_end=m2; count_end=m3; count_end=m4; count_end=m3; count_end=m2; count_end=m3; count_end=h1; count_end=m7; count_end=h1; count_end=m6; count_end=h1; count_end=m7; count_end=m6; count_end=m5; count_end=m4; count_end=m5; count_end=m4; count_end=m3; count_end=m4;

26

8'd108: count_end=m5; 8'd109: count_end=m6; 8'd110: count_end=m7; 8'd111: count_end=m1;

8'd112,8'd113: count_end=m6; 8'd114: count_end=h1; 8'd115: count_end=m7; 8'd116,8'd117: count_end=h1; 8'd118: count_end=m7; 8'd119: count_end=m6; 8'd120: count_end=m7; 8'd121: count_end=h1; 8'd122: count_end=h2; 8'd123: count_end=h1; 8'd124: count_end=m7; 8'd125: count_end=h1; 8'd126: count_end=m6; 8'd127: count_end=m7;

endcase end end

endmodule 3.

module fdiv(clk,f1k,f500hz,f200hz,f2hz,f1hz); input clk;

output f1k,f500hz,f200hz,f2hz,f1hz; reg f1k,f500hz,f2hz,f1hz;

integer count48k=0,count100=0,count5p=0,count5n=0;

//48k div,result 48m to 1k

always@(posedge clk) begin f1k<=(count48k<48000/2)?1'b1:1'b0; if(count48k==48000-1) count48k<=0; else count48k<=count48k+1; end

//2 div,result 1k to 500hz always@(posedge f1k ) begin f500hz=~f500hz; end

//5 div,result 1k to 200hz,50% reg f200p,f200n; //posedge ocunter

always @(posedge f1k) if(count5p==4) count5p<=0;

27

else count5p<=count5p+1; //posedge wave

always @(posedge f1k ) begin if(count5p<2) f200p <=1; else f200p <=0; end

//negedge counter

always @(negedge f1k ) if(count5n==4) count5n<=0; else count5n<=count5n+1; //negedge wave

always @(negedge f1k) begin if(count5n<2) f200n <=1; else f200n <=0; end

assign f200hz=f200n|f200p;

//100 div,result 200hz to 2hz always @(posedge f200hz) begin f2hz<=(count100<100/2)?1'b1:1'b0; if(count100==99) count100 <= 0; else count100<=count100+1; end

//2 div,result 2hz to 1hz always@(posedge f2hz) begin f1hz=~f1hz; end

endmodule 4.

module keyscan (

clk, //48MHZ reset,

row, //行 col, //列

key_value //键值 );

28

input clk,reset; input [3:0] row; output [3:0] col;

output [3:0] key_value;

reg [3:0] col;

reg [3:0] key_value;

reg [5:0] count; //delay_20ms reg [2:0] state; //状态标志 reg key_flag; //按键标志位

reg clk_500khz; //500KHZ时钟信号 reg [3:0] col_reg; //寄存扫描列值 reg [3:0] row_reg; //寄存扫描行值

always @(posedge clk or negedge reset)

if(!reset) begin clk_500khz<=0; count<=0; end else begin if(count>=50) begin clk_500khz<=~clk_500khz;count<=0;end else count<=count+1; end

always @(posedge clk_500khz or negedge reset) if(!reset) begin col<=4'b0000;state<=3'd0;end else begin case (state) 3'd0: begin col[3:0]<=4'b0000; key_flag<=1'b0; if(row[3:0]!=4'b1111) begin state<=3'd1;col[3:0]<=4'b1110;end //有键按下,扫描第一列 else state<=3'd0; end 3'd1: begin if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第一列 else begin state<=3'd2;col[3:0]<=4'b1101;end //扫描第二列 end 3'd2: begin if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第二列

29

else begin state<=3'd3;col[3:0]<=4'b1011;end //扫描第三列 end 3'd3: begin if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第三列 else begin state<=3'd4;col[3:0]<=4'b0111;end //扫描第四列 end 3'd4: begin if(row[3:0]!=4'b1111) begin state<=3'd5;end //判断是否是第一行 else state<=3'd0; end 3'd5: begin if(row[3:0]!=4'b1111) begin col_reg<=col; //保存扫描列值 row_reg<=row; //保存扫描行值 state<=3'd5; key_flag<=1'b1; //有键按下 end else begin state<=0; end end endcase end

always @(clk_500khz or col_reg or row_reg) begin

if(key_flag==1'b1) begin case ({col_reg,row_reg})

8'b1110_1110:key_value<=8'h7; 8'b1110_1101:key_value<=8'h4; 8'b1110_1011:key_value<=8'h1; 8'b1110_0111:key_value<=8'h0;

8'b1101_1110:key_value<=8'h8; 8'b1101_1101:key_value<=8'h5; 8'b1101_1011:key_value<=8'h2; 8'b1101_0111:key_value<=8'ha;

30

8'b1011_1110:key_value<=8'h9; 8'b1011_1101:key_value<=8'h6; 8'b1011_1011:key_value<=8'h3; 8'b1011_0111:key_value<=8'hb;

8'b0111_1110:key_value<=8'hf; 8'b0111_1101:key_value<=8'he; 8'b0111_1011:key_value<=8'hd;

end endmodule

8'b0111_0111:key_value<=8'hc; endcase end 31

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo3.com 版权所有 蜀ICP备2023022190号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务