RTC实时时钟显示

发布时间 2024-01-10 18:54:29作者: carpe--diem

PCF8563 是飞利浦公司推出的一款工业级内含 I2C 总线接口功能的具有极低功耗的多功能时钟/日历芯片。 PCF8563 的多种报警功能、定时器功能、时钟输出功能以及中断输出功能, 能完成各种复杂的定时服务。
PCF8563 内有 16(00~0F) 个 8 位寄存器:一个可自动增量的地址寄存器,一个内置 32.768KHz 的振荡器(带有一个内部集成的电容)一个分频器(用于给实时时钟 RTC 提供源时钟)一个可编程时钟输出,一个定时器,一个报警器,一个掉电检测器和一个 I2C 总线接口。
1、 前两个寄存器(内存地址 00H 和 01H) : 用于控制寄存器和状态寄存器。
2、内存地址 02H~08H 寄存器: 用于时钟计数器(秒到年计数器) 。
3、地址 09H~0CH 寄存器: 用于报警寄存器(定义报警条件) 。
4、地址 0DH 寄存器:用于控制 CLKOUT 管脚的输出频率。
5、地址 0EH 和 0FH 寄存器: 分别用于定时器控制和定时器。
秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器都以二进制编码的十进制(BCD)格式编码, 星期和星期报警寄存器不以 BCD 格式编码。当一个 RTC 寄存器被读时,所有计数器的内容被锁存,因此,在传送条件下,可以防止对时钟日历芯片的错读

秒寄存器(02h)

image.png

分寄存器(03h)

image.png

时寄存器(04h)

image.png

日寄存器(05h)

image.png

月寄存器(07h)

image.png

年寄存器(08h)

image.png
年数据为当前年份的末两位。

PCF8563 通信接口

image.png
上电到 SCL 下降沿(起始信号)需等待 8ms 的时间,也就是我们上电之后需先等待 8ms 后再开始对 PCF8563 芯片进行配置。

实验目标

通过数码管显示实时时间,并通过按键切换显示年月日。

程序框图

image.png

状态跳转图

image.png
image.png

代码

pcf8563_ctrl RTC时钟芯片控制
输入时间19_09_07_16_00_00,需要循环读取数据

