1 实验简介
实验通过阅读 DS1302 芯片手册,了解 DS1302 操作时序和相关寄存器,然后设计程序将DS1302 RTC 时间通过串口发送到 PC,通过串口调试助手可以看到时间信息。
2 实验原理
RTC( Real-Time Clock)实时时钟为系统提供一个可靠的时间,并且在断电的情况下, RTC 实时时钟也可以通过电池供电,一直运行下去。 RTC 通过类 SPI 总线向 FPGA 传送 8 位数据( BCD 码)。数据包括秒,分,小时,日期,天,月和年。在本实验中我们将读取 RTC 的时,分,秒的数据通过串口发送到 PC。
2.1 硬件介绍
PGL22G 开发板上 RTC 设计采用 DALLAS 公司的低功耗实时时钟芯片 DS1302, DS1302 的 VCC2为主电源, VCC1 为后备电源。在主电源关闭的情况下,也可以通过电池保持时钟的连续运行。DS1302 外接 32.768kHz 晶振为 RTC 电路提供振荡源。 RTC 部分的原理图如下图所示
2.2 DS1302 时序和控制
2.2.1 写数据时序
DS1302 芯片写操作的时序图。第一个字节是“访问寄存器的地址”,第二字节是“写数据”。
在写操作的时候,都是“上升沿有效”,然而还有一个条件,就是 CE( /RST)信号必须拉高。
(数据都是从 LSB 开始发送,亦即是最低位开始至最高位结束) 。
2.2.2 读数据时序
基本上和写操作的时序图大同小异,区别的地方就是在第二个字节是“读数据”的动作。第
二字节读数据开始时, SCLK 信号都是下降沿送出数据,这个时候可以使用上升沿读取数据。 CE
( /RST)信号同样是必须拉高。(第一节数据是从 LSB 开始输出,第二节数据是从 LSB 开始读
入) 。
3 程序设计
通过分析 DS1302 读写时序,可以看出和 SPI 时序类似,只丌过数据输出和输入分时复用了,本实验利用 SPI Master 模块来做为 DS1302 的底层读写控制模块,然后再编写一个 RTC 读写模块。ds1302_io 模块完成 DS1302 寄存器读写控制, 状态机如下图所示。状态“S_IDLE”空闲状态,收到读写寄存器请求写迚入“S_CE_HIGH”状态,将 CE 拉高,然
后根据请求类型,迚入读( S_READ)戒写状态(S_WRITE)。“ S_WRITE”状态下一个状态迚入写地址状态“ S_WRITE_ADDR” ,再迚入写数据状态S_WRITE_DATA”,完成一个寄存器的写入,最后应答,拉低 CE。“ S_READ ” 状 态 下 一 个 状 态 迚 入 读 地 址 状 态 “ S_READ_ADDR ” , 再 迚 入 读 数 据 状 态“S_READ_DATA”,完成一个寄存器的读取,最后应答,拉低 CE。
module ds1302_test( input rst, input clk, output ds1302_ce, output ds1302_sclk, inout ds1302_io, output[7:0] read_second, output[7:0] read_minute, output[7:0] read_hour, output[7:0] read_date, output[7:0] read_month, output[7:0] read_week, output[7:0] read_year);localparam S_IDLE = 0;localparam S_READ = 1;localparam S_WRITE = 2;localparam S_READ_CH = 3;localparam S_WRITE_CH = 4;localparam S_WAIT = 5;reg[2:0] state,next_state;reg write_time_req;reg write_time_req_latch;wire write_time_ack;reg read_time_req;wire read_time_ack;reg[7:0] write_second_reg;reg[7:0] write_minute_reg;reg[7:0] write_hour_reg;reg[7:0] write_date_reg;reg[7:0] write_month_reg;reg[7:0] write_week_reg;reg[7:0] write_year_reg;wire CH;assign CH = read_second[7];ds1302 ds1302_m0( .rst(rst), .clk(clk), .ds1302_ce(ds1302_ce), .ds1302_sclk(ds1302_sclk), .ds1302_io(ds1302_io), .write_time_req(write_time_req), .write_time_ack(write_time_ack), .write_second(write_second_reg), .write_minute(write_minute_reg), .write_hour(write_hour_reg), .write_date(write_date_reg), .write_month(write_month_reg), .write_week(write_week_reg), .write_year(write_year_reg), .read_time_req(read_time_req), .read_time_ack(read_time_ack), .read_second(read_second), .read_minute(read_minute), .read_hour(read_hour), .read_date(read_date), .read_month(read_month), .read_week(read_week), .read_year(read_year) );always@(posedge clk)begin if(write_time_ack) write_time_req <= 1'b0; else if(state == S_WRITE_CH) write_time_req <= 1'b1;endalways@(posedge clk)begin if(read_time_ack) read_time_req <= 1'b0; else if(state == S_READ || state == S_READ_CH) read_time_req <= 1'b1;endalways@(posedge clk or posedge rst)begin if(rst) state <= S_IDLE; else state <= next_state; endalways@(posedge clk or posedge rst)begin if(rst) begin write_second_reg <= 8'h00; write_minute_reg <= 8'h00; write_hour_reg <= 8'h00; write_date_reg <= 8'h00; write_month_reg <= 8'h00; write_week_reg <= 8'h00; write_year_reg <= 8'h00; end else if(state == S_WRITE_CH) begin write_second_reg <= 8'h01; write_minute_reg <= 8'h10; write_hour_reg <= 8'h13; write_date_reg <= 8'h13; write_month_reg <= 8'h12; write_week_reg <= 8'h02; write_year_reg <= 8'h17;//2017-12-13 13:10:01 endendalways@(*)begin case(state) S_IDLE: next_state <= S_READ_CH; S_READ_CH: if(read_time_ack) next_state <= CH ? S_WRITE_CH : S_READ; else next_state <= S_READ_CH; S_WRITE_CH: if(write_time_ack) next_state <= S_WAIT; else next_state <= S_WRITE_CH; S_WAIT: next_state <= S_READ; S_READ: if(read_time_ack) next_state <= S_IDLE; else next_state <= S_READ; default: next_state <= S_IDLE; endcaseendendmodule 复制代码