11-verilog的RTC驱动代码

发布于:2025-08-17 ⋅ 阅读:(19) ⋅ 点赞:(0)

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.波形

在这里插入图片描述
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到