SPI协议解读

1. spi通信协议

        SPI是一个同步的数据总线,总共有四根线,分别为:SCLK、CS、MISO、MOSI。产生时钟的一侧为主机(master),另一侧为从机(slave),主机只有一个,从机可以有多个,分为一主一从、一主多从以及菊花式连接方式的一主多从。

整体的传输大概可以分为以下几个过程:

①:主机先将NSS信号拉低,这样保证开始接收数据;

②:当接收端检测到时钟的边沿信号时,它将立即读取数据线上的信号,这样就得到了一位数据(1bit);

③:主机发送到从机时:主机产生相应的时钟信号,然后数据一位一位地将从MOSI信号线上进行发送到从机;

④:主机接收从机数据:如果从机需要将数据发送回主机,则主机将继续生成预定数量的时钟信号,并且从机会将数据通过MISO信号线发送;

2. 四种工作模式

CPOL表示SCLK的空闲状态的有效电平;
CPOL = 0表示SCLK的空闲状态是低电平,CPOL = 1表示SCLK的空闲状态是高电平;
CPHA规定了数据的采样时间;
CPHA = 0表示在SCLK的有效电平的第一个跳变边沿进行数据采样,CPHA= 1表示在SCLK的有效电平的第二个跳边沿进行数据采样;

Mode0:CPOL=0,CPHA =0:当空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就    是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿,数据发送是在下降沿。

Mode1:CPOL=0,CPHA=1:当空闲态时,SCLK处于低电平,数据发送是在第2个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。


Mode2:CPOL=1,CPHA=0:当空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。


Mode3:CPOL=1,CPHA=1:当空闲态时,SCLK处于高电平,数据发送是在第2个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

3. 时序图

SPI0:(最常用)

SPI1:

SPI2:

SPI3:

代码及仿真:

spi_drive:

