【时序协议】-SPI-驱动模块-实验

分析

SPI驱动模块的作用:

SPI驱动模块在SPI通信中起了一个桥梁的作用,

通过指令发送模块将写操作、读操作等指令输入到驱动模块中,作为data_in

然后去Flash中读到数据后,通过data_out将读取到的数据返回给指令发送模块

状态机

输出信号

SCK、MOSI

SCK使用计数器实验4分频,在计数器cnt_4[0]=1的地方进行SCK翻转,就可以得到SCK输出信号

MOSI信号也需要使用cnt_4实现,分析如下图

MOSI的重点就是确定出什么时候采集信号、什么时候更新数据(即通过模式确定这2个重点)

一、设计文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/04/29 21:51:51
// Design Name: 
// Module Name: SPI
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// SPI驱动模块,工作在模式0,即SCK空闲状态为低电平,SCK下降沿更新MOSI的数据,MSB
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module SPI_drive_my(
    
input           clk             ,
input           rst_n           ,
input           spi_start       ,
input           spi_end         ,
input   [7:0]   data_in         ,
input           W_R             ,//判断模块工作模式:读、写

input             spi_MISO        ,
output    reg      spi_SCK         ,
output    reg      spi_CE          ,
output    reg     spi_MOSI        ,

output  reg        out_done        ,
output  reg        in_done         ,
output  reg [7:0]   data_out        
      
    );
// 定义状态机
parameter IDLE = 3'b001,
          WR = 3'b010,
          RD = 3'b100;
reg     [2:0]       state       ;
reg     [2:0]       next_state  ;

//定义计数器
reg     [1:0]       cnt_4      ;
reg     [3:0]       cnt_8       ;
parameter           MAX_CNT_4  =   3        ,
                    MAX_CNT_8   =   7       ;

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        state <= IDLE     ;
    else
        state <= next_state     ;
end

