ZYNQ-FPGA-RTC (实时显示时钟)

一、PCF8563介绍

        通过PCF8563模块完成实时时钟的数据产生。PCF8563具有报警功能、定时器功能、时
钟输出功能以及中断输出功能。

        内存地址00H、01H用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址02H~08H 用作TIME 计时器(秒~年计时器);地址09H~0CH 用于报警(ALARM)寄存器(定义报警条件);地址0DH 控制CLKOUT 管脚的输出频率;地址0EH 和0FH 分别用于定时器控制寄存器和定时器寄存器。

        如何使用该模块实现时钟显示?

        首先需要等待器件初始化8ms,然后我们需要对该模块中写入一个初始时间,然后该模块就会自动向后计时。因此整体的控制模块是先写一次然后循环进行读。

        该模块通过IIC协议与zynq进行通信,其在开发板上的器件地址是1010_001。因此我们需要在iic第一个周期选择该器件,第二个周期开始进行写数据,

        其中

        秒寄存器地址为02h,其7位表示是否准确1是不准确 0是准确,0-6这七位用BCD码表示秒数

        分寄存器地址为03h,其7位无效,0-6位用BCD码表示分钟时间

        时寄存器地址位04h,6-7位无效,0-5位用BCD码表示小时

        天寄存器地址为05h,6-7位无效,0-5位用BCD码表示天    (注意没有06h)

        月寄存器地址为07h,7位0表示当前世纪,1表示下个世纪,5-6位没用,0-4位BCD表示月份

        年寄存器地址为08h,0-7位用BCD码表示年

二、系统框图

iic_dri是eeprom实验中的iic驱动模块,但是要记得把器件地址改为1010001

lcd_rgb_char也是之前lcd彩条显示实验的代码,但需要修改其中display模块以及顶层调用模块

pcf8565_ctrl模块是对pcf8563功能进行封装的模块

三、模块代码

3.1pcf8565模块


module pcf8563_ctrl #(
    // 初始时间设置,从高到低为年到秒,各占8bit
    parameter  TIME_INIT = 48'h23_11_6_22_00_00)(
    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一次操作完成

    //PCF8563T的秒、分、时、日、月、年数据
    output   reg   [7:0]  sec,        //秒
    output   reg   [7:0]  min,        //分
    output   reg   [7:0]  hour,       //时
    output   reg   [7:0]  day,        //日
    output   reg   [7:0]  mon,        //月
    output   reg   [7:0]  year        //年
);

//parameter define
localparam  WAIT_TIME = 13'd8_000;

//reg define
reg   [3:0]     flow_cnt  ;            // 转态流控制
reg   [12:0]    wait_cnt  ;            // 计数等待

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

