RTC时钟实验

RTC时钟实验

本节的实验任务是通过领航者Zynq开发板上的PCF8563实时时钟芯片,在 RGB LCD 液晶屏上来显示时间。

PCF8563 简介

​ PCF8563 是 PHILIPS 公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示:

PCF8563 有 16 个可寻址的 8 位寄存器, 但不是所有位都有用到。前两个寄存器(内存地址 00H、01H)用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址 02H~08H 用作 TIME 计时器(秒~年计时器); 地址 09H~0CH 用于报警(ALARM)寄存器(定义报警条件); 地址 0DH 控制 CLKOUT 管脚的输出频率;地址 0EH 和 0FH 分别用于定时器控制寄存器和定时器寄存器。 PCF8563 通过 I2C 接口与 Zynq 进行通信

具体的BIT描述见PCF8563数据手册-

秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器中的数据编码格式为 BCD用的是8421BCD码 ,只有星期和星期报警寄存器中的数据不以 BCD 格式编码。 BCD 码(Binary-Coded Decimal)是一种二进制的数字编码形式,用四个二进制位来表示一位十进制数(0~9) ,能够使二进制和十进制之间的转换得以快捷的进行 。

硬件设计

领航者开发板上 PCF8563 接口部分的原理图如下图所示

​ PCF8563 作为 I2C 接口的从器件与 EEPROM 等模块统一挂接在领航者开发板上的 IIC 总线上。OSCI、 OSCO 与外部 32.768KHz 的晶振相连, 为芯片提供驱动时钟; SCL 和 SDA 分别是 I2C 总线的串行时钟接口和串行数据接口。

程序设计

根据实验任务,我们可以大致规划出系统的控制流程: ZYNQ 首先通过 I2C 总线向 PCF8563 写入初始时间值,然后不断地读取时间数据,并将读到的时间数据显示到 LCD 上。 由此画出系统的功能框图如下所示:

​ 由系统框图可知, 顶层模块(rtc_lcd)例化了以下三个模块,分别是 IIC 驱动模块(iic_dri) 、PCF8563 控制模块(pcf8563_ctrl)和 LCD 字符显示模块(lcd_disp_char) 。 其中 LCD 字符显示模块例化了读取 ID 模块(rd_id)、时钟分频模块(clk_div) 、 LCD 显示模块(lcd_display)以及 LCD 驱动模块(lcd_driver)

各模块端口及信号连接如图 23.4.2 所示:

PCF8563 实时时钟控制模块(pcf8563_ctrl)通过与 IIC 驱动模块(iic_dri)进行通信来实现对 PCF8563 实时时钟数据的读取; PCF8563 实时时钟控制模块(pcf8563_ctrl)再将从 IIC 读取的时间数据送给 LCD 字符显示模块(lcd_disp_char),以进行显示。

顶层模块

module rtc_lcd(
    input                sys_clk,     //系统时钟
    input                sys_rst_n,   //系统复位

    //RGB LCD接口
    output               lcd_de,      //LCD 数据使能信号
    output               lcd_hs,      //LCD 行同步信号
    output               lcd_vs,      //LCD 场同步信号
    output               lcd_bl,      //LCD 背光控制信号
    output               lcd_clk,     //LCD 像素时钟
    inout        [23:0]  lcd_rgb,     //LCD RGB888颜色数据
    
    //RTC实时时钟
    output               iic_scl,     //RTC的时钟线scl
    inout                iic_sda      //RTC的数据线sda    
    );                                                      

//parameter define
parameter    SLAVE_ADDR = 7'b101_0001   ; //器件地址(SLAVE_ADDR)
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    TIME_INIT  = 48'h19_01_01_09_30_00;//初始时间

//wire define
wire          dri_clk   ;   //I2C操作时钟
wire          i2c_exec  ;   //I2C触发控制
wire  [15:0]  i2c_addr  ;   //I2C操作地址
wire  [ 7:0]  i2c_data_w;   //I2C写入的数据
wire          i2c_done  ;   //I2C操作结束标志
wire          i2c_ack   ;   //I2C应答标志 0:应答 1:未应答
wire          i2c_rh_wl ;   //I2C读写控制
wire  [ 7:0]  i2c_data_r;   //I2C读出的数据