`timescale 1ns / 1ns
module pcf8563_ctrl 
#(
    parameter TIME_INIT = 48'h19_09_07_16_00_00
 )
 (
    input wire          sys_clk ,   //系统时钟,频率 50MHz
    input wire          i2c_clk ,   //i2c 驱动时钟
    input wire          sys_rst_n , //复位信号,低有效
    input wire          i2c_end ,   //i2c 一次读/写操作完成
    input wire [7:0]    rd_data ,   //输出 i2c 设备读取数据
    input wire          key_flag ,  //按键消抖后标志信号

    output reg          wr_en ,     //输出写使能信号
    output reg          rd_en ,     //输出读使能信号
    output reg          i2c_start , //输出 i2c 触发信号
    output reg [15:0]   byte_addr , //输出 i2c 字节地址
    output reg [7:0]    wr_data ,   //输出 i2c 设备数据
    output reg [23:0]   data_out    //输出到数码管显示的 bcd 码数据
);
//reg define
    reg [3:0]   state ;     //状态机状态
    reg [12:0]  cnt_wait ;  //等待计数器
    reg [7:0]   year ;      //年数据
    reg [7:0]   month ;     //月数据
    reg [7:0]   day ;       //日数据
    reg [7:0]   hour ;      //小时数据
    reg [7:0]   minute ;    //年数据
    reg [7:0]   second ;    //秒数据
    reg         data_flag ; //数据切换标志信号

//parameter define
    localparam S_WAIT = 4'd1 , //上电等待状态
    INIT_SEC    = 4'd2 , //初始化秒
    INIT_MIN    = 4'd3 , //初始化分
    INIT_HOUR   = 4'd4 , //初始化小时
    INIT_DAY    = 4'd5 , //初始化日
    INIT_MON    = 4'd6 , //初始化月
    INIT_YEAR   = 4'd7 , //初始化年
    RD_SEC      = 4'd8 , //读秒
    RD_MIN      = 4'd9 , //读分
    RD_HOUR     = 4'd10 , //读小时
    RD_DAY      = 4'd11 , //读日
    RD_MON      = 4'd12 , //读月
    RD_YEAR     = 4'd13 ; //读年  

    localparam CNT_WAIT_8MS = 8000 ; //8ms 时间计数值
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//状态计时
    always @(posedge i2c_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
            cnt_wait <= 13'd0;
        else 
            case (state)
                    S_WAIT: 
                        if(cnt_wait == CNT_WAIT_8MS)
                            cnt_wait <= 13'd0;
                        else
                            cnt_wait <= cnt_wait + 1'b1;
                    INIT_SEC,INIT_MIN,INIT_HOUR,INIT_DAY,INIT_MON,
                    INIT_YEAR,RD_SEC,RD_MIN,RD_HOUR,RD_DAY,RD_MON,RD_YEAR:
                        if(i2c_end == 1'b1)
                                cnt_wait <= 13'd0;
                            else
                                cnt_wait <= cnt_wait + 1'b1;
                    default: cnt_wait <= 13'd0;
                endcase      
    end
//产生数据切换的标志信号
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            data_flag <= 1'b0;
        else if(key_flag == 1'b1)
            data_flag <= ~data_flag;
        else
            data_flag <= data_flag;
//data_flag 为 0 时显示时分秒,为 1 时显示年月日
    always@(posedge sys_clk or negedge sys_rst_n)
        if(!sys_rst_n )
            data_out <= 24'd0;
        else if(data_flag == 1'b0)
            data_out <= {hour,minute,second};
        else
            data_out <= {year,month,day};

//状态机跳转
    always @(posedge i2c_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)
            state <= 4'd0;
        else 
            case (state)
                S_WAIT: //上电等待8ms
                    if(cnt_wait == CNT_WAIT_8MS)
                        state <= INIT_SEC;
                    else
                        state <= state;
                INIT_SEC://初始化秒状态
                    if(i2c_end == 1'b1)
                        state <= INIT_MIN;
                    else
                        state <= state;
                INIT_MIN://初始化分状态
                    if(i2c_end == 1'b1)
                        state <= INIT_HOUR;
                    else
                        state <= state;
                INIT_HOUR://初始化时状态
                    if(i2c_end == 1'b1)
                        state <= INIT_DAY;
                    else
                        state <= state;
                INIT_DAY://初始化天状态
                    if(i2c_end == 1'b1)
                        state <= INIT_MON;
                    else
                        state <= state;
                INIT_MON://初始化月状态
                    if(i2c_end == 1'b1)
                        state <= INIT_YEAR;
                    else
                        state <= state;
                INIT_YEAR://初始化年状态
                    if(i2c_end == 1'b1)
                        state <= RD_SEC;
                    else
                        state <= state;
                RD_SEC://读秒
                    if(i2c_end == 1'b1)
                        state <= RD_MIN;
                    else
                        state <= state;
                RD_MIN://读分
                    if(i2c_end == 1'b1)
                        state <= RD_HOUR;
                    else
                        state <= state;
                RD_HOUR://读时
                    if(i2c_end == 1'b1)
                        state <= RD_DAY;
                    else
                        state <= state;
                RD_DAY://读天
                    if(i2c_end == 1'b1)
                        state <= RD_MON;
                    else
                        state <= state;
                RD_MON://读月
                    if(i2c_end == 1'b1)
                        state <= RD_YEAR;
                    else
                        state <= state;
                RD_YEAR://读年
                    if(i2c_end == 1'b1)
                        state <= RD_SEC;
                    else
                        state <= state;
                default: state <= S_WAIT;
            endcase
    end
//各状态下的信号赋值
    always @(posedge i2c_clk or negedge sys_rst_n) begin
        if(!sys_rst_n)begin
            wr_en       <= 1'b0 ;
            rd_en       <= 1'b0 ;
            i2c_start   <= 1'b0 ;
            byte_addr   <= 16'd0 ;
            wr_data     <= 8'd0 ;
            year        <= 8'd0 ;
            month       <= 8'd0 ;
            day         <= 8'd0 ;
            hour        <= 8'd0 ;
            minute      <= 8'd0 ;
            second      <= 8'd0 ;
        end
        else 
            case (state)
                S_WAIT:begin //上电等待状态
                    wr_en       <= 1'b0;
                    rd_en       <= 1'b0;
                    i2c_start   <= 1'b0;
                    byte_addr   <= 16'h0 ;
                    wr_data     <= 8'h00 ;
                end
                INIT_SEC: //初始化秒
                    if(cnt_wait == 13'b1)begin
                        wr_en       <= 1'b1;
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h02 ;
                        wr_data     <= TIME_INIT[7:0]; 
                    end
                    else begin
                        wr_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h02 ;
                        wr_data     <= TIME_INIT[7:0]; 
                    end
                INIT_MIN: //初始化分
                    if(cnt_wait == 13'b1)begin
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h03;
                        wr_data     <= TIME_INIT[15:8]; 
                    end
                    else begin
                        
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h03 ;
                        wr_data     <= TIME_INIT[15:8];
                    end
                INIT_HOUR: //初始化时
                    if(cnt_wait == 13'b1)begin
                        
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h04;
                        wr_data     <= TIME_INIT[23:16]; 
                    end
                    else begin
                        
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h04;
                        wr_data     <= TIME_INIT[23:16]; 
                    end
                INIT_DAY: //初始化天
                    if(cnt_wait == 13'b1)begin
                        
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h05;
                        wr_data     <= TIME_INIT[31:24]; 
                    end
                    else begin
                        
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h05;
                        wr_data     <= TIME_INIT[31:24];
                    end
                INIT_MON: //初始化月
                    if(cnt_wait == 13'b1)begin
                       
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h07;
                        wr_data     <= TIME_INIT[39:32]; 
                    end
                    else begin
                       
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h07;
                        wr_data     <= TIME_INIT[39:32]; 
                    end
                INIT_YEAR: //初始化年
                    if(cnt_wait == 13'b1)begin
                        
                        i2c_start   <= 1'b1;
                        byte_addr   <= 16'h08;
                        wr_data     <= TIME_INIT[47:40]; 
                    end
                    else begin
                       
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h08;
                        wr_data     <= TIME_INIT[47:40]; 
                    end
                RD_SEC: //读秒
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        second <= rd_data[6:0];
                    else begin
                        wr_en       <= 1'b0;
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h02;
                        wr_data <= 8'd0 ;
                    end
                RD_MIN: //读分
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        minute <= rd_data[6:0];
                    else begin
                        
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h03;
                    end
                RD_HOUR: //读时
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        hour <= rd_data[5:0];
                    else begin
                        
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h04;
                    end
                RD_DAY: //读天
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        day <= rd_data[5:0];
                    else begin
                       
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h05;
                    end
                RD_MON: //读月
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        month <= rd_data[4:0];
                    else begin
                       
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h7;
                    end
                RD_YEAR: //读年
                    if(cnt_wait == 13'b1)
                        i2c_start   <= 1'b1;
                    else if(i2c_end == 1'b1)
                        year <= rd_data[7:0];
                    else begin
                        
                        rd_en       <= 1'b1;
                        i2c_start   <= 1'b0;
                        byte_addr   <= 16'h08;
                    end
                default: begin
                    wr_en       <= 1'b0 ;
                    rd_en       <= 1'b0 ;
                    i2c_start   <= 1'b0 ;
                    byte_addr   <= 16'd0 ;
                    wr_data     <= 8'd0 ;
                    year        <= 8'd0 ;
                    month       <= 8'd0 ;
                    day         <= 8'd0 ;
                    hour        <= 8'd0 ;
                    minute      <= 8'd0 ;
                    second      <= 8'd0 ;
                end
            endcase
    end
endmodule

i2c协议控制代码 驱动RTC时钟芯片

`timescale  1ns/1ns

