1. 了解基于FPGA的数字电子时钟的实现原理及设计方法; 掌握Quartus_II环境下的模块化、层次化的设计与实现方法; 掌握数字应用系统的 Verilog HDL设计与实现技术。
2. 对数字时钟进行功能模块划分,对各模块进行详细的功能定义。
3. 对划分好的功能模块进行详细编程设计及仿真设计,包括定时计数、显示、时间调整、响铃等。
4. 分析仿真结果,并进行顶层模块设计及功能测试。
完整可执行工程文件见文末。
1、设计模块1:LCD显示静态字符串lcd_mode
a.模块功能要求
(1)硬件工作原理
LCD模块内部示意图如下所示:
(2)DDRAM 数据显示用的RAM(Data Display RAM)
这块存储器是用来存放我们要LCM显示的资料,只要将标准的ASCII码放入DDRAM中,内部线路会自动以该ASCII码为地址,将相应的数据送到显示器上。
IR 寄存器负责存储MAX II要写给LCM的指令码。当芯片要下一个命令到IR寄存器时,必须要控制LCM的RS、R/W及E这三个引脚。当RS和R/W引脚信号均为0,E引脚信号由1变为0时,就会把在lcd_data[0]-[7]引脚上的数据存入IR寄存器。
本实验中所使用的字符与字符码对照表如下:
(3)控制器指令
程序流程图如下:
在LCD上显示静态字符串,进行静态字符的对齐设置在Set address这一步骤中进行,即设置LCM起始字符的显示位置。
b.实验步骤
(1)利用wizard新建项目,所选器件为MAX II 系列型号为EPM1270T144C5N的器件。
(2)新建Verilog文件,名称与模块名一致,保存为lcd1602.v。程序内容如下:
module lcd1602(clk,rst,rs,rw,lcd_en,lcd_data);
input clk,rst;
output rs,rw,lcd_en;
output [7:0]lcd_data;
wire en_20us,en_200us,en_1s;
reg [3:0]state_q,data_q;
reg [7:0]lcd_data;
reg lcd_en;
assign rw=1'b0;
assign rs=state_q[3] | state_q[2] & state_q[1] & ~state_q[0];
assign state_end=en_200us & ~lcd_en & (~rs | data_q[3] & data_q[2] & data_q[1] & data_q[0]);
div_mode U1(clk,rst,1'b1,en_20us,en_200us,en_1s);
always@(posedge clk or negedge rst)
if(~rst)
state_q<=4'b0000;
else if(state_end)
begin
if(state_q[3]) //when q[3]=1xxx(8), give 5
state_q<=4'b0101;
else
state_q<=state_q +1'b1; //state_q's period = 5-8
end
always@(posedge clk or negedge rst)
if(~rst)
data_q<=4'b0000;
else if(state_end)
data_q<=4'b0000;
else if(en_200us & ~lcd_en)
data_q<=data_q + 1'b1;
always@(posedge clk or negedge rst)
if(~rst)
lcd_en<=1'b0;
else if(en_200us)
lcd_en<=~lcd_en;
always@(*)
case(state_q)
4'b0000:lcd_data=8'b00000000;//warm up
4'b0001:lcd_data=8'b00111000;//function set:8'data/2 rows display
4'b0010:lcd_data=8'b00001100;
4'b0011:lcd_data=8'b00000001;//clear display
4'b0100:lcd_data=8'b00010100;//mode set
4'b0101:lcd_data=8'b10000000;//set address1
4'b0110:case(data_q)
4'b0000:lcd_data=8'b01000001;//'A'
4'b0001:lcd_data=8'b01000010;//'B'
4'b0010:lcd_data=8'b01000011;//'C'
4'b0011:lcd_data=8'b01000100;//'D'
4'b0100:lcd_data=8'b01000101;//'E'
4'b0101:lcd_data=8'b01000110;//'F'
4'b0110:lcd_data=8'b00110000;//'0'
4'b0111:lcd_data=8'b00110001;//'1'
4'b1000:lcd_data=8'b00110010;//'2'
4'b1001:lcd_data=8'b00110011;//'3'
4'b1010:lcd_data=8'b00110100;//'4'
4'b1011:lcd_data=8'b00110101;//'5'
4'b1100:lcd_data=8'b00110110;//'6'
4'b1101:lcd_data=8'b00110111;//'7'
4'b1110:lcd_data=8'b00111000;//'8'
4'b1111:lcd_data=8'b00111001;//'9'
default:lcd_data=8'bxxxxxxxx;
endcase
4'b0111:lcd_data=8'b11000000;
4'b1000:case(data_q)
4'b0000:lcd_data=8'b01000001;//'A'
4'b0001:lcd_data=8'b01000010;//'B'
4'b0010:lcd_data=8'b01000011;//'C'
4'b0011:lcd_data=8'b01000100;//'D'
4'b0100:lcd_data=8'b01000101;//'E'
4'b0101:lcd_data=8'b01000110;//'F'
4'b0110:lcd_data=8'b00110000;//'0'
4'b0111:lcd_data=8'b00110001;//'1'
4'b1000:lcd_data=8'b00110010;//'2'
4'b1001:lcd_data=8'b00110011;//'3'
4'b1010:lcd_data=8'b00110100;//'4'
4'b1011:lcd_data=8'b00110101;//'5'
4'b1100:lcd_data=8'b00110110;//'6'
4'b1101:lcd_data=8'b00110111;//'7'
4'b1110:lcd_data=8'b00111000;//'8'
4'b1111:lcd_data=8'b00111001;//'9'
default:lcd_data=8'bxxxxxxxx;
endcase
endcase
endmodule
(3)将lcd1602.v设为顶层实体后编译,纠错直至编译成功。
(4)新建仿真文件Vector Waveform File,保存为lcd1602.vmf。在左侧节点区导入所有管脚后,将state_q和data_q寄存器也导入,方便观察。
(5)首先根据仿真需求,在Edit→End Time中设置所需仿真时间。本实验中设为10ms。上电复位设置好。
(6)点击Processing→Simulator Tool,打开lcd1602.vmf文件,仿真模式选择Timing,仿真并报告,波形输出如下图所示。
2、设计模块2:六十/二十四进制计数器cnt60/24
a.模块功能要求
计数电路是一种计算输入脉冲数目的时序逻辑电路,被计数的输入信号就是时序电路的时钟脉冲,它不仅可以计数而且还可以用来其他特定的逻辑功能,如测量、定时控制、数字运算等等。数字钟的读数电路是用两个六十进制和一个二十四进制(或十二进制)计数器实现的。六十进制计数器应由一个十进制计数器与一个六进制计数器组成,分别对秒(或分)的个位和十位进行计数。
数字时钟计数电路的设计可用反馈归零法。当计数器正常读数时,反馈门不起作用,只有当进位脉冲到来时,反馈信号随即将计数电路清零,实现相应模的循环计数。以六十进制为例,当计数器计数状态为00,01,02,…,58 时,反馈门不起作用,只有当计数状态为59且最后一个系统时钟脉冲到来时,反馈信号随即将计数电路清零,实现模为60的循环计数。
b.实验步骤
(1)利用wizard新建项目,所选器件为MAX II 系列型号为EPM1270T144C5N的器件。
(2)新建Verilog文件,名称与模块名一致,保存为cnt60/24.v。程序内容如下:
module cnt60(clk,rst,en,qq10,qq1,co);
input clk,rst,en;
output [3:0]qq10,qq1;
output co;
wire ld,en1,en2;
assign co=qq10[2] & qq10[0] & en1;
assign ld=~co;
cnt10 U0(clk,rst,ld,en,4'b0000,qq1,en1);//10times
cnt10 U1(clk,rst,ld,en1,4'b0000,qq10,en2);//6times;qq-number,en-period
endmodule
module cnt24(clk,rst,en,qq10,qq1,co);
input clk,rst,en;
output [3:0]qq10,qq1;
output co;
wire ld,en1,en2;
assign co=qq10[1] & qq1[1] & qq1[0] & en;//qq10=2,qq1=3
assign ld=~co;
cnt10 U0(clk,rst,ld,en,4'b0000,qq1,en1);
cnt10 U1(clk,rst,ld,en1,4'b0000,qq10,en2);
endmodule
(3)设为顶层实体后编译,纠错直至编译成功。
(4)新建仿真文件Vector Waveform File。在左侧节点区导入所有管脚后,将qq_1和qq_10计数寄存器也导入,方便观察。
(5)根据仿真需求,在Edit→End Time中设置所需仿真时间。本实验中设为1ms。上电复位设置好,En设置为周期为1us,占空比为10%的clock信号。
(6)点击Processing→Simulator Tool,打开cnt60.vmf文件,仿真模式选择Timing,仿真并报告,波形输出如下图所示。
3、设计模块3:数字时钟顶层模块clock_mode
(1)利用wizard新建项目,所选器件为MAX II 系列型号为EPM1270T144C5N的器件。
(2)新建Verilog文件,名称与模块名一致,保存为clock_mode.v。程序内容如下:
module clock_mode(clk,rst,key1,key2,rs,rw,lcd_en,lcd_data);
input clk,rst,key1,key2;
output rs,rw,lcd_en;
output [7:0]lcd_data;
wire en0,en1,en2,en3,en4,en5,en6,en7,key_en1,key_en2;
wire [3:0]qqs10,qqs1,qqm10,qqm1,qqh10,qqh1;
reg [1:0]qq_mode;
always@(posedge clk or negedge rst)
if(~rst)
qq_mode<=2'b00;
else if(key_en1)
begin
if(qq_mode[1])//qq_mode:00-10
qq_mode<=2'b00;
else
qq_mode<=qq_mode + 1'b1;
end
assign en0=~(qq_mode[0] | qq_mode[1]);//qq_mode=b'00
assign en2=en1;
assign en4=(qq_mode[1])? key_en2 : ~qq_mode[0] & en3;
assign en6=(qq_mode[0])? key_en2 : ~qq_mode[1] & en5;
div_mode U0(clk,rst,en0,en1);//1s
cnt60 U1(clk,rst,en2,qqs10,qqs1,en3);//1min
cnt60 U2(clk,rst,en4,qqm10,qqm1,en5);//1h
cnt24 U3(clk,rst,en6,qqh10,qqh1,en7);//24h
lcd_mode U4(clk,rst,en_200us,qq_mode,qqs10,qqs1,qqm10,qqm1,qqh10,qqh1,rs,rw,lcd_en,lcd_data);
div_lcd U5(clk,rst,en_200us);
edge_mode U6(clk,rst,en_200us,key1,key_en1);
edge_mode U7(clk,rst,en_200us,key2,key_en2);
endmodule
(3)设为顶层实体后编译,编译成功后进行下载。
实验电路板接电源,确保其供电正常;连接下载电缆到实验板上的下载口;点击进入下载界面。
点击Hardware Setup选择所用硬件USB,Mode选项选择JTAG方式。点击Add File...加入下载数据流文件sy5.pof,检查器件名称是否正确,然后进行下载。
点击Start按钮开始下载,下载完成后不断电调试板子。当选择JTAG下载方式时,数据是直接下载到FPGA芯片中,当断电后数据即消失,下次上电后要重新下载数据。
1、模块功能仿真结果分析总结
(1)LCD显示静态字符串lcd_mode
寄存器state_q控制在5-8以内循环。
LCD控制器通过指令8'b10000000设置好起始字符的显示位置后,当state_q来到下一个值6,开始向LCD输入数据,data_q从0递增到15,每次输出一个字符码,根据码表分别对应“A-F”及“0-9”。
LCD控制器的下一条指令8'b11000000同理,设置好起始位置后开始传输第二个数据。
(2)六十进制计数器cnt60
当计数状态为59且最后一个系统时钟脉冲到来时,反馈信号随即将计数电路清零,实现模为60的循环计数。
干扰信号:其高电平期间无clk上升沿。
(3)edge_mode
每个En高电平来临时,都将qq寄存器低位中的值移入高位中,并将当前sig值置入qq寄存器低位中。当qq中值为b10时,即按键按下输入低电平信号时,输出一个sig_en高电平信号。
2、模块功能测试记录与分析总结
(1)LCD显示静态字符串lcd_mode
模块引脚定义如下图所示:
每条指令/数据作用时间400us。按下复位键后,显示屏上出现数据。
(2)数字时钟模块clock_mode
模块引脚定义如下图所示:
下载完成后按下复位键,数字时钟开始工作。
按下按键1,时钟暂停,指示值为1,表示当前修改位为“小时”,按下按键2即可递增修改其值。
再次按下按键1,指示值为2,表示当前修改位为“分钟”,按下按键2即可递增修改其值。
再次按下按键1,指示值回到0,时钟以当前设置时间状态继续开始计时。
若遇到下载成功后板子上无效果的情况,应尝试按复位键或拨动板子左上角的开关来重启板子。
往期数字系统设计专栏文章: