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

分析

首先看看读操作的时序图

 

 

 大体上和写操作是一样的,不同点就在于,读操作的第二个字节是从DS1302芯片中读数传给FPGA,

于是IO端口在第一个字节是输出端,在第二个字节就是输入端了,这里就存在一个既能作为输入也能作为输出的端口

因为不知道如何实现这个功能,于是去网上搜,最后知道人家有一个专有名词,叫:双向端口

定义inout实现功能,于是我又去查这个inout是怎么用的

看了不少文章,有的写得太多了,没看明白,随后找到特权同学这篇:

inout用法浅析 - 与非网 (eefocus.com)

我理解的inout的用法如下:

1.在模块开始时定义一个inout端口
inout        IO  
2.用条件判断语句给端口赋值
当端口被赋值为高阻态就表明此时端口为输入端
assign IO = io_control ? data[0] : 1'bz    ;
其中,io_control =1时,IO为输出端;io_control =0时,IO为输入端,
data就是需要在IO上传输的数据
3.根据data在不同状态的值,就可以得到最后的IO信号
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    data <= 8'd0;
else if(state == IDLE && next_state == DRR)
    data <= ADDRESS_8;
else if(cnt_4 == 2'd3 && state == DRR)
    data <= {1'b0,data[7:1]};
else if(cnt_4 == 2'd3 && state == READ)
    data <= {IO,data[7:1]};
end

仿真

一、设计文件

module r_ctrl
(
input        clk    ,
input        rst_n ,
input        key    ,

output            CE        ,
output    reg    SCK    ,
output    [7:0]SAVE    ,
output     reg        done    ,
inout                IO        

);
parameter    IDLE    =    3'b001    ,
                DRR    =    3'b010    ,
                READ  =    3'b100;
                
parameter    ADDRESS_8 = 8'b0010_1001;

                
reg        [2:0]        state            ;
reg        [2:0]        next_state    ;

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

reg [7:0] data;

// 双向IO控制端
reg        io_control        ;

// 状态机                
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 == 3'd7)&& (cnt_4 == 2'd3) )
        next_state <= READ;
    READ:
        if((cnt_8 == 3'd7)&& (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 <= 8'd0;
else if(state == IDLE && next_state == DRR)
    data <= ADDRESS_8;
else if(cnt_4 == 2'd3 && state == DRR)
    data <= {1'b0,data[7:1]};
else if(cnt_4 == 2'd3 && state == READ)
    data <= {IO,data[7:1]};
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
done <= 1'd0;
else if(state == READ && next_state == IDLE)
done <= 1'd1;
else 
done <= 1'd0;
end

assign SAVE = data;

assign IO = io_control ? data[0] : 1'bz        ;

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
io_control <= 1'b0;
else if(state == DRR)
io_control <= 1'b1;// inout为输出端口
else 
io_control <= 1'b0;
end


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);

// 这个计数器用来计数SCK的时钟周期的大小
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_4 <= 2'b0;
else if(state != IDLE)
    cnt_4 <= cnt_4+2'b1;
end

always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)
    cnt_8 <= 3'b0;
else if(cnt_4 == 2'd3) begin// 此处cnt_4=3时,cnt_16+1;和IO数据下降沿更新同时改变,这样就能计数传了多少个数了
    cnt_8 <= cnt_8+3'd1;
    if((cnt_8 == 3'd7))
        cnt_8 <= 3'b0;
end
end
endmodule 

二、测试文件

wait、force

这里的仿真文件学到了新东西:

`timescale 1ns/1ns

module tb_r_ctrl;

reg clk;
reg rst_n ;
reg key;

wire CE;
wire SCK;
wire IO;
wire [7:0] SAVE;
wire done;

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;

wait(r_ctrl_inst.state == r_ctrl_inst.READ)
force IO = 1'b0;
@ (negedge SCK)
force IO = 1'b0;
@ (negedge SCK)
force IO = 1'b1;
@ (negedge SCK)
force IO = 1'b0;
@ (negedge SCK)
force IO = 1'b1;
end

always #10 clk = ~ clk;

r_ctrl r_ctrl_inst
(
.clk(clk)        ,
.rst_n(rst_n)  ,
.key(key)        ,

.CE(CE)            ,
.SCK(SCK)        ,
.SAVE(SAVE)        ,
.done(done)        ,
.IO(IO)        

);

endmodule 

三、波形图

 

posted @ 2022-04-26 10:01  刘小颜  阅读(551)  评论(0编辑  收藏  举报