`timescale 1ns / 1ps

module spi_drive#(
    parameter                           P_DATA_WIDTH        = 8 ,
                                        P_READ_DATA_WIDTH   = 8 , 
                                        P_CPOL              = 0 ,
                                        P_CPHL              = 0 
)(                  
    input                               i_clk               ,
    input                               i_rst               ,

    output                              o_spi_clk           ,
    output                              o_spi_cs            ,
    output                              o_spi_mosi          ,
    input                               i_spi_miso          ,

    input   [P_DATA_WIDTH - 1 :0]       i_user_data         ,
    input                               i_user_valid        ,
    output                              o_user_ready        ,

    output  [P_READ_DATA_WIDTH - 1:0]   o_user_read_data    ,
    output                              o_user_read_valid   
);


reg                                 ro_spi_clk          ;
reg                                 ro_spi_cs           ;
reg                                 ro_spi_mosi         ;
reg                                 ro_user_ready       ;
reg  [P_DATA_WIDTH - 1:0]           r_user_data         ;
reg                                 r_run               ;
reg  [15:0]                         r_cnt               ;
reg                                 r_spi_cnt           ;
reg  [P_READ_DATA_WIDTH - 1:0]      ro_user_read_data   ;
reg                                 ro_user_read_valid  ;
reg                                 r_run_1d            ;

wire                                w_user_active       ;
wire                                w_run_negedge       ;


assign o_spi_clk            = ro_spi_clk            ;
assign o_spi_cs             = ro_spi_cs             ;
assign o_spi_mosi           = ro_spi_mosi           ;
assign o_user_ready         = ro_user_ready         ;
assign o_user_read_data     = ro_user_read_data     ;
assign o_user_read_valid    = ro_user_read_valid    ;
assign w_run_negedge        = !r_run & r_run_1d     ;


assign w_user_active = i_user_valid & o_user_ready;

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_ready <='d1;
    else if(w_user_active)
        ro_user_ready <= 'd0;
    else if(w_run_negedge)
        ro_user_ready <= 'd1;
    else 
        ro_user_ready <= ro_user_ready;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_user_data <= 'd0;
    else if(w_user_active)
        r_user_data <= i_user_data;
    else if(r_spi_cnt)
        r_user_data <= r_user_data << 1;
    else 
        r_user_data <= r_user_data;    
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_run <= 'd0;
    else if(r_spi_cnt && r_cnt == 7)
        r_run <= 'd0;
    else if(w_user_active)
        r_run <= 'd1;
    else 
        r_run <= r_run;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_run_1d <= 'd0;
    else
        r_run_1d <= r_run;
end
always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_cnt <= 'd0;
    else if(r_spi_cnt && r_cnt == 7)
        r_cnt <= 'd0;
    else if(r_spi_cnt)
        r_cnt <= r_cnt + 1;
    else 
        r_cnt <= r_cnt;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        r_spi_cnt <= 'd0;
    else if(r_run)
        r_spi_cnt <= r_spi_cnt + 1;
    else 
        r_spi_cnt <= 'd0;
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_spi_clk <= P_CPOL;
    else if(r_run)
        ro_spi_clk <= ~ro_spi_clk;
    else 
        ro_spi_clk <= P_CPOL; 
end

always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_spi_cs <= 'd1;
    else if(w_user_active)
        ro_spi_cs <= 'd0;
    else if(!r_run)
        ro_spi_cs <= 'd1;
    else 
        ro_spi_cs <= ro_spi_cs;
end


always@(posedge i_clk,posedge i_rst)
begin
    if(i_rst)
        ro_spi_mosi <= 'd0;
    else if(w_user_active)
        ro_spi_mosi <= i_user_data[P_DATA_WIDTH - 1];
    else if(r_spi_cnt)
        ro_spi_mosi <= r_user_data[P_DATA_WIDTH - 2];
    else 
        ro_spi_mosi <= ro_spi_mosi;
end     

always@(posedge ro_spi_clk,posedge i_rst)
begin
    if(i_rst)
        ro_user_read_data <= 'd0;
   
    else
        ro_user_read_data <= {ro_user_read_data[P_DATA_WIDTH - 2 : 0],i_spi_miso}; 
end

always@(posedge i_clk,posedge i_rst) 
begin
    if(i_rst)
        ro_user_read_valid <= 'd0;
    else if(r_spi_cnt && r_cnt == 7)
        ro_user_read_valid <= 'd1;
    else 
        ro_user_read_valid <= 'd0;
end

endmodule

spi_drive_tb:

`timescale 1ns / 1ns

module spi_drive_tb();

localparam P_CLK_CYCLE = 20;

reg     clk,rst;

initial 
begin
    rst = 'd1;
    #100;
    @(posedge clk)rst = 'd0;
end

always
begin
    clk = 0;
    #(P_CLK_CYCLE/2);
    clk = 1;
    #(P_CLK_CYCLE/2);
end

wire        w_spi_clk           ;
wire        w_spi_cs            ;
wire        w_spi_mosi          ;
wire        w_user_ready        ;
wire [7:0]  w_user_read_data    ;
wire        w_user_read_valid   ;
reg  [7:0]  r_user_data         ;
reg         r_user_valid        ;

spi_drive#(
    .P_DATA_WIDTH        (      8           ),
    .P_READ_DATA_WIDTH   (      8           ), 
    .P_CPOL              (      0           ),
    .P_CPHL              (      0           )
)   
spi_drive_u0    
(                   
    .i_clk               (clk               ),
    .i_rst               (rst               ),

    .o_spi_clk           (w_spi_clk         ),
    .o_spi_cs            (w_spi_cs          ),
    .o_spi_mosi          (w_spi_mosi        ),
    .i_spi_miso          (w_spi_mosi        ),

    .i_user_data         (8'h55             ),
    .i_user_valid        (1                 ),
    .o_user_ready        (w_user_ready      ),

    .o_user_read_data    (w_user_read_data  ),
    .o_user_read_valid   (w_user_read_valid )
);
     


endmodule

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值