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 #(// 初始時間設置,從高到低為年到秒,各占8bitparameter TIME_INI = 48'h18_01_02_03_04_05)(//system clock 50MHzinput clk , // 時鐘信號input rst_n , // 復位信號//i2c interfaceoutput 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 interfaceinput 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) beginif(!rst_n) beginkey_dy0 <= 1'b1;key_dy1 <= 1'b1;endelse beginkey_dy0 <= key_value;key_dy1 <= key_dy0 ;end
end//按鍵切換
always @(posedge clk or negedge rst_n ) beginif(!rst_n)switch<= 1'b0;else if (neg_sap)switch <= ~switch;
end//從PCF8563T讀出的時間、日期數據
always @(posedge clk or negedge rst_n) beginif(!rst_n) beginsec <= 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;endelse begini2c_exec <= 1'b0;case(flow_cnt)//上電初始化4'd0: beginif(wait_cnt == 21'd1_000_000) beginwait_cnt<= 21'd0;flow_cnt<= flow_cnt + 1'b1;endelsewait_cnt<= wait_cnt + 1'b1;end//寫讀秒4'd1: begini2c_exec <= 1'b1;i2c_addr <= 8'h02;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[7:0];end4'd2: beginif(i2c_done == 1'b1) beginsec <= i2c_data_r[6:0];flow_cnt<= flow_cnt + 1'b1;endend//寫讀分4'd3: begini2c_exec <= 1'b1;i2c_addr <= 8'h03;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[15:8];end4'd4: beginif(i2c_done == 1'b1) beginmin <= i2c_data_r[6:0];flow_cnt<= flow_cnt + 1'b1;endend//寫讀時4'd5: begini2c_exec <= 1'b1;i2c_addr <= 8'h04;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[23:16];end4'd6: beginif(i2c_done == 1'b1) beginhour <= i2c_data_r[5:0];flow_cnt<= flow_cnt + 1'b1;endend//寫讀天4'd7: begini2c_exec <= 1'b1;i2c_addr <= 8'h05;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[31:24];end4'd8: beginif(i2c_done == 1'b1) beginday <= i2c_data_r[5:0];flow_cnt<= flow_cnt + 1'b1;endend//寫讀月4'd9: begini2c_exec <= 1'b1;i2c_addr <= 8'h07;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[39:32];end4'd10: beginif(i2c_done == 1'b1) beginmon <= i2c_data_r[4:0];flow_cnt<= flow_cnt + 1'b1;endend//寫讀年4'd11: begini2c_exec <= 1'b1;i2c_addr <= 8'h08;flow_cnt <= flow_cnt + 1'b1;i2c_data_w<= TIME_INI[47:40];end4'd12: beginif(i2c_done == 1'b1) beginyear <= i2c_data_r;i2c_rh_wl<= 1'b1;flow_cnt <= 4'd0;endend4'd13: begin;enddefault: flow_cnt <= 4'd0;endcaseend
endendmodule
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) beginif(!rst_n) begindri_clk <= 1'b0;clk_cnt <= 10'd0; endelse if(clk_cnt == clk_divide[8:1] - 1'd1) beginclk_cnt <= 10'd0;dri_clk <= ~dri_clk;endelseclk_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) beginif(!rst_n) beginscl <= 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));//狀態技術+1i2c_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) beginif(i2c_begin==1'b1&&i2c_busy==1'b0) beginr_or_w<=i2c_rw;addr_t<=i2c_addr;data_w<=i2c_data_w;i2c_busy<=1'b1;flow_cnt_ok<=1'b1;endelse flow_cnt = 4'b0;endelse if(flow_cnt == 4'd1) begin//加載寫字節data_temp<={SLAVE_ADDR,1'b0};flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd3) begin//加載eeprom地址//data_temp<=addr_t[15:8];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd4) begin//加載eeprom地址//data_temp<=addr_t[15:8];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd5) begin//加載低8位地址data_temp<=addr_t[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd7) begin//加載要寫入的數據data_temp<=data_w[7:0];flow_cnt_ok<=1'b1;endelse if(flow_cnt == 4'd9) begin//發送STOPcnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0; //開始I2C7'd2 : scl <= 1'b1; 7'd3 : sda_out <= 1'b1; //傳送器件地址7'd4 : begini2c_busy<=1'b0;i2c_done<=1'b1;scl <= 1'b1;sda_out <= 1'b1;flow_cnt_ok<=1'b1;cnt <= 1'b0;enddefault : ; endcaseendelse 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;end7'd1 : sda_out <= 1'b0; //開始I2C7'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;enddefault : ; endcaseendelse if(flow_cnt == 4'd10) begin//加載要讀的芯片地址data_temp<={SLAVE_ADDR,1'b1};flow_cnt_ok<=1'b1;scl <= 1'b1;//添加起始位endelse if(flow_cnt == 4'd12) begin//發送STOPcnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'b0; //開始I2C7'd2 : scl <= 1'b1; 7'd3 : sda_out <= 1'b1; //傳送器件地址7'd4 : begini2c_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;enddefault : ; endcaseendelse if(flow_cnt == 4'd11) begin//加載要讀的芯片地址cnt <= cnt +1'b1 ;case(cnt)7'd1 : sda_out <= 1'bz; //開始I2C7'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;enddefault : ; endcase endelse ;
end
endmodule
4.波形