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 部分的原理图如下图所示  

1.png

2.2 DS1302 时序和控制
    2.2.1 写数据时序
DS1302 芯片写操作的时序图。第一个字节是“访问寄存器的地址”,第二字节是“写数据”。
在写操作的时候,都是“上升沿有效”,然而还有一个条件,就是 CE( /RST)信号必须拉高。
(数据都是从 LSB 开始发送,亦即是最低位开始至最高位结束) 。  

2.png



2.2.2 读数据时序
基本上和写操作的时序图大同小异,区别的地方就是在第二个字节是“读数据”的动作。第
二字节读数据开始时, SCLK 信号都是下降沿送出数据,这个时候可以使用上升沿读取数据。 CE
( /RST)信号同样是必须拉高。(第一节数据是从 LSB 开始输出,第二节数据是从 LSB 开始读
入) 。
3.png



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。


4.png
  • 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;
  • end
  • always@(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;
  • end
  • always@(posedge clk or posedge rst)
  • begin
  •         if(rst)
  •                 state <= S_IDLE;
  •         else
  •                 state <= next_state;       
  • end
  • always@(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
  •         end
  • end
  • always@(*)
  • 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;
  •         endcase
  • end
  • endmodule
  • 复制代码