SPI基础知识
SPI是全双工的同步串行通讯总线,一般是用于微处理器和外围扩展芯片之间的串行连接,SPI是典型的主机-从机系统,支持通过不同的片选信号来了解多个外设。
SPI接口通常由四根线组成,分别是提供时钟的SCLK,提供数据输出的MOSI,提供数据输入的MISO和提供片选信号的CS。
SCLK :Serial Clock 串行时钟信号,由主机产生发送给从机;
MOSI:Master output slave input 主机输出,从机输入(数据来自主机);
MISO:Master input slave output 主机输入,从机输出(数据来自从机);
SS/CS:Slave Select 片选信号,由主机发送,以控制与哪个从机通信,通常是低电平有效信号。
同一时刻只能有一个SPI设备处于工作状态。为了适配不同的外设 ,SPI支持通过寄存器来配置片选信号以及时钟信号的极性和相位(在SPI主机中,时钟相位、时钟极性需要根据从机中的时钟相位和时钟极性配置)。
SPI的数据传输原理:通过上图的主机-从机连接示意图,主机mcu发出传输信号开始,将要传输的数据一如8位移位寄存器,同时产生8个时钟信号依次的从CLK引脚送出,在CLK引脚的控制下,主机的MCU中8位的移位寄存器的数据依次从MOSI引脚送出,到达从机的MCU的MOSI引脚后移入他的移位寄存器。也就是说,对于SPI而言,是在主机的控制下,从机从主机读取数据或者向主机发送数据,至于传输速率、数据何时移入、移出、一次移动完成后是否中断,如何来定义主机从机这些问题,均可以通过对寄存器编程来解决。
通过上面介绍可知,SPI的数据传输都是在时钟信号SCK的控制下完成的,数据传输中涉及到时钟极性和时钟相位的问题。
根据CPOL、CPHA的四种组合,从而有四种的SPI模式,
时钟极性 0表示空闲时为低电平 1 表示空闲时为高电平
时钟相位 0在第一个边沿采样 1 在第二个边沿采样
在S32DS中可以使用模块图形配置界面对SPI进行配置
对应的资源如下:
其LPSPI是一种低功耗串行外围接口,设计为使用很小的CPU开销,支持DMA访问并且生成DMA请求以及FIFIO(先进先出的存储器) 主操作最多支持4个外围芯片选择。
波特率 Baud rate:
配置SPI的传输速率,但是这个配置的值并不等于实际的传输速率,会根据设置的模块时钟和设置的波特率得出一个合理的波特率值。
* This function takes in the desired bitsPerSec (baud rate) and calculates the nearest
* possible baud rate,
SS 片选:片选的极性Active low是低电平有效,低激活(默认是高状态,拉低时片选信号激活)Active high是高电平有效。
Bit order:选择的是大端还是小端在前。
Bits/frame:范围是8位到32位。
Phase:选择在第几个边沿进行采样。
我们在S32DS中并没有直接对CPOL、CPHA的选项,而是修改成为了时钟极性和phase。方便不熟悉spi的新人考虑的,但是两者的参数不是相等的关系。
typedef enum
{
LPSPI_SCK_ACTIVE_HIGH = 0U, /*!< Signal is Active High (idles low). */
LPSPI_SCK_ACTIVE_LOW = 1U /*!< Signal is Active Low (idles high). */
} lpspi_sck_polarity_t;
CPOL 是时钟的极性, 如果空闲状态是低电平,有效状态是高的时候, CP0L 为 0,相
反,时钟空闲状态是高电平,低有效的话, CPOL 为 1,这个属性对应了配置的 Clock Polarity
属性。 当你看完这个结构体示意的时候,可以知道 CPOL = 0 是对应 Clock Polarity 选项的
Active High。
typedef enum
{
LPSPI_CLOCK_PHASE_1ST_EDGE = 0U, /*!< Data captured on SCK 1st edge, changed on
2nd. */
LPSPI_CLOCK_PHASE_2ND_EDGE = 1U /*!< Data changed on SCK 1st edge, captured
on 2nd. */
} lpspi_clock_phase_t;
CPHA 的值受到两个因素的影响,第一是 CPOL,第二是采样的位置,根据上图我们可
以得出这样的结论: CPOL 为 0 的时候,在第一个跳变沿采样,则 CPHA 为 0,在第二个跳
变沿采样则为 1。 Phase 这个选项配置很简单了,你只需要判断采样的位置即可,下面结合
几个例程,你应该就可以明白这些属性的配置。
在使用SDK开发的过程中常用到的就是
-
LPSPI_DRV_MasterInit
-
LPSPI_DRV_MasterTransfer(Blocking)
-
LPSPI_DRV_SlaveInit
-
LPSPI_DRV_SlaveTransfer(Blocking)
SPI_MasterInit(&spi1Instance,&spi1_MasterConfig0);
uint8_t SPI_ReadWrite_Byte(uint8_t Tx_Buf)
{
uint8_t Rx_Buf[1] = {0};
SPI_MasterTransferBlocking(&spi1Instance,&Tx_Buf,&Rx_Buf,1,1000);//阻塞式 其中1是发送的帧数
return Rx_Buf[0];
}
/*SPI写多个字节*/
uint8_t SPI1_Write_Buff(uint8_t reg,uint8_t *Tx_Buf,int lens)
{
uint8_t status;
int ctr;
status = SPI_ReadWrite_Byte(reg);
for(ctr=0; ctr<lens; ctr++)
{
SPI_ReadWrite_Byte(Tx_Buf[ctr]); //写入数据
}
return status;
}
/*SPI读多个字节*/
uint8_t SPI1_Read_Buff(uint8_t reg,uint8_t *Tx_Buf,int lens)
{
uint8_t status;
int ctr;
status = SPI_ReadWrite_Byte(reg);
for(ctr=0;ctr<lens;ctr++)
{
Tx_Buf[ctr]=SPI_ReadWrite_Byte(0XFF);
}
return status;
}
对应的相关示例:
我们需要根据对应传感器的时序图,得出需要所有的信息。
示例一:
示例二:
示例三: