完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-13912-1-1.html 第三十章 实时时钟数码管显示实验 PCF8563是一款多功能时钟/日历芯片。因其功耗低、控制简单、封装小而广泛应用于电表、 水表、传真机、便携式仪器等产品中。本章我们将使用FPGA开发板上的PCF8563器件实现实时 时钟的显示。 本章包括以下几个部分: 30.1 PCF8563简介 30.2 实验任务 30.3 硬件设计 30.4 程序设计 30.5 下载验证 PCF8563简介 PCF8563是PHILIPS公司推出的一款工业级多功能时钟/日历芯片,具有报警功能、定时器 功能、时钟输出功能以及中断输出功能,能完成各种复杂的定时服务。其内部功能模块的框图 如图 30.1.1所示: 图 30.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进行通信。使用该器件时,先通过I2C接口向该器件相应的寄 存器写入初始的时间数据(秒~年),然后通过I2C接口读取相应的寄存器的时间数据。有关I2C 总线协议详细的介绍请大家参考“EEPROM读写实验”。 下面我们对本次实验用到的寄存器做简要的描述和说明,其他寄存器的描述和说明,请大 家参考PCF8563的数据手册。 秒寄存器的的地址为02h,说明如下表所示: 表 30.1.1 秒寄存器描述(地址02h) 当电源电压低于PCF8563器件的最低供电电压时,VL为“1”,表明内部完整的时钟周期信 号不能被保证,可能导致时钟/日历数据不准确。 BCD编码的秒数值如表 30.1.2所示: 表 30.1.2 秒数值的BCD编码 秒寄存器的地址为03h,说明如下表所示: 表 30.1.3 分钟寄存器描述(地址03h) 小时寄存器的地址为04h,说明如下表所示: 表 30.1.4 小时寄存器描述(地址04h) 天寄存器的地址为05h,说明如下表所示: 表 30.1.5 天寄存器描述(地址05h) 当年计数器的值是闰年时,PCF8563自动给二月增加一个值,使其成为29天。 月/世纪寄存器的地址为07h,说明如下表所示: 表 30.1.6 月/世纪寄存器(地址07h) 表 30.1.7 月份表 年寄存器的地址为08h,说明如下表所示: 表 30.1.8 寄存器(地址08h) 实验任务 本节实验任务是使用开拓者开发板上的PCF8563实时时钟模块通过数码管显示时间,数码 管默认显示年月日,按下KEY2之后显示时分秒,再次按下KEY2之后显示年月日。 硬件设计 开拓者开发板上PCF8563接口部分的原理图如图 30.3.1所示。 图 30.3.1 PCF8563接口原理图 PCF8563作为I2C接口的从器件与EEPROM等模块统一挂接在开拓者开发板上的IIC总线上。 OSCI、OSCO与外部32.768KHz的晶振相连,为芯片提供驱动时钟;SCL和SDA分别是I2C总线的串 行时钟接口和串行数据接口。 本实验中,各端口信号的管脚分配如下表所示: 表 30.3.1 PCF8563实时时钟数码管显示管脚分配 程序设计 根据实验任务,我们可以大致规划出系统的控制流程:FPGA首先通过I2C总线向PCF8563写 入初始时间值,然后读取时间数据,并在按键的控制下将读到的时间数据显示到数码管上。由 此画出系统的功能框图如下所示: 图 30.4.1 PCF8563T实时时钟数码管显示系统框图 由系统框图可知,FPGA部分包括五个模块,顶层模块(rtc)、IIC驱动模块(i2c_dri)、 PCF8563实时时钟模块(pcf8563)、按键消抖模块(key_debounce)以及数码管BCD驱动模块 (seg_bcd_dri)。其中在顶层模块中完成对另外四个模块的例化,并实现各模块控制及数据 信号的交互。 各模块端口及信号连接如图 30.4.2所示: 图 30.4.2 顶层模块原理图 PCF8563实时时钟模块(pcf8563)通过调用IIC驱动模块(i2c_dri)来实现对PCF8563实 时时钟数据的读取;同时根据按键消抖模块(key_debounce)输出的按键数据(key_value) 选择显示时间num(年月日/时分秒),并将其传递给数码管BCD驱动模块(seg_bcd_dri)显示。 顶层模块的代码如下: 1 module rtc( 2 //system clock 3 input sys_clk , // 系统时钟 4 input sys_rst_n , // 系统复位 5 6 //pcf8563 interface 7 output rtc_scl , // i2c时钟线 8 inout rtc_sda , // i2c数据线 9 10 //user interface 11 input key0 , // 开关按键 12 output [5:0] sel , // 数码管位选 13 output [7:0] seg_led // 数码管段选 14 ); 15 16 //parameter define 17 parameter SLAVE_ADDR = 7'h51 ; // 器件地址 18 parameter BIT_CTRL = 1'b0 ; // 字地址位控制参数(16b/8b) 19 parameter CLK_FREQ = 26'd50_000_000; // i2c_dri模块的驱动时钟频率(CLK_FREQ) 20 parameter I2C_FREQ = 18'd250_000 ; // I2C的SCL时钟频率 21 parameter POINT = 6'b010100 ; // 控制点亮数码管小数点的位置 22 //初始时间设置,从高到低为年到秒,各占8bit 23 parameter TIME_INI = 48'h18_05_23_09_30_00; 24 25 //wire define 26 wire clk ; // I2C操作时钟 27 wire i2c_exec ; // i2c触发控制 28 wire [15:0] i2c_addr ; // i2c操作地址 29 wire [ 7:0] i2c_data_w; // i2c写入的数据 30 wire i2c_done ; // i2c操作结束标志 31 wire i2c_rh_wl ; // i2c读写控制 32 wire [ 7:0] i2c_data_r; // i2c读出的数据 33 wire [23:0] num ; // 数码管要显示的数据 34 wire key_value ; // 按键消抖后的数据 35 36 //***************************************************** 37 //** main code 38 //***************************************************** 39 40 //例化i2c_dri,调用IIC协议 41 i2c_dri #( 42 .SLAVE_ADDR (SLAVE_ADDR), // slave address从机地址,放此处方便参数传递 43 .CLK_FREQ (CLK_FREQ ), // i2c_dri模块的驱动时钟频率(CLK_FREQ) 44 .I2C_FREQ (I2C_FREQ ) // I2C的SCL时钟频率 45 ) u_i2c_dri( 46 //global clock 47 .clk (sys_clk ), // i2c_dri模块的驱动时钟(CLK_FREQ) 48 .rst_n (sys_rst_n ), // 复位信号 49 //i2c interface 50 .i2c_exec (i2c_exec ), // I2C触发执行信号 51 .bit_ctrl (BIT_CTRL ), // 器件地址位控制(16b/8b) 52 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号 53 .i2c_addr (i2c_addr ), // I2C器件内地址 54 .i2c_data_w (i2c_data_w), // I2C要写的数据 55 .i2c_data_r (i2c_data_r), // I2C读出的数据 56 .i2c_done (i2c_done ), // I 2C一次操作完成 57 .scl (rtc_scl ), // I2C的SCL时钟信号 58 .sda (rtc_sda ), // I2C的SDA信号 59 //user interface 60 .dri_clk (clk ) // I2C操作时钟 61 ); 62 63 //例化PCF8563测量模块 64 pcf8563 #(.TIME_INI(TIME_INI) 65 ) u_pcf8563( 66 //system clock 67 .clk (clk ), // 时钟信号 68 .rst_n (sys_rst_n ), // 复位信号 69 //i2c interface 70 .i2c_rh_wl (i2c_rh_wl ), // I2C读写控制信号 71 .i2c_exec (i2c_exec ), // I2C触发执行信号 72 .i2c_addr (i2c_addr ), // I2C器件内地址 73 .i2c_data_w (i2c_data_w), // I2C要写的数据 74 .i2c_data_r (i2c_data_r), // I2C读出的数据 75 .i2c_done (i2c_done ), // I2C一次操作完成 76 //user interface 77 .key_value (key_value ), // 按键切换输入 78 .num (num ) // 数码管要显示的数据 79 ); 80 81 //例化数码管驱动模块 82 seg_bcd_dri u_seg_bcd_dri( 83 //input 84 .clk (sys_clk ), // 时钟信号 85 .rst_n (sys_rst_n ), // 复位信号 86 .num (num ), // 6个数码管要显示的数值 87 .point (POINT ), // 小数点具体显示的位置,从高到低,高有效 88 //output 89 .sel (sel ), // 数码管位选 90 .seg_led (seg_led ) // 数码管段选 91 ); 92 93 //例化消抖模块 94 key_debounce u_key_debounce( 95 .clk (sys_clk ), //外部50M时钟 96 .rst_n (sys_rst_n ), //外部复位信号,低有效 97 .key (key0 ), //外部按键输入 98 .key_value (key_value ), //按键消抖后的数据 99 .key_flag () //按键数据有效信号 100 ); 101 102 endmodule 程序中第23行的TIME_INI参数是PCF8563初始化时的时间数据,可以通过修改此参数值使 PCF8563从不同的时间开始计时,例如从2018年5月23号09:30:00开始计时,需要将该参数值 设置为48’h180523093000。 顶层模块中主要完成对其余模块的例化。其中I2C驱动模块(i2c_dri)程序与“EEPROM读 写实验”章节中的IIC驱动模块(i2c_dri)程序完全相同,有关IIC驱动模块的详细介绍请大 家参考“EEPROM读写实验”。按键消抖模块可参考“按键控制蜂鸣器实验”。 PCF8563实时时钟模块的代码如下所示: 1 module pcf8563 #( 2 // 初始时间设置,从高到低为年到秒,各占8bit 3 parameter TIME_INI = 48'h18_03_19_09_30_00)( 4 //system clock 50MHz 5 input clk , // 时钟信号 6 input rst_n , // 复位信号 7 8 //i2c interface 9 output reg i2c_rh_wl , // I2C读写控制信号 10 output reg i2c_exec , // I2C触发执行信号 11 output reg [15:0] i2c_addr , // I2C器件内地址 12 output reg [ 7:0] i2c_data_w , // I2C要写的数据 13 input [ 7:0] i2c_data_r , // I2C读出的数据 14 input i2c_done , // I2C一次操作完成 15 16 //user interface 17 input key_value , // 按键切换输入 18 output [23:0] num // 数码管要显示的数据 19 ); 20 21 //reg define 22 reg key_dy0 ; // 延迟打拍 23 reg key_dy1 ; // 延迟打拍 24 reg switch ; // 按键切换显示日期、时间 25 reg [3:0] flow_cnt ; // 状态流控制 26 reg [12:0] wait_cnt ; // 计数等待 27 28 //PCF8563T的秒、分、时、日、月、年数据 29 reg [7:0] sec ; // 秒 30 reg [7:0] min ; // 分 31 reg [7:0] hour ; // 时 32 reg [7:0] day ; // 日 33 reg [7:0] mon ; // 月 34 reg [7:0] year ; // 年 35 wire [23:0] rtc_time ; //时间,从低位到高位依次是秒、分、时,各8bit 36 wire [23:0] rtc_date ; //日期,从低位到高位依次是日、月、年,各8bit 37 38 //wire define 39 wire neg_sap ; // 采下降沿得到的信号 40 41 //***************************************************** 42 //** main code 43 //***************************************************** 44 45 assign neg_sap = (~key_dy0 & key_dy1); // 按键按下时,得到一个周期的高电平信号 46 assign rtc_time = {hour,min,sec}; 47 assign rtc_date = {year,mon,day}; 48 //通过switch切换时间/日期显示 49 assign num = switch ? rtc_time : rtc_date; 50 51 //打拍(采按键时的下降沿) 52 always @(posedge clk or negedge rst_n) begin 53 if(!rst_n) begin 54 key_dy0 <= 1'b1; 55 key_dy1 <= 1'b1; 56 end 57 else begin 58 key_dy0 <= key_value; 59 key_dy1 <= key_dy0 ; 60 end 61 end 62 63 //按键切换 64 always @(posedge clk or negedge rst_n ) begin 65 if(!rst_n) 66 switch<= 1'b0; 67 else if (neg_sap) 68 switch <= ~switch; 69 end 70 71 //从PCF8563T读出的时间、日期数据 72 always @(posedge clk or negedge rst_n) begin 73 if(!rst_n) begin 74 sec <= 8'h0; 75 min <= 8'h0; 76 hour <= 8'h0; 77 day <= 8'h0; 78 mon <= 8'h0; 79 year <= 8'h0; 80 i2c_exec <= 1'b0; 81 i2c_rh_wl <= 1'b0; 82 i2c_addr <= 8'd0; 83 i2c_data_w <= 8'd0; 84 flow_cnt <= 4'd0; 85 wait_cnt <= 13'd0; 86 end 87 else begin 88 i2c_exec <= 1'b0; 89 case(flow_cnt) 90 //上电初始化 91 4'd0: begin 92 if(wait_cnt == 13'd8000) begin 93 wait_cnt<= 12'd0; 94 flow_cnt<= flow_cnt + 1'b1; 95 end 96 else 97 wait_cnt<= wait_cnt + 1'b1; 98 end 99 //写读秒 100 4'd1: begin 101 i2c_exec <= 1'b1; 102 i2c_addr <= 8'h02; 103 flow_cnt <= flow_cnt + 1'b1; 104 i2c_data_w<= TIME_INI[7:0]; 105 end 106 4'd2: begin 107 if(i2c_done == 1'b1) begin 108 sec <= i2c_data_r[6:0]; 109 flow_cnt<= flow_cnt + 1'b1; 110 end 111 end 112 //写读分 113 4'd3: begin 114 i2c_exec <= 1'b1; 115 i2c_addr <= 8'h03; 116 flow_cnt <= flow_cnt + 1'b1; 117 i2c_data_w<= TIME_INI[15:8]; 118 end 119 4'd4: begin 120 if(i2c_done == 1'b1) begin 121 min <= i2c_data_r[6:0]; 122 flow_cnt<= flow_cnt + 1'b1; 123 end 124 end 125 //写读时 126 4'd5: begin 127 i2c_exec <= 1'b1; 128 i2c_addr <= 8'h04; 129 flow_cnt <= flow_cnt + 1'b1; 130 i2c_data_w<= TIME_INI[23:16]; 131 end 132 4'd6: begin 133 if(i2c_done == 1'b1) begin 134 hour <= i2c_data_r[5:0]; 135 flow_cnt<= flow_cnt + 1'b1; 136 end 137 end 138 //写读天 139 4'd7: begin 140 i2c_exec <= 1'b1; 141 i2c_addr <= 8'h05; 142 flow_cnt <= flow_cnt + 1'b1; 143 i2c_data_w<= TIME_INI[31:24]; 144 end 145 4'd8: begin 146 if(i2c_done == 1'b1) begin 147 day <= i2c_data_r[5:0]; 148 flow_cnt<= flow_cnt + 1'b1; 149 end 150 end 151 //写读月 152 4'd9: begin 153 i2c_exec <= 1'b1; 154 i2c_addr <= 8'h07; 155 flow_cnt <= flow_cnt + 1'b1; 156 i2c_data_w<= TIME_INI[39:32]; 157 end 158 4'd10: begin 159 if(i2c_done == 1'b1) begin 160 mon <= i2c_data_r[4:0]; 161 flow_cnt<= flow_cnt + 1'b1; 162 end 163 end 164 //写读年 165 4'd11: begin 166 i2c_exec <= 1'b1; 167 i2c_addr <= 8'h08; 168 flow_cnt <= flow_cnt + 1'b1; 169 i2c_data_w<= TIME_INI[47:40]; 170 end 171 4'd12: begin 172 if(i2c_done == 1'b1) begin 173 year <= i2c_data_r; 174 i2c_rh_wl<= 1'b1; 175 flow_cnt <= 4'd1; 176 end 177 end 178 default: flow_cnt <= 4'd0; 179 endcase 180 end 181 end 182 183 endmodule 程序中第47行的assign语句和第52行的always语句检测按键数据(消抖后)的下降沿,并 输出一个时钟周期的脉冲信号(neg_sap);然后根据neg_sap完成 switch信号的切换,如64~69 行所示;最终在第49行根据switch选择传递给数码管显示模块的时间数据(num)。 图 30.4.3为采集过程中SignalTap抓取的波形图。从图中可以看到当前读到的时间数据为 18年05月23日09:30:02。 图 30.4.3 SignalTap波形图 数码管BCD驱动模块的代码如下所示: 1 module seg_bcd_dri( 2 //input 3 input clk , // 时钟信号 4 input rst_n , // 复位信号 5 input [23:0] num , // 6个数码管要显示的数值 6 input [5:0] point , // 小数点具体显示的位置,从高到低,高有效 7 8 //output 9 output reg [5:0] 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] num1; // 送给要显示的数码管,要亮的灯 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 sel <= 6'b000001; 54 num1 <= 4'b0; 55 end 56 else begin 57 case (cnt) 58 3'd0:begin 59 sel <= 6'b111110; 60 num1 <= num[3:0] ; 61 point1 <= point[0] ; 62 end 63 3'd1:begin 64 sel <= 6'b111101; 65 num1 <= num[7:4] ; 66 point1 <= point[1] ; 67 end 68 3'd2:begin 69 sel <= 6'b111011; 70 num1 <= num[11:8]; 71 point1 <= point[2] ; 72 end 73 3'd3:begin 74 sel <= 6'b110111 ; 75 num1 <= num[15:12]; 76 point1 <= point[3] ; 77 end 78 3'd4:begin 79 sel <= 6'b101111 ; 80 num1 <= num[19:16]; 81 point1 <= point[4] ; 82 end 83 3'd5:begin 84 sel <= 6'b011111 ; 85 num1 <= num[23:20]; 86 point1 <= point[5] ; 87 end 88 default: begin 89 sel <= 6'b000000; 90 num1 <= 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(num1) 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 由于是BCD编码,从低到高每4位二进制数代表一位十进制数,所以在第57行的case语句块 中,我们只需把num信号相应位的值赋给num1即可。有关数码管显示更详细的解释可参考“动 态数码管显示实验”。 下载验证 首先我们打开PCF8563T实时时钟数码管显示实验工程,在工程所在的路径下打开rtc/par 文件夹,在里面找到“rtc.qpf”并双击打开。注意工程所在的路径名只能由字母、数字以及 下划线组成,不能出现中文、空格以及特殊字符等。工程打开后如图 30.5.1所示。 图 30.5.1 PCF8563T实时时钟数码管显示实验工程 然后将下载器一端连电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源 开关。 接下来我们下载程序,验证PCF8563的实时时钟数码管显示功能。 工程打开后通过点击工具栏中的“Programmer”图标打开下载界面,通过“Add File”按 钮选择rtc/par/output_files目录下的“rtc.sof”文件。开发板电源打开后,在程序下载界 面点击“Hardware Setup”,在弹出的对话框中选择当前的硬件连接为“USB-Blaster[USB- 0]”。然后点击“Start”将工程编译完成后得到的sof文件下载到开发板中,如图 30.5.2所 示。 图 30.5.2 程序下载界面 下载完成后观察到开发板上数码管显示的值为我们设置的初始年月日值,当按下key2后, 数码管显示时分秒,并且显示时间不停的变化,说明PCF8563实时时钟数码管显示实验程序下 载验证成功。 |
|
相关推荐
|
|
1489 浏览 1 评论
助力AIoT应用:在米尔FPGA开发板上实现Tiny YOLO V4
1068 浏览 0 评论
2572 浏览 1 评论
2260 浏览 0 评论
矩阵4x4个按键,如何把识别结果按编号01-16(十进制)显示在两个七段数码管上?
2530 浏览 0 评论
1992 浏览 54 评论
6036 浏览 113 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-27 13:00 , Processed in 0.708310 second(s), Total 62, Slave 44 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号