always @(*) begin
    case(state)
        IDLE:
            if(spi_start == 1'b1 && W_R == 1'b0)
                next_state <= RD  ;
            else if(spi_start == 1'b1 && W_R == 1'b1)
                next_state <= WR  ;
        RD:
            if(spi_end) 
                next_state <= IDLE  ;        
            else if(cnt_4 == MAX_CNT_4-2'd1 &&  cnt_8 == MAX_CNT_8-4'd1) begin
                    if(W_R == 1'b0) 
                        next_state <= RD  ;
                    else
                        next_state <= WR  ;
             
            end                     
        WR:
            if(spi_end) 
                next_state <= IDLE  ;
            else if(cnt_4 == MAX_CNT_4-2'd1 &&  cnt_8 == MAX_CNT_8-4'd1) begin
                    if(W_R == 1'b0) 
                        next_state <= RD  ;
                    else
                        next_state <= WR  ;
             
            end              
            default:next_state <= IDLE  ;
    endcase
end

// 计数器
// SCK时钟计数器
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_4  <=  2'd0   ;
    else if(state != IDLE)
        cnt_4  <=  cnt_4  +   2'd1   ;
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cnt_8 <= 8'd0;
    else if(cnt_4 == MAX_CNT_4)begin
            cnt_8 <= cnt_8 + 8'd1;
        if(cnt_8 == MAX_CNT_8)
            cnt_8 <= 8'd0;
    end

end

// 输出信号
// always@(posedge clk or negedge rst_n)begin
//     if(!rst_n)
//         spi_SCK <= 1'd0;
//     else if(spi_end)
//         spi_SCK <= 1'd0;
//     else if(cnt_4[0] && spi_end == 1'b0)
//         spi_SCK <= ~spi_SCK;
    
// end
// always@(posedge clk or negedge rst_n)begin
//     if(!rst_n)
//         spi_SCK <= 1'd0;
//     else if(cnt_4[0] && state != IDLE)
//         spi_SCK <= ~spi_SCK;
//     else 
//         spi_SCK <= 1'd0;
// end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        spi_SCK <= 1'd0;
    else if(cnt_4 == 2'd0 && state != IDLE)
        spi_SCK <= 2'd0;
    else if(cnt_4 == 2'd2 && state != IDLE)
        spi_SCK <= 1'd1;
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        spi_CE <= 1'b1;
    else if(spi_start == 1'b1)//SPI开始工作
        spi_CE <= 1'b0;
    else if(next_state == IDLE && cnt_8 == MAX_CNT_8 || spi_end)//SPI结束工作
        spi_CE <= 1'b1;
    else 
        spi_CE <= spi_CE;
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        spi_MOSI <= 1'b0;
    else if(!spi_CE && cnt_4 == 2'd0)//0模式、BSM 
        spi_MOSI <= data_in[7-cnt_8];
    else if(spi_CE)
        spi_MOSI <= 1'b0;
    else
        spi_MOSI <= spi_MOSI;
end
// 发送数据完成-标志位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        out_done <= 1'b0;
    else if(cnt_4 == 2'd3 && cnt_8 == 3'd7 && state == RD)
        out_done <= 1'd1;
    else 
        out_done <= 1'b0;
end
// 接收数据完成-标志位
always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        in_done <= 1'b0;
    else if(cnt_4 == 2'd2 && cnt_8 == 3'd7 && state == WR)
        in_done <= 1'd1;
    else 
        in_done <= 1'b0;
end

always@(posedge clk or negedge rst_n)begin
    if(!rst_n)
        data_out <= 8'd0;
    else if(cnt_4 == 2'd0 && state == RD)
        data_out[7-cnt_8] <= spi_MISO;
    else
        data_out <= data_out;
end

    
endmodule

 

二、测试文件

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2022/05/05 22:42:17
// Design Name: 
// Module Name: tb_SPI_drive
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module tb_SPI_drive();

reg clk,rst_n,spi_start,spi_end,W_R;
reg [7:0]data_in;
wire spi_SCK,spi_CE,spi_MOSI,out_done,in_done,spi_MISO;
wire [7:0]data_out;


initial begin
    clk = 1'b0;                        //初始时钟为0
    rst_n <= 1'b0;                    //初始复位
    spi_start <= 1'b0;    
    data_in <= 8'd0;        
    spi_end <= 1'b0;
    W_R <= 1'b1;    
    #80                                    //80个时钟周期后
    rst_n <= 1'b1;                    //拉高复位,系统进入工作状态
    #30                                    //30个时钟周期后拉高SPI开始信号,开始SPI传输
    spi_start <= 1'b1;
    data_in <= 8'h06;//写使能
    #20    
    spi_start <= 1'b0;
    @(posedge in_done);                //一个BYTE发送完成
    spi_start <= 1'b1;
    data_in <= 8'h02;//PP
    @(posedge in_done);    
    spi_start <= 1'b1;
    data_in <= 8'b01010101;//地址
    @(posedge in_done);    
    spi_start <= 1'b1;
    data_in <= 8'b01010101;//地址
    @(posedge in_done);
    spi_start <= 1'b1;
    data_in <= 8'b01010101;//地址
    @(posedge in_done);
    W_R <= 1'b0;
    spi_start <= 1'b1;



    @(negedge spi_SCK);
    force spi_MISO = 1'b0;
    @(negedge spi_SCK);
    force spi_MISO = 1'b1;   
    @(negedge spi_SCK);
    force spi_MISO = 1'b0;
    @(negedge spi_SCK);
    force spi_MISO = 1'b1;
    @(negedge spi_SCK);
    force spi_MISO = 1'b1;
    @(negedge spi_SCK);
    force spi_MISO = 1'b0;
    @(negedge spi_SCK);
    force spi_MISO = 1'b0;
    @(negedge spi_SCK);
    force spi_MISO = 1'b1;
    @(negedge out_done);    

    spi_end <= 1'b1;    
    #20    spi_end <= 1'b0;    //拉高一个周期结束信号
end
    
//------------<设置时钟>----------------------------------------------
always #10 clk = ~clk;


SPI_drive_my SPI_drive_my_inst
    (
    
.clk(clk)             ,
.rst_n(rst_n)           ,
.spi_start(spi_start)       ,
.spi_end(spi_end)         ,
.data_in(data_in)         ,
.W_R(W_R)             ,

.spi_MISO(spi_MISO)        ,
.spi_SCK(spi_SCK)         ,
.spi_CE(spi_CE)          ,
.spi_MOSI(spi_MOSI)        ,

.out_done(out_done)        ,
.in_done(in_done)         ,
.data_out(data_out)        
      
    );
endmodule

三、波形图

 

posted @ 2022-05-09 20:48  刘小颜  阅读(147)  评论(0编辑  收藏  举报