OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 1025|回复: 0

[国产FPGA] 《ATK-DFPGL22G 之FPGA开发指南》第三十一章 RTC实时时钟数码管显示实验

[复制链接]

1117

主题

1128

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4666
金钱
4666
注册时间
2019-5-8
在线时间
1224 小时
发表于 2023-12-5 16:35:47 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2023-12-2 17:05 编辑

第三十一章 RTC实时时钟数码管显示实验
1)实验平台:正点原子 ATK-DFPGL22G开发板

2) 章节摘自【正点原子】ATK-DFPGL22G之FPGA开发指南_V1.0


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz-PGL22G.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)FPGA技术交流QQ群:435699340

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

PCF8563是一款多功能时钟/日历芯片。因其功耗低、控制简单、封装小而广泛应用于电表、水表、传真机、便携式仪器等产品中。本章我们将使用开发板上的 PCF8563 器件实现实时时钟的显示。
本章包括以下几个部分:
1.1PCF8563简介
1.2实验任务
1.3硬件设计
1.4程序设计
1.5下载验证

1.1 PCF8563简介
PCF8563是PHILIPS公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示:                              
image001.png
图 31.1.1 PCF8563功能框图
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,说明如下表所示:
QQ截图20231202165716.png
表 31.1.1 秒寄存器描述(地址02h)
当电源电压低于PCF8563器件的最低供电电压时,VL为“1”,表明内部完整的时钟周期信号不能被保证,可能导致时钟/日历数据不准确。
BCD编码的秒数值如下表所示:
QQ截图20231202165727.png
表 31.1.2 秒数值的BCD编码
寄存器的地址为03h,说明如下表所示:
QQ截图20231202165738.png
表 31.1.3 分钟寄存器描述(地址03h)
小时寄存器的地址为04h,说明如下表所示:

QQ截图20231202165747.png
表 31.1.4 小时寄存器描述(地址04h)
天寄存器的地址为05h,说明如下表所示:
QQ截图20231202165759.png
表 31.1.5 天寄存器描述(地址05h)

QQ截图20231202165808.png
表 31.1.6 月份表
年寄存器的地址为08h,说明如下表所示:
QQ截图20231202165816.png
表 31.1.7 寄存器(地址08h)


1.2 实验任务
本节的实验任务是通过ATK-DFPGL22G开发板上的PCF8563实时时钟芯片,根据输入按键KEY0来切换数码管显示时间或者日期。

1.3 硬件设计
ATK-DFPGL22G开发板上PCF8563接口部分的原理图如下图所示。
image005.png
图 31.3.1 PCF8563接口原理图
PCF8563作为I2C接口的从器件与EEPROM等模块统一挂接在ATK-DFPGL22G开发板上的IIC总线上。OSCI、OSCO与外部32.768KHz的晶振相连,为芯片提供驱动时钟;SCL和SDA分别是I2C总线的串行时钟接口和串行数据接口。
本实验中,各管脚分配如下表所示:

QQ截图20231202165840.png
表 31.3.1 RTC实时时钟数码管显示管脚分配

