zoukankan      html  css  js  c++  java
  • 板级通信总线之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
  • 相关阅读:
    lsblk命令详解
    lspci命令详解
    numastat命令详解
    lsscsi命令详解
    lscpu命令详解
    linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明
    elk收集tomcat的日志
    npm安装
    centos7.5 解决缺少libstdc++.so.6库的原因及解决办法
    linux下正确卸载rpm包
  • 原文地址:https://www.cnblogs.com/lobster89/p/5197266.html
Copyright © 2011-2022 走看看