SPI通信协议

一、SPI简介

SPI(Serial Peripheral Interface,串行外围设备接口)是一种高速、全双工、同步通信总线。SPI 通讯协议的优点是支持全双工通信,通讯方式较为简单,且相对数据传输速率较快;缺点是没有指定的流控制,没有应答机制,在数据可靠性上有一定缺陷。

来自主机或者从机的数据在clk上升沿或下降沿同步,主机和从机可以通过MOSI、MISO线路同时传输数据。SPI接口可以是3线式(SCLK、CS、DIO)或者4线式(SCLK、CS、MOSI、MISO)  

SPI采用主从控制模式,通常由一个主模块和一个或多个从模块组成(不支持多主机),来自主机或者从机的数据在clk上升沿或下降沿同步,一般使用四条线进行通信SCLK、CS、MOSI、MISO) 。

MISO ( Master Input Slave Output ) : 主设备数据输入,从设备数据输出;
MOSI ( Master Output Slave Input ) : 主设备数据输出,从设备数据输入;
SCLK ( Serial Clock ) : 时钟信号,由主设备产生;
CS/SS ( Chip Select/Slave Select ) : 从设备片选信号,由主设备控制,通常低电平有效。

二、SPI四种通信方式

SPI总线在传输数据的同时也传输了时钟信号,时钟信号通过时钟极性(CPOL)时钟相位(CPHA)控制两个SPI设备何时交换数据以及何时对接收数据进行采样,保证数据在两个设备之间同步传输。

时钟极性(Clock Polarity, CPOL/CKP),它是指时钟信号在空闲状态下是高电平还是低电平,当时钟空闲时为低电平即 CPOL=0,反之则 CPOL=1


时钟相位(Clock Phase, CPHA/CKE),它是指时钟信号开始有效的第一个边沿和数据的关系。当时钟信号有效的第一个边沿处于数据稳定期的正中间时定义CPHA=0,反之时钟信号有效的第一个边沿不处于数据稳定期的正中间定义CPHA=1。所以在时钟信号SCK的第一个跳变沿采样即CPHA=0,再时钟信号SCK的第二个跳变沿采样为CPHA=1。
 

那么根据SPI的时钟极性和时钟相位特性可以设置4种不同的SPI通信操作模式:

SPI模式CPOLCPHA空闲时SCK时钟采样时刻
000低电平第1个边沿(奇)
101低电平第2个边沿(偶)
210高电平第1个边沿(奇)
311高电平第2个边沿(偶)

 Mode0:CKP=0,CPHA =0:当空闲态时,SCK处于低电平,数据采样是在第1个边沿,即SCK由低电平到高电平的跳变,所以数据采样是在上升沿(准备数据),(发送数据)数据发送是在下降沿。
Mode1:CKP=0,CPHA=1:当空闲态时,SCK处于低电平,数据发送是在第2个边沿,即SCK由低电平到高电平的跳变,所以数据采样是在下降沿,数据发送是在上升沿。
Mode2:CKP=1,CPHA=0:当空闲态时,SCK处于高电平,数据采集是在第1个边沿,即SCK由高电平到低电平的跳变,所以数据采集是在下降沿,数据发送是在上升沿。
Mode3:CKP=1,CPHA=1:当空闲态时,SCK处于高电平,数据发送是在第2个边沿,即SCK由高电平到低电平的跳变,所以数据采集是在上升沿,数据发送是在下降沿。

SPI协议规定一个SPI设备不能在数据通信过程中仅仅充当一个发送者(Transmitter)或者接受者(Receiver)。在片选信号CS为0的情况下,每个clock周期内,SPI设备都会发送并接收1bit数据,相当于有1bit数据被交换。

MOSI及 MISO的数据在SCK的上升沿期间变化输出,在 SCK的下降沿时被采样。即在 SCK 的下降沿时刻,MOSI及 MISO 的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI 及 MISO为下一次表示数据做准备。

SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。

SPI通信过程

主机将8位二进制数据10101100发送给从机,从机将8位二进制数据11001010发送给主机。

首先主机要将从机片选信号SS拉低此时代表从机被选中,在此之前 SCK 信号要始终处于低电平。SS拉低后时钟信号SCK开始启动,此时主机和从机的数据开始交互。

时钟主机数据MOSI从机数据MISO
初始低电平10101100初始值011001010初始值0
10101100X11001010X1
0010110011100101011
11011001X00010101X1
0101100110001010101
10110011X10101010X0
0011001101010101010
11100110X01010101X0
0110011000101010100
11001100X10101010X1
0100110011010101011
10011001X11010101X0
0001100101101010110
10110010X00101011X1
0011001010010101101
11100101X01010110X0
0110010100101011000

上表中时钟信号1代表时钟上升沿,0代表下降沿。
采用SPI通信Model 0,所以第一个时钟上升沿到来时完成数据采样,主机将要发送的数据最高位放到 MOSI上,而从机也将要发送的数据最高位放到 MISO上(保证两个 SPI 通讯设备之间使用同样的协定,一般都会采用MSB 先行模式),当第一个时钟的下降沿到来时主机把MISO上的数据存入移位寄存器中,而从机把 MOSI上的数据存入移位寄存器中,这样主机从机就完成了1bit 数据的交互。
当第二个时钟上升沿到来时,重复第一个时钟的操作就完成了 2bit 数据的交互,当 8 个时钟过去后,主机和从机就完成了 8bit 数据交互,此时主机中的数据由10101100 变成了11001010,而从机中的数据也由 11001010变成了10101100。

三、SPI协议实现

通过状态机的方式实现SPC协议Model 0

 发送

        当FPGA通过SPI总线往从机发送一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始发送数据,整个发送数据过程其实可以分为16个状态:

        状态0:SCK为0,MOSI为要发送的数据的最高位,即I_data_in[7]
        状态1:SCK为1,MOSI保持不变
        状态2:SCK为0,MOSI为要发送的数据的次高位,即I_data_in[6]
        状态3:SCK为1,MOSI保持不变
        状态4:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[5]
        状态5:SCK为1,MOSI保持不变
        状态6:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[4]
   状态7:SCK为1,MOSI保持不变
   状态8:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[3]
        状态9:SCK为1,MOSI保持不变
        状态10:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[2]
        状态11:SCK为1,MOSI保持不变
        状态12:SCK为0,MOSI为要发送的数据的下一位,即I_data_in[1]
        状态13:SCK为1,MOSI保持不变
        状态14:SCK为0,MOSI为要发送的数据的最低位,即I_data_in[0]
        状态15:SCK为1,MOSI保持不变
        一个字节数据发送完毕以后,产生一个发送完成标志位O_tx_done并把CS/SS信号拉高完成一次发送。

接收:

        当FPGA通过SPI总线从从机中接收一个字节(8-bit)的数据时,首先FPGA把CS/SS片选信号设置为0,表示准备开始接收数据,整个接收数据过程其实也可以分为16个状态,但是与发送过程不同的是,为了保证接收到的数据准确,必须在数据的正中间采样,接收过程的每个状态执行的操作为:

        状态0:SCK为0,不锁存MISO上的数据
        状态1:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[7]
        状态2:SCK为0,不锁存MISO上的数据
        状态3:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[6]
        状态4:SCK为0,不锁存MISO上的数据
        状态5:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[5]
        状态6:SCK为0,不锁存MISO上的数据
        状态7:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[4]
        状态8:SCK为0,不锁存MISO上的数据
        状态9:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[3]
        状态10:SCK为0,不锁存MISO上的数据
        状态11:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[2]
        状态12:SCK为0,不锁存MISO上的数
        状态13:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[1]        
        状态14:SCK为0,不锁存MISO上的数据
        状态15:SCK为1,锁存MISO上的数据,即把MISO上的数据赋值给O_data_out[0]
        一个字节数据接收完毕以后,产生一个接收完成标志位O_rx_done并把CS/SS信号拉高完成一次数据的接收。

module spi_module
(
    input               I_clk       , // 全局时钟50MHz
    input               I_rst_n     , // 复位信号,低电平有效
    input               I_rx_en     , // 读使能信号
    input               I_tx_en     , // 发送使能信号
    input        [7:0]  I_data_in   , // 要发送的数据
    output  reg  [7:0]  O_data_out  , // 接收到的数据
    output  reg         O_tx_done   , // 发送一个字节完毕标志位
    output  reg         O_rx_done   , // 接收一个字节完毕标志位
    
    // 四线标准SPI信号定义
    input               I_spi_miso  , // SPI串行输入,用来接收从机的数据
    output  reg         O_spi_sck   , // SPI时钟
    output  reg         O_spi_cs    , // SPI片选信号
    output  reg         O_spi_mosi    // SPI输出,用来给从机发送数据          
);

