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_ce 14 ); 15 16 parameter f_clk = 50_000_000; 17 parameter f = 100_000; 18 parameter t = f_clk / f / 2; 19 20 reg [13:0] cnt; 21 22 always @ (posedge clk, negedge rst_n) 23 begin 24 if(rst_n == 1'b0) 25 cnt <= 14'd0; 26 else if(wr_en) 27 begin 28 if(cnt == 33 * t - 1) 29 cnt <= 14'd0; 30 else 31 cnt <= cnt + 1'b1; 32 end 33 else 34 cnt <= 14'd0; 35 end 36 37 always @ (posedge clk, negedge rst_n) 38 begin 39 if(rst_n == 1'b0) 40 begin 41 wr_scl <= 1'b0; 42 wr_sda <= 1'b0; 43 wr_ce <= 1'b0; 44 end 45 else if(wr_en) 46 case(cnt) 47 0 : begin wr_ce <= 1'b1; wr_sda <= wr_addr[0]; end 48 1 * t - 1 : begin wr_scl <= 1'b1; end 49 2 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[1]; end 50 3 * t - 1 : begin wr_scl <= 1'b1; end 51 4 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[2]; end 52 5 * t - 1 : begin wr_scl <= 1'b1; end 53 6 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[3]; end 54 7 * t - 1 : begin wr_scl <= 1'b1; end 55 8 * t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[4]; end 56 9 * t - 1 : begin wr_scl <= 1'b1; end 57 10* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[5]; end 58 11* t - 1 : begin wr_scl <= 1'b1; end 59 12* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[6]; end 60 13* t - 1 : begin wr_scl <= 1'b1; end 61 14* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_addr[7]; end 62 15* t - 1 : begin wr_scl <= 1'b1; end 63 16* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[0]; end 64 17* t - 1 : begin wr_scl <= 1'b1; end 65 18* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[1]; end 66 19* t - 1 : begin wr_scl <= 1'b1; end 67 20* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[2]; end 68 21* t - 1 : begin wr_scl <= 1'b1; end 69 22* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[3]; end 70 23* t - 1 : begin wr_scl <= 1'b1; end 71 24* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[4]; end 72 25* t - 1 : begin wr_scl <= 1'b1; end 73 26* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[5]; end 74 27* t - 1 : begin wr_scl <= 1'b1; end 75 28* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[6]; end 76 29* t - 1 : begin wr_scl <= 1'b1; end 77 30* t - 1 : begin wr_scl <= 1'b0; wr_sda <= wr_data[7]; end 78 31* t - 1 : begin wr_scl <= 1'b1; end 79 32* t - 1 : begin wr_scl <= 1'b0; wr_ce <= 1'b0; end 80 default : ; 81 endcase 82 end 83 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_en 16 ); 17 18 parameter f_clk = 50_000_000; 19 parameter f = 100_000; 20 parameter t = f_clk / f / 2; 21 22 reg [13:0] cnt; 23 24 always @ (posedge clk, negedge rst_n) 25 begin 26 if(rst_n == 1'b0) 27 cnt <= 14'd0; 28 else if(rd_en) 29 begin 30 if(cnt == 33 * t - 1) 31 cnt <= 14'd0; 32 else 33 cnt <= cnt + 1'b1; 34 end 35 else 36 cnt <= 14'd0; 37 end 38 39 always @ (posedge clk, negedge rst_n) 40 begin 41 if(rst_n == 1'b0) 42 begin 43 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 end 49 else if(rd_en) 50 case(cnt) 51 0 : begin rd_ce <= 1'b1; rd_sda <= rd_addr[0]; o_en <= 1'b1;end 52 1 * t - 1 : begin rd_scl <= 1'b1; end 53 2 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[1]; end 54 3 * t - 1 : begin rd_scl <= 1'b1; end 55 4 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[2]; end 56 5 * t - 1 : begin rd_scl <= 1'b1; end 57 6 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[3]; end 58 7 * t - 1 : begin rd_scl <= 1'b1; end 59 8 * t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[4]; end 60 9 * t - 1 : begin rd_scl <= 1'b1; end 61 10* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[5]; end 62 11* t - 1 : begin rd_scl <= 1'b1; end 63 12* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[6]; end 64 13* t - 1 : begin rd_scl <= 1'b1; end 65 14* t - 1 : begin rd_scl <= 1'b0; rd_sda <= rd_addr[7]; end 66 15* t - 1 : begin rd_scl <= 1'b1; end 67 16* t - 1 : begin rd_scl <= 1'b0; o_en <= 1'b0; end 68 17* t - 1 : begin rd_scl <= 1'b1; rd_data[0] <= SDA; end 69 18* t - 1 : begin rd_scl <= 1'b0; end 70 19* t - 1 : begin rd_scl <= 1'b1; rd_data[1] <= SDA; end 71 20* t - 1 : begin rd_scl <= 1'b0; end 72 21* t - 1 : begin rd_scl <= 1'b1; rd_data[2] <= SDA; end 73 22* t - 1 : begin rd_scl <= 1'b0; end 74 23* t - 1 : begin rd_scl <= 1'b1; rd_data[3] <= SDA; end 75 24* t - 1 : begin rd_scl <= 1'b0; end 76 25* t - 1 : begin rd_scl <= 1'b1; rd_data[4] <= SDA; end 77 26* t - 1 : begin rd_scl <= 1'b0; end 78 27* t - 1 : begin rd_scl <= 1'b1; rd_data[5] <= SDA; end 79 28* t - 1 : begin rd_scl <= 1'b0; end 80 29* t - 1 : begin rd_scl <= 1'b1; rd_data[6] <= SDA; end 81 30* t - 1 : begin rd_scl <= 1'b0; end 82 31* t - 1 : begin rd_scl <= 1'b1; rd_data[7] <= SDA; end 83 32* t - 1 : begin rd_scl <= 1'b0; rd_ce <= 1'b0; o_en <= 1'b1; end 84 default : ; 85 endcase 86 end 87 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 //wr 7 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 //rd 16 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] h 30 ); 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 begin 39 if(rst_n == 1'b0) 40 begin 41 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 end 55 else 56 case(state) 57 3'd0 : begin 58 if(wr_done) 59 state <= 3'd1; 60 else 61 begin 62 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 end 69 end 70 3'd1 : begin 71 if(wr_done) 72 state <= 3'd2; 73 else 74 begin 75 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 end 82 end 83 3'd2 : begin 84 if(wr_done) 85 begin 86 state <= 3'd3; 87 wr_en <= 1'b0; 88 end 89 else 90 begin 91 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 end 98 end 99 3'd3 : begin 100 if(rd_done) 101 begin 102 state <= 3'd4; 103 s <= {1'b0,rd_data[6:0]}; 104 end 105 else 106 begin 107 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 end 114 end 115 3'd4 : begin 116 if(rd_done) 117 begin 118 state <= 3'd5; 119 m <= rd_data; 120 end 121 else 122 begin 123 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 end 130 end 131 3'd5 : begin 132 if(rd_done) 133 begin 134 state <= 3'd6; 135 h <= rd_data; 136 rd_en <= 1'b0; 137 end 138 else 139 begin 140 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 end 147 end 148 3'd6 : begin 149 if(cnt == t - 1) 150 begin 151 state <= 3'd3; 152 cnt <= 20'd0; 153 end 154 else 155 begin 156 state <= 3'd6; 157 cnt <= cnt + 1'b1; 158 end 159 end 160 endcase 161 end 162 163 endmodule
在控制模塊中,我們前三個狀態要把時間的初值寫進芯片,比如我們寫入時分秒,那么我們需要按照手冊給出相應的命令。
在這里我們需要解釋一下小時的數據格式。BIT7如果為0代表使用的是24小時制,如果為1代表使用的是12小時制。BIT6恒為0。BIT5,如果是12小時制,0代表上午,1代表下午,如果是24小時制,BIT5和BIT4共同組成了小時的十位。BIT3到BIT0為小時的個位。
頂層模塊代碼如下:
1 module RTC( //real time clock 2 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] seg 12 ); 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 //wr 72 .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 //rd 81 .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 / 1ps 2 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; 14 15 initial begin 16 clk = 0; 17 rst_n = 0; 18 #105; 19 rst_n = 1; 20 #100000; 21 $stop; 22 end 23 24 always #10 clk = ~clk; 25 26 RTC RTC_inst( //real time clock 27 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 ); 38 39 endmodule
仿真圖如下:
前三個狀態,分別寫入了時分秒等數據,3 4 5三個狀態分別是讀時分秒的狀態,最后一個狀態是做的延時,在一秒時間內,讀出的數據是沒有變化的,因此我們可以減少讀操作的頻率來降低工作頻率。在rd_done信號拉高時,可以看到時分秒都有數據被賦值,及讀出正常。
審核編輯:湯梓紅
-
FPGA
+關注
關注
1620文章
21510瀏覽量
598940 -
實時時鐘
+關注
關注
4文章
232瀏覽量
65530 -
RTC
+關注
關注
2文章
511瀏覽量
65901 -
Vivado
+關注
關注
19文章
797瀏覽量
65855 -
時鐘系統
+關注
關注
1文章
95瀏覽量
11615
原文標題:FPGA零基礎學習之Vivado-RTC實時時鐘系統設計
文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論