//先向PCF8563中写入初始化日期和时间,再从中读出日期和时间
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   <= 13'd0;
    end
    else begin
        i2c_exec <= 1'b0;
        case(flow_cnt)
            //上电初始化
            4'd0: begin
                if(wait_cnt == (WAIT_TIME - 13'd1)) begin
                    wait_cnt<= 13'd0;
                    flow_cnt<= flow_cnt + 4'b1;
                end
                else
                    wait_cnt<= wait_cnt + 13'b1;
            end
            //写读秒
            4'd1: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h02;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[7:0];
            end
            4'd2: begin
                if(i2c_done == 1'b1) begin
                    sec     <= i2c_data_r[6:0];
                    flow_cnt<= flow_cnt + 4'b1;
                end
            end
            //写读分
            4'd3: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h03;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[15:8];
            end
            4'd4: begin
                if(i2c_done == 1'b1) begin
                    min     <= i2c_data_r[6:0];
                    flow_cnt<= flow_cnt + 4'b1;
                end
            end
            //写读时
            4'd5: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h04;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[23:16];
            end
            4'd6: begin
                if(i2c_done == 1'b1) begin
                    hour    <= i2c_data_r[5:0];
                    flow_cnt<= flow_cnt + 4'b1;
                end
            end
            //写读天
            4'd7: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h05;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[31:24];
            end
            4'd8: begin
                if(i2c_done == 1'b1) begin
                    day     <= i2c_data_r[5:0];
                    flow_cnt<= flow_cnt + 4'b1;
                end
            end
            //写读月
            4'd9: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h07;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[39:32];
            end
            4'd10: begin
                if(i2c_done == 1'b1) begin
                    mon     <= i2c_data_r[4:0];
                    flow_cnt<= flow_cnt + 4'b1;
                end
            end
            //写读年
            4'd11: begin
                i2c_exec  <= 1'b1;
                i2c_addr  <= 8'h08;
                flow_cnt  <= flow_cnt + 4'b1;
                i2c_data_w<= TIME_INIT[47:40];
            end
            4'd12: begin
                if(i2c_done == 1'b1) begin
                    year     <= i2c_data_r;
                    i2c_rh_wl<= 1'b1;
                    flow_cnt <= 4'd1;
                end
            end
            default: flow_cnt <= 4'd0;
        endcase
    end
end

endmodule

此模块代码使用了一个状态机,配合读写选择信号,巧妙地实现了第一次写后面几次读的过程

3.2lcd_display模块


module lcd_display(
    input                lcd_pclk , //LCD像素时钟
    input                rst_n    , //系统复位
    
    //日历数据
    input         [7:0]  sec,        //秒
    input         [7:0]  min,        //分
    input         [7:0]  hour,       //时
    input         [7:0]  day,        //日
    input         [7:0]  mon,        //月
    input         [7:0]  year,       //年
    
    //LCD数据接口
    input        [10:0]  pixel_xpos, //像素点横坐标
    input        [10:0]  pixel_ypos, //像素点纵坐标
    output  reg  [23:0]  pixel_data  //像素点数据
);

//parameter define
localparam CHAR_POS_X_1  = 11'd1;  //第1行字符区域起始点横坐标
localparam CHAR_POS_Y_1  = 11'd1;  //第1行字符区域起始点纵坐标
localparam CHAR_POS_X_2  = 11'd17; //第2行字符区域起始点横坐标
localparam CHAR_POS_Y_2  = 11'd17; //第2行字符区域起始点纵坐标
localparam CHAR_WIDTH_1  = 11'd80; //第1行字符区域的宽度,第1行共10个字符(加空格)
localparam CHAR_WIDTH_2  = 11'd64; //第2行字符区域的宽度,第2行共8个字符(加空格)
localparam CHAR_HEIGHT   = 11'd16; //单个字符的高度
localparam WHITE  = 24'hffffff;    //背景色,白色
localparam BLACK  = 24'h000000;    //字符颜色,黑色

//reg define
reg  [127:0]  char  [9:0] ;        //字符数组

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

//字符数组初始值,用于存储字模数据(由取模软件生成,单个数字字体大小:16*8)
always @(posedge lcd_pclk ) begin
    char[0] <= 128'h00000018244242424242424224180000 ;  // "0"
    char[1] <= 128'h000000107010101010101010107C0000 ;  // "1"
    char[2] <= 128'h0000003C4242420404081020427E0000 ;  // "2"
    char[3] <= 128'h0000003C424204180402024244380000 ;  // "3"
    char[4] <= 128'h000000040C14242444447E04041E0000 ;  // "4"
    char[5] <= 128'h0000007E404040586402024244380000 ;  // "5"
    char[6] <= 128'h0000001C244040586442424224180000 ;  // "6"
    char[7] <= 128'h0000007E444408081010101010100000 ;  // "7"
    char[8] <= 128'h0000003C4242422418244242423C0000 ;  // "8"
    char[9] <= 128'h0000001824424242261A020224380000 ;  // "9"
end

//不同的区域绘制不同的像素数据
always @(posedge lcd_pclk or negedge rst_n ) begin
    if (!rst_n)  begin
        pixel_data <= BLACK;
    end
    
    //在第一行显示年的千位 固定值"2"
    else if((pixel_xpos >= CHAR_POS_X_1 - 1'b1) 
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 1 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1) 
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[2][(CHAR_HEIGHT+CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;        //显示字符为黑色
        else
            pixel_data <= WHITE;        //显示字符区域背景为白色
    end
    
    //在第一行显示年的百位 固定值"0"
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *1 - 1'b1) 
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 *2 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1) 
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[0][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示年的十位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 2 - 1'b1) 
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1) 
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[year[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示年的个位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 3 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 4 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1)
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[year[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示空格

    
    //在第一行显示月的十位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 5 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1)
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[mon[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示月的个位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 6 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 7 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1)
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[mon[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示空格


    
    //在第一行显示日的十位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 8 - 1'b1)
              && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1)
              && (pixel_ypos >= CHAR_POS_Y_1)
              && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)  ) begin
        if(char[day[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第一行显示日的个位
    else if((pixel_xpos >= CHAR_POS_X_1 + CHAR_WIDTH_1 / 10 * 9 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_1 + CHAR_WIDTH_1 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_1)
         && (pixel_ypos <  CHAR_POS_Y_1 + CHAR_HEIGHT)) begin
        if(char[day[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_1 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_1-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示时的十位
    else if((pixel_xpos >= CHAR_POS_X_2 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[hour[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示时的个位
    else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 1 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 2 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[hour[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    

    
    //在第二行显示分的十位
    else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 3 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)                  
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[min[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示分的个位
    else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 4 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 5 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[min[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    

    
    //在第二行显示秒的十位
    else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 6 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[sec[7:4]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1))%8) - 11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    
    //在第二行显示秒的个位    
    else if((pixel_xpos >= CHAR_POS_X_2 + CHAR_WIDTH_2 / 8 * 7 - 1'b1)
         && (pixel_xpos <  CHAR_POS_X_2 + CHAR_WIDTH_2 - 1'b1)
         && (pixel_ypos >= CHAR_POS_Y_2)
         && (pixel_ypos <  CHAR_POS_Y_2 + CHAR_HEIGHT)) begin
        if(char[sec[3:0]][(CHAR_HEIGHT + CHAR_POS_Y_2 - pixel_ypos) * 8
                        - ((pixel_xpos - (CHAR_POS_X_2-1'b1)) % 8) -11'd1])
            pixel_data <= BLACK;
        else
            pixel_data <= WHITE;
    end
    else
        pixel_data <= WHITE;    //屏幕背景为白色
end

endmodule 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值