本帖最后由 正点原子运营 于 2023-12-2 17:05 编辑
第三十一章 RTC实时时钟数码管显示实验 1)实验平台:正点原子 ATK-DFPGL22G开发板
2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0
6)FPGA技术交流QQ群:435699340
PCF8563是一款多功能时钟/日历芯片。因其功耗低、控制简单、封装小而广泛应用于电表、水表、传真机、便携式仪器等产品中。本章我们将使用开发板上的 PCF8563 器件实现实时时钟的显示。
本章包括以下几个部分: 1.1PCF8563简介 1.2实验任务 1.3硬件设计 1.4程序设计 1.5下载验证
1.1 PCF8563简介PCF8563是PHILIPS公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示: PCF8563有16个可寻址的8位寄存器,但不是所有位都有用到。前两个寄存器(内存地址00H、01H)用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址02H~08H用作TIME计时器(秒~年计时器);地址09H~0CH用于报警(ALARM)寄存器(定义报警条件);地址0DH控制CLKOUT管脚的输出频率;地址0EH和0FH分别用于定时器控制寄存器和定时器寄存器。 秒、分钟、小时、日、月、年、分钟报警、小时报警、日报警寄存器中的数据编码格式为BCD,只有星期和星期报警寄存器中的数据不以BCD格式编码。BCD码(Binary-Coded Decimal‎)是一种二进制的数字编码形式,用四个二进制位来表示一位十进制数(0~9),能够使二进制和十进制之间的转换得以快捷的进行。 PCF8563通过I2C接口与FPGA进行通信。使用该器件时,FPGA先通过I2C接口向该器件相应的寄存器写入初始的时间数据(秒~年),然后通过I2C接口读取相应的寄存器的时间数据。有关I2C总线协议详细的介绍请大家参考“EEPROM读写实验”。 下面我们对本次实验用到的寄存器做简要的描述和说明,其他寄存器的描述和说明,请大家参考PCF8563的数据手册。 秒寄存器的的地址为02h,说明如下表所示: 当电源电压低于PCF8563器件的最低供电电压时,VL为“1”,表明内部完整的时钟周期信号不能被保证,可能导致时钟/日历数据不准确。 BCD编码的秒数值如下表所示: 寄存器的地址为03h,说明如下表所示: 小时寄存器的地址为04h,说明如下表所示: 天寄存器的地址为05h,说明如下表所示:
年寄存器的地址为08h,说明如下表所示:
1.2 实验任务本节的实验任务是通过ATK-DFPGL22G开发板上的PCF8563实时时钟芯片,根据输入按键KEY0来切换数码管显示时间或者日期。
1.3 硬件设计ATK-DFPGL22G开发板上PCF8563接口部分的原理图如下图所示。 PCF8563作为I2C接口的从器件与EEPROM等模块统一挂接在ATK-DFPGL22G开发板上的IIC总线上。OSCI、OSCO与外部32.768KHz的晶振相连,为芯片提供驱动时钟;SCL和SDA分别是I2C总线的串行时钟接口和串行数据接口。 本实验中,各管脚分配如下表所示: 表 31.3.1 RTC实时时钟数码管显示管脚分配 1.4 程序设计根据实验任务,我们可以大致规划出系统的控制流程:首先通过I2C总线向PCF8563写入初始日期值(年月日)和时间值(时分秒),然后不断地读取日期和时间数据,并根据输入的按键,选择将日期或者时间数据显示到数码管上。由此画出系统的功能框图如下图所示: 由系统框图可知,FPGA顶层模块例化了以下五个模块、分别是IIC驱动模块(i2c_dri)、PCF8563控制模块(pcf8563_ctrl)、按键消抖模块(key_debounce)、显示值切换模块(key_sw_disp)以及数码管BCD驱动模块(seg_bcd_dri)。 PCF8563控制模块通过调用IIC驱动模块来实现对PCF8563实时时钟数据的配置和读取;而显示值切换模块根据按键消抖模块输出的按键数据(key_value)选择显示日期或者时间(年月日/时分秒),并将其传递给数码管BCD驱动模块(seg_bcd_dri),最终在数码管上显示日期或者时间。 顶层模块的代码如下: - 1 module rtc_seg_led(
- 2 input sys_clk, //系统时钟
- 3 input sys_rst_n, //系统复位
- 4
- 5 //按键
- 6 input key, //输入按键KEY0
- 7
- 8 //数码管
- 9 output [5:0 seg_sel, //数码管位选信号
- 10 output [7:0 seg_led, //数码管段选信号
- 11
- 12 //RTC实时时钟
- 13 output iic_scl, //RTC的时钟线scl
- 14 inout iic_sda //RTC的数据线sda
- 15 );
- 16
- 17 //parameter define
- 18 parameter SLAVE_ADDR = 7'b101_0001 ;//器件地址(SLAVE_ADDR)
- 19 parameter BIT_CTRL = 1'b0 ; //字地址位控制参数(16b/8b)
- 20 parameter CLK_FREQ = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
- 21 parameter I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率
- 22 parameter TIME_INIT = 48'h20_04_01_09_30_00;//初始时间
- 23
- 24 //wire define
- 25 wire dri_clk ; //I2C操作时钟
- 26 wire i2c_exec ; //I2C触发控制
- 27 wire [15:0 i2c_addr ; //I2C操作地址
- 28 wire [ 7:0 i2c_data_w; //I2C写入的数据
- 29 wire i2c_done ; //I2C操作结束标志
- 30 wire i2c_ack ; //I2C应答标志 0:应答 1:未应答
- 31 wire i2c_rh_wl ; //I2C读写控制
- 32 wire [ 7:0 i2c_data_r; //I2C读出的数据
- 33
- 34 wire [7:0 sec ; //秒
- 35 wire [7:0 min ; //分
- 36 wire [7:0 hour ; //时
- 37 wire [7:0 day ; //日
- 38 wire [7:0 mon ; //月
- 39 wire [7:0 year ; //年
- 40
- 41 wire key_value; //消抖后的按键值
- 42 wire [5:0 point ; //数码管小数点控制
- 43 wire [23:0 disp_data; //数码管显示的数值控制
- 44
- 45 //*****************************************************
- 46 //** main code
- 47 //*****************************************************
- 48
- 49 //i2c驱动模块
- 50 i2c_dri #(
- 51 .SLAVE_ADDR (SLAVE_ADDR), //从机地址
- 52 .CLK_FREQ (CLK_FREQ ), //模块输入的时钟频率
- 53 .I2C_FREQ (I2C_FREQ ) //IIC_SCL的时钟频率
- 54 ) u_i2c_dri(
- 55 .clk (sys_clk ),
- 56 .rst_n (sys_rst_n ),
- 57 //i2c interface
- 58 .i2c_exec (i2c_exec ),
- 59 .bit_ctrl (BIT_CTRL ),
- 60 .i2c_rh_wl (i2c_rh_wl ),
- 61 .i2c_addr (i2c_addr ),
- 62 .i2c_data_w (i2c_data_w),
- 63 .i2c_data_r (i2c_data_r),
- 64 .i2c_done (i2c_done ),
- 65 .i2c_ack (i2c_ack ),
- 66 .scl (iic_scl ),
- 67 .sda (iic_sda ),
- 68 //user interface
- 69 .dri_clk (dri_clk )
- 70 );
- 71
- 72 //PCF8563控制模块
- 73 pcf8563_ctrl #(
- 74 .TIME_INIT (TIME_INIT)
- 75 )u_pcf8563_ctrl(
- 76 .clk (dri_clk ),
- 77 .rst_n (sys_rst_n ),
- 78 //IIC
- 79 .i2c_rh_wl (i2c_rh_wl ),
- 80 .i2c_exec (i2c_exec ),
- 81 .i2c_addr (i2c_addr ),
- 82 .i2c_data_w (i2c_data_w),
- 83 .i2c_data_r (i2c_data_r),
- 84 .i2c_done (i2c_done ),
- 85 //时间和日期
- 86 .sec (sec ),
- 87 .min (min ),
- 88 .hour (hour ),
- 89 .day (day ),
- 90 .mon (mon ),
- 91 .year (year )
- 92 );
- 93
- 94 //消抖模块
- 95 key_debounce u_key_debounce(
- 96 .sys_clk (sys_clk ), //外部50M时钟
- 97 .sys_rst_n (sys_rst_n ), //外部复位信号,低有效
- 98 .key (key ), //外部按键输入
- 99 .key_value (key_value ), //按键消抖后的数据
- 100 .key_flag () //按键数据有效信号
- 101 );
- 102
- 103 //显示值切换模块
- 104 key_sw_disp u_key_sw_disp(
- 105 .clk (sys_clk),
- 106 .rst_n (sys_rst_n),
- 107
- 108 .key_value (key_value),
- 109 .sec (sec ),
- 110 .min (min ),
- 111 .hour (hour),
- 112 .day (day ),
- 113 .mon (mon ),
- 114 .year (year),
- 115
- 116 .point (point),
- 117 .disp_data (disp_data)
- 118 );
- 119
- 120 //数码管驱动模块
- 121 seg_bcd_dri u_seg_bcd_dri(
- 122 //input
- 123 .clk (sys_clk ), //时钟信号
- 124 .rst_n (sys_rst_n ), //复位信号
- 125 .data (disp_data ), //6个数码管要显示的数值
- 126 .point (point ), //小数点具体显示的位置,从左往右,高有效
- 127 //output
- 128 .seg_sel (seg_sel ), //数码管位选
- 129 .seg_led (seg_led ) //数码管段选
- 130 );
- 131
- 132 endmodule
复制代码程序中第18行至第22行定义了一些参数,其中TIME_INIT表示PCF8563初始化时的时间数据,可以通过修改此参数值使PCF8563从不同的时间开始计时,例如从2020年4月1号09:30:00开始计时,需要将该参数值设置为48’h20_04_01_09_30_00。 顶层模块中主要完成对其余模块的例化。其中I2C驱动模块(i2c_dri)程序与“EEPROM读写实验”章节中的IIC驱动模块(i2c_dri)程序完全相同,有关IIC驱动模块的详细介绍请大家参考“EEPROM读写实验”。按键消抖模块可参考“按键控制蜂鸣器实验”。 PCF8563实时时钟控制模块的代码如下所示: - 1 module pcf8563_ctrl #(
- 2 // 初始时间设置,从高到低为年到秒,各占8bit
- 3 parameter TIME_INIT = 48'h20_10_26_09_30_00)(
- 4 input clk , //时钟信号
- 5 input rst_n , //复位信号
- 6
- 7 //i2c interface
- 8 output reg i2c_rh_wl , //I2C读写控制信号
- 9 output reg i2c_exec , //I2C触发执行信号
- 10 output reg [15:0 i2c_addr , //I2C器件内地址
- 11 output reg [7:0 i2c_data_w, //I2C要写的数据
- 12 input [7:0 i2c_data_r, //I2C读出的数据
- 13 input i2c_done , //I2C一次操作完成
- 14
- 15 //PCF8563T的秒、分、时、日、月、年数据
- 16 output reg [7:0 sec, //秒
- 17 output reg [7:0 min, //分
- 18 output reg [7:0 hour, //时
- 19 output reg [7:0 day, //日
- 20 output reg [7:0 mon, //月
- 21 output reg [7:0 year //年
- 22 );
- 23
- 24 //reg define
- 25 reg [3:0 flow_cnt ; // 状态流控制
- 26 reg [12:0 wait_cnt ; // 计数等待
- 27
- 28 //*****************************************************
- 29 //** main code
- 30 //*****************************************************
- 31
- 32 //先向PCF8563中写入初始化日期和时间,再从中读出日期和时间
- 33 always @(posedge clk or negedge rst_n) begin
- 34 if(!rst_n) begin
- 35 sec <= 8'h0;
- 36 min <= 8'h0;
- 37 hour <= 8'h0;
- 38 day <= 8'h0;
- 39 mon <= 8'h0;
- 40 year <= 8'h0;
- 41 i2c_exec <=1'b0;
- 42 i2c_rh_wl <= 1'b0;
- 43 i2c_addr <=8'd0;
- 44 i2c_data_w <= 8'd0;
- 45 flow_cnt <=4'd0;
- 46 wait_cnt <=13'd0;
- 47 end
- 48 else begin
- 49 i2c_exec <= 1'b0;
- 50 case(flow_cnt)
- 51 //上电初始化
- 52 4'd0: begin
- 53 if(wait_cnt == 13'd8000) begin
- 54 wait_cnt<= 12'd0;
- 55 flow_cnt<= flow_cnt + 1'b1;
- 56 end
- 57 else
- 58 wait_cnt<= wait_cnt + 1'b1;
- 59 end
- 60 //写读秒
- 61 4'd1: begin
- 62 i2c_exec <= 1'b1;
- 63 i2c_addr <= 8'h02;
- 64 flow_cnt <= flow_cnt + 1'b1;
- 65 i2c_data_w<= TIME_INIT[7:0];
- 66 end
- 67 4'd2: begin
- 68 if(i2c_done == 1'b1) begin
- 69 sec <= i2c_data_r[6:0];
- 70 flow_cnt<= flow_cnt + 1'b1;
- 71 end
- 72 end
- 73 //写读分
- 74 4'd3: begin
- 75 i2c_exec <= 1'b1;
- 76 i2c_addr <= 8'h03;
- 77 flow_cnt <= flow_cnt + 1'b1;
- 78 i2c_data_w<= TIME_INIT[15:8];
- 79 end
- 80 4'd4: begin
- 81 if(i2c_done == 1'b1) begin
- 82 min <= i2c_data_r[6:0];
- 83 flow_cnt<= flow_cnt + 1'b1;
- 84 end
- 85 end
- 86 //写读时
- 87 4'd5: begin
- 88 i2c_exec <= 1'b1;
- 89 i2c_addr <= 8'h04;
- 90 flow_cnt <= flow_cnt + 1'b1;
- 91 i2c_data_w<= TIME_INIT[23:16];
- 92 end
- 93 4'd6: begin
- 94 if(i2c_done == 1'b1) begin
- 95 hour <= i2c_data_r[5:0];
- 96 flow_cnt<= flow_cnt + 1'b1;
- 97 end
- 98 end
- 99 //写读天
- 100 4'd7: begin
- 101 i2c_exec <= 1'b1;
- 102 i2c_addr <= 8'h05;
- 103 flow_cnt <= flow_cnt + 1'b1;
- 104 i2c_data_w<= TIME_INIT[31:24];
- 105 end
- 106 4'd8: begin
- 107 if(i2c_done == 1'b1) begin
- 108 day <= i2c_data_r[5:0];
- 109 flow_cnt<= flow_cnt + 1'b1;
- 110 end
- 111 end
- 112 //写读月
- 113 4'd9: begin
- 114 i2c_exec <= 1'b1;
- 115 i2c_addr <= 8'h07;
- 116 flow_cnt <= flow_cnt + 1'b1;
- 117 i2c_data_w<= TIME_INIT[39:32];
- 118 end
- 119 4'd10: begin
- 120 if(i2c_done == 1'b1) begin
- 121 mon <= i2c_data_r[4:0];
- 122 flow_cnt<= flow_cnt + 1'b1;
- 123 end
- 124 end
- 125 //写读年
- 126 4'd11: begin
- 127 i2c_exec <= 1'b1;
- 128 i2c_addr <= 8'h08;
- 129 flow_cnt <= flow_cnt + 1'b1;
- 130 i2c_data_w<= TIME_INIT[47:40];
- 131 end
- 132 4'd12: begin
- 133 if(i2c_done == 1'b1) begin
- 134 year <= i2c_data_r;
- 135 i2c_rh_wl<= 1'b1;
- 136 flow_cnt <= 4'd1;
- 137 end
- 138 end
- 139 default: flow_cnt <= 4'd0;
- 140 endcase
- 141 end
- 142 end
- 143
- 144 endmodule
复制代码程序中定义了一个状态流控制计数器(flow_cnt),flow_cnt初始值为0,在flow_cnt等于0进行延时,随后从0开始累加至12,将初始日期和时间(TIME_INIT)写入PCF8563中;在flow_cnt等于12时,i2c_rh_wl(I2C读写控制信号)由低电平改为高电平,即IIC由写操作切换成读操作,与此同时,flow_cnt赋值为1,循环从PCF8563中读取秒、分、时、日、月和年。 下图为Fabric Debugger抓取的波形图,从图中可以看到当前读到的时间为20年4月1日09:31:08。需要说明的是,flow_cnt在循环从0累加至12,其中flow_cnt等于奇数的时间很短,所以需要放大波形才能便于观察。 图 31.4.2 Fabric Debugger波形图 显示值切换模块代码如下: - 1 module key_sw_disp(
- 2 input clk , //时钟
- 3 input rst_n , //复位
- 4
- 5 input key_value , //按键
- 6 input [7:0 sec , //秒
- 7 input [7:0 min , //分钟
- 8 input [7:0 hour , //小时
- 9 input [7:0 day , //日
- 10 input [7:0 mon , //月
- 11 input [7:0 year , //年
- 12
- 13 output [5:0 point , //数码管小数点控制
- 14 output [23:0 disp_data //数码管显示的数值控制
- 15 );
- 16
- 17 //reg define
- 18 reg sw_flag ;
- 19 reg key_value_d0;
- 20 reg key_value_d1;
- 21
- 22 //wire define
- 23 wire neg_key_value;
- 24
- 25 //*****************************************************
- 26 //** main code
- 27 //*****************************************************
- 28
- 29 //采集输入信号的下降沿
- 30 assign neg_key_value = key_value_d1 & (~key_value_d0);
- 31 //切换输出数码管要显示的数据
- 32 assign disp_data = (sw_flag == 1'b0) ? {sec,min,hour} : {day,mon,year};
- 33 //数码管小数点显示位置
- 34 assign point = (sw_flag == 1'b0) ? 6'b010100: 6'b000100;
- 35
- 36 //对输入的按键信号打两拍
- 37 always @(posedge clk or negedge rst_n) begin
- 38 if(!rst_n) begin
- 39 key_value_d0 <= 1'b0;
- 40 key_value_d1 <= 1'b0;
- 41 end
- 42 else begin
- 43 key_value_d0 <= key_value;
- 44 key_value_d1 <= key_value_d0;
- 45 end
- 46 end
- 47
- 48 //控制sw_flag信号翻转
- 49 always @(posedge clk or negedge rst_n) begin
- 50 if(!rst_n)
- 51 sw_flag <= 1'b0;
- 52 else if(neg_key_value)
- 53 sw_flag <= ~sw_flag;
- 54 end
- 55
- 56 endmodule
复制代码由于数码管总共可以显示6位数据,没有办法同时显示日期和时间,因此根据输入的按键来切换数码管显示日期和时间。 sw_flag信号用来切换数码管显示日期和时间,sw_flag初始值为0,当检查到输入按键的下降沿后,sw_flag取反,如程序中第49至54行所示。当sw_flag等于0时,数码管显示时间值;当sw_flag等于1时,数码管显示日期值,如程序中第32行代码所示。 数码管BCD驱动模块的代码如下所示: - 1 module seg_bcd_dri(
- 2 //input
- 3 input clk , // 时钟信号
- 4 input rst_n , // 复位信号
- 5 input [23:0 data , // 6个数码管要显示的数值
- 6 input [5:0 point , // 小数点具体显示的位置,从高到低,高有效
- 7 input en,
- 8
- 9 //output
- 10 output reg [5:0 seg_sel, // 数码管位选
- 11 output reg [7:0 seg_led // 数码管段选
- 12 );
- 13
- 14 //parameter define
- 15 localparam CLK_DIVIDE = 4'd10 ; // 时钟分频系数
- 16 localparam MAX_NUM = 13'd5000 ; // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
- 17
- 18
- 19 //reg define
- 20 reg [15:0 cnt0 ; // 1ms计数
- 21 reg [7:0 cnt ; // 切换显示数码管用
- 22 reg [3:0 data1 ; // 送给要显示的数码管,要亮的灯
- 23 reg point1 ; // 要显示的小数点
- 24 reg [3:0 clk_cnt ; // 时钟分频计数器
- 25 reg dri_clk ; // 数码管的驱动时钟,5MHz
- 26 reg [12:0 cnt0 ; // 数码管驱动时钟计数器
- 27 reg flag ; // 标志信号(标志着cnt0计数达1ms)
- 28 reg [7:0 cnt_sel ; // 数码管位选计数器
- 29
- 30 //*****************************************************
- 31 //** main code
- 32 //*****************************************************
- 33
- 34 //对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
- 35 always @(posedge clk or negedge rst_n) begin
- 36 if(!rst_n) begin
- 37 clk_cnt <= 4'd0;
- 38 dri_clk <= 1'b1;
- 39 end
- 40 else if(clk_cnt == CLK_DIVIDE / 2 - 1) begin
- 41 clk_cnt <= 4'd0;
- 42 dri_clk <= ~dri_clk;
- 43 end
- 44 else begin
- 45 clk_cnt <= clk_cnt +1'b1;
- 46 dri_clk <= dri_clk;
- 47 end
- 48 end
- 49
- 50 //每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
- 51 always @ (posedge dri_clk or negedge rst_n) begin
- 52 if (rst_n == 1'b0) begin
- 53 cnt0 <= 13'b0;
- 54 flag <= 1'b0;
- 55 end
- 56 else if (cnt0 < MAX_NUM - 1'b1) begin
- 57 cnt0 <= cnt0 + 1'b1;
- 58 flag <= 1'b0;
- 59 end
- 60 else begin
- 61 cnt0 <= 13'b0;
- 62 flag <= 1'b1;
- 63 end
- 64 end
- 65
- 66 //cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
- 67 always @ (posedge dri_clk or negedge rst_n) begin
- 68 if (rst_n == 1'b0)
- 69 cnt_sel <= 8'b0;
- 70 else if(flag) begin
- 71 if(cnt_sel < 8'd11)
- 72 cnt_sel <= cnt_sel + 1'b1;
- 73 else
- 74 cnt_sel <= 8'b0;
- 75 end
- 76 else
- 77 cnt_sel <= cnt_sel;
- 78 end
- 79
- 80 //控制数码管位选信号,使6位数码管轮流显示
- 81 always @ (posedge dri_clk or negedge rst_n) begin
- 82 if(!rst_n) begin
- 83 seg_sel <= 6'b000000; //位选信号低电平有效
- 84 data1 <= 4'b0;
- 85 point1 <= 1'b1; //共阳极数码管,低电平导通
- 86 end
- 87 else begin
- 88 if(en) begin
- 89 case (cnt_sel)
- 90 8'd0 :begin
- 91 seg_sel <= 6'b000001; //显示数码管最低位
- 92 data1 <= data[7:4 ; //显示的数据
- 93 point1 <= ~point[5]; //显示的小数点
- 94 end
- 95 8'd1 :begin
- 96 seg_sel <= 6'b000000; //清空使MOS放电
- 97 data1 <= 4'd10;
- 98 end
- 99 8'd2 :begin
- 100 seg_sel <= 6'b000010; //显示数码管第1位
- 101 data1 <= data[3:0 ;
- 102 point1 <= ~point[4];
- 103 end
- 104 8'd3 :begin
- 105 seg_sel <= 6'b000000; //清空使MOS放电
- 106 data1 <= 4'd10;
- 107 end
- 108 8'd4 :begin
- 109 seg_sel <= 6'b000100; //显示数码管第2位
- 110 data1 <= data[15:12];
- 111 point1 <= ~point[3];
- 112 end
- 113 8'd5 :begin
- 114 seg_sel <= 6'b000000; //清空使MOS放电
- 115 data1 <= 4'd10;
- 116 end
- 117 8'd6 :begin
- 118 seg_sel <= 6'b001000; //显示数码管第3位
- 119 data1 <= data[11:8];
- 120 point1 <= ~point[2];
- 121 end
- 122 8'd7 :begin
- 123 seg_sel <= 6'b000000; //清空使MOS放电
- 124 data1 <= 4'd10;
- 125 end
- 126 8'd8 :begin
- 127 seg_sel <= (6'b010000); //显示数码管第4位
- 128 data1 <= data[23:20];
- 129 point1 <= ~point[1];
- 130 end
- 131 8'd9 :begin
- 132 seg_sel <= 6'b000000; //清空使MOS放电
- 133 data1 <= 4'd10;
- 134 end
- 135 8'd10 :begin
- 136 seg_sel <= 6'b100000; //显示数码管最高位
- 137 data1 <= data[19:16];
- 138 point1 <= ~point[0];
- 139 end
- 140 8'd11 :begin
- 141 seg_sel <= 6'b000000; //清空使MOS放电
- 142 data1 <= 4'd10;
- 143 end
- 144 default :begin
- 145 seg_sel <= 6'b000000;
- 146 data1 <= 4'b0;
- 147 point1 <= 1'b1;
- 148 end
- 149 endcase
- 150 end
- 151 else begin
- 152 seg_sel <= 6'b000000; //使能信号为0时,所有数码管均不显示
- 153 data1 <= 4'b0;
- 154 point1 <= 1'b1;
- 155 end
- 156 end
- 157 end
- 158
- 159 //数码管显示数据
- 160 always @ (posedge dri_clk or negedge rst_n) begin
- 161 if(rst_n == 1'b0)
- 162 seg_led <= ~(8'hc0);
- 163 else begin
- 164 case(data1)
- 165 4'd0: seg_led <= ~{point1,7'b1000000};
- 166 4'd1: seg_led <= ~{point1,7'b1111001};
- 167 4'd2: seg_led <= ~{point1,7'b0100100};
- 168 4'd3: seg_led <= ~{point1,7'b0110000};
- 169 4'd4: seg_led <= ~{point1,7'b0011001};
- 170 4'd5: seg_led <= ~{point1,7'b0010010};
- 171 4'd6: seg_led <= ~{point1,7'b0000010};
- 172 4'd7: seg_led <= ~{point1,7'b1111000};
- 173 4'd8: seg_led <= ~{point1,7'b0000000};
- 174 4'd9: seg_led <= ~{point1,7'b0010000};
- 175 4'd10: seg_led <= ~8'b11111111; //不显示任何字符
- 176 default: seg_led <= ~{point1,7'b1000000};
- 177 endcase
- 178 end
- 179 end
- 180 endmodule
复制代码由于是PCF8563的数据是BCD编码,从低到高每4位二进制数代表一位十进制数,所以在第89行的case语句块中,我们只需把data信号相应位的值赋给data1即可。有关数码管显示更详细的解释可参考“动态数码管显示实验”。
1.5 下载验证首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接,然后连接电源线并打开电源开关。 最后我们下载程序,验证RTC实时时钟数码管显示功能。程序下载完成后观察到开发板上数码管显示的值为我们设置的初始日期值,并且在不断实时变化;当按下KEY0按键后,数码管显示年月日,说明PCF8563实时时钟数码管显示实验程序下载验证成功。 数码管显示的日期如下图所示: |