电子信息学院
课程设计报告
课 程 名 称: 《数字系统设计课程设计》
题 目: 多功能数字钟设计
年级/专业/班: 20##级电科X班
学 生 姓名1: xxx
学 号:
学 生 姓名2: xxx
学 号:
2014 年 7 月 1 日
目 录
1 课程设计目标和流程分析... 3
1.1 课程设计目标... 3
1.2 开发环境说明... 3
1.3 设计流程说明... 3
2 系统设计... 3
2.1 系统架构设计... 3
2.2 子模块分析... 3
2.3 调试结果... 3
3 小结... 3
附录:主要代码... 3
课程设计目标和流程分析
1.1 课程设计目标
基于Verilog语言描述系统的功能;在quartusII环境中编译通过;仿真通过并得到正确的波形;掌握数字系统的分析和设计方法。能够熟练的、合理的选用集成电路器件。掌握数字钟得设计制作方法。
用Verilog硬件描述语言设计数字钟,实现的目标如下:
1)具有时、分、秒计数显示功能,以二十四小时循环计时。
2)具有调节小时,分钟的功能,可以通过按键选择时和分。
3)具有整点报时及闹铃时间可调的功能。
4)数字钟具有四种模式:正常显示、时间调整、闹铃时间调整、秒表。
1.2 开发环境说明
硬件:采用基于EP1C12Q240C8芯片的FPGA开发平台
FPGA(Field-Programmable Gate Array),即现场可编程门阵列,它是在PAL、GAL、CPLD等可编程器件的基础上进一步发展的产物。它是作为专用集成电路(ASIC)领域中的一种半定制电路而出现的,既解决了定制电路的不足,又克服了原有可编程器件门电路数有限的缺点。FPGA采用了逻辑单元阵列LCA(Logic Cell Array)这样一个概念,内部包括可配置逻辑模块CLB(Configurable Logic Block)、输入输出模块IOB(Input Output Block)和内部连线(Interconnect)三个部分。 现场可编程门阵列(FPGA)是可编程器件,与传统逻辑电路和门阵列(如PAL,GAL及CPLD器件)相比,FPGA具有不同的结构。FPGA利用小型查找表(16×1RAM)来实现组合逻辑,每个查找表连接到一个D触发器的输入端,触发器再来驱动其他逻辑电路或驱动I/O,由此构成了既可实现组合逻辑功能又可实现时序逻辑功能的基本逻辑单元模块,这些模块间利用金属连线互相连接或连接到I/O模块。FPGA的逻辑是通过向内部静态存储单元加载编程数据来实现的,存储在存储器单元中的值决定了逻辑单元的逻辑功能以及各模块之间或模块与I/O间的联接方式,并最终决定了FPGA所能实现的功能,FPGA允许无限次的编程。:
硬件:采用quartusII
Quartus II 是Altera公司的综合性PLD/FPGA开发软件,支持原理图、VHDL、VerilogHDL以及AHDL(Altera Hardware Description Language)等多种设计输入形式,内嵌自有的综合器以及仿真器,可以完成从设计输入到硬件配置的完整PLD设计流程。
语言:Verilog HDL硬件描述语言简介
Verilog HDL就是在用途最广泛的C语言的基础上发展起来的一种硬件描述语言,它是由GDA(Gateway Design Automation)公司的PhilMoorby在1983年末首创的,最初只设计了一个仿真与验证工具,之后又陆续开发了相关的故障模拟与时序分析工具。1985年Moorby推出它的第三个商用仿真器Verilog-XL,获得了巨大的成功,从而使得Verilog HDL迅速得到推广应用。1989年CADENCE公司收购了GDA公司,使得VerilogHDL成为了该公司的独家专利。1990年CADENCE公司公开发表了Verilog HDL,并成立LVI组织以促进Verilog HDL成为IEEE标准,即IEEE Standard 1364-1995. 模块是Verilog 的基本描述单位,用于描述某个设计的功能或结构及其与其他模块通信的外部端口。一个设计的结构可使用开关级原语、门级原语和用户定义的原语方式描述; 设计的数据流行为使用连续赋值语句进行描述; 时序行为使用过程结构描述。一个模块可以在另一个模块中使用。 说明部分用于定义不同的项,例如模块描述中使用的寄存器和参数。语句定义设计的功能和结构。说明部分和语句可以散布在模块中的任何地方;但是变量、寄存器、线网和参数等的说明部分必须在使用前出现。为了使模块描述清晰和具有良好的可读性, 最好将所有的说明部分放在语句前。
1.3 设计流程说明
2 系统设计
2.2 关键模块代码分析
2.2.1分频模块
分频模块用于为系统的实现提供稳定的工作频率和计时信号源,要求产生两路不同频率的信号,分频为1HZ频率的信号作为计时模块的秒信号的产生,100HZ的信号用于秒表计时模块的信号,1000HZ的信号用于数码管动态显示的扫描频率。
分频模块的实现:
-----------------------------产生100HZ的秒表信号-----------------------
always @(posedge clk) //定义clock上升沿触发
begin
c3= c3 + 1'b1;
c1=c1 + 1'b1;
c2=c2 + 1'b1;
if(c3 == 25'd480000) //miaobiao
begin
c3 = 25'd0; //计数器清零
sec = ~sec; //置位秒标志
end
------------------------------产生1000HZ的秒信号------------------------
if(c1== 25'd24000000) //0.5S到了吗?
begin
c1 = 25'd0; //计数器清零
clk_1s= ~clk_1s; //置位秒标志
end
if(c2== 25'd2400000) //tiaoshi?
begin
c2 = 25'd0; //计数器清零
clk_xiaozhun= ~clk_xiaozhun; //置位秒标志
end
end
----------------------------产生1000HZ数码管动态扫描显示部分----------------
always @(posedge clk) //count[17:15]大约1ms改变一次
begin
if(layer==2'b01)
begin dat=hour;end
else if(layer==2'b00)
begin dat=hour1;end
else if(layer==2'b10)
begin dat=hour2;end
else
begin dat=hour3;end
case(c3[17:15])
……
2.2.2计时模块
模块功能为正常计时,即每秒钟读一次数,秒表加1,秒计时满60进1给分计时,分计时满60进1给小时计时,小时计时满24清零。从功能上讲分别为模60计数器和模24计数器。
always @(negedge sec )//miaobiao
begin
if(!key_done[1]&&layer==2'b01) //是清零键吗?
begin
hour = 24'h0; //是,则清零
end
else if(!keyen)
begin
hour[3:0] = hour[3:0] + 1'b1; //秒加1
if(hour[3:0] == 4'ha)
begin
hour[3:0] = 4'h0;
hour[7:4] = hour[7:4] + 1'b1; //秒的十位加一
if(hour[7:4] == 4'ha)
begin
hour[7:4] = 4'h0;
hour[11:8] = hour[11:8] + 1'b1;//分个位加一
if(hour[11:8] == 4'ha)
begin
hour[11:8] = 4'h0;
hour[15:12] = hour[15:12] + 1'b1;//分十位加一
if(hour[15:12] == 4'h6)
begin
hour[15:12] = 4'h0;
hour[19:16] = hour[19:16] + 1'b1;//时个位加一
if(hour[19:16] == 4'ha)
begin
hour[19:16] = 4'h0;
hour[23:20] = hour[23:20] + 1'b1;//时十位加一
end
if(hour[23:16] == 8'h24)
hour[23:16] = 8'h0;
end
end
end
end
end
end
2.2.3 校准模块
校准模块分为闹钟校准模块和当前时间校准模块,layer为模式,当layer==2’b10时,进入当前时间校准模块,当layer==2’b11时,进入闹钟校准模块,当pos为1时,对分的个位或十位进行校时,当pos为0时,对时的个位或十位进行校时。
2.2.4 闹钟模块
当当前时间与设置的闹钟时间一致时,闹钟响起,本设计采用的是音乐蜂鸣器,通过写入乐谱对应的频率可实现音乐的输出,歌曲为《世上只有妈妈好》。
2.2.5 整点报时模块
(hour1[15:12]==0&&hour1[11:8]==0&&hour1[7:4]==0&&hour1[3:0]==0)
当分秒对应00:00时,让蜂鸣器发出响声
2.3 调试结果
通过四个按键的控制(S1:秒表启动及暂停、加功能;S2秒表清零、减功能;S3:模式选择;S4:分和时的选择)能在数码管上显示时间,并能对时间进行调整,能进行秒表计数功能,当当前时间与设置闹钟时间一致时闹钟响起,并在整点时蜂鸣器发出一声“嘀”的报时声。
3 小结
通过近来对FPGA的学习,我对ISE软件总体操作步骤已比较熟悉。接下来的时间是要提高对FPGA的掌握能力,将FPGA运用到数字信号处理和通信原理上。 ,另外,对Verilog语言的学习不仅是能读懂别人的程序,更重要的是能写出条理清晰的程序。通过做多功能数字钟,我发现自己对Verilog语言还不是很熟悉,接下来的时间要加强语言的学习,多看一些语言方面的书籍。
附录:主要代码
module clockxjx (clk,key,dig,seg,beep); //模块名clock
input clk; //输入时钟
input[3:0] key; //输入按键
output[7:0] dig; //数码管选择输出引脚
output[7:0] seg; //数码管段输出引脚
output beep;
reg[24:0]c1,c2;
reg clk_1s,clk_xiaozhun;
reg[24:0] dat;//xianshi register
//xinyue
reg beep_r; //寄存器
reg[7:0] state; //乐谱状态机
reg[15:0]count,count_end;
reg[23:0]count1;
//end yinyue
reg pos;
reg[7:0] seg_r; //定义数码管输出寄存器
reg[7:0] dig_r; //定义数码管选择输出寄存器
reg[3:0] disp_dat; //定义显示数据寄存器
reg[24:0]c3; //定义计数寄存器
reg[23:0]hour1,hour2,hour3; //定义现在时刻寄存器
reg[23:0]hour;//miaobiao
reg sec,keyen; //定义标志位
reg[1:0] layer; //定义标志位state
reg[3:0]dout1,dout2,dout3; //寄存器
wire[3:0]key_done; //按键消抖输出
//乐谱参数:D=F/2K (D:参数,F:时钟频率,K:音高频率)
parameter L_5=16'd61224,
L_6=16'd54545,
M_1=16'd45863,
M_2=16'd40865,
M_3=16'd36402,
M_5=16'd30612,
M_6=16'd27273,
H_1=16'd22956;
parameter TIME = 12000000; //控制每一个音的长短(250ms)
assign beep = beep_r; //输出音乐
assign dig = dig_r; //输出数码管选择
assign seg = seg_r; //输出数码管译码结果
always@(posedge clk)
begin
if((hour3[23:8]==hour1[23:8])||(hour1[15:12]==0&&hour1[11:8]==0&&hour1[7:4]==0&&hour1[3:0]==0))
begin
count <= count + 1'b1; //计数器加1
if(count == count_end)
begin
count <= 16'h0; //计数器清零
beep_r <= !beep_r; //输出取反
end
end
else beep_r =0;
end
//秒信号产生部分
always @(posedge clk) //定义clock上升沿触发
begin
c3= c3 + 1'b1;
c1=c1 + 1'b1;
c2=c2 + 1'b1;
if(c3 == 25'd480000) //miaobiao
begin
c3 = 25'd0; //计数器清零
sec = ~sec; //置位秒标志
end
if(c1== 25'd24000000) //0.5S到了吗?
begin
c1 = 25'd0; //计数器清零
clk_1s= ~clk_1s; //置位秒标志
end
if(c2== 25'd2400000) //tiaoshi?
begin
c2 = 25'd0; //计数器清零
clk_xiaozhun= ~clk_xiaozhun; //置位秒标志
end
end
//按键消抖处理部分
assign key_done = (dout1 | dout2 | dout3); //按键消抖输出
always @(posedge c3[17])
begin
dout1 <= key;
dout2 <= dout1;
dout3 <= dout2;
end
always @(negedge key_done[2])//121
begin
layer=layer+1'b1;
//if(layer==2'b11)
// begin layer=2'b00;end
end
always @(negedge key_done[3])//xiao shi shi fen haishi shi seclet
begin
if(layer==2'b10||layer==2'b11)
pos=~pos;
end
always @(negedge key_done[0])
begin
if(layer==2'b01)
begin keyen = ~keyen;end //将琴键开关转换为乒乓开关
end
//数码管动态扫描显示部分
always @(posedge clk) //count[17:15]大约1ms改变一次
begin
if(layer==2'b01)
begin dat=hour;end
else if(layer==2'b00)
begin dat=hour1;end
else if(layer==2'b10)
begin dat=hour2;end
else
begin dat=hour3;end
case(c3[17:15]) //选择扫描显示数据
3'd0:disp_dat = dat[3:0]; //秒个位
3'd1:disp_dat = dat[7:4]; //秒十位
3'd2:disp_dat = 4'ha; //显示"-"
3'd3:disp_dat = dat[11:8]; //分个位
3'd4:disp_dat = dat[15:12]; //分十位
3'd5:disp_dat = 4'ha; //显示"-"
3'd6:disp_dat = dat[19:16]; //时个位
3'd7:disp_dat = dat[23:20]; //时十位
endcase
case(c3[17:15]) //选择数码管显示位
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
end
always @(posedge clk)
begin
case(disp_dat)
4'h0:seg_r = 8'hc0; //显示0
4'h1:seg_r = 8'hf9; //显示1
4'h2:seg_r = 8'ha4; //显示2
4'h3:seg_r = 8'hb0; //显示3
4'h4:seg_r = 8'h99; //显示4
4'h5:seg_r = 8'h92; //显示5
4'h6:seg_r = 8'h82; //显示6
4'h7:seg_r = 8'hf8; //显示7
4'h8:seg_r = 8'h80; //显示8
4'h9:seg_r = 8'h90; //显示9
4'ha:seg_r = 8'hbf; //显示-
default:seg_r = 8'hff; //不显示
endcase
if((c3[17:15]== 3'd2)&sec)
seg_r = 8'hff;
end
//计时处理部分
always @(negedge sec )//miaobiao
begin
if(!key_done[1]&&layer==2'b01) //是清零键吗?
begin
hour = 24'h0; //是,则清零
end
else if(!keyen)
begin
hour[3:0] = hour[3:0] + 1'b1; //秒加1
if(hour[3:0] == 4'ha)
begin
hour[3:0] = 4'h0;
hour[7:4] = hour[7:4] + 1'b1; //秒的十位加一
if(hour[7:4] == 4'ha)
begin
hour[7:4] = 4'h0;
hour[11:8] = hour[11:8] + 1'b1;//分个位加一
if(hour[11:8] == 4'ha)
begin
hour[11:8] = 4'h0;
hour[15:12] = hour[15:12] + 1'b1;//分十位加一
if(hour[15:12] == 4'h6)
begin
hour[15:12] = 4'h0;
hour[19:16] = hour[19:16] + 1'b1;//时个位加一
if(hour[19:16] == 4'ha)
begin
hour[19:16] = 4'h0;
hour[23:20] = hour[23:20] + 1'b1;//时十位加一
end
if(hour[23:16] == 8'h24)
hour[23:16] = 8'h0;
end
end
end
end
end
end
//shizhong
always @(negedge clk_1s)//clock
begin
if(layer!=2'b10)
begin
hour1[3:0] = hour1[3:0] + 1'b1; //秒加1
if(hour1[3:0] == 4'ha)
begin
hour1[3:0] = 4'h0;
hour1[7:4] = hour1[7:4] + 1'b1; //秒的十位加一
if(hour1[7:4] == 4'h6)
begin
hour1[7:4] = 4'h0;
hour1[11:8] = hour1[11:8] + 1'b1;//分个位加一
if(hour1[11:8] == 4'ha)
begin
hour1[11:8] = 4'h0;
hour1[15:12] = hour1[15:12] + 1'b1;//分十位加一
if(hour1[15:12] == 4'h6)
begin
hour1[15:12] = 4'h0;
hour1[19:16] = hour1[19:16] + 1'b1;//时个位加一
if(hour1[19:16] == 4'ha)
begin
hour1[19:16] = 4'h0;
hour1[23:20] = hour1[23:20] + 1'b1;//时十位加一
end
if(hour1[23:16] == 8'h24)
hour1[23:16] = 8'h0;
end
end
end
end
end
else
begin
if(layer==2'b10)
hour1=hour2;
end
end
always @(posedge clk_xiaozhun)
begin
if(layer==2'b10)//jiaoshi
begin
if(pos==1'b1)
begin
if(!key_done[0])
begin
if(hour2[11:8]==4'h9&&hour2[15:12]==4'h5)
begin
hour2[11:8]=4'h0;
hour2[15:12]=4'h0;
end
else
begin if(hour2[11:8]==4'h9)
begin
hour2[11:8]=4'h0;
hour2[15:12]=hour2[15:12] + 1'b1;//分十位加1
end
else
hour2[11:8]=hour2[11:8] + 1'b1;
end
end
if(!key_done[1])
begin
if(hour2[11:8]!=4'h0)
begin
hour2[11:8] = hour2[11:8]-1'b1;//分个位jian1
end
else
begin
if(hour2[15:12] > 4'h0)
begin
hour2[15:12]<= hour2[15:12]-1'b1;//分十位jian一
hour2[11:8] <= 4'h9;
end
else if(hour2[15:12]==4'h0)
begin
hour2[15:12] <= 4'h5;
hour2[11:8] <= 4'h9;
end
end
end
end
else//tiaoshi
begin
if(!key_done[0])
begin
if(hour2[19:16]==4'h3&&hour2[23:20]==4'h2)
begin
hour2[19:16]=4'h0;
hour2[23:20]=4'h0;
end
else
begin if(hour2[19:16]==4'h9)
begin
hour2[19:16]=4'h0;
hour2[23:20]=hour2[23:20] + 1'b1;//分十位加1
end
else
hour2[19:16]=hour2[19:16] + 1'b1;
end
end
if(!key_done[1])
begin
if(hour2[23:16]==8'h00)
begin
hour2[23:20] <= 4'h2;
hour2[19:16] <= 4'h3;
end
else if(hour2[23:16]==8'h10)
begin
hour2[23:20] <= 4'h0;
hour2[19:16] <= 4'h9;
end
else if(hour2[23:16]==8'h20)
begin
hour2[23:20] <= 4'h1;
hour2[19:16] <= 4'h9;
end
else begin hour2[19:16] = hour2[19:16]-1'b1;end//分个位jian1
end
end
end
else
hour2=hour1;
end
always @(posedge clk_xiaozhun)
begin
if(layer==2'b11)//jiaoshi
begin
//hour[7:0]=8'd00;
if(pos==1'b1)
begin
if(!key_done[0])
begin
if(hour3[11:8]==4'h9&&hour3[15:12]==4'h5)
begin
hour3[11:8]=4'h0;
hour3[15:12]=4'h0;
end
else
begin if(hour3[11:8]==4'h9)
begin
hour3[11:8]=4'h0;
hour3[15:12]=hour3[15:12] + 1'b1;//分十位加1
end
else
hour3[11:8]=hour3[11:8] + 1'b1;
end
end
if(!key_done[1])
begin
if(hour3[11:8]!=4'h0)
begin
hour3[11:8] = hour3[11:8]-1'b1;//分个位jian1
end
else
begin
if(hour3[15:12] > 4'h0)
begin
hour3[15:12]<= hour3[15:12]-1'b1;//分十位jian一
hour3[11:8] <= 4'h9;
end
else if(hour3[15:12]==4'h0)
begin
hour3[15:12] <= 4'h5;
hour3[11:8] <= 4'h9;
end
end
end
end
else//tiaoshi
begin
if(!key_done[0])
begin
if(hour3[19:16]==4'h3&&hour3[23:20]==4'h2)
begin
hour3[19:16]=4'h0;
hour3[23:20]=4'h0;
end
else
begin if(hour3[19:16]==4'h9)
begin
hour3[19:16]=4'h0;
hour3[23:20]=hour3[23:20] + 1'b1;//分十位加1
end
else
hour3[19:16]=hour3[19:16] + 1'b1;
end
end
if(!key_done[1])
begin
if(hour3[23:16]==8'h00)
begin
hour3[23:20] <= 4'h2;
hour3[19:16] <= 4'h3;
end
else if(hour3[23:16]==8'h10)
begin
hour3[23:20] <= 4'h0;
hour3[19:16] <= 4'h9;
end
else if(hour3[23:16]==8'h20)
begin
hour3[23:20] <= 4'h1;
hour3[19:16] <= 4'h9;
end
else begin hour3[19:16] = hour3[19:16]-1'b1;end//分个位jian1
end
end
end
end
always @(posedge clk)
begin
if(count1 < TIME) //一个节拍250mS
count1 = count1 + 1'b1;
else
begin
count1 = 24'd0;
if(state == 8'd147)
state = 8'd0;
else
state = state + 1'b1;
case(state)
8'd0,8'd1,8'd2,8'd3,8'd4,8'd5: count_end=M_6;
8'd6,8'd7: count_end=M_5;
8'd8,8'd9,8'd10,8'd11: count_end=M_3;
8'd12,8'd13,8'd14,8'd15: count_end=M_5;
8'd16,8'd17,8'd18,8'd19: count_end=H_1;
8'd20,8'd21: count_end=M_6;
8'd22,8'd23: count_end=M_5;
8'd24,8'd25,8'd26,8'd27,8'd28,8'd29,8'd30,8'd31: count_end=M_6;
8'd32,8'd33,8'd34,8'd35: count_end=M_3;
8'd36,8'd37: count_end=M_5;
8'd38,8'd39: count_end=M_6;
8'd40,8'd41,8'd42,8'd43: count_end=M_5;
8'd44,8'd45,8'd46,8'd47: count_end=M_3;
8'd48,8'd49: count_end=M_1;
8'd50,8'd51: count_end=M_6;
8'd52,8'd53: count_end=M_5;
8'd54,8'd55: count_end=M_3;
8'd56,8'd57,8'd58,8'd59,8'd60,8'd61,8'd62,8'd63: count_end=M_2;
8'd64,8'd65,8'd66,8'd67,8'd68,8'd69: count_end=M_2;
8'd70,8'd71: count_end=M_3;
8'd72,8'd73,8'd74,8'd75: count_end=M_5;
8'd76,8'd77: count_end=M_5;
8'd78,8'd79: count_end=M_6;
8'd80,8'd81,8'd82,8'd83: count_end=M_3;
8'd84,8'd85,8'd86,8'd87: count_end=M_2;
8'd88,8'd89,8'd90,8'd91,8'd92,8'd93,8'd94,8'd95: count_end=M_1;
8'd96,8'd97,8'd98,8'd99,8'd100,8'd101: count_end=M_5;
8'd102,8'd103: count_end=M_3;
8'd104,8'd105: count_end=M_2;
8'd106,8'd107: count_end=M_1;
8'd108,8'd109: count_end=L_6;
8'd110,8'd111: count_end=M_1;
8'd112,8'd113,8'd114,8'd115,8'd116,8'd117,8'd118,8'd119,8'd120,8'd121,8'd122,8'd123,8'd124,8'd125,8'd126,8'd127,8'd128: count_end=L_5;
default:count_end = 16'h0;
endcase
end
end
endmodule