1.4 程序设计
根据实验任务,我们可以大致规划出系统的控制流程:首先通过I2C总线向PCF8563写入初始日期值(年月日)和时间值(时分秒),然后不断地读取日期和时间数据,并根据输入的按键,选择将日期或者时间数据显示到数码管上。由此画出系统的功能框图如下图所示:
image007.png
图 31.4.1 系统框图
由系统框图可知,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. 1  module rtc_seg_led(
  2. 2       input               sys_clk,     //系统时钟
  3. 3       input               sys_rst_n,   //系统复位
  4. 4  
  5. 5       //按键
  6. 6       input               key,         //输入按键KEY0
  7. 7      
  8. 8       //数码管
  9. 9       output        [5:0  seg_sel,     //数码管位选信号
  10. 10      output        [7:0  seg_led,     //数码管段选信号
  11. 11      
  12. 12      //RTC实时时钟
  13. 13      output              iic_scl,     //RTC的时钟线scl
  14. 14      inout               iic_sda      //RTC的数据线sda   
  15. 15      );                                                     
  16. 16
  17. 17 //parameter define
  18. 18 parameter    SLAVE_ADDR = 7'b101_0001   ;//器件地址(SLAVE_ADDR)
  19. 19 parameter    BIT_CTRL  = 1'b0          ; //字地址位控制参数(16b/8b)
  20. 20 parameter    CLK_FREQ  = 26'd50_000_000; //i2c_dri模块的驱动时钟频率(CLK_FREQ)
  21. 21 parameter    I2C_FREQ  = 18'd250_000  ; //I2C的SCL时钟频率
  22. 22 parameter    TIME_INIT = 48'h20_04_01_09_30_00;//初始时间
  23. 23
  24. 24 //wire define
  25. 25 wire          dri_clk   ;   //I2C操作时钟
  26. 26 wire          i2c_exec  ;   //I2C触发控制
  27. 27 wire  [15:0  i2c_addr  ;   //I2C操作地址
  28. 28 wire  [ 7:0  i2c_data_w;   //I2C写入的数据
  29. 29 wire          i2c_done  ;   //I2C操作结束标志
  30. 30 wire          i2c_ack   ;   //I2C应答标志 0:应答 1:未应答
  31. 31 wire          i2c_rh_wl ;   //I2C读写控制
  32. 32 wire  [ 7:0  i2c_data_r;   //I2C读出的数据
  33. 33
  34. 34 wire    [7:0  sec      ;   //秒
  35. 35 wire    [7:0  min      ;   //分
  36. 36 wire    [7:0  hour     ;   //时
  37. 37 wire    [7:0  day      ;   //日
  38. 38 wire    [7:0  mon      ;   //月
  39. 39 wire    [7:0  year     ;   //年
  40. 40
  41. 41 wire           key_value;   //消抖后的按键值
  42. 42 wire    [5:0  point    ;   //数码管小数点控制
  43. 43 wire    [23:0 disp_data;   //数码管显示的数值控制
  44. 44
  45. 45 //*****************************************************
  46. 46 //**                    main code
  47. 47 //*****************************************************
  48. 48
  49. 49 //i2c驱动模块
  50. 50 i2c_dri #(
  51. 51      .SLAVE_ADDR  (SLAVE_ADDR),  //从机地址
  52. 52      .CLK_FREQ    (CLK_FREQ  ),  //模块输入的时钟频率
  53. 53      .I2C_FREQ    (I2C_FREQ  )   //IIC_SCL的时钟频率
  54. 54 ) u_i2c_dri(
  55. 55      .clk         (sys_clk   ),  
  56. 56      .rst_n       (sys_rst_n ),  
  57. 57      //i2c interface
  58. 58      .i2c_exec    (i2c_exec  ),
  59. 59      .bit_ctrl    (BIT_CTRL  ),
  60. 60      .i2c_rh_wl   (i2c_rh_wl ),
  61. 61      .i2c_addr    (i2c_addr  ),
  62. 62      .i2c_data_w  (i2c_data_w),
  63. 63      .i2c_data_r  (i2c_data_r),
  64. 64      .i2c_done    (i2c_done  ),
  65. 65      .i2c_ack     (i2c_ack   ),
  66. 66      .scl         (iic_scl   ),
  67. 67      .sda         (iic_sda   ),
  68. 68      //user interface
  69. 69      .dri_clk     (dri_clk   )  
  70. 70 );
  71. 71
  72. 72 //PCF8563控制模块
  73. 73 pcf8563_ctrl #(
  74. 74      .TIME_INIT (TIME_INIT)
  75. 75     )u_pcf8563_ctrl(
  76. 76      .clk         (dri_clk   ),
  77. 77      .rst_n       (sys_rst_n ),
  78. 78      //IIC
  79. 79      .i2c_rh_wl   (i2c_rh_wl ),
  80. 80      .i2c_exec    (i2c_exec  ),
  81. 81      .i2c_addr    (i2c_addr  ),
  82. 82      .i2c_data_w  (i2c_data_w),
  83. 83      .i2c_data_r  (i2c_data_r),
  84. 84      .i2c_done    (i2c_done  ),
  85. 85      //时间和日期
  86. 86      .sec         (sec       ),
  87. 87      .min         (min       ),
  88. 88      .hour        (hour      ),
  89. 89      .day         (day       ),
  90. 90      .mon         (mon       ),
  91. 91      .year        (year      )
  92. 92      );
  93. 93
  94. 94 //消抖模块
  95. 95 key_debounce u_key_debounce(
  96. 96      .sys_clk     (sys_clk   ),    //外部50M时钟
  97. 97      .sys_rst_n   (sys_rst_n ),    //外部复位信号,低有效
  98. 98      .key         (key       ),    //外部按键输入
  99. 99      .key_value   (key_value ),    //按键消抖后的数据
  100. 100     .key_flag    ()               //按键数据有效信号
  101. 101 );
  102. 102
  103. 103 //显示值切换模块
  104. 104 key_sw_disp u_key_sw_disp(
  105. 105     .clk          (sys_clk),
  106. 106     .rst_n        (sys_rst_n),
  107. 107              
  108. 108     .key_value    (key_value),
  109. 109     .sec          (sec ),
  110. 110     .min          (min ),
  111. 111     .hour         (hour),
  112. 112     .day          (day ),
  113. 113     .mon          (mon ),
  114. 114     .year         (year),
  115. 115               
  116. 116     .point        (point),
  117. 117     .disp_data    (disp_data)
  118. 118     );
  119. 119
  120. 120 //数码管驱动模块
  121. 121 seg_bcd_dri u_seg_bcd_dri(
  122. 122   //input
  123. 123   .clk          (sys_clk   ),    //时钟信号
  124. 124   .rst_n        (sys_rst_n ),    //复位信号
  125. 125   .data         (disp_data ),    //6个数码管要显示的数值
  126. 126   .point        (point     ),    //小数点具体显示的位置,从左往右,高有效
  127. 127   //output
  128. 128   .seg_sel      (seg_sel   ),    //数码管位选
  129. 129   .seg_led      (seg_led   )     //数码管段选
  130. 130 );   
  131. 131     
  132. 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. 1  module pcf8563_ctrl #(
  2. 2       // 初始时间设置,从高到低为年到秒,各占8bit
  3. 3       parameter  TIME_INIT = 48'h20_10_26_09_30_00)(
  4. 4       input                clk       , //时钟信号
  5. 5       input                rst_n     , //复位信号
  6. 6  
  7. 7       //i2c interface
  8. 8       output   reg         i2c_rh_wl , //I2C读写控制信号
  9. 9       output   reg         i2c_exec  , //I2C触发执行信号
  10. 10      output   reg  [15:0  i2c_addr  , //I2C器件内地址
  11. 11      output   reg  [7:0   i2c_data_w, //I2C要写的数据
  12. 12      input         [7:0   i2c_data_r, //I2C读出的数据
  13. 13      input                i2c_done  , //I2C一次操作完成
  14. 14
  15. 15      //PCF8563T的秒、分、时、日、月、年数据
  16. 16      output   reg   [7:0  sec,        //秒
  17. 17      output   reg   [7:0  min,        //分
  18. 18      output   reg   [7:0  hour,       //时
  19. 19      output   reg   [7:0  day,        //日
  20. 20      output   reg   [7:0  mon,        //月
  21. 21      output   reg   [7:0  year        //年
  22. 22 );
  23. 23
  24. 24 //reg define
  25. 25 reg   [3:0    flow_cnt  ;            // 状态流控制
  26. 26 reg   [12:0   wait_cnt  ;            // 计数等待
  27. 27
  28. 28 //*****************************************************
  29. 29 //**                    main code
  30. 30 //*****************************************************
  31. 31
  32. 32 //先向PCF8563中写入初始化日期和时间,再从中读出日期和时间
  33. 33 always @(posedge clk or negedge rst_n) begin
  34. 34      if(!rst_n) begin
  35. 35          sec        <= 8'h0;
  36. 36          min        <= 8'h0;
  37. 37          hour      <= 8'h0;
  38. 38          day        <= 8'h0;
  39. 39          mon        <= 8'h0;
  40. 40          year       <= 8'h0;
  41. 41          i2c_exec   <=1'b0;
  42. 42          i2c_rh_wl  <= 1'b0;
  43. 43          i2c_addr   <=8'd0;
  44. 44          i2c_data_w <= 8'd0;
  45. 45          flow_cnt   <=4'd0;
  46. 46          wait_cnt   <=13'd0;
  47. 47      end
  48. 48      else begin
  49. 49          i2c_exec <= 1'b0;
  50. 50          case(flow_cnt)
  51. 51              //上电初始化
  52. 52              4'd0: begin
  53. 53                  if(wait_cnt == 13'd8000) begin
  54. 54                      wait_cnt<= 12'd0;
  55. 55                      flow_cnt<= flow_cnt + 1'b1;
  56. 56                  end
  57. 57                  else
  58. 58                      wait_cnt<= wait_cnt + 1'b1;
  59. 59              end
  60. 60              //写读秒
  61. 61              4'd1: begin
  62. 62                  i2c_exec  <= 1'b1;
  63. 63                  i2c_addr  <= 8'h02;
  64. 64                  flow_cnt  <= flow_cnt + 1'b1;
  65. 65                  i2c_data_w<= TIME_INIT[7:0];
  66. 66              end
  67. 67              4'd2: begin
  68. 68                  if(i2c_done == 1'b1) begin
  69. 69                      sec     <= i2c_data_r[6:0];
  70. 70                      flow_cnt<= flow_cnt + 1'b1;
  71. 71                  end
  72. 72              end
  73. 73              //写读分
  74. 74              4'd3: begin
  75. 75                  i2c_exec <= 1'b1;
  76. 76                  i2c_addr  <= 8'h03;
  77. 77                  flow_cnt  <= flow_cnt + 1'b1;
  78. 78                  i2c_data_w<= TIME_INIT[15:8];
  79. 79              end
  80. 80              4'd4: begin
  81. 81                  if(i2c_done == 1'b1) begin
  82. 82                      min     <= i2c_data_r[6:0];
  83. 83                      flow_cnt<= flow_cnt + 1'b1;
  84. 84                  end
  85. 85              end
  86. 86              //写读时
  87. 87              4'd5: begin
  88. 88                  i2c_exec  <= 1'b1;
  89. 89                  i2c_addr  <= 8'h04;
  90. 90                  flow_cnt  <= flow_cnt + 1'b1;
  91. 91                  i2c_data_w<= TIME_INIT[23:16];
  92. 92              end
  93. 93              4'd6: begin
  94. 94                  if(i2c_done == 1'b1) begin
  95. 95                      hour    <= i2c_data_r[5:0];
  96. 96                      flow_cnt<= flow_cnt + 1'b1;
  97. 97                  end
  98. 98              end
  99. 99              //写读天
  100. 100             4'd7: begin
  101. 101                 i2c_exec  <= 1'b1;
  102. 102                 i2c_addr  <= 8'h05;
  103. 103                 flow_cnt  <= flow_cnt + 1'b1;
  104. 104                 i2c_data_w<= TIME_INIT[31:24];
  105. 105             end
  106. 106             4'd8: begin
  107. 107                 if(i2c_done == 1'b1) begin
  108. 108                     day     <= i2c_data_r[5:0];
  109. 109                     flow_cnt<= flow_cnt + 1'b1;
  110. 110                 end
  111. 111             end
  112. 112             //写读月
  113. 113             4'd9: begin
  114. 114                 i2c_exec  <= 1'b1;
  115. 115                 i2c_addr  <= 8'h07;
  116. 116                 flow_cnt  <= flow_cnt + 1'b1;
  117. 117                 i2c_data_w<= TIME_INIT[39:32];
  118. 118             end
  119. 119             4'd10: begin
  120. 120                 if(i2c_done == 1'b1) begin
  121. 121                     mon     <= i2c_data_r[4:0];
  122. 122                     flow_cnt<= flow_cnt + 1'b1;
  123. 123                 end
  124. 124             end
  125. 125             //写读年
  126. 126             4'd11: begin
  127. 127                 i2c_exec  <= 1'b1;
  128. 128                 i2c_addr  <= 8'h08;
  129. 129                 flow_cnt  <= flow_cnt + 1'b1;
  130. 130                 i2c_data_w<= TIME_INIT[47:40];
  131. 131             end
  132. 132             4'd12: begin
  133. 133                 if(i2c_done == 1'b1) begin
  134. 134                     year     <= i2c_data_r;
  135. 135                     i2c_rh_wl<= 1'b1;
  136. 136                     flow_cnt <= 4'd1;
  137. 137                 end
  138. 138             end
  139. 139             default: flow_cnt <= 4'd0;
  140. 140         endcase
  141. 141     end
  142. 142 end
  143. 143
  144. 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等于奇数的时间很短,所以需要放大波形才能便于观察。
image009.png
图 31.4.2 Fabric Debugger波形图
显示值切换模块代码如下:
  1. 1 module key_sw_disp(                                                            
  2. 2      input               clk       , //时钟  
  3. 3      input               rst_n     , //复位  
  4. 4                                                            
  5. 5      input               key_value , //按键
  6. 6      input       [7:0    sec       , //秒
  7. 7      input       [7:0    min       , //分钟
  8. 8      input       [7:0    hour      , //小时
  9. 9      input       [7:0    day       , //日
  10. 10     input       [7:0    mon       , //月
  11. 11     input       [7:0    year      , //年
  12. 12     
  13. 13     output      [5:0    point     , //数码管小数点控制
  14. 14     output      [23:0  disp_data   //数码管显示的数值控制
  15. 15      );
  16. 16
  17. 17 //reg define
  18. 18 reg   sw_flag     ;
  19. 19 reg   key_value_d0;
  20. 20 reg   key_value_d1;
  21. 21
  22. 22 //wire define
  23. 23 wire  neg_key_value;
  24. 24      
  25. 25 //*****************************************************
  26. 26 //**                   main code
  27. 27 //*****************************************************     
  28. 28
  29. 29 //采集输入信号的下降沿
  30. 30 assign neg_key_value = key_value_d1 & (~key_value_d0);
  31. 31 //切换输出数码管要显示的数据
  32. 32 assign disp_data = (sw_flag == 1'b0) ? {sec,min,hour} : {day,mon,year};
  33. 33 //数码管小数点显示位置
  34. 34 assign point = (sw_flag == 1'b0) ? 6'b010100: 6'b000100;
  35. 35
  36. 36 //对输入的按键信号打两拍
  37. 37 always @(posedge clk or negedge rst_n) begin
  38. 38     if(!rst_n) begin
  39. 39         key_value_d0 <= 1'b0;
  40. 40         key_value_d1 <= 1'b0;
  41. 41     end
  42. 42     else begin
  43. 43         key_value_d0 <= key_value;
  44. 44         key_value_d1 <= key_value_d0;
  45. 45     end
  46. 46 end
  47. 47
  48. 48 //控制sw_flag信号翻转
  49. 49 always @(posedge clk or negedge rst_n) begin
  50. 50     if(!rst_n)
  51. 51         sw_flag <= 1'b0;
  52. 52     else if(neg_key_value)
  53. 53         sw_flag <= ~sw_flag;
  54. 54 end        
  55. 55
  56. 56 endmodule
复制代码
由于数码管总共可以显示6位数据,没有办法同时显示日期和时间,因此根据输入的按键来切换数码管显示日期和时间。
sw_flag信号用来切换数码管显示日期和时间,sw_flag初始值为0,当检查到输入按键的下降沿后,sw_flag取反,如程序中第49至54行所示。当sw_flag等于0时,数码管显示时间值;当sw_flag等于1时,数码管显示日期值,如程序中第32行代码所示。
数码管BCD驱动模块的代码如下所示:
  1. 1  module seg_bcd_dri(
  2. 2      //input
  3. 3      input                 clk    ,         // 时钟信号
  4. 4      input                 rst_n  ,         // 复位信号
  5. 5      input        [23:0    data   ,         // 6个数码管要显示的数值
  6. 6      input        [5:0     point  ,         // 小数点具体显示的位置,从高到低,高有效
  7. 7      input                 en,
  8. 8  
  9. 9      //output
  10. 10     output  reg  [5:0     seg_sel,         // 数码管位选
  11. 11     output  reg  [7:0    seg_led          // 数码管段选
  12. 12 );
  13. 13
  14. 14 //parameter define
  15. 15 localparam  CLK_DIVIDE = 4'd10     ;       // 时钟分频系数
  16. 16 localparam  MAX_NUM   = 13'd5000 ;       // 对数码管驱动时钟(5MHz)计数1ms所需的计数值
  17. 17
  18. 18
  19. 19 //reg define
  20. 20 reg    [15:0            cnt0     ;       // 1ms计数
  21. 21 reg    [7:0             cnt      ;       // 切换显示数码管用
  22. 22 reg    [3:0             data1    ;       // 送给要显示的数码管,要亮的灯
  23. 23 reg                       point1   ;       // 要显示的小数点
  24. 24 reg    [3:0             clk_cnt  ;       // 时钟分频计数器
  25. 25 reg                       dri_clk  ;       // 数码管的驱动时钟,5MHz
  26. 26 reg    [12:0            cnt0     ;       // 数码管驱动时钟计数器
  27. 27 reg                       flag     ;       // 标志信号(标志着cnt0计数达1ms)
  28. 28 reg    [7:0             cnt_sel  ;       // 数码管位选计数器
  29. 29
  30. 30 //*****************************************************
  31. 31 //**                    main code
  32. 32 //*****************************************************
  33. 33
  34. 34 //对系统时钟10分频,得到的频率为5MHz的数码管驱动时钟dri_clk
  35. 35 always @(posedge clk or negedge rst_n) begin
  36. 36     if(!rst_n) begin
  37. 37         clk_cnt <= 4'd0;
  38. 38         dri_clk <= 1'b1;
  39. 39     end
  40. 40     else if(clk_cnt == CLK_DIVIDE / 2 - 1) begin
  41. 41         clk_cnt <= 4'd0;
  42. 42         dri_clk <= ~dri_clk;
  43. 43     end
  44. 44     else begin
  45. 45         clk_cnt <= clk_cnt +1'b1;
  46. 46         dri_clk <= dri_clk;
  47. 47     end
  48. 48 end
  49. 49
  50. 50 //每当计数器对数码管驱动时钟计数时间达1ms,输出一个时钟周期的脉冲信号
  51. 51 always @ (posedge dri_clk or negedge rst_n) begin
  52. 52      if (rst_n == 1'b0) begin
  53. 53          cnt0 <= 13'b0;
  54. 54          flag <= 1'b0;
  55. 55       end
  56. 56      else if (cnt0 < MAX_NUM - 1'b1) begin
  57. 57          cnt0 <= cnt0 + 1'b1;
  58. 58          flag <= 1'b0;
  59. 59       end
  60. 60      else begin
  61. 61          cnt0 <= 13'b0;
  62. 62          flag <= 1'b1;
  63. 63       end
  64. 64 end
  65. 65
  66. 66 //cnt_sel从0计数到5,用于选择当前处于显示状态的数码管
  67. 67 always @ (posedge dri_clk or negedge rst_n) begin
  68. 68      if (rst_n == 1'b0)
  69. 69          cnt_sel <= 8'b0;
  70. 70      else if(flag) begin
  71. 71          if(cnt_sel < 8'd11)
  72. 72              cnt_sel <= cnt_sel + 1'b1;
  73. 73         else
  74. 74              cnt_sel <= 8'b0;
  75. 75      end
  76. 76      else
  77. 77          cnt_sel <= cnt_sel;
  78. 78 end
  79. 79
  80. 80 //控制数码管位选信号,使6位数码管轮流显示
  81. 81 always @ (posedge dri_clk or negedge rst_n) begin
  82. 82      if(!rst_n) begin
  83. 83          seg_sel <= 6'b000000;              //位选信号低电平有效
  84. 84          data1 <= 4'b0;           
  85. 85          point1 <= 1'b1;                    //共阳极数码管,低电平导通
  86. 86      end
  87. 87      else begin
  88. 88          if(en) begin
  89. 89              case (cnt_sel)
  90. 90                  8'd0 :begin
  91. 91                      seg_sel <= 6'b000001;  //显示数码管最低位
  92. 92                      data1 <= data[7:4 ;    //显示的数据
  93. 93                      point1 <= ~point[5];    //显示的小数点
  94. 94                  end
  95. 95                  8'd1 :begin
  96. 96                      seg_sel <= 6'b000000;  //清空使MOS放电
  97. 97                      data1 <= 4'd10;
  98. 98                  end
  99. 99                  8'd2 :begin
  100. 100                     seg_sel <= 6'b000010;  //显示数码管第1位
  101. 101                     data1 <= data[3:0 ;
  102. 102                     point1 <= ~point[4];
  103. 103                 end
  104. 104                 8'd3 :begin
  105. 105                     seg_sel <= 6'b000000;  //清空使MOS放电
  106. 106                     data1 <= 4'd10;
  107. 107                 end
  108. 108                 8'd4 :begin
  109. 109                     seg_sel <= 6'b000100;  //显示数码管第2位
  110. 110                     data1 <= data[15:12];
  111. 111                     point1 <= ~point[3];
  112. 112                 end
  113. 113                 8'd5 :begin
  114. 114                     seg_sel <= 6'b000000;   //清空使MOS放电
  115. 115                     data1 <= 4'd10;
  116. 116                 end
  117. 117                 8'd6 :begin
  118. 118                     seg_sel <= 6'b001000;   //显示数码管第3位
  119. 119                     data1 <= data[11:8];
  120. 120                     point1 <= ~point[2];
  121. 121                 end
  122. 122                 8'd7 :begin
  123. 123                     seg_sel <= 6'b000000;   //清空使MOS放电
  124. 124                     data1 <= 4'd10;
  125. 125                 end
  126. 126                 8'd8 :begin
  127. 127                     seg_sel <= (6'b010000); //显示数码管第4位
  128. 128                     data1 <= data[23:20];
  129. 129                     point1 <= ~point[1];
  130. 130                 end
  131. 131                 8'd9 :begin
  132. 132                     seg_sel <= 6'b000000;   //清空使MOS放电
  133. 133                     data1 <= 4'd10;
  134. 134                 end
  135. 135                 8'd10 :begin
  136. 136                     seg_sel <= 6'b100000;   //显示数码管最高位
  137. 137                     data1 <= data[19:16];
  138. 138                     point1 <= ~point[0];
  139. 139                 end
  140. 140                 8'd11 :begin
  141. 141                     seg_sel <= 6'b000000;  //清空使MOS放电
  142. 142                     data1 <= 4'd10;
  143. 143                 end
  144. 144                 default :begin
  145. 145                     seg_sel <= 6'b000000;
  146. 146                     data1 <= 4'b0;
  147. 147                     point1 <= 1'b1;
  148. 148                 end
  149. 149             endcase
  150. 150         end
  151. 151         else begin
  152. 152             seg_sel <= 6'b000000;          //使能信号为0时,所有数码管均不显示
  153. 153             data1 <= 4'b0;
  154. 154             point1 <= 1'b1;
  155. 155         end
  156. 156     end
  157. 157 end
  158. 158
  159. 159 //数码管显示数据
  160. 160 always @ (posedge dri_clk or negedge rst_n) begin
  161. 161     if(rst_n == 1'b0)
  162. 162         seg_led <= ~(8'hc0);
  163. 163     else begin
  164. 164         case(data1)
  165. 165             4'd0: seg_led <= ~{point1,7'b1000000};
  166. 166             4'd1: seg_led <= ~{point1,7'b1111001};
  167. 167             4'd2: seg_led <= ~{point1,7'b0100100};
  168. 168             4'd3: seg_led <= ~{point1,7'b0110000};
  169. 169             4'd4: seg_led <= ~{point1,7'b0011001};
  170. 170             4'd5: seg_led <= ~{point1,7'b0010010};
  171. 171             4'd6: seg_led <= ~{point1,7'b0000010};
  172. 172             4'd7: seg_led <= ~{point1,7'b1111000};
  173. 173             4'd8: seg_led <= ~{point1,7'b0000000};
  174. 174             4'd9: seg_led <= ~{point1,7'b0010000};
  175. 175             4'd10: seg_led <= ~8'b11111111;           //不显示任何字符
  176. 176             default: seg_led <= ~{point1,7'b1000000};
  177. 177         endcase
  178. 178     end
  179. 179 end
  180. 180 endmodule
复制代码
由于是PCF8563的数据是BCD编码,从低到高每4位二进制数代表一位十进制数,所以在第89行的case语句块中,我们只需把data信号相应位的值赋给data1即可。有关数码管显示更详细的解释可参考“动态数码管显示实验”。

1.5 下载验证
首先我们将下载器与开发板上的JTAG接口连接,下载器另外一端与电脑连接,然后连接电源线并打开电源开关。
最后我们下载程序,验证RTC实时时钟数码管显示功能。程序下载完成后观察到开发板上数码管显示的值为我们设置的初始日期值,并且在不断实时变化;当按下KEY0按键后,数码管显示年月日,说明PCF8563实时时钟数码管显示实验程序下载验证成功。
数码管显示的日期如下图所示:
image011.png
图 31.5.1 数码管显示日期
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:00

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表