【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1

1)实验平台:正点原子新起点V2开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=609758951113
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)对正点原子FPGA感兴趣的同学可以加群讨论:994244016
4)关注正点原子公众号,获取最新资料更新
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第1张图片

第三十一章RTC实时时钟数码管显示实验

PCF8563是一款多功能时钟/日历芯片。因其功耗低、控制简单、封装小而广泛应用于电表、水表、传真机、便携式仪器等产品中。本章我们将使用新起点开发板上的PCF8563器件实现实时时钟的显示。
本章包括以下几个部分:
3030.1简介
30.2实验任务
30.3硬件设计
30.4程序设计
30.5下载验证
31.1简介
PCF8563是PHILIPS公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第2张图片

图 31.1.1 PCF8563功能框图
PCF8563有16个可寻址的8位寄存器,但不是所有位都有用到。前两个寄存器(内存地址00H、01H)用作控制寄存器和状态寄存器(CONTROL_STATUS);内存地址02H08H用作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,说明如下表所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第3张图片

当电源电压低于PCF8563器件的最低供电电压时,VL为“1”,表明内部完整的时钟周期信号不能被保证,可能导致时钟/日历数据不准确。
BCD编码的秒数值如下表所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第4张图片

图 31.1.2 秒数值的BCD编码
分寄存器的地址为03h,说明如下表所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第5张图片

小时寄存器的地址为04h,说明如下表所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第6张图片

天寄存器的地址为05h,说明如下表所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第7张图片

当年计数器的值是闰年时,PCF8563自动给二月增加一个值,使其成为29天。
月/世纪寄存器的地址为07h,说明如下表所示:
表 31.1.5 月/世纪寄存器(地址07h)
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第8张图片
表 31.1.6 月份表
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第9张图片

年寄存器的地址为08h,说明如下表所示:
表 31.1.7 寄存器(地址08h)
在这里插入图片描述
本节的实验任务是通过新起点开发板上的PCF8563实时时钟芯片,根据输入按键KEY0来切换数码管显示时间或者日期。
31.3硬件设计
新起点开发板上PCF8563接口部分的原理图如下图所示。

【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第10张图片

图 31.3.1 PCF8563接口原理图
PCF8563作为I2C接口的从器件与EEPROM等模块统一挂接在新起点开发板上的IIC总线上。 OSCI、OSCO与外部32.768KHz的晶振相连,为芯片提供驱动时钟;SCL和SDA分别是I2C总线的串行时钟接口和串行数据接口。
本实验中,各管脚分配如下表所示。
表 31.3.1 RTC实时时钟数码管显示管脚分配
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第11张图片

对应的管脚分配约束语句如下所示:
#系统时钟和复位

set_location_assignment PIN_M2 -to sys_clk
set_location_assignment PIN_M1 -to sys_rst_n

#KEY0
set_location_assignment PIN_E16 -to key

#IIC
set_location_assignment PIN_D8 -to iic_scl
set_location_assignment PIN_C8 -to iic_sda

#数码管
set_location_assignment PIN_N16 -to seg_sel[0]
set_location_assignment PIN_N15 -to seg_sel[1]
set_location_assignment PIN_P16 -to seg_sel[2]
set_location_assignment PIN_P15 -to seg_sel[3]
set_location_assignment PIN_R16 -to seg_sel[4]
set_location_assignment PIN_T15 -to seg_sel[5]
                                              
set_location_assignment PIN_M11 -to seg_led[0]
set_location_assignment PIN_N12 -to seg_led[1]
set_location_assignment PIN_C9 -to seg_led[2]
set_location_assignment PIN_N13 -to seg_led[3]
set_location_assignment PIN_M10 -to seg_led[4]
set_location_assignment PIN_N11 -to seg_led[5]
set_location_assignment PIN_P11 -to seg_led[6]
set_location_assignment PIN_D9 -to seg_led[7]

31.4程序设计
根据实验任务,我们可以大致规划出系统的控制流程:首先通过I2C总线向PCF8563写入初始日期值(年月日)和时间值(时分秒),然后不断地读取日期和时间数据,并根据输入的按键,选择将日期或者时间数据显示到数码管上。由此画出系统的功能框图如下图所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第12张图片

图 31.4.1 系统框图
由系统框图可知,FPGA顶层模块例化了以下五个模块、分别是IIC驱动模块(i2c_dri)、PCF8563控制模块(pcf8563_ctrl)、按键消抖模块(key_debounce)、显示值切换模块(key_sw_disp)以及数码管BCD驱动模块(seg_bcd_dri)。
各模块端口及信号连接如下图所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第13张图片

图 31.4.2 顶层模块原理图
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中读取秒、分、时、日、月和年。
下图为SignalTap抓取的波形图,从图中可以看到当前读到的时间为20年4月1日09:31:29。需要说明的是,flow_cnt在循环从0累加至12,由于flow_cnt等于奇数值的时间很短,所以图中奇数值被隐藏。
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第14张图片