wire    [7:0]  sec      ;   //秒
wire    [7:0]  min      ;   //分
wire    [7:0]  hour     ;   //时
wire    [7:0]  day      ;   //日
wire    [7:0]  mon      ;   //月
wire    [7:0]  year     ;   //年

//*****************************************************
//**                    main code
//*****************************************************

//i2c驱动模块
i2c_dri #(
    .SLAVE_ADDR  (SLAVE_ADDR),  //EEPROM从机地址
    .CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率
    .I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
) u_i2c_dri(
    .clk         (sys_clk   ),  
    .rst_n       (sys_rst_n ),  
    //i2c interface
    .i2c_exec    (i2c_exec  ), 
    .bit_ctrl    (BIT_CTRL  ), 
    .i2c_rh_wl   (i2c_rh_wl ), 
    .i2c_addr    (i2c_addr  ), 
    .i2c_data_w  (i2c_data_w), 
    .i2c_data_r  (i2c_data_r), 
    .i2c_done    (i2c_done  ), 
    .i2c_ack     (i2c_ack   ), 
    .scl         (iic_scl   ), 
    .sda         (iic_sda   ), 
    //user interface
    .dri_clk     (dri_clk   )  
);

//PCF8563控制模块
pcf8563_ctrl #(
    .TIME_INIT (TIME_INIT)
   )u_pcf8563_ctrl(
    .clk         (dri_clk   ),
    .rst_n       (sys_rst_n ),
    //IIC
    .i2c_rh_wl   (i2c_rh_wl ),
    .i2c_exec    (i2c_exec  ),
    .i2c_addr    (i2c_addr  ),
    .i2c_data_w  (i2c_data_w),
    .i2c_data_r  (i2c_data_r),
    .i2c_done    (i2c_done  ),
    //时间和日期
    .sec         (sec       ),
    .min         (min       ),
    .hour        (hour      ),
    .day         (day       ),
    .mon         (mon       ),
    .year        (year      )
    );

//LCD字符显示模块
lcd_disp_char u_lcd_disp_char(
    .sys_clk     (sys_clk   ),
    .sys_rst_n   (sys_rst_n ),
    //时间和日期
    .sec         (sec       ),
    .min         (min       ),
    .hour        (hour      ),
    .day         (day       ),
    .mon         (mon       ),
    .year        (year      ),
    //RGB LCD接口
    .lcd_de      (lcd_de    ),
    .lcd_hs      (lcd_hs    ),
    .lcd_vs      (lcd_vs    ),
    .lcd_bl      (lcd_bl    ),
    .lcd_clk     (lcd_clk   ),
    .lcd_rgb     (lcd_rgb   )
    );

endmodule

IIC驱动模块
module i2c_dri
#(
 parameter   SLAVE_ADDR = 7'b1010000   ,  //EEPROM从机地址
 parameter   CLK_FREQ   = 26'd50_000_000, //模块输入的时钟频率
 parameter   I2C_FREQ   = 18'd250_000     //IIC_SCL的时钟频率
)
(                                                            
input                clk        ,    
input                rst_n      ,   
                                    
//i2c interface                      
input                i2c_exec   ,  //I2C触发执行信号
input                bit_ctrl   ,  //字地址位控制(16b/8b)
input                i2c_rh_wl  ,  //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_done   ,  //I2C一次操作完成
output  reg          i2c_ack    ,  //I2C应答标志 0:应答 1:未应答
output  reg          scl        ,  //I2C的SCL时钟信号
inout                sda        ,  //I2C的SDA信号
                                  
//user interface                   
output  reg          dri_clk       //驱动I2C操作的驱动时钟
);

//localparam define
localparam  st_idle     = 8'b0000_0001; //空闲状态
localparam  st_sladdr   = 8'b0000_0010; //发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100; //发送16位字地址
localparam  st_addr8    = 8'b0000_1000; //发送8位字地址
localparam  st_data_wr  = 8'b0001_0000; //写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000; //发送器件地址读
localparam  st_data_rd  = 8'b0100_0000; //读数据(8 bit)
localparam  st_stop     = 8'b1000_0000; //结束I2C操作

