FPGA之DS1302
0赞DS1302是比较经典的时钟芯片,刚好开发板上有这一个芯片。然后去准备用verilog去控制控制。
以下是DS1302的封装和管脚说明。
其中,主要就是控制CE,I/O,SCLK这三个信号。。其他都是电路固定接好的。不需要控制。这里要注意一下I/O这个管脚是双向口。所以在FPGA中要用inout端口表示。
然后就是时序。
以上就是读和写的时序。
无论是写还是读,都是在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中规定的时序。
比如规定,SCLK的高电平和低电平的时间最小是250ns(5V供电下),那么设计的时候,就要保证SCLK的高电平和低电平的持续时间至少是250ns。不过一般要留一些余量。比如可以设置时间为1us。毕竟对于实时时钟,不需要这么快的操作速度。
然后是控制字的说明:
前面的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抓取的波形:
可以看到是正常读取数据的,只不过操作比较快,所以看不到读取数据的变化。
以下附上整个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