板级通信总线之SPI及其Verilog实现

打算写几篇专题,系统总结下常用的片上总线、现场总线,就先从最常用的SPI开始吧。

1. SPI是干什么的?除了SPI还有那些其它电路板及的通讯总线?有何差别?

相信接触过MCU的同学对SPI都不陌生,详细定义就不罗嗦了。SPI常用的场合包括ADC读写、存储芯片读写、MCU间通讯等等。可以一主多从(通过片选来选择Slave),也可以做成菊花链等等形式的拓扑。与SPI类似的总线还有IIC、UART等,甚至还有很多单根线的总线,原理都是基于简单的串行通信,区别在于收发时序和连接拓扑。要熟练使用这些总线,关键在于理解其时序图,在此基础上创造各种变种的总线形式也不是难事(当然为了设计的通用性不建议这么做)。

以下维基百科的SPI词条介绍非常全面,推荐阅读。

https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Data_transmission

2. SPI是什么样的?

在此借用一张维基百科上的图,SPI通常有4根线,SS用于选定当前通信的slave,SCLK为通信的基准时钟,采样/发送都在时钟边沿执行,MOSI、MISO为串行的数据线。

以下是一个典型的SPI时序图,Master和Slave均在时钟上升沿采样,下降沿发送数据。数据从最高位(MSB)开始发送。

需要注意图中所有的时序关系都要被满足,包括CS下降沿到第1个时钟上升沿间隔(tsclk_su)、数据的建立时间(tSU)、保持时间(tHD)等等。通常这些参数由具体的器件决定,如果不满足则有通信失败的风险。

3. 如何使用SPI?SPI有哪几种配置模式(相位、极性)?

根据SPI时钟信号的空闲状态、是上升沿采样还是下降沿采样,SPI有四种模式。CPOL=0表示时钟空闲时为低电平,反之为高电平;CPHA=0表示时钟信号第一个边沿是采样边沿,反之表示第2个边沿是采样边沿。对于带SPI接口的MCU而言,通常可由软件配置CPOL(Clock Polarity)、CPHA(Clock Phase),以适应和不同类型器件的通信。

 

4. 如何用verilog 编写SPI协议?

以下是使用verilog写的SPI主从通信代码,经过实测通信OK,可供参考。主从都在下降沿置数,同时在下降沿采样上一次发送的数据。为了尽量在主时钟较慢的情况下提高通信速度,采用的是非同步采样方式(即直接用SCLK边沿触发采样/发送,而不用主时钟对SCLK进行同步)。