//reg define
reg            sda_dir   ; //I2C数据(SDA)方向控制
reg            sda_out   ; //SDA输出信号
reg            st_done   ; //状态结束
reg            wr_flag   ; //写标志
reg    [ 6:0]  cnt       ; //计数
reg    [ 7:0]  cur_state ; //状态机当前状态
reg    [ 7:0]  next_state; //状态机下一状态
reg    [15:0]  addr_t    ; //地址
reg    [ 7:0]  data_r    ; //读取的数据
reg    [ 7:0]  data_wr_t ; //I2C需写的数据的临时寄存
reg    [ 9:0]  clk_cnt   ; //分频时钟计数

//wire define
wire          sda_in     ; //SDA输入信号
wire   [8:0]  clk_divide ; //模块驱动时钟的分频系数

//*****************************************************
//**                    main code
//*****************************************************

//SDA控制
assign  sda     = sda_dir ?  sda_out : 1'bz;     //SDA数据输出或高阻
assign  sda_in  = sda ;                          //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

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
if(!rst_n)
   cur_state <= st_idle;
else
   cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @(*) begin
next_state = st_idle;
case(cur_state)
   st_idle: begin                          //空闲状态
      if(i2c_exec) begin
          next_state = st_sladdr;
      end
      else
          next_state = st_idle;
   end
   st_sladdr: begin
       if(st_done) begin
           if(bit_ctrl)                    //判断是16位还是8位字地址
              next_state = st_addr16;
           else
              next_state = st_addr8 ;
       end
       else
           next_state = st_sladdr;
   end
   st_addr16: begin                        //写16位字地址
       if(st_done) begin
           next_state = st_addr8;
       end
       else begin
           next_state = st_addr16;
       end
   end
   st_addr8: begin                         //8位字地址
       if(st_done) begin
           if(wr_flag==1'b0)               //读写判断
               next_state = st_data_wr;
           else
               next_state = st_addr_rd;
       end
       else begin
           next_state = st_addr8;
       end
   end
   st_data_wr: begin                       //写数据(8 bit)
       if(st_done)
           next_state = st_stop;
       else
           next_state = st_data_wr;
   end
   st_addr_rd: begin                       //写地址以进行读数据
       if(st_done) begin
           next_state = st_data_rd;
       end
       else begin
           next_state = st_addr_rd;
       end
   end
   st_data_rd: begin                       //读取数据(8 bit)
       if(st_done)
           next_state = st_stop;
       else
           next_state = st_data_rd;
   end
   st_stop: begin                          //结束I2C操作
       if(st_done)
           next_state = st_idle;
       else
           next_state = st_stop ;
   end
   default: next_state= st_idle;
endcase
end

//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
//复位初始化
if(!rst_n) begin
   scl       <= 1'b1;
   sda_out   <= 1'b1;
   sda_dir   <= 1'b1;                          
   i2c_done  <= 1'b0;                          
   i2c_ack   <= 1'b0;                          
   cnt       <= 1'b0;                          
   st_done   <= 1'b0;                          
   data_r    <= 1'b0;                          
   i2c_data_r<= 1'b0;                          
   wr_flag   <= 1'b0;                          
   addr_t    <= 1'b0;                          
   data_wr_t <= 1'b0;                          
end                                              
else begin                                       
   st_done <= 1'b0 ;                            
   cnt     <= cnt +1'b1 ;                       
   case(cur_state)                              
        st_idle: begin                          //空闲状态
           scl     <= 1'b1;                     
           sda_out <= 1'b1;                     
           sda_dir <= 1'b1;                     
           i2c_done<= 1'b0;                     
           cnt     <= 7'b0;               
           if(i2c_exec) begin                   
               wr_flag   <= i2c_rh_wl ;         
               addr_t    <= i2c_addr  ;         
               data_wr_t <= i2c_data_w;  
               i2c_ack <= 1'b0;                      
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值