module  i2c_ctrl
#(
    parameter   DEVICE_ADDR     =   7'b1010_000     ,   //i2c设备地址
    parameter   SYS_CLK_FREQ    =   26'd50_000_000  ,   //输入系统时钟频率
    parameter   SCL_FREQ        =   18'd250_000         //i2c设备scl时钟频率
)
(
    input   wire            sys_clk     ,   //输入系统时钟,50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            wr_en       ,   //输入写使能信号
    input   wire            rd_en       ,   //输入读使能信号
    input   wire            i2c_start   ,   //输入i2c触发信号
    input   wire            addr_num    ,   //输入i2c字节地址字节数
    input   wire    [15:0]  byte_addr   ,   //输入i2c字节地址
    input   wire    [7:0]   wr_data     ,   //输入i2c设备数据

    output  reg             i2c_clk     ,   //i2c驱动时钟
    output  reg             i2c_end     ,   //i2c一次读/写操作完成
    output  reg     [7:0]   rd_data     ,   //输出i2c设备读取数据
    output  reg             i2c_scl     ,   //输出至i2c设备的串行时钟信号scl
    inout   wire            i2c_sda         //输出至i2c设备的串行数据信号sda
);

//************************************************************************//
//******************** Parameter and Internal Signal *********************//
//************************************************************************//
// parameter define
parameter   CNT_CLK_MAX     =   (SYS_CLK_FREQ/SCL_FREQ) >> 2'd3   ;   //cnt_clk计数器计数最大值

parameter   CNT_START_MAX   =   8'd100; //cnt_start计数器计数最大值

parameter   IDLE            =   4'd00,  //初始状态
            START_1         =   4'd01,  //开始状态1
            SEND_D_ADDR     =   4'd02,  //设备地址写入状态 + 控制写
            ACK_1           =   4'd03,  //应答状态1
            SEND_B_ADDR_H   =   4'd04,  //字节地址高八位写入状态
            ACK_2           =   4'd05,  //应答状态2
            SEND_B_ADDR_L   =   4'd06,  //字节地址低八位写入状态
            ACK_3           =   4'd07,  //应答状态3
            WR_DATA         =   4'd08,  //写数据状态
            ACK_4           =   4'd09,  //应答状态4
            START_2         =   4'd10,  //开始状态2
            SEND_RD_ADDR    =   4'd11,  //设备地址写入状态 + 控制读
            ACK_5           =   4'd12,  //应答状态5
            RD_DATA         =   4'd13,  //读数据状态
            N_ACK           =   4'd14,  //非应答状态
            STOP            =   4'd15;  //结束状态

// wire  define
wire            sda_in          ;   //sda输入数据寄存
wire            sda_en          ;   //sda数据写入使能信号

// reg   define
reg     [7:0]   cnt_clk         ;   //系统时钟计数器,控制生成clk_i2c时钟信号
reg     [3:0]   state           ;   //状态机状态
reg             cnt_i2c_clk_en  ;   //cnt_i2c_clk计数器使能信号
reg     [1:0]   cnt_i2c_clk     ;   //clk_i2c时钟计数器,控制生成cnt_bit信号
reg     [2:0]   cnt_bit         ;   //sda比特计数器
reg             ack             ;   //应答信号
reg             i2c_sda_reg     ;   //sda数据缓存
reg     [7:0]   rd_data_reg     ;   //自i2c设备读出数据

//************************************************************************//
//******************************* Main Code ******************************//
//************************************************************************//
// cnt_clk:系统时钟计数器,控制生成clk_i2c时钟信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk <=  8'd0;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        cnt_clk <=  8'd0;
    else
        cnt_clk <=  cnt_clk + 1'b1;

