一、SPI是一种常用的串行通信接口,与UART不同的地方在于。SPI可以同时挂多个从机,但是UART只能点对点的传输数据,此外SPI有四条线实现数据的传输,而UART采用的是2条实现串行数据的传输

 

1.SPI的主从机的接口模型

master和slave在时钟的上升沿采样,下降沿发送数据。数据从最高位(MSB)开始发送。)

 SPI以及IIC的verilog实现以及两者之间的对比

 

用3条通讯总线和1条片选线。

  • MOSI:Master Output Slave Input,顾名思义,即主设备输出/从设备输入。数据从主机输出到从机,主机发送数据。
  • MISO:Master Iutput Slave Onput,主设备输入/从设备输出,数据由从机输出到主机,主机接收数据。
  • SCK:即时钟信号线,用于通讯同步。该信号由主机产生,其支持的最高通讯速率为fpclk/2,即所挂载总线速率的一半。如SPI2挂载在APB1总线上,则其最高速率为36MHz / 2 = 18MHz。类似木桶效应,两个设备之间通讯时,通讯速率受限于较低速的设备。
  • NSS:即片选信号线,用于选择通讯的从设备,也可用CS表示。每个从设备都有一条独立的NSS信号线,主机通过将某个设备的NSS线置低电平来选择与之通讯的从设备。所以SPI通讯以NSS线电平置低为起始信号,以NSS线电平被拉高为停止信号。
SPI以及IIC的verilog实现以及两者之间的对比
2.SPI如何使用,以及对应的有几种配置模式(相位、极性)
SPI配置模式分类根据的是时钟信号空闲状态。、以及上升沿采样还是下降沿采样,
CPOL=0表示的是时钟空闲的时候为低电平,反之是高电平
CPHA=0表示的是时钟信号的第一个边沿是采样沿
CPHA=1表示的是时钟信号的第二个边沿是采样沿
对应的时序图如下:
SPI以及IIC的verilog实现以及两者之间的对比

 SPI以及IIC的verilog实现以及两者之间的对比

CPOL、CPHA

  • CPOL:即在没有数据传输时,时钟的空闲状态的电平。
  • CPHA:即数据的采样时刻。

SPI以及IIC的verilog实现以及两者之间的对比

有一点需要注意的是,主机和从机需要工作在相同的模式下才能正常通讯。

3.起始、停止信号(转于知乎)

SPI以及IIC的verilog实现以及两者之间的对比

如上图,编号1和6即为起始和停止信号的发生区域。NSS电平由高变低,则产生起始信号;NSS电平由低变高,则产生停止信号。从机检测到自己的NSS线电平被置低,则开始与主机进行通讯;反之,检测到NSS电平被拉高,则停止通讯。

4.数据有效性

MOSI和MISO线在SCK的每个时钟周期传输一位数据,开发者可以自行设置MSB或LSB先行,不过需要保证两个通讯设备都使用同样的协定。从图16-1看到,在SCK的上升沿和下降沿时进行触发和采样。

SPI有四种通讯模式,在SCK上升沿触发,下降沿采样只是其中一种模式。四种模式的主要区别便是总线空闲时SCK的状态及数据采样时刻。这涉及到“时钟极性CPOL”和“时钟相位CPHA”,由CPOL和CPHA的组合而产生了四种的通讯模式。

SPI以及IIC的verilog实现以及两者之间的对比

5.SPI的verilog实现:结合实际的应用场景对该通信协议进行分析:在一个网络通信模型中,可以将基带部分作为主控,RF部分作为受控部分,把SPI接口作两者之间传输数据的接口,它完成的主要工作是

(1)将从base band接收到的16位的并行数据,转换为RF所能接收的串行数据,并将该数据根据SPI协议送给RF。

(2)产生RF所需的时钟信号SCLK,使能信号CSB。

(3)接收从RF传回的串行数据,并将其转换为并行数据。

(4)将base band发送的数据,与RF返回的数据进行比较,并把比较结果传给base band。

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
Serial2Parallel_Master

相关文章:

  • 2021-06-11
  • 2022-12-23
  • 2022-02-27
  • 2021-06-04
  • 2021-05-24
  • 2021-10-06
  • 2022-12-23
猜你喜欢
  • 2021-09-04
  • 2022-01-21
  • 2022-12-23
  • 2022-12-23
  • 2021-08-05
  • 2021-11-01
相关资源
相似解决方案