module Serial2Parallel_Master #(
    parameter SCLK_DIVIDER = 8'd0 //Sclk Freq = Clk/2 / (SCLK_DIVIDER + 1)
    )(
    input rst_n,
    input clk,
    input sDataRd,
    input [15:0] pDataWr,
    output dataCS,
    output dataSclk,
    output sDataWr,
    output [15:0] pDataRd
);

            
    // counter,used to generate dataSclk signal
    reg dataCS_reg;
    reg dataSclk_reg;
    reg[7:0] Count1;
    always @(posedge clk or negedge rst_n)
        if(!rst_n)
            Count1 <= 8'd0;
        else if(Count1 == SCLK_DIVIDER)
            Count1 <= 8'd0;
        else
            Count1 <= Count1 + 1'b1;
            
    // generate CS and Sclk sequence        
    reg [5:0] i;//Step number 
    always @(posedge clk or negedge rst_n)
        if(!rst_n)begin
            i <= 6'd0;
            dataSclk_reg <= 1'b1;//Sclk high at reset
            dataCS_reg <= 1'b1;     //CS high at reset
        end
        else begin
            case(i)
                //pull down CS at the beginning
                6'd0:
                if(Count1 == SCLK_DIVIDER) begin  
                    i <= i + 1'b1;
                    dataSclk_reg <= 1'b1;
                    dataCS_reg <= 1'b0;    
                end
                else;       
                //generate 1st to 17th Sclk falling edge
                6'd1,6'd3,6'd5,6'd7,6'd9,6'd11,6'd13,6'd15,6'd17,6'd19,6'd21,6'd23,6'd25,5'd27,6'd29,6'd31,6'd33:
                if(Count1 == SCLK_DIVIDER) begin
                    dataSclk_reg <= 1'b0;
                    dataCS_reg <= 1'b0;
                    i <= i + 1'b1;
                end
                else;
                //generate 1st to 16th Sclk rising edge
                6'd2,6'd4,6'd6,6'd8,6'd10,6'd12,6'd14,6'd16,6'd18,6'd20,6'd22,6'd24,6'd26,6'd28,6'd30,6'd32:
                if(Count1 == SCLK_DIVIDER) begin
                    dataSclk_reg <= 1'b1;
                    dataCS_reg <= 1'b0;    
                    i <= i + 1'b1;
                end
                else; 
                6'd34://CS and Sclk go high
                if(Count1 == SCLK_DIVIDER) begin
                    dataSclk_reg <= 1'b1;
                    dataCS_reg <= 1'b1;
                    i <= i + 1'b1;
                end
                else;
                6'd35://CS keep high, Sclk go low
                if(Count1 == SCLK_DIVIDER) begin
                    dataSclk_reg <= 1'b0;
                    dataCS_reg <= 1'b1;
                    i <= 6'd0;
                end
                else ;
                default ;
            endcase
        end ;
        
            
    // - receive and send SPI data    
    reg sDataWr_reg;
    reg [15:0] pDataRd_reg;
    reg rxDone_reg;
    reg [5:0] j;   
    always @(negedge dataSclk or negedge rst_n)
        if(!rst_n) begin
            j <= 6'd0;
            sDataWr_reg <= 1'b0;
            pDataRd_reg <= 16'd0;
            rxDone_reg <= 1'b0;
        end
        // - CS high,clear j & AD data
        else if(dataCS) begin 
            j <= 6'd0;
            sDataWr_reg <= 1'b0;
            pDataRd_reg <= 16'd0;
            rxDone_reg <= 1'b0;
        end
        else begin 
            // - first falling of Sclk, send MSB of send data
            if(j == 6'd0) begin    
                j <= j + 1'b1;
                sDataWr_reg <= pDataWr[15];//send data 
                pDataRd_reg <= 16'd0;//receive data clear
                rxDone_reg <= 1'b0;
            end
            
            // - 2nd to 16th falling of Sclk
            else if(j <= 6'd15) begin    
                j <= j + 1'b1;
                sDataWr_reg <= pDataWr[15-j];//send data 
                pDataRd_reg[16-j] <= sDataRd;//receive data
                rxDone_reg <= 1'b0;
            end
            
            // - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data
            else if(j == 6'd16) begin 
                j <= j + 1'b1;
                sDataWr_reg <= 1'b0;//send data clear
                pDataRd_reg[0] <= sDataRd;//receive data
                rxDone_reg <= 1'b1;//receive done
            end             
            else begin
                j <= j;
                sDataWr_reg <= sDataWr_reg;
                pDataRd_reg <= pDataRd_reg;
                rxDone_reg <= rxDone_reg;
            end 
        end
        

    // - data latch for pDataRd
    reg [15:0] pDataRd_l;
    always @(posedge clk or negedge rst_n)
        if(!rst_n)
            pDataRd_l <= 16'd0;
        else if(rxDone_reg) begin
            pDataRd_l <= pDataRd_reg;
        end
        else begin
            pDataRd_l <= pDataRd_l;
        end
        
    // - delay sDataWr for 1 main clk(10ns)
    reg sDataWr_dly;
    always @(posedge clk or negedge rst_n)
        if(!rst_n)
            sDataWr_dly <= 1'b0;
        else if(sDataWr_reg) begin
            sDataWr_dly <= 1'b1;
        end
        else begin
            sDataWr_dly <= 1'b0;
        end
        
    // - output assignment
    assign dataCS = dataCS_reg;
    assign dataSclk = dataSclk_reg;
    assign sDataWr = sDataWr_dly;
    assign pDataRd = pDataRd_l;
    

    
endmodule
View Code:SPI Master
 1 module Serial2Parallel_Slave (
 2 
 3     input rst_n,
 4     input clk,
 5      
 6     input dataCS,
 7     input dataSclk,
 8      
 9     input sDataRd,
10     input [15:0] pDataWr,
11 
12     output sDataWr,
13     output [15:0] pDataRd
14 );
15             
16     // - SPI read and write
17     reg sDataWr_reg;
18     reg [15:0] pDataRd_reg;
19     reg rxDone_reg;
20     reg [5:0] j;//operation steps
21     always @(negedge dataSclk or negedge rst_n)
22         if(!rst_n) begin
23             j <= 6'd0;
24             sDataWr_reg <= 1'b0;
25             pDataRd_reg <= 16'd0;
26             rxDone_reg <= 1'b0;
27         end
28         // - CS high,clear j & AD data
29         else if(dataCS) begin 
30             j <= 6'd0;
31             sDataWr_reg <= 1'b0;
32             pDataRd_reg <= 16'd0;
33             rxDone_reg <= 1'b0;
34         end
35         else begin 
36             // - first falling of Sclk, send MSB of send data
37             if(j == 6'd0) begin    
38                 j <= j + 1'b1;
39                 sDataWr_reg <= pDataWr[15];//send data 
40                 pDataRd_reg <= 16'd0;//receive data clear
41                 rxDone_reg <= 1'b0;
42             end
43             
44             // - 2nd to 16th falling of Sclk
45             else if(j <= 6'd15) begin    
46                 j <= j + 1'b1;
47                 sDataWr_reg <= pDataWr[15-j];//send data 
48                 pDataRd_reg[16-j] <= sDataRd;//receive data
49                 rxDone_reg <= 1'b0;
50             end
51             
52             // - at 17th falling of sclk_fbk, CS is still low, receive LSB of receive data
53             else if(j == 6'd16) begin 
54                 j <= j + 1'b1;
55                 sDataWr_reg <= 1'b0;//send data clear
56                 pDataRd_reg[0] <= sDataRd;//receive data
57                 rxDone_reg <= 1'b1;//receive done
58             end             
59             else begin
60                 j <= j;
61                 sDataWr_reg <= sDataWr_reg;
62                 pDataRd_reg <= pDataRd_reg;
63                 rxDone_reg <= rxDone_reg;
64             end 
65         end
66 
67     // - data latch for pDataRd
68     reg [15:0] pDataRd_l;
69     always @(posedge dataCS or negedge rst_n)
70         if(!rst_n)
71             pDataRd_l <= 16'd0;
72         else if(rxDone_reg) begin
73             pDataRd_l <= pDataRd_reg;
74         end
75         else begin
76             pDataRd_l <= pDataRd_l;
77         end
78         
79     // - output assignment
80     assign sDataWr = sDataWr_reg;
81     assign pDataRd = pDataRd_l;
82     
83     
84 endmodule
View Code:SPI Slave
原文地址:https://www.cnblogs.com/lobster89/p/5197266.html