赞
踩
SPI(Serial Peripheral interface)串行外围设备接口是同步全双工的通信总线,在芯片的管脚上只占用四根线。

SS/NSS/CS:从设备选择信号线(片选信号线)。由主设备控制,选择指定的从设备。
当主机要选择从设备时,把该从设备的SS信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以SS线置低电平为开始信号,以SS线被拉高作为结束信号。
SCK (Serial Clock):时钟信号线。用于通讯数据同步,只能由主设备产生,两个设备之间通讯时,通讯速率受限于低速设备
MOSI(Master Output, Slave Input):主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
MISO(Master Input, Slave Output):主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
SPI总线内部结构:

结构抽象图:

在时钟信号控制下,主机将要发送的数据写到数据缓存区(Memory),缓存区经过8位移位寄存器shift register,串口移位寄存器通过MOSI信号线将数据一位一位的移到从机,从机将MISO接口收到的数据经过移位寄存器一位一位的移到数据缓存区(Memory)。同时从机也将自己移位寄存器数据通过MOSI发送给主机,两个移位寄存器数据完成交互,读写同时进行。
因此,SPI读写操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;若主机要读取从机的一个字节,就必须发送一个空字节引发从机传输。
CPOL:指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即SPI通讯开始前、 CS片选线为高电平时SCK的状态)。
CPOL = 0:SCK在空闲状态时为低电平CPOL = 1:SCK在空闲状态时为高电平CPHA:指数据的采样的时刻
CPHA = 0:MOSI/MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样(第一个跳变沿开始)CPHA = 1:MOSI/MISO数据线上的信号将会在SCK时钟线的“偶数边沿”被采样(第二个跳变沿开始)
| SPI模式 | 时钟极性CPOL | 时钟相位CPHA | 空闲时SCK时钟 | 采样时刻 |
|---|---|---|---|---|
| 0 | 0 | 0 | 低电平 | 第一个边沿(奇) |
| 1 | 0 | 1 | 低电平 | 第二个边沿(偶) |
| 2 | 1 | 0 | 高电平 | 第一个边沿(奇) |
| 3 | 1 | 1 | 高电平 | 第二个边沿(偶) |
由CPOL及CPHA的不同状态,SPI分成了四种模式,主机与从机必须工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”。
SPI相对于IIC,没有规定最大传输速率、设备地址、通信应答机制、流控制规则;只要四根线连接正确,SPI模式一致,将设备的CS片选线拉低,即可与其直接通信,且读写数据同时进行。

由图知:SCK空闲时为低电平,则CPOL = 0;第二个边沿开始采样,则CPHA = 1;即SPI模式1。
1:NSS片选线由高变低,是SPI通讯的起始信号
2/3/4/5:奇数边沿触发、偶数边沿采样
6:NSS片选线由低变高,是SPI通讯的停止信号
初始化代码:
void SPI_Init(void) { /*##-1- Enable peripherals and GPIO Clocks #########################*/ /* Enable GPIO TX/RX clock */ SPI_SCK_GPIO_CLK_ENABLE(); SPI_MISO_GPIO_CLK_ENABLE(); SPI_MOSI_GPIO_CLK_ENABLE(); SPI_NSS_GPIO_CLK_ENABLE(); /*##-2- Configure peripheral GPIO #######################*/ /* SPI SCK GPIO pin configuration */ GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = SPI_SCK_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(SPI_SCK_GPIO_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(SPI_SCK_GPIO_PORT, SPI_SCK_PIN, GPIO_PIN_RESET); // CLK 初始化低 /* SPI MISO GPIO pin configuration */ GPIO_InitStruct.Pin = SPI_MISO_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; HAL_GPIO_Init(SPI_MISO_GPIO_PORT, &GPIO_InitStruct); /* SPI MOSI GPIO pin configuration */ GPIO_InitStruct.Pin = SPI_MOSI_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(SPI_MOSI_GPIO_PORT, &GPIO_InitStruct); GPIO_InitStruct.Pin = SPI_NSS_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(SPI_NSS_GPIO_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(SPI_NSS_GPIO_PORT, SPI_NSS_PIN, GPIO_PIN_SET); // NSS 初始化高 }
SCK NSS MOSI 为输出,MISO为输入;CLK 初始化低,NSS 初始化高
模拟 SPI 通信(CPOL=0,CPHA=0)
void SPI_WriteByte(uint8_t data) { uint8_t i = 0; uint8_t temp = 0; for(i = 0; i < 8; i++) { temp = ((data & 0x80) == 0x80) ? 1 : 0; data = data << 1; SPI_CLK(0); // CPOL=0 SPI_MOSI(temp); SPI_Delay(); SPI_CLK(1); // CPHA=0 第一个边沿采样 SPI_Delay(); } SPI_CLK(0); // CPOL=0 SCK空闲电平为低 } uint8_t SPI_ReadByte(void) { uint8_t i = 0; uint8_t data = 0; for(i = 0; i < 8; i++) { data = data << 1; SPI_CLK(0); SPI_Delay(); SPI_CLK(1); SPI_Delay(); if (SPI_MISO()) data++; } SPI_CLK(0); return data; }
uint8_t SPI_WriteReadByte(uint8_t data) { uint8_t i = 0; uint8_t temp = 0; uint8_t read_data = 0; for(i = 0; i < 8; i++) { temp = ((data & 0x80) == 0x80) ? 1 : 0; data = data << 1; read_data = read_data << 1; SPI_CLK(0); SPI_MOSI(temp); SPI_Delay(); SPI_CLK(1); SPI_Delay(); if (SPI_MISO()) read_data++; } SPI_CLK(0); return data; }


Motorola模式,如果选择 TI 模式,则不需要此步骤)SPI_CR1 寄存器中的 SSM 和 SSI位置1。如果 NSS 引脚配置成输出,只应将 SSOE 位置 1。如果选择 TI 模式,则不需要此 步骤。SPI_CR1 寄存器中的 SSM 位置 1,将 SSI 位清零。如果选择 TI 模式,则不需要此步骤。
STM32F4/L0均有TI帧格式错误标志,F1则没有
发送缓冲区为空 (TXE)
此标志置 1 时,表示发送缓冲区为空,可以将待发送的下一个数据加载到缓冲区中。对 SPI_DR 寄存器执行写操作时,将清零 TXE 标志。
接收缓冲区非空 (RXNE)
此标志置 1 时,表示接收缓冲区中存在有效的已接收数据。读取 SPI_DR 时,将清零该标志。
BUSY
BSY 标志由硬件置 1 和清零,用于指示 SPI 通信的状态,BSY = 1:表明SPI正忙于通信。但有个例外:在主模式的双向接收模式下(MSTR=1、BDM=1并且BDOE=0),在接收期间BSY标志保持为低。以下情况硬件将清零该标志:
主模式故障 (MODF)
为避免包含多个 MCU 的系统中发生多从模式冲突,必须在 MODF 位清零序列期间将 NSS 引脚拉高。在该清零序列后,可以将 SPE 和 MSTR 位恢复到原始状态。
溢出错误(OVR)
CRC错误
对SPI外设的影响及如何清除错误标志 手册未说明。
TI 模式帧格式错误



模式设置
硬件NSS信号
基本参数
时钟参数

高级参数
MX SPI 初始化参数:
void MX_SPI1_Init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; //主模式 hspi1.Init.Direction = SPI_DIRECTION_2LINES; // 双向全双工 hspi1.Init.DataSize = SPI_DATASIZE_8BIT; // 8位数据长度 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; // CPOL=0, CLK极性为低电平 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; // CPHA=0, CLK相位为第一个边沿采样 hspi1.Init.NSS = SPI_NSS_SOFT; // 软件NSS硬件 hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 波特率 = fpclk / 2 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; // 最高位先发送 hspi1.Init.TIMode = SPI_TIMODE_DISABLE; // 不使用TI模式 hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 不使用CRC校验 hspi1.Init.CRCPolynomial = 7; // CRC多项式为7 if (HAL_SPI_Init(&hspi1) != HAL_OK) { Error_Handler(); } }
轮询方式的SPI发送、接收、收发函数:
HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,
uint32_t Timeout);
中断、DMA方式也均有此类函数。
参考:
END
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。