reg [3:0]   R_tx_state      ; 
reg [3:0]   R_rx_state      ;

always @(posedge I_clk or negedge I_rst_n)
begin
    if(!I_rst_n)
        begin
            R_tx_state  <=  4'd0    ;
            R_rx_state  <=  4'd0    ;
            O_spi_cs    <=  1'b1    ;
            O_spi_sck   <=  1'b0    ;
            O_spi_mosi  <=  1'b0    ;
            O_tx_done   <=  1'b0    ;
            O_rx_done   <=  1'b0    ;
            O_data_out  <=  8'd0    ;
        end 
    else if(I_tx_en) // 发送使能信号打开的情况下
        begin
            O_spi_cs    <=  1'b0    ; // 把片选CS拉低
            case(R_tx_state)
                4'd1, 4'd3 , 4'd5 , 4'd7  , 
                4'd9, 4'd11, 4'd13, 4'd15 : //整合奇数状态
                    begin
                        O_spi_sck   <=  1'b1                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end
                4'd0:    // 发送第7位
                    begin
                        O_spi_mosi  <=  I_data_in[7]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end
                4'd2:    // 发送第6位
                    begin
                        O_spi_mosi  <=  I_data_in[6]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end
                4'd4:    // 发送第5位
                    begin
                        O_spi_mosi  <=  I_data_in[5]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd6:    // 发送第4位
                    begin
                        O_spi_mosi  <=  I_data_in[4]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd8:    // 发送第3位
                    begin
                        O_spi_mosi  <=  I_data_in[3]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end                            
                4'd10:    // 发送第2位
                    begin
                        O_spi_mosi  <=  I_data_in[2]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd12:    // 发送第1位
                    begin
                        O_spi_mosi  <=  I_data_in[1]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b0                ;
                    end 
                4'd14:    // 发送第0位
                    begin
                        O_spi_mosi  <=  I_data_in[0]        ;
                        O_spi_sck   <=  1'b0                ;
                        R_tx_state  <=  R_tx_state + 1'b1   ;
                        O_tx_done   <=  1'b1                ;
                    end
                default:R_tx_state  <=  4'd0                ;   
            endcase 
        end
    else if(I_rx_en) // 接收使能信号打开的情况下
        begin
            O_spi_cs    <=  1'b0        ; // 拉低片选信号CS
            case(R_rx_state)
                4'd0, 4'd2 , 4'd4 , 4'd6  , 
                4'd8, 4'd10, 4'd12, 4'd14 : //整合偶数状态
                    begin
                        O_spi_sck      <=  1'b0                ;
                        R_rx_state     <=  R_rx_state + 1'b1   ;
                        O_rx_done      <=  1'b0                ;
                    end
                4'd1:    // 接收第7位
                    begin                       
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[7]   <=  I_spi_miso          ;   
                    end
                4'd3:    // 接收第6位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[6]   <=  I_spi_miso          ; 
                    end
                4'd5:    // 接收第5位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[5]   <=  I_spi_miso          ; 
                    end 
                4'd7:    // 接收第4位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[4]   <=  I_spi_miso          ; 
                    end 
                4'd9:    // 接收第3位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[3]   <=  I_spi_miso          ; 
                    end                            
                4'd11:    // 接收第2位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[2]   <=  I_spi_miso          ; 
                    end 
                4'd13:    // 接收第1位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b0                ;
                        O_data_out[1]   <=  I_spi_miso          ; 
                    end 
                4'd15:    // 接收第0位
                    begin
                        O_spi_sck       <=  1'b1                ;
                        R_rx_state      <=  R_rx_state + 1'b1   ;
                        O_rx_done       <=  1'b1                ;
                        O_data_out[0]   <=  I_spi_miso          ; 
                    end
                default:R_rx_state  <=  4'd0                    ;   
            endcase 
        end    
    else
        begin
            R_tx_state  <=  4'd0    ;
            R_rx_state  <=  4'd0    ;
            O_tx_done   <=  1'b0    ;
            O_rx_done   <=  1'b0    ;
            O_spi_cs    <=  1'b1    ;
            O_spi_sck   <=  1'b0    ;
            O_spi_mosi  <=  1'b0    ;
            O_data_out  <=  8'd0    ;
        end      
