【时钟模块设计】-RTC实时时钟芯片DS1302 写操作

分析

首先我要明确,这个RTC是在干什么,通过查阅DS1302芯片手册,我知道了,要想使用这块芯片,我需要用FPGA生成3个信号,

然后用DS1302去实现具体功能,需要实现什么功能我先不用关心,现在需要关心的是怎么使用FPGA生成这3条信号

 接下来我需要把问题进行拆解,先考虑RTC的写操作,即第二个时序图

通过观察发现,我需要使用FPGA生成3条信号:复位、串行时钟、输入/输出

注:当设计很多计数器时,先生成变化快的计数器,即小计数器先生成

分析SCK信号

SCK时钟信号实际是对系统时钟进行分频,DS1302不需要50M这么快的变化频率

因此,这里需要用一个计数器对SCK的周期进行计数,我假设对系统时钟进行4分频,于是需要设计一个0-3的计数器

计数器要从CE被拉高的瞬间开始计数,通过观察,在cnt_4[0]=1时,SCK进行翻转

至此SCK信号生成完毕

分析I/O信号

从图上观察知,IO信号在CE被拉高瞬间就开始传输数据了,并且在SCK的下降沿更新数据

此时我们已经有了一个cnt_4计数器,需要借助这个计数器生成一个cnt_16计数器,用来记录已经传输的数据位数

由于IO信号的数据是在SCK的下降沿进行更新的,于是就在SCK下降沿对cnt_16进行+1操作,即当cnt_4=3时,对cnt_16+1,表示传完一个数据

但这个cnt_16只有一个功能:用来记录传输数据是否传完,如果16位传完了,状态就跳转到IDLE,就停止传输数据,

对于IO信号本身的采样和更新,到目前为止还没有考虑

 

接下来就考虑如何得到IO信号:

1.由于在写操作中,两个字节都是进行写操作,不涉及IO转换,于是将2个字节看成一个状态:DRR,

2.设计状态机:

当外部有按键输入,就开始传输数据

当数据位数计数器cnt_16=15,且时钟计数器cnt_4=3就代表16位数据传输完成,可以进入空闲状态了

3.接下来就是IO信号的设计难点了,图中显示,在CE拉高瞬间数据就已经开始传输了

那这个时候有两种思路:

  • 使用CE上升沿检测,当检测到CE上升沿,立马更新IO信号
  • 使用状态机,当前状态=IDLE并且下一个状态=DRR

此处选用状态机方法:

 

 上面使用状态机,只是判断IO信号第一个数据的传输,但是后面的数据位就没有状态机上面的判断条件

于是有了下面这个写法,

注:这个写法是一个大佬告诉我的,不得不说,真的很巧妙

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data <= 16'd0;
    else if(state == IDLE && next_state == DRR)
    data <= {W_DATA,ADDRESS_8};
else if(cnt_4 == 2'd3)
    data <= {1'b0,data[15:1]};
end
assign IO = data[0];

巧妙1:声明一个data中间量,然后用组合逻辑把data给IO

巧妙2:在状态机判断处,将要传输的2个字节数据拼接,data变化瞬间,组合逻辑的IO同时改变,实现了CE拉高瞬间,传输data[0]

巧妙3:在SCK下降沿(即cnt_4=3)进行右移操作,这样每次data一改变,IO就同时改变

注:IO传输数据时,按照先传低位,再传高位的顺序

至此IO数据信号生成完毕

分析CE信号

CE信号最好生成,只要状态不是IDLE就让CE保持高电平

程序

一、设计文件

module w_ctrl_02
(
input        clk    ,
input        rst_n  ,
input        key    ,

output          CE        ,
output    reg    SCK      ,
output          IO        

);
parameter    IDLE    =    2'b01    ,
              DRR    =    2'b10    ;
                
parameter    ADDRESS_8 = 8'b0010_1001,//8位地址
                W_DATA = 8'b0101_0011;//8位数据
                
reg        [2:0]        state            ;
reg        [2:0]        next_state      ;

// 计满自动清0
reg        [1:0]        cnt_4            ;
reg        [3:0]        cnt_8            ;    

reg [15:0] data;

// 状态机                
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    state <= IDLE;
else 
    state <= next_state;
end

always @(*)
case(state)
    IDLE:    
        if(key == 1'b1)
        next_state <=    DRR;    
    DRR:    
        if((cnt_8 == 4'd15)&& (cnt_4 == 2'd3) )
        next_state <= IDLE;
    default:next_state <= IDLE;
endcase 

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data <= 16'd0;
else if(state == IDLE && next_state == DRR)
    data <= {W_DATA,ADDRESS_8};
else if(cnt_4 == 2'd3)
    data <= {1'b0,data[15:1]};
end
assign IO = data[0];

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    SCK <= 1'b0;
else if(cnt_4[0])
    SCK <= ~SCK;
end

assign CE = (state != IDLE);

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_4 <= 2'b0;
//else if(state != IDLE)
else if(CE == 1'd1)
    cnt_4 <= cnt_4+2'b1;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_8 <= 4'b0;
else if(cnt_4 == 2'd3) begin
    cnt_8 <= cnt_8+1'b1;
    if((cnt_8 == 4'd15))
        cnt_8 <= 4'b0;
end
end
endmodule 

二、测试文件

`timescale 1ns/1ns

module tb_w_ctrl;

reg clk;
reg rst_n ;
reg key;

wire CE;
wire SCK;
wire IO;

initial begin 

clk = 1'b0;
rst_n = 1'b0;
key = 1'b0;

#10

rst_n =1'b1;
#10
key = 1'b1;
#20
key = 1'b0;
end

always #10 clk = ~ clk;

w_ctrl_02 w_ctrl_02_inst
(
.clk(clk)        ,
.rst_n(rst_n)  ,
.key(key)        ,

.CE(CE)            ,
.SCK(SCK)        ,
.IO(IO)        

);

endmodule 

三、波形图

 

posted @ 2022-04-24 23:12  刘小颜  阅读(836)  评论(0编辑  收藏  举报