// i2c_clk:i2c驱动时钟
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_clk <=  1'b1;
    else    if(cnt_clk == CNT_CLK_MAX - 1'b1)
        i2c_clk <=  ~i2c_clk;

// cnt_i2c_clk_en:cnt_i2c_clk计数器使能信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk_en  <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        cnt_i2c_clk_en  <=  1'b0;
    else    if(i2c_start == 1'b1)
        cnt_i2c_clk_en  <=  1'b1;

// cnt_i2c_clk:i2c_clk时钟计数器,控制生成cnt_bit信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_i2c_clk <=  2'd0;
    else    if(cnt_i2c_clk_en == 1'b1)
        cnt_i2c_clk <=  cnt_i2c_clk + 1'b1;

// cnt_bit:sda比特计数器
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  3'd0;
    else    if((state == IDLE) || (state == START_1) || (state == START_2)
                || (state == ACK_1) || (state == ACK_2) || (state == ACK_3)
                || (state == ACK_4) || (state == ACK_5) || (state == N_ACK))
        cnt_bit <=  3'd0;
    else    if((cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        cnt_bit <=  3'd0;
    else    if((cnt_i2c_clk == 2'd3) && (state != IDLE))
        cnt_bit <=  cnt_bit + 1'b1;

// state:状态机状态跳转
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        state   <=  IDLE;
    else    case(state)
        IDLE:
            if(i2c_start == 1'b1)
                state   <=  START_1;
            else
                state   <=  state;
        START_1:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_D_ADDR;
            else
                state   <=  state;
        SEND_D_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_1;
            else
                state   <=  state;
        ACK_1:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(addr_num == 1'b1)
                        state   <=  SEND_B_ADDR_H;
                    else
                        state   <=  SEND_B_ADDR_L;
                end
             else
                state   <=  state;
        SEND_B_ADDR_H:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_2;
            else
                state   <=  state;
        ACK_2:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  SEND_B_ADDR_L;
            else
                state   <=  state;
        SEND_B_ADDR_L:
            if((cnt_bit == 3'd7) && (cnt_i2c_clk == 3))
                state   <=  ACK_3;
            else
                state   <=  state;
        ACK_3:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                begin
                    if(wr_en == 1'b1)
                        state   <=  WR_DATA;
                    else    if(rd_en == 1'b1)
                        state   <=  START_2;
                    else
                        state   <=  state;
                end
             else
                state   <=  state;
        WR_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_4;
            else
                state   <=  state;
        ACK_4:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  STOP;
            else
                state   <=  state;
        START_2:
            if(cnt_i2c_clk == 3)
                state   <=  SEND_RD_ADDR;
            else
                state   <=  state;
        SEND_RD_ADDR:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  ACK_5;
            else
                state   <=  state;
        ACK_5:
            if((cnt_i2c_clk == 3) && (ack == 1'b0))
                state   <=  RD_DATA;
            else
                state   <=  state;
        RD_DATA:
            if((cnt_bit == 3'd7) &&(cnt_i2c_clk == 3))
                state   <=  N_ACK;
            else
                state   <=  state;
        N_ACK:
            if(cnt_i2c_clk == 3)
                state   <=  STOP;
            else
                state   <=  state;
        STOP:
            if((cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
                state   <=  IDLE;
            else
                state   <=  state;
        default:    state   <=  IDLE;
    endcase

// ack:应答信号
always@(*)
    case    (state)
        IDLE,START_1,SEND_D_ADDR,SEND_B_ADDR_H,SEND_B_ADDR_L,
        WR_DATA,START_2,SEND_RD_ADDR,RD_DATA,N_ACK:
            ack <=  1'b1;
        ACK_1,ACK_2,ACK_3,ACK_4,ACK_5:
            if(cnt_i2c_clk == 2'd0)
                ack <=  sda_in ;
            else
                ack <=  ack;
        default:    ack <=  1'b1;
    endcase

// i2c_scl:输出至i2c设备的串行时钟信号scl
always@(*)
    case    (state)
        IDLE:
            i2c_scl <=  1'b1;
        START_1:
            if(cnt_i2c_clk == 2'd3)
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        SEND_D_ADDR,ACK_1,SEND_B_ADDR_H,ACK_2,SEND_B_ADDR_L,
        ACK_3,WR_DATA,ACK_4,START_2,SEND_RD_ADDR,ACK_5,RD_DATA,N_ACK:
            if((cnt_i2c_clk == 2'd1) || (cnt_i2c_clk == 2'd2))
                i2c_scl <=  1'b1;
            else
                i2c_scl <=  1'b0;
        STOP:
            if((cnt_bit == 3'd0) &&(cnt_i2c_clk == 2'd0))
                i2c_scl <=  1'b0;
            else
                i2c_scl <=  1'b1;
        default:    i2c_scl <=  1'b1;
    endcase

// i2c_sda_reg:sda数据缓存
always@(*)
    case    (state)
        IDLE:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  8'd0;
            end
        START_1:
            if(cnt_i2c_clk <= 2'd0)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_D_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b0;
        ACK_1:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_H:
            i2c_sda_reg <=  byte_addr[15 - cnt_bit];
        ACK_2:
            i2c_sda_reg <=  1'b1;
        SEND_B_ADDR_L:
            i2c_sda_reg <=  byte_addr[7 - cnt_bit];
        ACK_3:
            i2c_sda_reg <=  1'b1;
        WR_DATA:
            i2c_sda_reg <=  wr_data[7 - cnt_bit];
        ACK_4:
            i2c_sda_reg <=  1'b1;
        START_2:
            if(cnt_i2c_clk <= 2'd1)
                i2c_sda_reg <=  1'b1;
            else
                i2c_sda_reg <=  1'b0;
        SEND_RD_ADDR:
            if(cnt_bit <= 3'd6)
                i2c_sda_reg <=  DEVICE_ADDR[6 - cnt_bit];
            else
                i2c_sda_reg <=  1'b1;
        ACK_5:
            i2c_sda_reg <=  1'b1;
        RD_DATA:
            if(cnt_i2c_clk  == 2'd2)
                rd_data_reg[7 - cnt_bit]    <=  sda_in;
            else
                rd_data_reg <=  rd_data_reg;
        N_ACK:
            i2c_sda_reg <=  1'b1;
        STOP:
            if((cnt_bit == 3'd0) && (cnt_i2c_clk < 2'd3))
                i2c_sda_reg <=  1'b0;
            else
                i2c_sda_reg <=  1'b1;
        default:
            begin
                i2c_sda_reg <=  1'b1;
                rd_data_reg <=  rd_data_reg;
            end
    endcase

// rd_data:自i2c设备读出数据
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        rd_data <=  8'd0;
    else    if((state == RD_DATA) && (cnt_bit == 3'd7) && (cnt_i2c_clk == 2'd3))
        rd_data <=  rd_data_reg;

// i2c_end:一次读/写结束信号
always@(posedge i2c_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        i2c_end <=  1'b0;
    else    if((state == STOP) && (cnt_bit == 3'd3) &&(cnt_i2c_clk == 3))
        i2c_end <=  1'b1;
    else
        i2c_end <=  1'b0;

// sda_in:sda输入数据寄存
assign  sda_in = i2c_sda;
// sda_en:sda数据写入使能信号
assign  sda_en = ((state == RD_DATA) || (state == ACK_1) || (state == ACK_2)
                    || (state == ACK_3) || (state == ACK_4) || (state == ACK_5))
                    ? 1'b0 : 1'b1;
// i2c_sda:输出至i2c设备的串行数据信号sda
assign  i2c_sda = (sda_en == 1'b1) ? i2c_sda_reg : 1'bz;

endmodule

按键滤波代码实现

`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/12/25 18:59:24
// Design Name: 
// Module Name: key_filter
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module key_filter
#(parameter CNT_MAX = 20'd999_999
)
(
input wire sys_clk,
input wire sys_rst_n,
input wire key_in,

output reg key_flag
    );
    reg [19:0] cnt;
//cnt
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            cnt <= 20'b0;
        else if (key_in == 1'b1)
            cnt <= 20'b0;
        else if (cnt == CNT_MAX && key_in == 1'b0)
            cnt <= cnt ;
        else
            cnt <= cnt + 1'b1;
    end
//key_flag
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            key_flag <= 1'b0;
        else if (cnt == CNT_MAX - 1'b1)
            key_flag <= 1'b1;
        else
            key_flag <= 1'b0;
    end
endmodule

将RTC时钟芯片输出的数据显示

`timescale 1ns / 1ns
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/10/12 17:33:11
// Design Name: 
// Module Name: display
// Project Name:
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module seg_display(
    input   wire            sys_clk,
    input   wire            sys_rst_n,
    input   wire [23:0]     data,
    input   wire [5:0]      point,
    input   wire            sign,
    input   wire            seg_en,
    
    output  reg  [5:0]      seg_sel,
    output  reg  [7:0]      seg_led
    );

//parameter define
    parameter CNT_MAX = 16'd49_999;

//wire define
    wire [3:0] unit;
    wire [3:0] ten;
    wire [3:0] hun;
    wire [3:0] tho;
    wire [3:0] t_tho;
    wire [3:0] h_hun;

//reg define
    reg [15:0] cnt_1ms;
    reg        flag_1ms;
    reg [2:0]  cnt_sel;
    reg [5:0]  sel_reg;
    reg [3:0]  data_disp;
    reg        dot_disp;

//cnt_1ms
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            cnt_1ms <= 16'd0;
        else if(cnt_1ms == CNT_MAX)
            cnt_1ms <= 16'd0;
        else 
            cnt_1ms <= cnt_1ms + 1'b1;
    end

//flag_1ms
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            flag_1ms <= 1'b0;
        else if(cnt_1ms == CNT_MAX - 1'b1)
            flag_1ms <= 1'b1;
        else    
            flag_1ms <= 1'b0;
    end

//cnt_sel
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            cnt_sel <= 3'd0;
        else if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
            cnt_sel <= 3'd0;
        else if(flag_1ms == 1'b1)
            cnt_sel <= cnt_sel + 1'b1;
        else
            cnt_sel <= cnt_sel ;
    end

//sel_reg
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            sel_reg <= 6'b111_111;
        else if( flag_1ms == 1'b1)
            case (cnt_sel)
                3'd0: sel_reg <= 6'b111110;
                3'd1: sel_reg <= 6'b111101;
                3'd2: sel_reg <= 6'b111011;
                3'd3: sel_reg <= 6'b110111;
                3'd4: sel_reg <= 6'b101111;
                3'd5: sel_reg <= 6'b011111;
                default: sel_reg <= 6'b111_111;
            endcase
        else
            sel_reg <= sel_reg;
    end


//data_disp
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            data_disp <= 4'b0;
        else if((flag_1ms == 1'b1) && (seg_en == 1'b1))
            case (cnt_sel)
                3'd0 :  data_disp <= data[3:0] ;
                3'd1 :  data_disp <= data[7:4] ;
                3'd2 :  data_disp <= data[11:8] ;
                3'd3 :  data_disp <= data[15:12] ;
                3'd4 :  data_disp <= data[19:16] ;
                3'd5 :  data_disp <= data[23:20] ; 
                default: data_disp <= 4'b0 ; 
            endcase
        else
            data_disp <= data_disp;
    end

//dot_disp
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            dot_disp <= 1'b1;
        else if(flag_1ms == 1'b1)
            dot_disp <= ~point[cnt_sel];
        else
            dot_disp <= dot_disp;
    end

//seg_sel
    always @(posedge sys_clk or negedge sys_rst_n) begin
        if(sys_rst_n == 1'b0)
            seg_sel <= 6'b000_000;
        else 
            seg_sel <= sel_reg;
    end
//seg_led
    always@(posedge sys_clk or negedge sys_rst_n)
        if(sys_rst_n == 1'b0)
            seg_led <= 8'b1111_1111;
        else
            case(data_disp)
                4'd0 : seg_led <= {dot_disp,7'b100_0000}; //显示数字 0
                4'd1 : seg_led <= {dot_disp,7'b111_1001}; //显示数字 1
                4'd2 : seg_led <= {dot_disp,7'b010_0100}; //显示数字 2
                4'd3 : seg_led <= {dot_disp,7'b011_0000}; //显示数字 3
                4'd4 : seg_led <= {dot_disp,7'b001_1001}; //显示数字 4
                4'd5 : seg_led <= {dot_disp,7'b001_0010}; //显示数字 5
                4'd6 : seg_led <= {dot_disp,7'b000_0010}; //显示数字 6
                4'd7 : seg_led <= {dot_disp,7'b111_1000}; //显示数字 7
                4'd8 : seg_led <= {dot_disp,7'b000_0000}; //显示数字 8
                4'd9 : seg_led <= {dot_disp,7'b001_0000}; //显示数字 9
                default:seg_led <= 8'b1100_0000;
            endcase

endmodule

顶层模块

module rtc (
    input  wire      sys_clk,
    input  wire      sys_rst_n,
    input  wire      key_in,

    output  wire [7:0]  seg,
    output  wire [5:0]  sel,
    inout   wire        sda,
    output  wire        scl
);
    parameter TIME_INIT = 48'h23_12_26_16_00_00;

//wire define

    wire i2c_clk ; //i2c 驱动时钟
    wire i2c_end ; //i2c 一次读/写操作完成
    wire [7:0] rd_data ; //输出 i2c 设备读取数据
    wire key_flag ; //按键消抖后标志信号
    wire wr_en ; //写使能信号
    wire rd_en ; //读使能信号
    wire i2c_start ; //i2c 触发信号
    wire [15:0] byte_addr ; //i2c 字节地址
    wire [7:0] wr_data ; //写入 i2c 设备数据
    wire [23:0] data_out ; //数码管显示数据  


pcf8563_ctrl 
#(
    .TIME_INIT  (TIME_INIT)
)
pcf8563_ctrl_inst
(

    .sys_clk        (sys_clk),    
    .i2c_clk        (i2c_clk),
    .sys_rst_n      (sys_rst_n),
    .i2c_end        (i2c_end),
    .rd_data        (rd_data),
    .key_flag       (key_flag),
    .wr_en          (wr_en),
    .rd_en          (rd_en),
    .i2c_start      (i2c_start),
    .byte_addr      (byte_addr),
    .wr_data        (wr_data),
    .data_out       (data_out)
);
seg_display seg_display_inst
(
    .sys_clk        (sys_clk),    
    .sys_rst_n      (sys_rst_n),
    .data           (data_out),
    .point          (6'b000000),
    .seg_en         (1'b1),
    .sign           (1'b0),
    .seg_sel        (sel),
    .seg_led        (seg)
);
i2c_ctrl
#(
    .DEVICE_ADDR    (7'b101_0001      ), //i2c设备器件地址
    .SYS_CLK_FREQ   (26'd50_000_000  ), //i2c_ctrl模块系统时钟频率
    .SCL_FREQ       (18'd250_000     )  //i2c的SCL时钟频率
)
i2c_ctrl_inst
(
    .sys_clk     (sys_clk   ),   //输入系统时钟,50MHz
    .sys_rst_n   (sys_rst_n ),   //输入复位信号,低电平有效
    .wr_en       (wr_en     ),   //输入写使能信号
    .rd_en       (rd_en     ),   //输入读使能信号
    .i2c_start   (i2c_start ),   //输入i2c触发信号
    .addr_num    (1'b0      ),   //输入i2c字节地址字节数
    .byte_addr   (byte_addr ),   //输入i2c字节地址
    .wr_data     (wr_data   ),   //输入i2c设备数据

    .rd_data     (rd_data   ),   //输出i2c设备读取数据
    .i2c_end     (i2c_end   ),   //i2c一次读/写操作完成
    .i2c_clk     (i2c_clk   ),   //i2c驱动时钟
    .i2c_scl     (scl       ),   //输出至i2c设备的串行时钟信号scl
    .i2c_sda     (sda       )    //输出至i2c设备的串行数据信号sda
);
key_filter  key_filter_inst
(
    .sys_clk    (sys_clk    ),  //系统时钟50Mhz
    .sys_rst_n  (sys_rst_n  ),  //全局复位
    .key_in     (key_in     ),  //按键输入信号

    .key_flag   (key_flag       )   //key_flag为1时表示按键有效,0表示按键无效
);    
endmodule