图 31.4.3 SignalTap II波形图
显示值切换模块代码如下:

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) ? {hour,min,sec} : {year,mon,day};
33 //数码管小数点显示位置
34 assign point = 6'b010100;
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   
8      //output
9      output  reg  [5:0]     seg_sel,        // 数码管位选
10     output  reg  [7:0]     seg_led         // 数码管段选
11  );
12  
13  //parameter define
14  parameter  WIDTH0 = 50_000;
15  
16  //reg define
17  reg    [15:0]             cnt0;           // 1ms计数
18  reg    [2:0]              cnt;            // 切换显示数码管用
19  reg    [3:0]              data1;           // 送给要显示的数码管,要亮的灯
20  reg                       point1;         // 要显示的小数点
21  
22  //*****************************************************
23  //**                    main code
24  //*****************************************************
25  
26  //计数1ms
27  always @(posedge clk or negedge rst_n) begin
28      if(rst_n == 1'b0)
29          cnt0 <= 15'b0;
30      else if(cnt0 < WIDTH0)
31          cnt0 <= cnt0 + 1'b1;
32      else
33          cnt0 <= 15'b0;
34  end
35  
36  //计数器,用来计数6个状态(因为有6个灯)
37  always @(posedge clk or negedge rst_n) begin
38      if(rst_n == 1'b0)
39          cnt <= 3'b0;
40      else if(cnt < 3'd6) begin
41          if(cnt0 == WIDTH0)
42              cnt <= cnt + 1'b1;
43          else
44              cnt <= cnt;
45      end
46      else
47          cnt <= 3'b0;
48  end
49  
50  //6个数码管轮流显示,完成刷新(从右到左)
51  always @(posedge clk or negedge rst_n) begin
52      if(rst_n == 1'b0) begin
53          seg_sel     <= 6'b000001;
54          data1 <= 4'b0;
55      end
56      else begin
57          case (cnt)
58              3'd0:begin
59                   seg_sel <= 6'b111110;
60                   data1   <= data[3:0] ;
61                   point1 <= point[0] ;
62              end
63              3'd1:begin
64                   seg_sel <= 6'b111101;
65                   data1   <= data[7:4] ;
66                   point1 <= point[1] ;
67              end
68              3'd2:begin
69                   seg_sel <= 6'b111011;
70                   data1   <= data[11:8];
71                   point1 <= point[2] ;
72              end
73              3'd3:begin
74                   seg_sel <= 6'b110111 ;
75                   data1   <= data[15:12];
76                   point1 <= point[3]  ;
77              end
78              3'd4:begin
79                   seg_sel <= 6'b101111 ;
80                   data1   <= data[19:16];
81                   point1 <= point[4]  ;
82              end
83              3'd5:begin
84                   seg_sel <= 6'b011111 ;
85                   data1   <= data[23:20];
86                   point1 <= point[5]  ;
87              end
88              default: begin
89                   seg_sel <= 6'b000000;
90                   data1   <= 4'b0;
91                   point1 <= 1'b1;
92              end
93          endcase
94      end
95  end
96  
97  //数码管显示数据
98  always @ (posedge clk or negedge rst_n) begin
99      if(rst_n == 1'b0)
100         seg_led <= 7'b0;
101     else begin
102         case(data1)
103             4'd0: seg_led <= {~point1,7'b1000000};
104             4'd1: seg_led <= {~point1,7'b1111001};
105             4'd2: seg_led <= {~point1,7'b0100100};
106             4'd3: seg_led <= {~point1,7'b0110000};
107             4'd4: seg_led <= {~point1,7'b0011001};
108             4'd5: seg_led <= {~point1,7'b0010010};
109             4'd6: seg_led <= {~point1,7'b0000010};
110             4'd7: seg_led <= {~point1,7'b1111000};
111             4'd8: seg_led <= {~point1,7'b0000000};
112             4'd9: seg_led <= {~point1,7'b0010000};
113             default: seg_led <= {point1,7'b1000000};
114         endcase
115     end
116 end
117 endmodule

由于是PCF8563的数据是BCD编码,从低到高每4位二进制数代表一位十进制数,所以在第57行的case语句块中,我们只需把data信号相应位的值赋给data1即可。有关数码管显示更详细的解释可参考“动态数码管显示实验”。
31.5下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接,然后连接电源线并打开电源开关。
最后我们下载程序,验证RTC实时时钟数码管显示功能。程序下载完成后观察到开发板上数码管显示的值为我们设置的初始日期值,并且在不断实时变化;当按下KEY0按键后,数码管显示年月日,说明PCF8563实时时钟数码管显示实验程序下载验证成功。
数码管显示的日期如下图所示:
【正点原子FPGA连载】第三十一章RTC实时时钟数码管显示实验 -摘自【正点原子】新起点之FPGA开发指南_V2.1_第15张图片
图 31.5.1 数码管显示日期

你可能感兴趣的:(嵌入式,fpga开发,单片机,嵌入式硬件)