weiqi7777

FPGA之DS1302

0
阅读(3911)


DS1302是比较经典的时钟芯片,刚好开发板上有这一个芯片。然后去准备用verilog去控制控制。

以下是DS1302的封装和管脚说明。

clip_image001clip_image003

其中,主要就是控制CE,I/O,SCLK这三个信号。。其他都是电路固定接好的。不需要控制。这里要注意一下I/O这个管脚是双向口。所以在FPGA中要用inout端口表示。

然后就是时序。

clip_image005

以上就是读和写的时序。

无论是写还是读,都是在CE为高的时候操作,当CE为低的时候,读和写都不可操作。所以在读和写之前,首先要将CE拉高,并保持到操作结束。

另外要注意,在CE拉高之前,SCLK都是要保持低电平的。

写操作:

拉高CE,保持一段时间,然后SCLK开始产生固定周期的15个脉冲信号。在SCLK的上升沿,将I/O数据写入到DS1302中。前面8位是控制字,后面8位是数据。写是以LSB先行,即先发地位,再发高位。写完数据后,保持一段时间,在将CE拉低。结束一次写数据。这里要注意,要继续写的话,是要等CE拉低一段时间之后,才能继续写的。这个是时序规定的。

读操作:

拉高CE,保持一段时间,然后SCLK开始产生固定周期的16个脉冲信号。前8个脉冲式写数据,在SCLK的上升沿将数据写入到DS1302中。后8个脉冲式读数据,在SCLK的下降沿,I/O上产生读取的数据。所以在这个时候,要将数据进行读取。这里要注意,读的第一个数据,是在第8个时钟脉冲的下降沿的时候产生的.

总体来说,时序是比较简单的。只要注意到datasheet中规定的时序。

clip_image007

比如规定,SCLK的高电平和低电平的时间最小是250ns(5V供电下),那么设计的时候,就要保证SCLK的高电平和低电平的持续时间至少是250ns。不过一般要留一些余量。比如可以设置时间为1us。毕竟对于实时时钟,不需要这么快的操作速度。

然后是控制字的说明:

clip_image009

前面的8位就是控制寄存器,后面是表示这个寄存器的每一位表示的意思。主要用到的前8个。

这里有特殊的位:

秒寄存器的BIT7 定义为时间暂停位,当BIT7 为1 时,时钟振荡器停止工作,DS1302 进入低功耗模式,电源消耗小于100 微安,当BIT7 为0 时,时钟振荡器启动,DS1302 正常工作。所以如果要使DS1302正常工作,需要将最高位写0。

小时寄存器的BIT7 定义为12 或24 小时工作模式选择位,当BIT7 为高时,为12 小时工作模式,此时BIT5 为AM/PM 位,低电平标示AM,高电平标示PM,在24 小时模式下,BIT5 为第二个10 小时位标示(20~23 时)。

写保护寄存器的BIT7:WP 是写保护位,工作时,出WP 外的其他位都置为0,对时钟日历寄存器或RAM 进行写操作之前,WP 必须为0,当WP 为高电平的时候,不能对任何时钟/日历寄存器或RAM 进行写操作。

然后就开始设计程序了。

首先是信号列表:


module ds1302_module(
 input				clk,
 input				rst_n,
 
 input 				enable,          //enable operate DS1302
 input    [7:0]                 command,         //send first byte command
 input    [7:0]			write_data,      //send second byte data 
 
 //ds1302 interface 
 output	  reg			ds1302_ce,       //ds1302 CE port
 output	  reg			ds1302_sclk,     //ds1302 SCLK port
 inout 					ds1302_data,     //ds1302 I/O port
 
 output   reg[7:0]		ds1302_read_data,  //read byte data from ds1302
 output	  reg			finish,            //operate DS1302 finish
 
 output   		ds1302_data_look   //input data when I/O is input
 
    );


注释也比较清楚,至于最后一个信号,是用来chipscope调试用的。因为chipscope不能直接看inout信号,需要将inout信号分解成输入和输出。

