基于FPGA的数字时钟显示(万年历lcd1602)

一、  lcd1602a的驱动和配置

(1)  lcd1602a的管脚分配图,在程序中,我们需要对相应的管脚进行操作,才能使其正确显示

基于FPGA的数字时钟显示(万年历lcd1602)_第1张图片

(2) 由于我使用的板子EP4CE6上的晶振是50MHZ,而lcd1602a所需要的晶振是500HZ,所以我们要想驱动他,首先要产生一个500HZ的时钟,所以我定义了如下lcd时钟代码:

always@(posedge clk)//获得LCD时钟
begin
	count<=count+1;
	if(count==32'd50000)//2ms
	begin
		count<=32'b0;
		lcd_clk<=~lcd_clk;
	end
end

(3) 因为lcd中存储的字符是ASCII码,直接写入的字符不能被正确的写入,ASCII中’0’的地址是0x38,所以写入的字符都要加上0X38才能正确显示

基于FPGA的数字时钟显示(万年历lcd1602)_第2张图片

 

                two_7<=(shi/8'd10)+8'b00110000;
		two_8<=(shi%8'd10)+8'b00110000;
		two_10<=(fen/8'd10)+8'b0010000;
		two_11<=(fen%8'd10)+8'b00110000;
		two_13<=(miao/8'd10)+8'b00110000;
		two_14<=(miao%8'd10)+8'b00110000;
		one_1<=(year/16'd1000)+8'b00110000;
		one_2<=((year%16'd1000)/16'd100)+8'b00110000;
		one_3<=((year%16'd100)/8'd10)+8'b00110000;
		one_4<=(year%8'd10)+8'b00110000;
		one_6<=(month/8'd10)+8'b00110000;
		one_7<=(month%8'd10)+8'b00110000;
		one_9<=(dat/8'd10)+8'b00110000;
		one_10<=(dat%8'd10)+8'b00110000;

(4)  主要lcd的完成是依托于状态机的,我定义了两种参数,一种是数据寄存器的参数,一种是指令寄存器的参数,指令寄存的参数是定义lcd的显示状态的,而数据寄存器定义的是他应该显示些什么,当对产生的lcd时钟进行操作的时候,就是一步一步执行状态中的各个状态,状态机中进行的各个状态也会对rs进行配置

 

parameter state0 = 8'h00,//设置8位格式,2行,5*7    8'h38 
			 state1 = 8'h01,//整体显示,关光标,不闪烁  8'h0C闪烁 8'h0e
			 state2 = 8'h02,//设定输入方式,增量不移位 8'h06
			 state3 = 8'h03,//清除显示     8'h01
			 state4 = 8'h04,//显示第一行的指令  80H
			 state5 = 8'h05,//显示第二行的指令  80H+40H
			 scan = 8'h06,
			 nul = 8'h07;
			 
parameter data0 = 8'h10,//2行32个数据状态data1 = 8'h11,
			data2 = 8'h12,data3 = 8'h13,data4 = 8'h14,data5 = 8'h15,
			 data6 = 8'h16,data7 = 8'h17,data8 = 8'h18,data9 = 8'h19,
			 data10 = 8'h20,data11 = 8'h21,data12 = 8'h22,
			 data13 = 8'h23,data14 = 8'h24,data15 = 8'h25,
			 data16 = 8'h26,data17 = 8'h27,data18 = 8'h28,
			 data19 = 8'h29,data20 = 8'h30,data21 = 8'h31,
			 data22 = 8'h32,data23 = 8'h33,data24 = 8'h34,
			 data25 = 8'h35,data26 = 8'h36,data27 = 8'h37,
			 data28 = 8'h38,data29 = 8'h39,data30 = 8'h40,
			 data31 = 8'h41;

always@(posedge lcd_clk)
begin
	case(next)
	state0:
	begin rs<=1'b0; data<=8'h38; next<=state1; end//rs高是数据寄存器,rs低是指令寄存器
	state1:
	begin rs<=1'b0; data<=8'h0e; next<=state2; end
	state2:
	begin rs<=1'b0; data<=8'h06; next<=state3; end
	state3:
	begin rs<=1'b0; data<=8'h01; next<=state4; end
	state4:
	begin rs<=1'b0; data<=8'h80; next<=data0; end//显示第一行
	data0 :
	begin rs<=1'b1; data<=one_1; next<=data1 ; end
	data1 :
	begin rs<=1'b1; data<=one_2; next<=data2 ; end
	data2 :
	begin rs<=1'b1; data<=one_3; next<=data3 ; end
	data3 :
	begin rs<=1'b1; data<=one_4; next<=data4 ; end
	data4 :
	begin rs<=1'b1; data<=one_5; next<=data5 ; end
	data5 :
	begin rs<=1'b1; data<=one_6; next<=data6 ; end
	data6 :
	begin rs<=1'b1; data<=one_7; next<=data7 ; end
	data7 :
	begin rs<=1'b1; data<=one_8; next<=data8 ; end
	data8 :
	begin rs<=1'b1; data<=one_9; next<=data9 ; end
	data9 :
	begin rs<=1'b1; data<=one_10; next<=data10 ; end
	data10 :
	begin rs<=1'b1; data<=one_11; next<=data11 ; end
	data11 :
	begin rs<=1'b1; data<=one_12; next<=data12 ; end
	data12 :
	begin rs<=1'b1; data<=one_13; next<=data13 ; end
	data13 :
	begin rs<=1'b1; data<=one_14; next<=data14 ; end
	data14 :
	begin rs<=1'b1; data<=one_15; next<=data15 ; end
	data15 :
	begin rs<=1'b1; data<=one_16; next<=state5 ; end
	state5:
	begin rs<=1'b0;data<=8'hC0; next<=data16; end //显示第二行
	data16 :
	begin rs<=1'b1; data<=two_1; next<=data17 ; end
	data17 :
	begin rs<=1'b1; data<=two_2; next<=data18 ; end
	data18 :
	begin rs<=1'b1; data<=two_3; next<=data19 ; end
	data19 :
	begin rs<=1'b1; data<=two_4; next<=data20 ; end
	data20 :
	begin rs<=1'b1; data<=two_5; next<=data21 ; end
	data21 :
	begin rs<=1'b1; data<=two_6; next<=data22 ; end
	data22 :
	begin rs<=1'b1; data<=two_7; next<=data23 ; end
	data23 :
	begin rs<=1'b1; data<=two_8; next<=data24 ; end
	data24 :
	begin rs<=1'b1; data<=two_9; next<=data25 ; end
	data25 :
	begin rs<=1'b1; data<=two_10; next<=data26 ; end
	data26 :
	begin rs<=1'b1; data<=two_11; next<=data27 ; end
	data27 :
	begin rs<=1'b1; data<=two_12; next<=data28 ; end
	data28 :
	begin rs<=1'b1; data<=two_13; next<=data29 ; end
	data29 :
	begin rs<=1'b1; data<=two_14; next<=data30 ; end
	data30 :
	begin rs<=1'b1; data<=two_15; next<=data31 ; end
	data31 :
	begin rs<=1'b1; data<=two_16; next<=scan ; end
	scan://交替更新第一行和第二行数据
	begin next<=state4; end
	default: next<=state0; 
	endcase
end

二、时钟计时及校准
1. 判断模式
由于本开发板上只有一个三个按键,所以本工程定义的功能一个为调模式的按键,其他两个为加减按键,为了能对按键按下的次数进行相应的操作,所以我们定义了一个按键寄存器以存储相应的按键次数

//获取按键的寄存器
always@(posedge clk or negedge rst)
begin
	if(!rst)
	begin
		flag<=4'b0;
	end
	else
		if(key1)
		begin
			flag<=flag+1'b1;
			if(flag==4'b1011)   flag<=4'b0000;
		end
end

2. 计时
这个计时是对计数器进行操作,如果计数器达到1秒,秒加1,秒到达59,分加1,以此类推,其中因为不同的月份对应的天数不同,所以事先我定义了闰年平年,大月小月的判断,根据判断值选择相应的天数

判断
// 判断是否为31天的月份
		if(month==8'd1||month==8'd3||month==8'd5||month==8'd7||month==8'd8||month==8'd10||month==8'd12)
				dat_flag<=8'd31;
// 判断是否为30天的月份
		else if(month==8'd4||month==8'd6||month==8'd9||month==8'd11)
				dat_flag<=8'd30;
// 判断是否为闰年和平年
		else if(month==8'd2)
		begin
			if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
				dat_flag<=28;
			else dat_flag<=27;
		end

计时
two_1<="C"; two_2<="l"; two_3<="o"; two_4<="c"; two_5<="k";two_12<="-";
			en_sel<=1'b1;
			count1<=count1+1'b1;
			if(count1==32'd49_999_999)
			begin
				count1<=1'b0;
				miao<=miao+1'b1;
				if(miao==8'd59)
				begin
					miao<=1'b0;
					fen<=fen+1'b1;
					if(fen==8'd59)
					begin
						fen<=1'b0;
						shi<=shi+1'b1;
						if(shi==8'd23)
						begin
							shi<=1'b0;
							dat<=dat+1'b1;
							week<=week+1'b1;
							if(week==4'b0110)  week<=4'b0;
							if(dat==dat_flag)
							begin
								dat<=8'd1;
								month<=month+1'b1;
								if(month==8'd12)
								begin
									month<=8'd1;
									year<=year+1'b1;
									if(year==16'd9999)   year<=16'd0;//可以计1万年
								end
							end
						end
					end
				end
			end
		end

3. 校准时的闪烁时钟
为了和其他未被调整的时间进行区分,当调整数被选中时,让他进行闪烁,所以我定义了一个闪烁时钟

//选中就闪烁
always@(posedge clk or negedge rst)//获得校准时间选中闪烁状态
begin
	if(!rst)
	begin
	scan_flag<=1'b0;
	end
	else
	begin
		count2<=count2+1;
		if(count2==32'd10_000_000)//0.2s
		begin
			count2<=32'b0;
			scan_flag<=~scan_flag;
		end
	end
end

4. 调整时间
下面用年来举例,其他调整方法类似,包括调整闹钟
根据按键次数进行选择,当按键次数为3时,进入调年模式,然后进入闪烁模式,直至调整其他时间,当进入闪烁模式时,可以进行加或减的操作,key2加,key3减

4'b0010://调年
		begin
			case(scan_flag)
			1'b0:
			begin
				count1<=32'b0;
				one_1<=8'd20;one_2<=8'd20;one_3<=8'd20;one_4<=8'd20;
			end
			1'b1:
			begin
				count1<=32'b0;
			end
			endcase
		
			if(key2)//加数
			begin 
				year<=year+1'b1;
				if(year==16'd9999)
					year<=16'd0;
			end
			if(key3)//减数
			begin
				year<=year-1'b1;
				if(year==16'd0)
				year<=16'd9999;
			end
		end

三、闹钟整点报时
对闹钟的操作,是要再定义一个时,一个分,当调整闹钟时,调整的是这个时,这个分,而不是时间中的时和分,感觉这点很重要,然后让他们进行比较的操作,如果同时相等,说明计时达到了闹钟的时间,否则没有,达到闹钟时间的时候,前两位led灯亮
对整点报时的操作就是看fen是不是为0,如果为0,后两位led灯亮,如果不是整点,led灯灭

4'b1001://设置闹铃
		begin
			two_1<="A"; two_2<="l"; two_3<="a"; two_4<="r"; two_5<="m";
			two_7<=(alarm_shi/8'd10)+8'b00110000;
			two_8<=(alarm_shi%8'd10)+8'b00110000;
			two_10<=(alarm_fen/8'd10)+8'b00110000;
			two_11<=(alarm_fen%8'd10)+8'b00110000;
			two_12<=" ";
			two_13<=" ";
			two_14<=" ";
		end
		
		4'b1010://设置闹钟小时
		begin
			case(scan_flag)
			1'b0:
			begin
				count1<=32'b0;
				two_7<=8'h20;
				two_8<=8'h20;
			end
			1'b1:
			begin
				count1<=32'b0;
			two_7<=(alarm_shi/8'd10)+8'b00110000;
			two_8<=(alarm_shi%8'd10)+8'b00110000;
			end
			endcase
			
			if(key2)//加数
			begin 
				alarm_shi<=alarm_shi+8'b00000001;
				if(alarm_shi==8'd23)
					alarm_shi<=8'b0;
			end
			if(key3)//减数
			begin
				alarm_shi<=alarm_shi-8'b00000001;
				if(alarm_shi==8'b0)
					alarm_shi<=23;
			end
			two_10<=(alarm_fen/8'd10)+8'b00110000;
			two_11<=(alarm_fen%8'd10)+8'b00110000;
			two_12<=" ";
			two_13<=" ";
			two_14<=" ";
		end
	
	
		4'b1011://调闹钟分钟
		begin
			case(scan_flag)
			1'b0:
			begin
				count1<=32'b0;
				two_10<=8'h20;
				two_11<=8'h20;
			end
			1'b1:
			begin
				two_10<=(alarm_fen/8'd10)+8'b00110000;
				two_11<=(alarm_fen%8'd10)+8'b00110000;
				count1<=32'b0;
			end
			endcase
			
			if(key2)//加数
			begin 
				alarm_fen<=alarm_fen+8'b00000001;
				if(alarm_fen==8'd23)
					alarm_fen<=8'b0;
			end
			if(key3)//减数
			begin
				alarm_fen<=alarm_fen-8'b00000001;
				if(alarm_fen==8'b0)
					alarm_fen<=23;
			end
			two_7<=(alarm_shi/8'd10)+8'b00110000;
			two_8<=(alarm_shi%8'd10)+8'b00110000;
			two_12<=" ";
			two_13<=" ";
			two_14<=" ";
		end
	endcase
	
		if(shi==alarm_shi && fen==alarm_fen)//闹钟响应
		begin
			led<=4'b0011;//前两位响应
		end
//		else
//		begin
//			led<=4'b0000;
//		end
		else if(fen==0)//整点报时
		begin
			led<=4'b1100;//后两位响应
		end
		else
		begin
			led<=4'b0000;
		end

四、管脚配置

基于FPGA的数字时钟显示(万年历lcd1602)_第3张图片

五、工程效果图




这里附上工程文件:工程文件http://download.csdn.net/download/qiaoningning/10262919


你可能感兴趣的:(FPGA,万年历,verilog)