module digitalclk_top(clk,reset,key,lcd_rw,lcd_e,lcd_rs,lcd_data,beep);
input clk;
input reset;
input [2:0]key;
output lcd_rw;
output lcd_rs;
output lcd_e;
output [7:0]lcd_data;
output beep;
wire [2:0]debkey;
wire [255:0]disp;
debkey U0
(.clk(clk),
.reset(reset),
.key(key),
.debkey(debkey));
digitalclk U1
(.clk(clk),
.reset(reset),
.key(debkey),
.lcd_data_disp(disp),
.beep(beep));
lcd_ip U3
(.clk(clk),
.rst(reset),
.data_buf(disp),
.lcd_rw(lcd_rw),
.lcd_rs(lcd_rs),
.lcd_e(lcd_e),
.lcd_data(lcd_data));
endmodule
//按键去抖模块
module debkey(clk,reset,key,debkey);
input clk;
input reset;
input [2:0]key;
output [2:0]debkey;
//---------------------------------------------------------------
//100Hz分频
parameter T100Hz = 249999;
integer cnt_100Hz;
reg clk_100Hz;
always @(posedge clk or negedge reset)
if(!reset)
cnt_100Hz <= 32'b0;
else
begin
cnt_100Hz <= cnt_100Hz + 1'b1;
if(cnt_100Hz == T100Hz)
begin
cnt_100Hz <= 32'b0;
clk_100Hz <= ~clk_100Hz;
end
end
//---------------------------------------------------------------
//去抖模块
reg [2:0]key_rrr,key_rr,key_r;
always @(posedge clk_100Hz or negedge reset)
if(!reset)
begin
key_rrr <= 1'b1;
key_rr <= 1'b1;
key_r <= 1'b1;
end
else
begin
key_rrr <= key_rr;
key_rr <= key_r;
key_r <= key;
end
//---------------------------------------------------------------
assign debkey = key_rrr & key_rr & key_r;
endmodule
//lcd显示模块
module lcd_ip(clk,rst,data_buf,lcd_e,lcd_rw,lcd_rs,lcd_data,);
input clk;
input rst;
input [255:0]data_buf; //数据接口
output lcd_e;
output lcd_rw;
output lcd_rs;
output [7:0]lcd_data;
//---------------------------------------------------------------
//分频得到clk_800Hz
reg [16:0]cnt;
reg clk_lcd;
always @(posedge clk or negedge rst)
if(!rst)
cnt <= 1'b0;
else
begin
cnt <= cnt + 1'b1;
if( cnt == 17'd31249)
begin
cnt <= 1'b0;
clk_lcd <= ~clk_lcd;
end
end
//---------------------------------------------------------------
reg [2:0]func; //状态机func
reg [3:0]com_cnt; //com_buf_bit的计数模块
reg [5:0]data_cnt; //data_buf_bit计数模块
reg [87:0]com_buf_bit; //11条lcd屏幕指令 每条8bit,11条就需要 11x8=88bit
reg [255:0]data_buf_bit;//lcd 每行16个字符,一共两行,总共32个字符,一个字符需要8bit显示,所以32x8=256bit 参考LCD1602液晶显示控制部分
parameter set0=4'd1,set1=4'd2,dat1=4'd3,set2=4'd4,dat2=4'd5,done=4'd6;
parameter com_buf={8'h02,8'h06,8'h0C,8'h38,8'h80,8'h00,8'h00,8'h00,8'h00,8'h00,8'h00};//LCD1602指令,对屏幕初始化
//---------------------------------------------------------------
reg [7:0]lcd_data;
reg lcd_rs;
reg en;
reg [255:0]data; //因为封装成ip核所以采用一个寄存器把data_buf值存起来,
//后面做对比用,如果data值变化,显示内容也就变化
always @(posedge clk_lcd or negedge rst)
begin
if(!rst)
com_buf_bit <= 8'h01; //清屏指令
else
begin
en <= 0; //写指令
case(func)
//液晶屏初始化
set0: begin
com_buf_bit <= com_buf;
data_buf_bit <= data_buf;
data <= data_buf; //data_buf存到寄存器data里面
com_cnt <= 1'b0;
data_cnt <= 1'b0;
func <= set1;
end
//---------------------------------------------------------------
set1: begin
lcd_rs <= 0; //写指令
lcd_data <= com_buf_bit[87:80];
com_buf_bit <= (com_buf_bit<<8);
com_cnt <= com_cnt + 1'b1;
if(com_cnt <= 10) //共11次,11条lcd指令
func <= set1;
else
begin
func <= dat1;
com_cnt <= 1'b0;
end
end
//---------------------------------------------------------------
dat1: begin
lcd_rs <= 1; //写数据
lcd_data <= data_buf_bit[255:248];
data_buf_bit <= (data_buf_bit<<8);
data_cnt <= data_cnt + 1'b1;
if(data_cnt < 15) //共16次 液晶屏第一行显示的内容
func <= dat1;
else
func <= set2;
end
//---------------------------------------------------------------
set2: begin
lcd_rs <= 0; //写指令
lcd_data <= 8'hC0; //表示第二行第一位
func <= dat2;
end
//---------------------------------------------------------------
dat2: begin
lcd_rs <= 1; //写数据
lcd_data <= data_buf_bit[255:248];
data_buf_bit <= (data_buf_bit<<8);
data_cnt <= data_cnt + 1'b1;
if(data_cnt < 31) //共32次 把第二行的内容显示在屏幕上
func <= dat2; //不能把第一行字符显示出来,所以采用分两次写数据
else
begin
func <= done;
data_cnt <= 1'b0;
end
end
//---------------------------------------------------------------
done: begin
if(data_buf!==data ) //判断有没有新送进来的数据,对比法
func <= set0; //有就回液晶屏初始化
else
begin
func <= done; //没有就结束
en <= 1;
end
end
default:func <= set0;
endcase
end
end
//---------------------------------------------------------------
assign lcd_e = clk_lcd | en;
assign lcd_rw = 0;
endmodule
//数字时钟顶层模块
module digitalclk(clk,reset,key,lcd_data_disp,beep);
input clk;
input reset;
input [2:0]key;
output [255:0]lcd_data_disp;
output beep;
wire [2:0]mode;
wire [255:0]clock_disp,alarm_disp,adj_disp;
wire [255:0]sync_clock,sync_adj;
wire alarm_en;
digitalclk_mode U4
(.clk(clk),
.reset(reset),
.key(key),
.mode(mode));
clock U5
(.clk(clk),
.reset(reset),
.mode(mode),
.sync_clock(sync_clock),
.lcd_data_disp(clock_disp));
alarm U6
(.clk(clk),
.reset(reset),
.mode(mode),
.key(key),
.lcd_data_disp(alarm_disp));
adj U7
(.clk(clk),
.reset(reset),
.mode(mode),
.key(key),
.sync_adj(sync_adj),
.lcd_data_disp(adj_disp));
disp_sel U8
(.clk(clk),
.mode(mode),
.alarm_disp(alarm_disp),
.clock_disp(clock_disp),
.adj_disp(adj_disp),
.sync_clock(sync_clock),
.sync_adj(sync_adj),
.lcd_data_disp(lcd_data_disp),
.alarm_en(alarm_en));
beep U9
(.clk(clk),
.reset(reset),
.alarm_en(alarm_en),
.beep(beep));
endmodule
//数字时钟功能模块
module digitalclk_mode(clk,reset,key,mode);
input clk;
input reset;
input [2:0]key;
output [2:0]mode;
//---------------------------------------------------------------
parameter clock=2'd0,alarm=2'd1,adj=2'd2;
//---------------------------------------------------------------
reg [2:0]func;
reg [2:0]mode;
always @(posedge key[0] or negedge reset)
if(!reset)
func <= 1'b0;
else
begin
func <= func + 1'b1; //产生不同的mode码
if(func == 2'd2)
func <= 1'b0;
end
//---------------------------------------------------------------
always @(posedge clk)
case(func)
clock : mode = 3'b001; //时钟模式
alarm : mode = 3'b010; //闹钟模式
adj : mode = 3'b100; //校时模式
default : mode = 3'b001;
endcase
endmodule
//mode为0010时为时钟功能
//时钟功能
module clock(clk,reset,mode,sync_clock,lcd_data_disp);
input clk;
input reset;
input [2:0]mode;
input [255:0]sync_clock;
output [255:0]lcd_data_disp;
//---------------------------------------------------------------
//分频模块
reg clk_1Hz;
reg clk_100Hz;
integer i,j;
always @(posedge clk)
begin
i <= i + 1'b1;
if(i==32'd249999)
begin i <= 1'b0; clk_100Hz <= ~clk_100Hz;end
j <= j + 1'b1;
if(j==32'd24999999)
begin j <= 1'b0; clk_1Hz <= ~clk_1Hz; end
end
//---------------------------------------------------------------
//闪烁模块 1秒闪一次
always @(posedge clk)
if(mode==3'b001)
begin
clock_disp[23:16] <= clk_1Hz ? " " : ":";
clock_disp[47:40] <= clk_1Hz ? " " : ":";
clock_disp[71:64] <= clk_1Hz ? " " : ":";
end
//---------------------------------------------------------------
//百分秒模块
reg [255:0]clock_disp;
always @(posedge clk_100Hz or negedge reset)
if(!reset)
clock_disp[15:0] <= 1'b0;
else
begin
clock_disp[7:0] <= clock_disp[7:0] + 1'b1; //百分秒的个位
if(clock_disp[7:0]==4'h9)
begin
clock_disp[7:0] <= 1'b0;
clock_disp[15:8] <= clock_disp[15:8] + 1'b1; //百分秒的十位
if(clock_disp[15:8]==4'h9)
clock_disp[15:8] <= 1'b0;
end
end
//---------------------------------------------------------------
//计时模块
always @(posedge clk_1Hz or negedge reset)
if(!reset) //重置清零
begin
clock_disp[39:24] <= 1'b0;
clock_disp[63:48] <= 1'b0;
clock_disp[87:72] <= 1'b0;
end
else
begin
clock_disp[255:88] = "MODE H M S MSCLOCK";
//秒计时模块
clock_disp[31:24] <= clock_disp[31:24] + 1'b1; //秒的个位
if(clock_disp[31:24]==8'h9)
begin
clock_disp[31:24] <= 1'b0;
clock_disp[39:32] <= clock_disp[39:32] + 1'b1; //秒的十位
//---------------------------------------------------------------
//分计时模块
if(clock_disp[39:32]==8'h5)
begin
clock_disp[39:32] <= 1'b0;
clock_disp[55:48] <= clock_disp[55:48] + 1'b1; //分钟的个位
if(clock_disp[55:48]==8'h9)
begin
clock_disp[55:48] <= 1'b0;
clock_disp[63:56] <= clock_disp[63:56] + 1'b1; //分钟的十位
//---------------------------------------------------------------
//小时计时模块
if(clock_disp[63:56]==8'h5)
begin
clock_disp[63:56] <= 1'b0;
clock_disp[79:72] <= clock_disp[79:72] + 1'b1; //时钟的个位
if(clock_disp[79:72]==8'h9)
begin
clock_disp[79:72] <= 1'b0;
clock_disp[87:80] <= clock_disp[87:80] + 1'b1; //时钟的十位
end
end
//---------------------------------------------------------------
end
end
end
//---------------------------------------------------------------
//如果记到23:59:59 时清零 00:00:00
else if(clock_disp[87:72]==16'h0204) //0000 0010 0000 0100
clock_disp[87:72] <= 1'b0;
//----------------------------------------------------------
//后台同步校准模式的时钟和分钟
else if(mode==3'b100)
begin
clock_disp[87:72] = sync_clock[87:72];
clock_disp[63:48] = sync_clock[63:48];
end
end
//----------------------------------------------------------
assign lcd_data_disp = clock_disp ;
endmodule
//mode=3'b010时为闹钟功能
//校时可以长按按键,可以快速加数字
//闹钟功能
module alarm(clk,reset,mode,key,lcd_data_disp);
input clk;
input reset;
input [2:0]key;
input [2:0]mode;
output [255:0]lcd_data_disp;
//---------------------------------------------------------------
//校时分频200ms的频率用来更新时间
integer i;
reg clk_alarm;
always @(posedge clk)
begin
i <= i + 1'b1;
if(i==32'd4999999)
begin i <= 1'b0; clk_alarm <= ~clk_alarm; end
end
//---------------------------------------------------------------
reg [255:0]alarm_disp;
always @(posedge clk_alarm or negedge reset)
if(!reset) //复位时清零
alarm_disp[87:72] <= 1'b0;
else if(mode==3'b010)
begin
//设置一些常量,没使用到的要把初始值赋为0,可以减少警告
alarm_disp[255:88] <= "MODE H M S MSALARM";
alarm_disp[71:64] <= ":";
alarm_disp[47:40] <= ":";
alarm_disp[39:24] <= 1'b0;
alarm_disp[23:16] <= ":";
alarm_disp[15:0] <= 1'b0;
//---------------------------------------------------------------
//闹钟设置时钟
if(!key[1])
begin
alarm_disp[79:72] <= alarm_disp[79:72] + 1'b1; //时钟的个位
if(alarm_disp[79:72]==4'h9)
begin
alarm_disp[79:72] <= 1'b0;
alarm_disp[87:80] <= alarm_disp[87:80] + 1'b1; //时钟的十位
if(alarm_disp[87:80]==4'h5)
alarm_disp[87:80] <= 1'b0;
end
else if(alarm_disp[87:72]==16'h0203) //时钟计算到23的时候回00
alarm_disp[87:72] <= 1'b0;
end
//---------------------------------------------------------------
//闹钟设置分钟
if(!key[2])
begin
alarm_disp[55:48] <= alarm_disp[55:48] + 1'b1; //时钟的个位
if(alarm_disp[55:48]==4'h9)
begin
alarm_disp[55:48] <= 1'b0;
alarm_disp[63:56] <= alarm_disp[63:56] + 1'b1; //时钟的十位
if(alarm_disp[63:56]==4'h5)
alarm_disp[63:56] <= 1'b0;
end
end
end
//---------------------------------------------------------------
assign lcd_data_disp = alarm_disp ;
endmodule
//mode=3'b100时为校时功能
//校时可以长按按键,可以快速加数字
//校时功能
module adj(reset,clk,mode,key,sync_adj,lcd_data_disp);
input clk;
input reset;
input [2:0]key;
input [2:0]mode;
input [255:0]sync_adj;
output [255:0]lcd_data_disp;
//---------------------------------------------------------------
//校时分频100ms的频率用来更新时间,
integer i,j;
reg clk_adj,clk_1Hz;
always @(posedge clk)
begin
i <= i + 1'b1;
if(i==32'd4999999)
begin i <= 1'b0; clk_adj <= ~clk_adj; end
j <= j + 1'b1;
if(j==32'd24999999)
begin j <= 1'b0; clk_1Hz <= ~clk_1Hz; end
end
//---------------------------------------------------------------
reg [255:0]adj_disp;
always @(posedge clk_adj or negedge reset)
if(!reset) //复位时清零
adj_disp[87:72] <= 1'b0;
else if(mode==3'b100)
begin
//设置一些常量,没使用到的要把初始值赋为0,可以减少警告
adj_disp[255:88] <= "MODE H M S MSADJ ";
adj_disp[71:64] <= ":";
adj_disp[47:40] <= ":";
adj_disp[23:16] <= ":";
adj_disp[15:0] <= 1'b0;
//---------------------------------------------------------------
//校时状态下调节时钟
if(!key[1])
begin
adj_disp[79:72] <= adj_disp[79:72] + 1'b1; //时钟的个位
if(adj_disp[79:72]==4'h9)
begin
adj_disp[79:72] <= 1'b0;
adj_disp[87:80] <= adj_disp[87:80] + 1'b1; //时钟的十位
if(adj_disp[87:80]==4'h5)
adj_disp[87:80] <= 1'b0;
end
else if(adj_disp[87:72]==16'h0203) //时钟计算到23的时候回00
adj_disp[87:72] <= 1'b0;
end
//---------------------------------------------------------------
//校时状态下调节分钟
if(!key[2])
begin
adj_disp[55:48] <= adj_disp[55:48] + 1'b1; //时钟的个位
if(adj_disp[55:48]==4'h9)
begin
adj_disp[55:48] <= 1'b0;
adj_disp[63:56] <= adj_disp[63:56] + 1'b1; //时钟的十位
if(adj_disp[63:56]==4'h5)
adj_disp[63:56] <= 1'b0;
end
end
end
//---------------------------------------------------------------
else if(mode==3'b001) //后台同步时钟
begin
adj_disp[87:72] = sync_adj[87:72];
adj_disp[63:48] = sync_adj[63:48];
end
//---------------------------------------------------------------
//校准模式下分钟显示
always @(posedge clk_1Hz or negedge reset)
if(!reset)
adj_disp[39:24] <= 1'b0;
else
begin
adj_disp[31:24] <= adj_disp[31:24] + 1'b1; //分钟的个位
if(adj_disp[31:24]==8'h9)
begin
adj_disp[31:24] <= 1'b0;
adj_disp[39:32] <= adj_disp[39:32] + 1'b1; //分钟的十位
if(adj_disp[39:32]==8'h5)
adj_disp[39:32] <= 1'b0;
end
end
//---------------------------------------------------------------
assign lcd_data_disp = adj_disp ;
endmodule
//LCD数据选择
module disp_sel(clk,mode,alarm_disp,clock_disp,adj_disp,sync_clock,sync_adj,lcd_data_disp,alarm_en);
input clk;
input [2:0]mode;
input [255:0]clock_disp;
input [255:0]alarm_disp,adj_disp;
output reg[255:0]sync_clock;
output reg[255:0]sync_adj;
output [255:0]lcd_data_disp;
output alarm_en;
//---------------------------------------------------------------
reg[255:0] result_disp;
always @(posedge clk)
begin
if(mode==3'b001) //时钟功能时选择时钟的的数据
begin
result_disp <= clock_disp ;
sync_adj <= clock_disp;
end
else if(mode==3'b010) //闹钟功能时选择闹钟的数据
begin
result_disp <= alarm_disp;
end
else if(mode==3'b100) //校时功能时选择闹钟的数据
begin
result_disp <= adj_disp;
sync_clock <= adj_disp;
end
end
//---------------------------------------------------------------
//闹钟响蜂鸣器模块
reg alarm_en = 0;
always @(posedge clk)
if(mode==3'b001)
begin
if((clock_disp[63:48]!==16'b0) || (clock_disp[87:72]!==16'b0))//还没想到好的方法处理半夜0点尖叫,只能采用折中方法
begin //这类似几年前ios系统的0点不响事件,这是一个bug//
if(clock_disp[39:24]==alarm_disp[39:24]) //响一分钟模块
begin
if((clock_disp[87:72]==alarm_disp[87:72]) & (clock_disp[63:48]==alarm_disp[63:48]))
alarm_en <= 1;
else
alarm_en <= 0;
end
end
end
else alarm_en <= 0;
//---------------------------------------------------------------
//数据处理
assign lcd_data_disp[255:88] = result_disp[255:88];
assign lcd_data_disp[87:80] = result_disp[87:80] + 8'd48;
assign lcd_data_disp[79:72] = result_disp[79:72] + 8'd48;
assign lcd_data_disp[71:64] = result_disp[71:64];
assign lcd_data_disp[63:56] = result_disp[63:56] + 8'd48;
assign lcd_data_disp[55:48] = result_disp[55:48] + 8'd48;
assign lcd_data_disp[47:40] = result_disp[47:40];
assign lcd_data_disp[39:32] = result_disp[39:32] + 8'd48;
assign lcd_data_disp[31:24] = result_disp[31:24] + 8'd48;
assign lcd_data_disp[23:16] = result_disp[23:16];
assign lcd_data_disp[15:8] = result_disp[15:8] + 8'd48;
assign lcd_data_disp[7:0] = result_disp[7:0] + 8'd48;
endmodule
//蜂鸣器模块
module beep(clk,reset,alarm_en,beep);
input clk;
input reset;
input alarm_en;
output beep;
//---------------------------------------------------------------
parameter IDLE= 8'b00000001,
DO = 8'b00000010,
RE = 8'b00000100,
MI = 8'b00001000,
FA = 8'b00010000,
SO = 8'b00100000,
LA = 8'b01000000,
SI = 8'b10000000;
//---------------------------------------------------------------
reg[31:0] cnt_1Hz;
always @(posedge clk or negedge reset)
if(!reset)
cnt_1Hz <= 32'd0;
else
begin
if(cnt_1Hz >= 32'd49_999_99)
cnt_1Hz <= 32'd0;
else
cnt_1Hz <= cnt_1Hz + 32'd1;
end
//---------------------------------------------------------------
reg[7:0]beep_status;
always @(posedge clk or negedge reset)
if(!reset)
beep_status <= IDLE;
else
begin
if( cnt_1Hz == 32'd49_999_99)
beep_status[7:0] <= {beep_status[6:0],beep_status[7]};
end
//---------------------------------------------------------------
reg[31:0]cnt;
reg beep;
always @(posedge clk or negedge reset)
begin
if(!reset)
begin
cnt <= 0;
beep <= 1;
end
else if(alarm_en)
case(beep_status)
IDLE: beep <= 1;
DO : begin if(cnt>=11944) begin cnt <= 0; beep <= ~beep ;end //1
else cnt <= cnt + 1; end
RE : begin if(cnt>=10642) begin cnt <= 0; beep <= ~beep ;end //2
else cnt <= cnt + 1; end
MI : begin if(cnt>=9480) begin cnt <= 0; beep <= ~beep ;end //3
else cnt <= cnt + 1; end
FA : begin if(cnt>=8947) begin cnt <= 0; beep <= ~beep ;end //4
else cnt <= cnt + 1; end
SO : begin if(cnt>=7971) begin cnt <= 0; beep <= ~beep ;end //5
else cnt <= cnt + 1; end
LA : begin if(cnt>=7102) begin cnt <= 0; beep <= ~beep ;end //6
else cnt <= cnt + 1; end
SI : begin if(cnt>=6327) begin cnt <= 0; beep <= ~beep ;end //7
else cnt <= cnt + 1; end
endcase
else beep <= 1;
end
endmodule
|