然后就是状态机的设计:


//define state
localparam idle_state = 'd0;
localparam ready_state = 'd1;
localparam write_command_state = 'd2;
localparam write_data_state = 'd3;
localparam read_data_state = 'd4;
localparam finish_state = 'd5;
//end define state


这里只有5个状态,从状态的命名也可以看出该状态是干嘛的。

然后是inout端口ds1302_data的处理


reg read_enable;
reg ds1302_data_reg;
assign ds1302_data = read_enable ? 1'bz:ds1302_data_reg;
assign ds1302_data_look = read_enable ? ds1302_data:1'b0;


和IIC的处理一样。需要一个读使能信号。

然后就是定义一些变量,用来计数每个状态的持续时间,发送的哪一位,保存读取时候每一次移位的值和I/O输出的值。


//counter  100  to delay 2000ns
 reg [7:0]  delay_time;
 reg [7:0]  delay_time_next;
 always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
     delay_time <= 'd0;
 else
     delay_time <= delay_time_next;
 end
  
  //record transfer which bit, serial transfer,so need a variable value 
 reg [2:0] bit_counter;        //current transfer bit
 reg [2:0] bit_counter_next;   //next transfer bit
 always@(posedge clk or negedge rst_n)  begin
    if(!rst_n)
     bit_counter <= 'd0;
 else
     bit_counter <= bit_counter_next;
 end
  
 //save the every shift data to read_data
 reg [7:0]  read_data;
 reg [7:0]  read_data_reg;
 always@(posedge clk or negedge rst_n) begin
    if(!rst_n) 
    read_data <= 8'd0;
 else
    read_data <= read_data_reg;
 end
  
 //save the ds1302 I/O output value
 reg ds1302_data_reg_reg;
 always@(posedge clk or negedge rst_n) begin
   if(!rst_n)
   ds1302_data_reg_reg <= 'd0;
 else
   ds1302_data_reg_reg <= ds1302_data_reg;
 end


然后就是状态机的设计:


reg [2:0] state;
 reg [2:0] state_next;
  
 always@(posedge clk or negedge rst_n) begin
    if(!rst_n)
     state <= idle_state;
 else
     state <= state_next;
 end
  
 always@(*)  begin
    state_next = state;
 ds1302_ce = 1'b1;
 delay_time_next = delay_time;
 finish		= 1'b0;
 read_data_reg = read_data;
 bit_counter_next = bit_counter;
 ds1302_data_reg = ds1302_data_reg_reg;
 read_enable = 0; //default ds1302_data  is output
 read_finish = 'b0;
 case(state)
 idle_state: begin
    delay_time_next   = 'd0;
 ds1302_ce		    = 'b0;
 bit_counter_next  = 'd0;
 read_data_reg     = 'd0;
 if(enable)
    state_next = ready_state;
 end
 ready_state: begin		 
    if(delay_time >= 99)   //delay 2us
   begin
  state_next = write_command_state;
  delay_time_next = 'd0;
 end
 else
   begin
  delay_time_next = delay_time + 1'b1;
 end
 end
 //write byte command data
 write_command_state: begin
    if(delay_time == 50) //delay 1us ,let ds1302_data data meet setup and hold time
     ds1302_data_reg = command[bit_counter];
    if(delay_time >= 199)    //delay 4us , 1-bits data transfer finish
    begin
    if(bit_counter_next >= 7)  //send_8-bits data  finish
   begin
   bit_counter_next = 'd0;
 if(command[0] == 0)
      state_next = write_data_state;
 else
   state_next = read_data_state;
 delay_time_next = 'd0;
 end
 else
   begin
   bit_counter_next = bit_counter + 1'b1;
 delay_time_next = 'd0;
 end
 end
 else
    delay_time_next = delay_time + 1'b1;
 end
 //write 8-bits data to ds1302
 write_data_state: begin
    if(delay_time == 50) //delay 1us ,let ds1302 data meet setup and hold time
     ds1302_data_reg = write_data[bit_counter];
    if(delay_time >= 199)    //delay 4us , 1-bits data transfer finish
    begin
    if(bit_counter_next >= 7)  //send_8-bits data  finish
   begin
   bit_counter_next = 'd0;
   state_next = finish_state;
 delay_time_next = 'd0; 
 end
 else
   begin
   bit_counter_next = bit_counter + 1'b1;
 delay_time_next = 'd0;
 end
 end
 else
    delay_time_next = delay_time + 1'b1;
 end
 //read 8-bits data from ds1302
 read_data_state: begin
    read_enable = 1'b1; // set ds1302_data is input
    if(delay_time == 50)     //delay 1us ,let data meet setup and hold time			     
  read_data_reg = {ds1302_data,read_data[7:1]};
    if(delay_time >= 199)    //delay 4us
    begin
    if(bit_counter_next >= 7)  //read_8-bits data
   begin
   bit_counter_next = 'd0;
 delay_time_next = 'd0;
 //counter_number_next = counter_number + 1'b1;
 state_next = finish_state; 
 end
 else
   begin
   bit_counter_next = bit_counter + 1'b1;
 delay_time_next = 'd0;
 end
 end
 else
    delay_time_next = delay_time + 1'b1;
 end
 finish_state: begin
    ds1302_ce		    = 'b0;
 if(delay_time == 150)
     read_finish = 1'b1;
 if(delay_time >= 199)  //delay 2us
   begin
 state_next = idle_state;
 finish = 1'b1;
 end		   
 else
   delay_time_next = delay_time + 1'b1; 
 end
 default: state_next = idle_state;
 endcase
 end


