rtc(real time clock)实时时钟,在电脑、手机等电子产品中都有,应用较多。它的主要作用就是,在产品断电之后,时间还可以继续走数。这样我们在重新使用电子产品时,时间仍然正确。芯片本身可以通过纽扣电池供电,接下来我们一起学习一下rtc的驱动。
此次,rtc用到的主芯片为ds1302,优点为控制简单,接下来我们一起了解一下芯片的特性以及控制。
一、芯片特征
在官方文档中,给出了以下重要特点描述:
1、实时时钟系统可以计数时、分、秒、日、月、星期、区分闰年平年的年份等,最多支持2100年。
2、有31*8bit的存储容量的ram。
3、实时时钟系统或者ram的读写,可以单字节或者多字节(突发模式)进行数据传输。
在芯片计时过程中,可以准确的计出时分秒等,还有每个月各有多少天。有30、31、28、29天的区分,年份有闰年平年的区分。小时有12小时制和24小时制。有am和pm的区分。
芯片的控制是通过ce、i/o(data line)和sclk。数据线可以一次传输1字节或者31字节。
二、端口
图中的mcu在此时是指的我们的fpga,那么,fpga与芯片进行数据交互时,是通过三个串行线进行的,并且i/o为双端口类型。芯片的端口中,除了三个主控端口(ce、i/o、sclk)外,还有x1和x2。
这两个端口为晶振接口,芯片需要一个外挂晶振来提供时钟,以便用来计时。vcc1和vcc2为两路电源,其中vcc2为板卡提供的电源,vcc1为纽扣电池供电。供电关系会在下面的管脚说明里面进行讲解。
gnd为电源地。
三、管脚说明
四、命令格式
下图展示了命令字节,一个命令字节由数据的发送者决定。最高位必须为1,如果是0,将禁止向芯片写数据。bit6如果是0将与实时时钟系统通信,如果是1将与ram通信。bit1到bit5为寄存器地址;bit0如果为0为写操作,1为读操作。
五、读写控制
首先是写操作,在8个sclk时钟周期内,主机发送一个写命令字节,数据输入在接下来的8个sclk时钟的上升沿,数据开始为bit0,也就是说,数据在发送时,从低位开始发送。
数据读操作,在8个sclk时钟周期内,主机发送一个读命令,数据输出在接下来的8个sclk时钟下降沿。第一个数据bit出现在命令字节最后一个bit被写入之后的第一个下降沿。通常数据传输需要在ce为高时。读数据时,也是从低位开始。
六、接口协议
基于芯片的读写方式,我们可以使用spi协议进行数据读写,那么接下来我们介绍一下spi协议。
spi协议有四种模式,如下图:
spi的四种模式是按照其时钟极性(cpol)和时钟相位(cpha)共同决定的,cpol=0,即sclk=0,表示sclk时钟信号线在空闲状态时的电平为低电平,因此有效状态为高电平。cpha=0,即表示在时钟的第一个岩信号进行采样。cpol和cpha共有四种组合,固有四种通信模式。
spi为主从模式,在通信线上,需要4通信线:
cs – 从设备使能信号,由主设备控制
scl – 时钟信号,由主设备产生
miso – 主设备数据输入,从设备数据输出
mosi – 主设备数据输出,从设备数据输入
但是,一般为了节省资源,会使用3跟通信线,分别为ce、scl、sda。其中sda为双端口。数据的输出和输入都使用这条线。
在我们的ds1302时序图中,读写时序跟spi的第一种模式一样,所以我们在写代码的时候可以使用spi协议去写。
接下来我们开始新建工程写代码。
新建文件,按照写时序,通过线性序列机写出写模块。
代码如下:
1 module rtc_wr(2 3 input wire clk,4 input wire rst_n,5 6 input wire wr_en,7 input wire [7:0] wr_addr,8 input wire [7:0] wr_data,9 10 output wire wr_done,11 output reg wr_scl,12 output reg wr_sda,13 output reg wr_ce14 );1516 parameter f_clk = 50_000_000;17 parameter f = 100_000;18 parameter t = f_clk / f / 2;19 20 reg [13:0] cnt; 2122 always @ (posedge clk, negedge rst_n)23 begin24 if(rst_n == 1'b0)25 cnt <= 14'd0;26 else if(wr_en)27 begin28 if(cnt == 33 * t - 1)29 cnt <= 14'd0;30 else31 cnt <= cnt + 1'b1;32 end33 else34 cnt <= 14'd0;35 end36 37 always @ (posedge clk, negedge rst_n)38 begin39 if(rst_n == 1'b0)40 begin41 wr_scl <= 1'b0;42 wr_sda <= 1'b0;43 wr_ce <= 1'b0;44 end45 else if(wr_en)46 case(cnt)47 0 : begin wr_ce <= 1'b1; wr_sda <= wr_addr[0]; end48 1 * t - 1 : begin wr_scl <= 1'b1; end49 2 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[1]; end50 3 * t - 1 : begin wr_scl <= 1'b1; end51 4 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[2]; end52 5 * t - 1 : begin wr_scl <= 1'b1; end53 6 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[3]; end54 7 * t - 1 : begin wr_scl <= 1'b1; end55 8 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[4]; end56 9 * t - 1 : begin wr_scl <= 1'b1; end57 10* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[5]; end58 11* t - 1 : begin wr_scl <= 1'b1; end59 12* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[6]; end60 13* t - 1 : begin wr_scl <= 1'b1; end61 14* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[7]; end62 15* t - 1 : begin wr_scl <= 1'b1; end63 16* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[0]; end64 17* t - 1 : begin wr_scl <= 1'b1; end65 18* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[1]; end66 19* t - 1 : begin wr_scl <= 1'b1; end67 20* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[2]; end68 21* t - 1 : begin wr_scl <= 1'b1; end69 22* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[3]; end70 23* t - 1 : begin wr_scl <= 1'b1; end71 24* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[4]; end72 25* t - 1 : begin wr_scl <= 1'b1; end73 26* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[5]; end74 27* t - 1 : begin wr_scl <= 1'b1; end75 28* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[6]; end76 29* t - 1 : begin wr_scl <= 1'b1; end77 30* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[7]; end78 31* t - 1 : begin wr_scl <= 1'b1; end79 32* t - 1 : begin wr_scl <= 1'b0; wr_ce <= 1'b0; end80 default : ;81 endcase82 end83 84 assign wr_done = (cnt == 33 * t - 1) ? 1'b1 : 1'b0;85 86 endmodule
同样的方式,新建文件读模块以及控制模块,代码如下:
1 module rtc_rd(2 3 input wire clk,4 input wire rst_n,5 6 input wire rd_en,7 input wire [7:0] rd_addr,8 input wire sda,9 10 output wire rd_done,11 output reg [7:0] rd_data,12 output reg rd_sda,13 output reg rd_scl,14 output reg rd_ce,15 output reg o_en16 );1718 parameter f_clk = 50_000_000;19 parameter f = 100_000;20 parameter t = f_clk / f / 2;21 22 reg [13:0] cnt; 2324 always @ (posedge clk, negedge rst_n)25 begin26 if(rst_n == 1'b0)27 cnt <= 14'd0;28 else if(rd_en)29 begin30 if(cnt == 33 * t - 1)31 cnt <= 14'd0;32 else33 cnt <= cnt + 1'b1;34 end35 else36 cnt <= 14'd0;37 end3839 always @ (posedge clk, negedge rst_n)40 begin41 if(rst_n == 1'b0)42 begin43 rd_data <= 8'd0;44 rd_scl <= 1'b0;45 rd_sda <= 1'b0;46 rd_ce <= 1'b0;47 o_en <= 1'b1;48 end49 else if(rd_en)50 case(cnt)51 0 : begin rd_ce <= 1'b1; rd_sda <= rd_addr[0]; o_en <= 1'b1;end52 1 * t - 1 : begin rd_scl <= 1'b1; end53 2 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[1]; end54 3 * t - 1 : begin rd_scl <= 1'b1; end55 4 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[2]; end56 5 * t - 1 : begin rd_scl <= 1'b1; end57 6 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[3]; end58 7 * t - 1 : begin rd_scl <= 1'b1; end59 8 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[4]; end60 9 * t - 1 : begin rd_scl <= 1'b1; end61 10* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[5]; end62 11* t - 1 : begin rd_scl <= 1'b1; end63 12* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[6]; end64 13* t - 1 : begin rd_scl <= 1'b1; end65 14* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[7]; end66 15* t - 1 : begin rd_scl <= 1'b1; end67 16* t - 1 : begin rd_scl <= 1'b0; o_en <= 1'b0; end68 17* t - 1 : begin rd_scl <= 1'b1; rd_data[0] <= sda; end69 18* t - 1 : begin rd_scl <= 1'b0; end70 19* t - 1 : begin rd_scl <= 1'b1; rd_data[1] <= sda; end71 20* t - 1 : begin rd_scl <= 1'b0; end 72 21* t - 1 : begin rd_scl <= 1'b1; rd_data[2] <= sda; end73 22* t - 1 : begin rd_scl <= 1'b0; end 74 23* t - 1 : begin rd_scl <= 1'b1; rd_data[3] <= sda; end75 24* t - 1 : begin rd_scl <= 1'b0; end 76 25* t - 1 : begin rd_scl <= 1'b1; rd_data[4] <= sda; end77 26* t - 1 : begin rd_scl <= 1'b0; end 78 27* t - 1 : begin rd_scl <= 1'b1; rd_data[5] <= sda; end79 28* t - 1 : begin rd_scl <= 1'b0; end 80 29* t - 1 : begin rd_scl <= 1'b1; rd_data[6] <= sda; end81 30* t - 1 : begin rd_scl <= 1'b0; end 82 31* t - 1 : begin rd_scl <= 1'b1; rd_data[7] <= sda; end83 32* t - 1 : begin rd_scl <= 1'b0; rd_ce <= 1'b0; o_en <= 1'b1; end84 default : ;85 endcase86 end87 88 assign rd_done = (cnt == 33 * t - 1) ? 1'b1 : 1'b0;89 90 endmodule
在读写模块中,按照框架设计,计数器必须在使能有效的条件下进行,所以,在写计数器时,必须判断使能信号。
控制模块如下:
1 module rtc_ctrl(2 3 input wire clk,4 input wire rst_n,5 6 //wr7 input wire wr_done,8 input wire wr_scl,9 input wire wr_sda,10 input wire wr_ce,11 output reg wr_en,12 output reg [7:0] wr_addr,13 output reg [7:0] wr_data,14 15 //rd16 input wire rd_done,17 input wire rd_scl,18 input wire rd_sda,19 input wire rd_ce,20 input wire [7:0] rd_data,21 output reg rd_en,22 output reg [7:0] rd_addr,23 24 output reg scl,25 output reg sda,26 output reg ce,27 output reg [7:0] s, 28 output reg [7:0] m,29 output reg [7:0] h30 );31 32 parameter t = 1_000_000;33 34 reg [19:0] cnt;35 reg [2:0] state;36 37 always @ (posedge clk, negedge rst_n)38 begin39 if(rst_n == 1'b0)40 begin41 wr_en <= 1'b0;42 wr_addr <= 8'd0;43 wr_data <= 8'd0;44 rd_en <= 1'b0;45 rd_addr <= 8'd0;46 scl <= 1'b0;47 sda <= 1'b0;48 ce <= 1'b0;49 s <= 8'd0;50 m <= 8'd0;51 h <= 8'd0;52 cnt <= 20'd0;53 state <= 3'd0;54 end55 else56 case(state)57 3'd0 : begin 58 if(wr_done)59 state <= 3'd1;60 else61 begin62 scl <= wr_scl;63 sda <= wr_sda;64 ce <= wr_ce;65 wr_addr <= 8'h80;66 wr_data <= 8'h30;67 wr_en <= 1'b1;68 end69 end70 3'd1 : begin 71 if(wr_done)72 state <= 3'd2;73 else74 begin75 scl <= wr_scl;76 sda <= wr_sda;77 ce <= wr_ce;78 wr_addr <= 8'h82;79 wr_data <= 8'h12;80 wr_en <= 1'b1;81 end82 end83 3'd2 : begin 84 if(wr_done)85 begin86 state <= 3'd3;87 wr_en <= 1'b0;88 end89 else90 begin91 scl <= wr_scl;92 sda <= wr_sda;93 ce <= wr_ce;94 wr_addr <= 8'h84;95 wr_data <= 8'h23;96 wr_en <= 1'b1;97 end98 end99 3'd3 : begin100 if(rd_done)101 begin102 state <= 3'd4;103 s <= {1'b0,rd_data[6:0]};104 end105 else106 begin107 scl <= rd_scl;108 sda <= rd_sda;109 ce <= rd_ce;110 rd_addr <= 8'h81;111 rd_en <= 1'b1;112 state <= 3'd3;113 end114 end115 3'd4 : begin116 if(rd_done)117 begin118 state <= 3'd5;119 m <= rd_data;120 end121 else122 begin123 scl <= rd_scl;124 sda <= rd_sda;125 ce <= rd_ce;126 rd_addr <= 8'h83;127 rd_en <= 1'b1;128 state <= 3'd4;129 end130 end131 3'd5 : begin132 if(rd_done)133 begin134 state <= 3'd6;135 h <= rd_data;136 rd_en <= 1'b0;137 end138 else139 begin140 scl <= rd_scl;141 sda <= rd_sda;142 ce <= rd_ce;143 rd_addr <= 8'h85;144 rd_en <= 1'b1;145 state <= 3'd5;146 end147 end148 3'd6 : begin 149 if(cnt == t - 1)150 begin151 state <= 3'd3;152 cnt <= 20'd0;153 end154 else155 begin156 state <= 3'd6;157 cnt <= cnt + 1'b1;158 end159 end160 endcase161 end162163 endmodule
在控制模块中,我们前三个状态要把时间的初值写进芯片,比如我们写入时分秒,那么我们需要按照手册给出相应的命令。
在这里我们需要解释一下小时的数据格式。bit7如果为0代表使用的是24小时制,如果为1代表使用的是12小时制。bit6恒为0。bit5,如果是12小时制,0代表上午,1代表下午,如果是24小时制,bit5和bit4共同组成了小时的十位。bit3到bit0为小时的个位。
顶层模块代码如下:
1 module rtc( //real time clock2 3 input wire clk,4 input wire rst_n,5 6 output wire scl,7 inout wire sda,8 output wire ce,9 10 output wire [5:0] sel,11 output wire [7:0] seg12 );13 14 wire wr_en;15 wire [7:0] wr_addr;16 wire [7:0] wr_data;17 wire wr_done;18 wire wr_scl;19 wire wr_sda;20 wire wr_ce;21 wire rd_en;22 wire [7:0] rd_addr;23 wire rd_done;24 wire [7:0] rd_data;25 wire rd_sda;26 wire rd_scl;27 wire rd_ce;28 wire o_en;29 wire o_buf;30 wire [7:0] s;31 wire [7:0] m;32 wire [7:0] h;33 34 rtc_wr rtc_wr_inst(35 36 .clk (clk),37 .rst_n (rst_n),38 39 .wr_en (wr_en),40 .wr_addr (wr_addr),41 .wr_data (wr_data),42 43 .wr_done (wr_done),44 .wr_scl (wr_scl),45 .wr_sda (wr_sda),46 .wr_ce (wr_ce)47 );48 49 rtc_rd rtc_rd_inst(50 51 .clk (clk),52 .rst_n (rst_n),53 54 .rd_en (rd_en),55 .rd_addr (rd_addr),56 .sda (sda),57 58 .rd_done (rd_done),59 .rd_data (rd_data),60 .rd_sda (rd_sda),61 .rd_scl (rd_scl),62 .rd_ce (rd_ce),63 .o_en (o_en)64 );65 66 rtc_ctrl rtc_ctrl_inst(67 68 .clk (clk),69 .rst_n (rst_n),70 71 //wr72 .wr_done (wr_done),73 .wr_scl (wr_scl),74 .wr_sda (wr_sda),75 .wr_ce (wr_ce),76 .wr_en (wr_en),77 .wr_addr (wr_addr),78 .wr_data (wr_data),79 80 //rd81 .rd_done (rd_done),82 .rd_scl (rd_scl),83 .rd_sda (rd_sda),84 .rd_ce (rd_ce), 85 .rd_data (rd_data),86 .rd_en (rd_en),87 .rd_addr (rd_addr),88 89 .scl (scl),90 .sda (o_buf),91 .ce (ce),92 .s (s), //数码管数据93 .m (m),94 .h (h)95 );96 97 assign sda = (o_en) ? o_buf : 1'hz;98 99 seven_tube_driver seven_tube_driver_inst(100 101 .clk (clk),102 .rst_n (rst_n),103 .s (s),104 .m (m),105 .h (h),106 107 .sel (sel),108 .seg (seg)109 );110 111 endmodule
在这里需要大家注意的是三态门的编写。
作为输入时,将数据线置为高祖态。
仿真代码如下:
1 `timescale 1ns / 1ps2 3 module rtc_tb;4 5 reg clk;6 reg rst_n;7 8 wire scl;9 wire sda;10 wire ce;11 12 wire [5:0] sel;13 wire [7:0] seg;1415 initial begin16 clk = 0; 17 rst_n = 0;18 #105;19 rst_n = 1;20 #100000;21 $stop;22 end23 24 always #10 clk = ~clk;25 26 rtc rtc_inst( //real time clock27 28 .clk (clk),29 .rst_n (rst_n),30 31 .scl (scl),32 .sda (sda),33 .ce (ce),34 35 .sel (sel),36 .seg (seg)37 );3839 endmodule
仿真图如下:
前三个状态,分别写入了时分秒等数据,3 4 5三个状态分别是读时分秒的状态,最后一个状态是做的延时,在一秒时间内,读出的数据是没有变化的,因此我们可以减少读操作的频率来降低工作频率。在rd_done信号拉高时,可以看到时分秒都有数据被赋值,及读出正常。
无线联盟为使用6GHz频谱802.11ax网络定名Wi-Fi 6E
整个2020 年Linus引起“轰动”的一些时刻
微薄工资也可以玩VR游戏啦!Oculus启动冬季促销VR游戏
英特尔X汉堡王:AI如何助力快餐行业破局?
新型老练电源大大缩短老练时间并显著提升铝电解电容器的电性能
基于FPGA的RTC实时时钟系统设计
可溶解无线控制储能电路:启动医界新革命
桥梁断面位置检测方案中的电涡流传感器技术
回路电阻测试仪接线及其原理图
三星遭遇了“当头一棒” 华为有可能成为他们的救星
你的工作在未来有多大概率会被AI替代?
如何制作一个简易arduino绘图仪
日本电产下调利润预期,电动汽车零部件业务低迷
突破性发明:新的量子计算机构建法 将实现批量生产量子计算机芯片
NXP可控硅调光及应用(一)
RISC-V有什么了不起的?
定压输入1KV 双隔离回路输出分布电源模块
东芝存储欲出手解危机 美私募巨头有意却被拒
Qorvo推出业界首个无缝集成Wi-Fi 6和物联网解决方案
TP1000高性能AC/DC电源控制器概述、特性及应用