搜索
bottom↓
回复: 0

《新起点V2之FPGA开发指南》第三十一章 RTC时钟数码管显示

[复制链接]

出0入234汤圆

发表于 2021-10-11 16:03:25 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2021-10-30 10:39 编辑

1)实验平台:正点原子新起点V2FPGA开发板
2)  章节摘自【正点原子】《新起点之FPGA开发指南 V2.1》
3)购买链接:https://detail.tmall.com/item.htm?id=609758951113
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-328002-1-1.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:712557122
1.png


2.jpg


3.png


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

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

1.1简介
       PCF8563是PHILIPS公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图如下图所示:
第三十一章 RTC实时时钟数码管显示实验251.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,说明如下表所示:
10.png

表 31.1.1 秒寄存器描述(地址02h)

       当电源电压低于PCF8563器件的最低供电电压时,VL为“1”,表明内部完整的时钟周期信号不能被保证,可能导致时钟/日历数据不准确。
       BCD编码的秒数值如下表所示:
第三十一章 RTC实时时钟数码管显示实验1090.png

图 31.1.2 秒数值的BCD编码

       分寄存器的地址为03h,说明如下表所示:
11.png


表 31.1.2 分钟寄存器描述(地址03h)

       小时寄存器的地址为04h,说明如下表所示:
12.png

表 31.1.3 小时寄存器描述(地址04h)

       天寄存器的地址为05h,说明如下表所示:
13.png


表 31.1.4 天寄存器描述(地址05h)

       当年计数器的值是闰年时,PCF8563自动给二月增加一个值,使其成为29天。
       月/世纪寄存器的地址为07h,说明如下表所示:
14.png


表 31.1.5 月/世纪寄存器(地址07h)

15.png

表 31.1.6 月份表

       年寄存器的地址为08h,说明如下表所示:
16.png


表 31.1.7 寄存器(地址08h)

1.2实验任务
       本节的实验任务是通过新起点开发板上的PCF8563实时时钟芯片,根据输入按键KEY0来切换数码管显示时间或者日期。
1.3硬件设计
       新起点开发板上PCF8563接口部分的原理图如下图所示。
第三十一章 RTC实时时钟数码管显示实验2219.png

图 31.3.1 PCF8563接口原理图

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


表 31.3.1 RTC实时时钟数码管显示管脚分配

        对应的管脚分配约束语句如下所示:
  1. #系统时钟和复位
  2. set_location_assignment PIN_M2 -to sys_clk
  3. set_location_assignment PIN_M1 -to sys_rst_n

  4. #KEY0
  5. set_location_assignment PIN_E16 -to key

  6. #IIC
  7. set_location_assignment PIN_D8 -to iic_scl
  8. set_location_assignment PIN_C8 -to iic_sda

  9. #数码管
  10. set_location_assignment PIN_N16 -to seg_sel[0]
  11. set_location_assignment PIN_N15 -to seg_sel[1]
  12. set_location_assignment PIN_P16 -to seg_sel[2]
  13. set_location_assignment PIN_P15 -to seg_sel[3]
  14. set_location_assignment PIN_R16 -to seg_sel[4]
  15. set_location_assignment PIN_T15 -to seg_sel[5]
  16.                                              
  17. set_location_assignment PIN_M11 -to seg_led[0]
  18. set_location_assignment PIN_N12 -to seg_led[1]
  19. set_location_assignment PIN_C9 -to seg_led[2]
  20. set_location_assignment PIN_N13 -to seg_led[3]
  21. set_location_assignment PIN_M10 -to seg_led[4]
  22. set_location_assignment PIN_N11 -to seg_led[5]
  23. set_location_assignment PIN_P11 -to seg_led[6]
  24. set_location_assignment PIN_D9 -to seg_led[7]
复制代码

1.4程序设计
       根据实验任务,我们可以大致规划出系统的控制流程:首先通过I2C总线向PCF8563写入初始日期值(年月日)和时间值(时分秒),然后不断地读取日期和时间数据,并根据输入的按键,选择将日期或者时间数据显示到数码管上。由此画出系统的功能框图如下图所示:
第三十一章 RTC实时时钟数码管显示实验4175.png

图 31.4.1 系统框图

       由系统框图可知,FPGA顶层模块例化了以下五个模块、分别是IIC驱动模块(i2c_dri)、PCF8563控制模块(pcf8563_ctrl)、按键消抖模块(key_debounce)、显示值切换模块(key_sw_disp)以及数码管BCD驱动模块(seg_bcd_dri)。
各模块端口及信号连接如下图所示:
第三十一章 RTC实时时钟数码管显示实验4391.png

图 31.4.2 顶层模块原理图

       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中读取秒、分、时、日、月和年。
       下图为SignalTap抓取的波形图,从图中可以看到当前读到的时间为20年4月1日09:31:29。需要说明的是,flow_cnt在循环从0累加至12,由于flow_cnt等于奇数值的时间很短,所以图中奇数值被隐藏。
第三十一章 RTC实时时钟数码管显示实验14541.png

图 31.4.3 SignalTap II波形图

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

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

图 31.5.1 数码管显示日期


回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 10:52

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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