代码也比较简单。只需要主要在什么时候要将read_enable信号使能,是I/O为输入。

最后是SCLK的控制:

对于SCLK,只有在写命令,写数据和读数据的时候,才会产生脉冲,在其余时候,都是为低电平。所以这里只要判断状态即可。在写命令,写数据和读数据状态,在状态的前半段时候,SCLK为电平,后半段时候为高电平,这样就产生了脉冲。这里看网上有说,最后完成的时候,在CE拉低的时候,SCLK要保持高电平,否则不会正常工作。但是我试过,CE拉低,SCLK为低电平的时候也是可以正常工作的。可能不同厂家芯片有点不一样。


 always@(*) begin
    if(state == idle_state || state == ready_state)
     ds1302_sclk = 1'b0;
 else if(state == finish_state)
     ds1302_sclk = 1'b1;
 else
     if(delay_time < 100)
     ds1302_sclk = 1'b0;
  else
     ds1302_sclk = 1'b1;
 end


最后就是读取数据的处理。


always@(posedge clk or negedge rst_n) begin
	   if(!rst_n)
		   ds1302_read_data <= 'd0;
		else
		   if(read_finish)
			  ds1302_read_data <= read_data;
	 end


因为read_data在读数据的时候,是一直在变的,而且在下一次读数据的时候,是会被清零的,所以就需要在读完时候保持该值。在读完数据后,read_finish会保持一个时钟的高电平,这样就可以将数据保存到ds1302_read_data,知道下一个数据读取结束。

最后就是编写顶层代码,去控制DS1302。


module ds1302_control(
		input						clk,
		input						rst_n,
			
		//ds1302 interface
		output				   ds1302_ce,
		output				   ds1302_sclk,
		inout 					ds1302_data,
		
		output [7:0]		sm_bit ,     //位选
		output [7:0]		sm_seg,       //段选
		
		output 				ds1302_data_look
    );
	 
	 localparam  idle_state				   	   ='d0;
	 localparam  cancel_write_protect_state   ='d1;
	 localparam  enable_ds1302_clock_state    ='d2;
	 localparam  read_second_state			   ='d3;
	 localparam  read_minute_state		      ='d4;
	 localparam  read_hour_state			    	='d5;
	 localparam  finish_state						='d6;
	 
//ds1302 interface
    wire finish;
	 reg enable_ds1302;
	 wire [7:0] ds1302_read_data;
