verilog的RTC驱动代码
1.例化
parameter SLAVE_ADDR = 7'h51 ; // 器件地址
parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b)
parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ)
parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率
parameter POINT = 6'b010100 ; // 控制点亮数码管小数点的位置
//初始时间设置,从高到低为年到秒,各占8bit
parameter TIME_INI = 48'h18_05_23_09_30_00;
//例化i2c_dri,调用IIC协议
i2c_dri #(
.SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递
.CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ)
.I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率
) u_i2c_dri(
//global clock
.clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ)
.rst_n (sys_rst_n ), // 复位信号
//i2c interface
.i2c_begin (i2c_exec ), // I2C触发执行信号
.i2c_rw (i2c_rh_wl ), // I2C读写控制信号
.i2c_addr (i2c_addr ), // I2C器件内地址
.i2c_data_w (i2c_data_w), // I2C要写的数据
.i2c_data_r (i2c_data_r), // I2C读出的数据
.i2c_done (i2c_done ), // I 2C一次操作完成
.scl (rtc_scl ), // I2C的SCL时钟信号
.sda (rtc_sda ), // I2C的SDA信号
//user interface
.dri_clk (clk ) // I2C操作时钟
);
//例化PCF8563测量模块
pcf8563 #(.TIME_INI(TIME_INI)
) u_pcf8563(
//system clock
.clk (clk ), // 时钟信号
.rst_n (sys_rst_n ), // 复位信号
//i2c interface
.i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号
.i2c_exec (i2c_exec ), // I2C触发执行信号
.i2c_addr (i2c_addr ), // I2C器件内地址
.i2c_data_w (i2c_data_w), // I2C要写的数据
.i2c_data_r (i2c_data_r), // I2C读出的数据
.i2c_done (i2c_done ), // I2C一次操作完成
//user interface
.key_value (key_value ), // 按键切换输入
.num (num ) // 数码管要显示的数据
);
2.驱动8563
module pcf8563 #(
// 初始时间设置,从高到低为年到秒,各占8bit
parameter TIME_INI = 48'h18_01_02_03_04_05)(
//system clock 50MHz
input clk , // 时钟信号
input rst_n , // 复位信号
//i2c interface
output reg i2c_rh_wl , // I2C读写控制信号
output reg i2c_exec , // I2C触发执行信号
output reg [15:0] i2c_addr , // I2C器件内地址
output reg [ 7:0] i2c_data_w , // I2C要写的数据
input [ 7:0] i2c_data_r , // I2C读出的数据
input i2c_done , // I2C一次操作完成
//user interface
input key_value , // 按键切换输入
output [23:0] num // 数码管要显示的数据
);
//reg define
reg key_dy0 ; // 延迟打拍
reg key_dy1 ; // 延迟打拍
reg switch ; // 按键切换显示日期、时间
reg [3:0] flow_cnt ; // 状态流控制
reg [20:0] wait_cnt ; // 计数等待
//PCF8563T的秒、分、时、日、月、年数据
reg [7:0] sec ; // 秒
reg [7:0] min ; // 分
reg [7:0] hour ; // 时
reg [7:0] day ; // 日
reg [7:0] mon ; // 月
reg [7:0] year ; // 年
wire [23:0] rtc_time ; // 时间,从低位到高位依次是秒、分、时,各8bit
wire [23:0] rtc_date ; // 日期,从低位到高位依次是日、月、年,各8bit
//wire define
wire neg_sap ; // 采下降沿得到的信号
//*****************************************************
//** main code
//*****************************************************
assign neg_sap = (~key_dy0 & key_dy1); // 按键按下时,得到一个周期的高电平信号
assign rtc_time = {hour,min,sec};
assign rtc_date = {year,mon,day};
//通过switch切换时间/日期显示
assign num = switch ? rtc_time : rtc_date;
//打拍(采按键时的下降沿)
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_dy0 <= 1'b1;
key_dy1 <= 1'b1;
end
else begin
key_dy0 <= key_value;
key_dy1 <= key_dy0 ;
end
end
//按键切换
always @(posedge clk or negedge rst_n ) begin
if(!rst_n)
switch<= 1'b0;
else if (neg_sap)
switch <= ~switch;
end
//从PCF8563T读出的时间、日期数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sec <= 8'h0;
min <= 8'h0;
hour <= 8'h0;
day <= 8'h0;
mon <= 8'h0;
year <= 8'h0;
i2c_exec <= 1'b0;
i2c_rh_wl <= 1'b0;
i2c_addr <= 8'd0;
i2c_data_w <= 8'd0;
flow_cnt <= 4'd0;
wait_cnt <= 21'd0;
end
else begin
i2c_exec <= 1'b0;
case(flow_cnt)
//上电初始化
4'd0: begin
if(wait_cnt == 21'd1_000_000) begin
wait_cnt<= 21'd0;
flow_cnt<= flow_cnt + 1'b1;
end
else
wait_cnt<= wait_cnt + 1'b1;
end
//写读秒
4'd1: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h02;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[7:0];
end
4'd2: begin
if(i2c_done == 1'b1) begin
sec <= i2c_data_r[6:0];
flow_cnt<= flow_cnt + 1'b1;
end
end
//写读分
4'd3: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h03;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[15:8];
end
4'd4: begin
if(i2c_done == 1'b1) begin
min <= i2c_data_r[6:0];
flow_cnt<= flow_cnt + 1'b1;
end
end
//写读时
4'd5: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h04;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[23:16];
end
4'd6: begin
if(i2c_done == 1'b1) begin
hour <= i2c_data_r[5:0];
flow_cnt<= flow_cnt + 1'b1;
end
end
//写读天
4'd7: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h05;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[31:24];
end
4'd8: begin
if(i2c_done == 1'b1) begin
day <= i2c_data_r[5:0];
flow_cnt<= flow_cnt + 1'b1;
end
end
//写读月
4'd9: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h07;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[39:32];
end
4'd10: begin
if(i2c_done == 1'b1) begin
mon <= i2c_data_r[4:0];
flow_cnt<= flow_cnt + 1'b1;
end
end
//写读年
4'd11: begin
i2c_exec <= 1'b1;
i2c_addr <= 8'h08;
flow_cnt <= flow_cnt + 1'b1;
i2c_data_w<= TIME_INI[47:40];
end
4'd12: begin
if(i2c_done == 1'b1) begin
year <= i2c_data_r;
i2c_rh_wl<= 1'b1;
flow_cnt <= 4'd0;
end
end
4'd13: begin
;
end
default: flow_cnt <= 4'd0;
endcase
end
end
endmodule
3.IIC驱动
module i2c_dri(
input clk ,
input rst_n ,
//i2c interface
input i2c_rw , //0是写,1是读
input i2c_begin , //I2C触发执行信号
input [15:0] i2c_addr , //I2C器件内地址
input [ 7:0] i2c_data_w , //I2C要写的数据
output reg [ 7:0] i2c_data_r , //I2C读出的数据
output reg i2c_busy , //I2C忙信号
output reg i2c_done , //I2C一次操作完成
output reg scl , //I2C的SCL时钟信号
inout sda , //I2C的SDA信号
output reg led2 ,
//
output reg dri_clk //I2C数据(SDA)方向控制
);
parameter SLAVE_ADDR = 7'b1010000 ; //EEPROM从机地址
parameter CLK_FREQ = 26'd50_000_000; //模块输入的时钟频率
parameter I2C_FREQ = 18'd250_000; //IIC_SCL的时钟频率
//reg define
reg [ 9:0] clk_cnt ; //分频时钟计数
reg [ 7:0] data_wr_t ; //I2C需写的数据的临时寄存
reg [15:0] addr_t ; //读写地址临时寄存
reg [ 7:0] data_r ; //数据
reg [ 7:0] data_w ; //数据
reg r_or_w ; //读写
reg sda_dir ; //I2C数据(SDA)方向控制
reg sda_out ; //
reg [ 3:0] flow_cnt ; // 流转计数
//wire define
wire sda_in ; //SDA输入信号
wire [8:0] clk_divide ; //模块驱动时钟的分频系数
assign sda = sda_out ; //SDA数据输出或高阻
assign clk_divide = (CLK_FREQ/I2C_FREQ) >> 2'd2;//模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dri_clk <= 1'b0;
clk_cnt <= 10'd0;
end
else if(clk_cnt == clk_divide[8:1] - 1'd1) begin
clk_cnt <= 10'd0;
dri_clk <= ~dri_clk;
end
else
clk_cnt <= clk_cnt + 1'b1;
end
//////////////////////////////////
reg [ 7:0] data_temp ; //数据
reg flow_cnt_ok ; // 流转计数
reg [ 6:0] cnt ; //计数
////////////////////////////////////////////////
/*
写
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
7:加载数据
8:写字节
9:发送停止位
读
0:空闲
1:加载器件地址
2:写字节
3:加载16为地址高字节
4:写字节
5:加载16为地址低字节
6:写字节
10:加载器件地址+读
8:写字节
11:读数据
12:发送停止
*/
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n) begin
scl <= 1'b1;
sda_out <= 1'b1;
cnt<= 7'b0;
flow_cnt<=4'b0;
i2c_busy<=1'b0;
flow_cnt_ok<= 1'b0;
cnt<= 1'b0;
end
////////////////////////////////////////////
else if (flow_cnt_ok&&(r_or_w==0))begin
flow_cnt<=((flow_cnt>=4'd9)? (4'd0):(flow_cnt+4'd1));//状态技术+1
i2c_done<=1'b0;
flow_cnt_ok<= 1'b0;
end
else if (flow_cnt_ok&&(r_or_w==1))begin
if(flow_cnt<=4'd5)
flow_cnt<=flow_cnt+1'b1;
else if (flow_cnt==4'd6)
flow_cnt<=4'd10;
else if (flow_cnt==4'd10)
flow_cnt<=4'd8;
else if (flow_cnt==4'd8)
flow_cnt<=4'd11;
else if (flow_cnt==4'd11)
flow_cnt<=4'd12;
else if (flow_cnt==4'd12)
flow_cnt<=4'd0;
i2c_done<=1'b0;
flow_cnt_ok<= 1'b0;
end
///////////////////////////////////////////////
else if(flow_cnt == 4'b0) begin
if(i2c_begin==1'b1&&i2c_busy==1'b0) begin
r_or_w<=i2c_rw;
addr_t<=i2c_addr;
data_w<=i2c_data_w;
i2c_busy<=1'b1;
flow_cnt_ok<=1'b1;
end
else
flow_cnt = 4'b0;
end
else if(flow_cnt == 4'd1) begin//加载写字节
data_temp<={SLAVE_ADDR,1'b0};
flow_cnt_ok<=1'b1;
end
else if(flow_cnt == 4'd3) begin//加载eeprom地址
//data_temp<=addr_t[15:8];
flow_cnt_ok<=1'b1;
end
else if(flow_cnt == 4'd4) begin//加载eeprom地址
//data_temp<=addr_t[15:8];
flow_cnt_ok<=1'b1;
end
else if(flow_cnt == 4'd5) begin//加载低8位地址
data_temp<=addr_t[7:0];
flow_cnt_ok<=1'b1;
end
else if(flow_cnt == 4'd7) begin//加载要写入的数据
data_temp<=data_w[7:0];
flow_cnt_ok<=1'b1;
end
else if(flow_cnt == 4'd9) begin//发送STOP
cnt <= cnt +1'b1 ;
case(cnt)
7'd1 : sda_out <= 1'b0; //开始I2C
7'd2 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1; //传送器件地址
7'd4 : begin
i2c_busy<=1'b0;
i2c_done<=1'b1;
scl <= 1'b1;
sda_out <= 1'b1;
flow_cnt_ok<=1'b1;
cnt <= 1'b0;
end
default : ;
endcase
end
else if(flow_cnt == 4'd2||flow_cnt == 4'd6||flow_cnt == 4'd8) begin//发送一个字节
cnt <= cnt +1'b1 ;
case(cnt)
//7'd0 :begin scl <= 1'b1;sda_out <= 1'b1;end
7'd1 : sda_out <= 1'b0; //开始I2C
7'd3 : scl <= 1'b0;
7'd4 : sda_out <= data_temp[7]; //传送器件地址
7'd5 : scl <= 1'b1;
7'd7 : scl <= 1'b0;
7'd8 : sda_out <= data_temp[6];
7'd9 : scl <= 1'b1;
7'd11: scl <= 1'b0;
7'd12: sda_out <= data_temp[5];
7'd13: scl <= 1'b1;
7'd15: scl <= 1'b0;
7'd16: sda_out <= data_temp[4];
7'd17: scl <= 1'b1;
7'd19: scl <= 1'b0;
7'd20: sda_out <= data_temp[3];
7'd21: scl <= 1'b1;
7'd23: scl <= 1'b0;
7'd24: sda_out <= data_temp[2];
7'd25: scl <= 1'b1;
7'd27: scl <= 1'b0;
7'd28: sda_out <= data_temp[1];
7'd29: scl <= 1'b1;
7'd31: scl <= 1'b0;
7'd32: sda_out <= data_temp[0]; //0:写
7'd33: scl <= 1'b1;
7'd35: scl <= 1'b0;
7'd36: sda_out <= 1'bz;
//
7'd37: scl <= 1'b1;
7'd38: begin //从机应答
if(sda == 1'b1) begin //高电平表示未应答
cnt <= 1'b0;
flow_cnt = 4'b0;
end
end
7'd39: begin
scl <= 1'b0;
cnt <= 1'b0;
flow_cnt_ok<=1'b1;
end
default : ;
endcase
end
else if(flow_cnt == 4'd10) begin//加载要读的芯片地址
data_temp<={SLAVE_ADDR,1'b1};
flow_cnt_ok<=1'b1;
scl <= 1'b1;//添加起始位
end
else if(flow_cnt == 4'd12) begin//发送STOP
cnt <= cnt +1'b1 ;
case(cnt)
7'd1 : sda_out <= 1'b0; //开始I2C
7'd2 : scl <= 1'b1;
7'd3 : sda_out <= 1'b1; //传送器件地址
7'd4 : begin
i2c_busy<=1'b0;
i2c_done<=1'b1;
i2c_data_r<=data_r;
scl <= 1'b1;
sda_out <= 1'b1;
flow_cnt_ok<=1'b1;
cnt <= 1'b0;
end
default : ;
endcase
end
else if(flow_cnt == 4'd11) begin//加载要读的芯片地址
cnt <= cnt +1'b1 ;
case(cnt)
7'd1 : sda_out <= 1'bz; //开始I2C
7'd3 : scl <= 1'b0;
7'd5 : scl <= 1'b1;
7'd6 : data_r[7]<=sda;
7'd7 : scl <= 1'b0;
7'd9 : scl <= 1'b1;
7'd10 : data_r[6]<=sda;
7'd11: scl <= 1'b0;
7'd13: scl <= 1'b1;
7'd14 : data_r[5]<=sda;
7'd15: scl <= 1'b0;
7'd17: scl <= 1'b1;
7'd18: data_r[4]<=sda;
7'd19: scl <= 1'b0;
7'd21: scl <= 1'b1;
7'd22: data_r[3]<=sda;
7'd23: scl <= 1'b0;
7'd25: scl <= 1'b1;
7'd26: data_r[2]<=sda;
7'd27: scl <= 1'b0;
7'd29: scl <= 1'b1;
7'd30:data_r[1]<=sda;
7'd31: scl <= 1'b0;
7'd33: scl <= 1'b1;
7'd34: data_r[0]<=sda;
7'd35: scl <= 1'b0;
7'd36: sda_out <= 1'b0;
7'd37: scl <= 1'b1;
7'd39: scl <= 1'b0;
7'd40: sda_out <= 1'b1;
7'd42: begin
cnt <= 1'b0;
flow_cnt_ok<=1'b1;
end
default : ;
endcase
end
else
;
end
endmodule
4.波形