end

endmodule

参考文献:

 正点原子、野火FPGA

【接口时序】4、SPI总线的原理与Verilog实现 - jgliu - 博客园 (cnblogs.com)

  • 16
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: ILI9488是一款广泛使用的TFT液晶屏驱动芯片,其SPI通信协议如下: 1. 在SPI通信开始前,先拉低CS (Chip Select)管脚,然后拉低RS (Register Select)管脚,表示接下来要写入命令。 2. 向ILI9488写入命令时,先发送一个字节的命令代码,然后再发送该命令需要的参数(如果有的话)。 3. 写入命令后,可以拉高RS管脚,表示接下来要写入数据。 4. 向ILI9488写入数据时,直接发送数据即可。 5. SPI通信结束后,拉高CS管脚,表示本次通信结束。 具体的命令代码和参数可以参考ILI9488的数据手册,例如: - 0x36:设置扫描方向 - 0x3A:设置像素格式 - 0x2A:设置列地址 - 0x2B:设置行地址 - 0x2C:写入像素数据 在具体实现时,需要根据ILI9488的数据手册来编写SPI通信的代码。 ### 回答2: ILI9488是一种常用于液晶显示屏控制器的芯片,它使用SPI(串行外设接口)进行通信。SPI是一种串行通信协议,它通过四个信号线进行数据传输,包括时钟线(SCLK),主设备输出数据线(MOSI),主设备输入数据线(MISO)和片选线(CS)。 在ILI9488的SPI通信协议中,通信通过以下步骤进行: 1. 在通信开始前,主设备通过拉低片选线来选择ILI9488芯片作为通信对象。 2. 主设备根据芯片规定的时钟频率和极性生成时钟信号,从而同步数据传输。 3. 主设备将需要传输的数据发送到MOSI线上,高位先传送。 4. 在传输数据的过程中,芯片会检测到时钟的上升或下降边沿,并在边沿处读取MOSI线上的数据。数据传输完毕后,芯片会将数据写入相应的寄存器或进行相关操作。 5. 芯片可通过MISO线向主设备发送数据。但对于ILI9488来说,这个功能多数情况下是不被使用的。 6. 传输完成后,主设备通过拉高片选线来结束通信。 ILI9488的SPI通信协议具有简单、可靠和较快的特点。通过SPI接口,可以实现对ILI9488的控制和显示操作。同时,SPI通信协议也可以提供对其他外设的控制和通信,为应用提供了较大的灵活性和可扩展性。值得注意的是,具体的通信细节如时钟频率、数据格式等需要根据ILI9488的数据手册来设置,以确保通信的正常进行。 ### 回答3: ILI9488是一种常见的液晶显示驱动IC,它采用SPI通信协议与主控芯片进行通信。SPI全称为串行外围接口协议,它是一种同步的、全双工的串行通信协议。 ILI9488的SPI通信协议在硬件层面上,需要使用四个引脚进行通信,分别为时钟线CLK、数据命令控制线D/C、数据输入线MOSI和数据输出线MISO。其中CLK用于传输时钟信号,D/C用于控制数据或命令的传输,MOSI用于主控芯片向ILI9488发送数据,MISO用于ILI9488向主控芯片发送数据。 ILI9488的SPI通信协议在数据传输上是基于字节的,通信的数据帧包含一个命令字节和若干数据字节。根据具体的功能需要,主控芯片通过D/C引脚发送命令字节或数据字节。命令字节用于控制ILI9488的各种操作,如初始化设置、显示模式选择、像素颜色设置等,而数据字节则用于传输实际显示的像素数据。 在通信过程中,主控芯片通过CLK引脚向ILI9488提供时钟信号,ILI9488根据时钟信号判断数据的有效性,并按照时钟信号的边沿读取或发送数据。通过MOSI和MISO引脚的数据交换,主控芯片可以向ILI9488发送命令字节或数据字节,并接收ILI9488的响应数据。 总之,ILI9488的SPI通信协议是通过CLK、D/C、MOSI和MISO四个引脚进行数据传输的,主控芯片通过时钟信号控制数据的读写,通过命令字节和数据字节实现与ILI9488的各种交互操作。这种协议结构简单,适用于许多SPI接口的设备,使得ILI9488与主控芯片之间的通信更加方便和高效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值