//ds1302 interface

   wire enable = 1'b1;

    	reg[3:0]     hour_units;
		reg[1:0]     hour_tens;
		
		reg[3:0]     minute_units;
		reg[2:0]     minute_tens;
		
		reg[3:0]     second_units;
		reg[2:0]     second_tens;
	 
	 (* KEEP =  "TRUE" *)   reg [7:0]  command;
	 (* KEEP =  "TRUE" *)   reg [7:0]  write_data;
	 
	 reg [2:0]  state;
	 reg [2:0]  state_next;
	 always@(posedge clk or negedge rst_n)  begin
	    if(!rst_n)
		   begin
		     state   	   <= idle_state;
			  command 		<= 8'h8e;
			  write_data   <= 8'd0;
			  hour_units   <= 'd0;
		     hour_tens		<= 'd0;
		     minute_units <= 'd0;
		     minute_tens  <= 'd0;
			  second_units <= 'd0;
		     second_tens  <= 'd0;
			end
       else
		  begin
		     case(state)
			  idle_state: begin
			     if(enable)
				    begin
					   state      <= cancel_write_protect_state;
					 end
			  end
			  cancel_write_protect_state: begin
			     enable_ds1302 <= 1'b1;
				  command    <= 8'h8e;
			     write_data <= 8'd0;
				  if(finish == 1)
				     begin
						  enable_ds1302 <= 1'b0; 
						  state			 <= enable_ds1302_clock_state;
					  end
			  end
			  enable_ds1302_clock_state: begin
			     enable_ds1302 <= 1'b1;
				  command       <= 8'h80;
				  write_data    <= 8'h17;
				  if(finish == 1)
				    begin
					    enable_ds1302 <= 1'b0; 
						 state			<= read_second_state;
					 end
			  end
			  read_second_state: begin
			     enable_ds1302 <= 1'b1;
				  command       <= 8'h81;  //read second register
				  if(finish == 1)
				     begin
						  enable_ds1302 <= 1'b0; 
						  second_units  <= ds1302_read_data[3:0];
						  second_tens   <= ds1302_read_data[6:4];
						  state			 <= read_minute_state;
					  end
			  end
			  read_minute_state: begin
			     enable_ds1302 <= 1'b1;
				  command       <= 8'h83; //read minute register
				  if(finish == 1)
				     begin
						  enable_ds1302 <= 1'b0; 
						  minute_units  <= ds1302_read_data[3:0];
						  minute_tens   <= ds1302_read_data[6:4];
						  state			 <= read_hour_state;
					  end
			  end
			  read_hour_state: begin
			     enable_ds1302 <= 1'b1;
				  command       <= 8'h85; //read hour register
				  if(finish == 1)
				     begin
						  enable_ds1302 <= 1'b0; 
						  hour_units    <= ds1302_read_data[3:0];
						  hour_tens     <= ds1302_read_data[5:4];
						  state			 <= finish_state;
					  end
			  end
			  finish_state: begin
			      state			 <= read_second_state;
			  end
			  default:  state	 <= idle_state;
			  endcase
		  end
    end
	 
    ds1302_module ds1302_module_1 (
						 .clk(clk), 
						 .rst_n(rst_n), 
						 .enable(enable_ds1302), 
						 .command(command[7:0]), 
						 .write_data(write_data[7:0]), 
						 .ds1302_ce(ds1302_ce), 
						 .ds1302_sclk(ds1302_sclk), 
						 .ds1302_data(ds1302_data), 
						 .ds1302_read_data(ds1302_read_data[7:0]), 
						 .finish(finish),
						 .ds1302_data_look(ds1302_data_look)
    );
	 
	 digitron_dynamic_display digitron_dynamic_display_1 (
    .clk(clk), 
    .rst_n(rst_n), 
    .display_data_1(second_units), 
    .display_data_2({1'b0,second_tens}), 
    .display_data_3(4'ha), 
    .display_data_4(minute_units), 
    .display_data_5({1'b0,minute_tens}), 
    .display_data_6(4'ha), 
    .display_data_7(hour_units), 
    .display_data_8({2'b0,hour_tens}), 
    .sm_bit(sm_bit), 
    .sm_seg(sm_seg)
    );
	 
endmodule


代码页比较简单,就是几个状态的跳转,首先是取消写保护,然后是使能振荡器,并初始化秒寄存器的值,这样芯片才能正常的计数。然后在读秒寄存器的值,读分寄存器的值,读时寄存器的值。读完之后,跳转到读秒寄存器状态。开始读秒寄存器值,然后在读。。。。。

在将读取的值通过数码管显示出来。

这里要注意,读完时状态后,是不能跳到idle_state状态的,因为这样会重新写秒寄存器的值,这样就不会计数了。

以下是chipscope抓取的波形:

clip_image011

可以看到是正常读取数据的,只不过操作比较快,所以看不到读取数据的变化。


以下附上整个DS1302代码:


`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    15:08:24 11/24/2014 
// Design Name: 
// Module Name:    ds1302_module 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module ds1302_module(
			input						clk,
			input						rst_n,
			
			input 					enable,          //enable operate DS1302
			input  [7:0]         command,         //send first byte command
			input  [7:0]			write_data,      //send second byte data 
			
			//ds1302 interface 
			output	reg			ds1302_ce,       //ds1302 CE port
			output	reg			ds1302_sclk,     //ds1302 SCLK port
			inout 					ds1302_data,     //ds1302 I/O port
			
			output   reg[7:0]		ds1302_read_data,  //read byte data from ds1302
			output	reg			finish,            //operate DS1302 finish
			
			output   				ds1302_data_look   //input data when I/O is input
			
    );
//define state
	 localparam  idle_state 		    = 'd0;
	 localparam  ready_state			 = 'd1;
	 localparam  write_command_state  = 'd2;
	 localparam  write_data_state		 = 'd3;
    localparam  read_data_state		 = 'd4;
	 localparam  finish_state			 = 'd5;
//end define state

    reg read_finish;

	 reg read_enable;
	 reg ds1302_data_reg;
	 assign  ds1302_data = read_enable ? 1'bz:ds1302_data_reg;
    
	 assign  ds1302_data_look = read_enable ? ds1302_data:1'b0;
	 
	 //counter  100  to delay 2000ns
	 reg [7:0]  delay_time;
	 reg [7:0]  delay_time_next;
	 always@(posedge clk or negedge rst_n) begin
	    if(!rst_n)
		     delay_time <= 'd0;
		 else
		     delay_time <= delay_time_next;
	 end
	 
	  //record transfer which bit, serial transfer,so need a variable value 
	 reg [2:0] bit_counter;        //current transfer bit
	 reg [2:0] bit_counter_next;   //next transfer bit
	 always@(posedge clk or negedge rst_n)  begin
	    if(!rst_n)
		     bit_counter <= 'd0;
		 else
		     bit_counter <= bit_counter_next;
	 end
	 
	 //save the every shift data to read_data
	 reg [7:0]  read_data;
	 reg [7:0]  read_data_reg;
	 always@(posedge clk or negedge rst_n) begin
	    if(!rst_n) 
		    read_data <= 8'd0;
		 else
		    read_data <= read_data_reg;
	 end
	 
	 //save the ds1302 I/O output value
	 reg ds1302_data_reg_reg;
	 always@(posedge clk or negedge rst_n) begin
	   if(!rst_n)
		   ds1302_data_reg_reg <= 'd0;
		else
		   ds1302_data_reg_reg <= ds1302_data_reg;
	 end
	 
	 reg [2:0] state;
	 reg [2:0] state_next;
	 
	 always@(posedge clk or negedge rst_n) begin
	    if(!rst_n)
		     state <= idle_state;
		 else
		     state <= state_next;
	 end
	 
	 always@(*)  begin
	    state_next = state;
		 ds1302_ce = 1'b1;
		 delay_time_next = delay_time;
		 finish		= 1'b0;
		 read_data_reg = read_data;
		 bit_counter_next = bit_counter;
		 ds1302_data_reg = ds1302_data_reg_reg;
		 read_enable = 0; //default ds1302_data  is output
		 read_finish = 'b0;
		 case(state)
		 idle_state: begin
		    delay_time_next   = 'd0;
			 ds1302_ce		    = 'b0;
			 bit_counter_next  = 'd0;
			 read_data_reg     = 'd0;
			 if(enable)
			    state_next = ready_state;
		 end
		 ready_state: begin		 
		    if(delay_time >= 99)   //delay 2us
			   begin
				  state_next = write_command_state;
				  delay_time_next = 'd0;
				end
			 else
			   begin
				  delay_time_next = delay_time + 1'b1;
				end
		 end
		 //write byte command data
		 write_command_state: begin
		    if(delay_time == 50) //delay 1us ,let ds1302_data data meet setup and hold time
			     ds1302_data_reg = command[bit_counter];
		    if(delay_time >= 199)    //delay 4us , 1-bits data transfer finish
			    begin
				    if(bit_counter_next >= 7)  //send_8-bits data  finish
					   begin
						   bit_counter_next = 'd0;
							if(command[0] == 0)
						      state_next = write_data_state;
							else
							   state_next = read_data_state;
							delay_time_next = 'd0;
						end
					 else
					   begin
						   bit_counter_next = bit_counter + 1'b1;
							delay_time_next = 'd0;
						end
				 end
			 else
			    delay_time_next = delay_time + 1'b1;
		 end
		 //write 8-bits data to ds1302
		 write_data_state: begin
		    if(delay_time == 50) //delay 1us ,let ds1302 data meet setup and hold time
			     ds1302_data_reg = write_data[bit_counter];
		    if(delay_time >= 199)    //delay 4us , 1-bits data transfer finish
			    begin
				    if(bit_counter_next >= 7)  //send_8-bits data  finish
					   begin
						   bit_counter_next = 'd0;
						   state_next = finish_state;
							delay_time_next = 'd0;					
						end
					 else
					   begin
						   bit_counter_next = bit_counter + 1'b1;
							delay_time_next = 'd0;
						end
				 end
			 else
			    delay_time_next = delay_time + 1'b1;
		 end
		 //read 8-bits data from ds1302
		 read_data_state: begin
		    read_enable = 1'b1; // set ds1302_data is input
		    if(delay_time == 50)     //delay 1us ,let data meet setup and hold time			     
				  read_data_reg = {ds1302_data,read_data[7:1]};
		    if(delay_time >= 199)    //delay 4us
			    begin
				    if(bit_counter_next >= 7)  //read_8-bits data
					   begin
						   bit_counter_next = 'd0;
							delay_time_next = 'd0;
							//counter_number_next = counter_number + 1'b1;
							state_next = finish_state;					
						end
					 else
					   begin
						   bit_counter_next = bit_counter + 1'b1;
							delay_time_next = 'd0;
						end
				 end
			 else
			    delay_time_next = delay_time + 1'b1;
		 end
		 finish_state: begin
		    ds1302_ce		    = 'b0;
			 if(delay_time == 150)
			     read_finish = 1'b1;
			 if(delay_time >= 199)  //delay 2us
			   begin
					state_next = idle_state;
					finish = 1'b1;
				end		   
			 else
			   delay_time_next = delay_time + 1'b1; 
		 end
		 default: state_next = idle_state;
		 endcase
	 end
	 
	 always@(*) begin
	    if(state == idle_state || state == ready_state)
		     ds1302_sclk = 1'b0;
		 else if(state == finish_state)
		     ds1302_sclk = 1'b1;
		 else
		     if(delay_time < 100)
			     ds1302_sclk = 1'b0;
			  else
			     ds1302_sclk = 1'b1;
	 end
	 
	 always@(posedge clk or negedge rst_n) begin
	   if(!rst_n)
		   ds1302_read_data <= 'd0;
		else
		   if(read_finish)
			  ds1302_read_data <= read_data;
	 end
	 
endmodule