|
//***********************************************************
//**********************小墨笔记*****************************
//简易动态可调电子钟设计
//实现时钟在1602上动态显示且可调
module lcd1602(clk,rst_n,rs,wr,lcden,data,sw1,sw2,sw3,sw4);
input clk ; //输入时钟50Mhz
input rst_n; //复位信号
input sw1,sw2,sw3,sw4; //输入四个独立按键
output rs; //1602数据命令选择端
output wr; //读写控制端
output lcden; //使能信号控制端
output [7:0]data; //8位数据总线
reg rs,lcden;
reg [7:0]data;
assign wr = 0;
//---------------------------分频模块---------------------------
reg clkr;
reg [17:0] cnt;
always @ (posedge clk or negedge rst_n) begin //产生1602时钟
if(!rst_n) begin
cnt <= 18'd50000;
clkr <= 0;
end
else if(cnt == 18'd50000) begin
clkr <= ~clkr;
cnt <= 18'd0;
end
else cnt<= cnt + 1'b1;
end
//------------------------状态机控制液晶初始化模块--------------------------
parameter
s0 = 5'd0,
s1 = 5'd1,
s2 = 5'd2,
s3 = 5'd3,
s4 = 5'd4,
s5 = 5'd5,
s6 = 5'd6,
s7 = 5'd7,
s8 = 5'd8,
s9 = 5'd9,
s10 = 5'd10,
s11 = 5'd11,
s12 = 5'd12,
s13 = 5'd13,
s14 = 5'd14,
s15 = 5'd15,
s16 = 5'd16,
s17 = 5'd17,
s18 = 5'd18,
s19 = 5'd19,
s20 = 5'd20,
s21 = 5'd21,
s22 = 5'd22,
s23 = 5'd23,
s24 = 5'd24,
s25 = 5'd25,
s26 = 5'd26;
reg [4:0] state;
reg init_over; //液晶初始化完成标志位
always @ (posedge clkr or negedge rst_n) begin
if(!rst_n) begin //上电复位初始化
rs <= 1'b1;
lcden <= 1'b0;
data <= 8'd0;
state <=4'd0;
end
else case (state)
s0 : begin
rs <= 0;
lcden <= 0;
data <= 8'h38; //送数据,设置16 x 2显示,5 X 7点阵,8位数据接口
state <= s1;
end
s1: begin //数据有效
lcden <= 1;
state <= s2;
end
s2 : begin
lcden <= 0;
data <= 8'h0c; // 开显示,显示光标,光标闪烁
state <= s3;
end
s3 : begin
lcden <= 1;
state <= s4;
end
s4 : begin
lcden <=0;
data <= 8'h06; // 写一个字符后地址指针加1
state <= s5;
end
s5 : begin
lcden <= 1;
state <= s6;
end
s6 : begin
lcden <= 0;
data <= 8'h01; // 显示清0,数据指针清0
state <= s7;
end
s7 : begin
lcden <= 1;
state <= s8;
init_over <=1; //初始化结束标志位
end
//---------------------------------1602初始化结束-----------------------------
s8 : begin
lcden <= 0;
data <= 8'h84;
state <= s9;
end
s9 : begin
lcden <= 1;
state <= s10;
end
s10 : begin
rs <= 1; //改为写数据状态
lcden <= 0;
data <= shi_s+8'h30; //写小时的十位
state <= s11;
end
s11 : begin
lcden <= 1;
state <= s12;
end
s12 : begin
lcden <=0;
data <= shi_g+8'h30; //写小时的个位
state <= s13;
end
s13 : begin
lcden <= 1;
state <= s14;
end
s14 : begin
lcden <= 0;
data <= ":"; //写冒号
state <= s15;
end
s15 : begin
lcden <= 1;
state <= s16;
end
s16 : begin
lcden <= 0;
data <= fen_s+8'h30; //写分钟的十位
state <= s17;
end
s17 : begin
lcden <= 1;
state <= s18;
end
s18 : begin
lcden <= 0;
data <= fen_g+ 8'h30; //写分钟的个位
state <= s19;
end
s19 : begin
lcden <= 1;
state <= s20;
end
s20 : begin
lcden <= 0;
data <= ":"; //写冒号
state <= s21;
end
s21 : begin
lcden <= 1;
state <= s22;
end
s22 : begin
lcden <= 0;
data <= miao_s+ 8'h30; //写秒钟的十位
state <= s23;
end
s23 : begin
lcden <= 1;
state <= s24;
end
s24 : begin
lcden <= 0;
data<= miao_g + 8'h30; //写秒钟的个位
state <= s25;
end
s25 : begin
lcden <= 1;
state <= s26;
end
s26 : begin
lcden <= 0;
state <= s8; //回到写起始地址的状态,来回刷新
rs <= 0;
end
default : begin
rs <= 1'b1;
lcden <= 1'b0;
data <= 8'd0;
state <= 0;
end
endcase
end
//-------------------------------时分秒产生模块---------------------------------
reg [5:0] shi;
reg [5:0] fen;
reg [5:0] miao;
reg [25:0] cntr; // 产生1秒钟的时钟,要计数到50_000_000;
always @ (posedge clk or negedge rst_n)begin
if(!rst_n) begin
shi <= 6'd18; //设置初始时间
fen<= 6'd22;
miao <= 6'd36;
cntr <= 26'd0;
end
else if(init_over ==1'b1 && flag ==1'b1)begin
if(cntr == 26'd50000000) begin
cntr <= 26'd0;
miao <= miao + 1'b1;
if(miao == 6'd59) begin
miao <= 6'd0;
fen <= fen +1'b1;
if(fen == 6'd59)begin
fen <= 6'd0;
shi <= shi +1'b1;
if(shi == 6'd23)
shi <= 6'd0;
end
end
end
else cntr <= cntr+1'b1;
end
else if(key_state == 4'b1000) begin
miao <= miao + 1'b1; //如果键1按下,那么调整秒针
if(miao == 6'd59) miao <= 6'd0;
end
else if(key_state == 4'b0100) begin
fen <= fen +1'b1; //如果键2按下,那么调整分针
if(fen == 6'd59) fen <= 6'd0;
end
else if(key_state == 4'b0010) begin
shi <= shi +1'b1; //如果键3按下,那么调整时针
if(shi == 6'd23) shi <= 6'd0;
end
end
//--------------------------------时分秒处理模块----------------------------
wire [3:0]miao_s;
wire [3:0]miao_g;
wire [3:0]fen_s;
wire [3:0]fen_g;
wire [3:0] shi_s;
wire [3:0] shi_g;
//对时分秒进行求模求余运算
assign miao_s = miao /10;
assign miao_g = miao %10;
assign fen_s = fen /10;
assign fen_g = fen %10;
assign shi_s = shi /10;
assign shi_g = shi %10;
//-----------------------------按键消抖部分----------------------------------
reg [3:0]key_rst;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) key_rst <= 4'b1111;
else key_rst <= {sw4,sw3,sw2,sw1}; //将按键状态送给第一级锁存器锁存
end
reg [3:0] key_rst_r; //定义二级锁存器
always @(posedge clk or negedge rst_n) begin
if(!rst_n) key_rst_r <= 4'b1111;
else key_rst_r <= key_rst; //送第二级锁存器
end
wire key_an; //抖动标志位
assign key_an = key_rst_r & (~key_rst); //边沿检测算法
reg [17:0] cnt_k;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) cnt_k <= 18'd0;
else if(key_an) cnt_k <= 18'd0; //一旦有抖动,计数器马上清零
else cnt_k <= cnt_k +1'b1;
end
reg [3:0] key;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) key <= 4'b1111;
else if(cnt_k == 18'd250000) // 等待5ms,再记录按键状态
key <= {sw4,sw3,sw2,sw1};
end
reg [3:0]key_r;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) key_r <= 4'b1111;
else key_r <= key; //送到二级锁存器
end
wire [3:0] key_state = key_r& (~key); //边沿检测算法
//--------------------------时钟暂停----------------------------
reg flag;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) flag <= 1'b1;
else if(key_state == 3'b0001) // 如果键4按下,那么时钟暂停
flag <= ~flag;
end
endmodule |
|