目录
(4)SPI的4种通讯模式 (CPOL - 时钟极性 与 CPHA - 时钟相位)
一、SPI的简介
1、SPI物理层
SPI 一般使用4条线进行通信:
- NSS:为片选线
- MOSI: 主机数据输出、从机数据输入
- MISO:主机数据输入、从机数据输出
- SCK:时钟线,由主机提供时钟输出
2、协议层
(1)SPI基本通讯时序
(2)起始信号与停止信号
- 开始信号:NSS 由 高变低
- 停止信号:NSS 由 低变高
(3)数据有效性
- SPI 使用 SCK线进行数据同步,MOSI与MISO在SCK的每个时钟周期传输一位数据、且数据的 输入 与 输出 是同步进行的
- 进行数据传输时,一般采取MSB先行(先发送高字节,再发低字节)
- SPI 每次数据传输可以8位或16位为单位,每次传输的单位数不受限制
(4)SPI的4种通讯模式 (CPOL - 时钟极性 与 CPHA - 时钟相位)
- 时钟极性 CPOL: 表示SCK的空闲状态时候的电平。 (如: CPOL = 0 ---- SCK空闲状态为低电平; CPOL = 1 ---- SCK空闲状态为高电平)
- 时钟相位 CPHA: 表示数据何时被采样。(如: CPHA = 0 ---- SCK的奇数边沿被采样; CPHA = 1 ---- SCK的偶数边沿被采样)
SPI的四种模式:
CPHA = 0时(采样边沿为 奇数边沿的SPI时序图)
CPHA = 1时(采样边沿为 偶数边沿的SPI时序图)
二、 软件模拟SPI示例
1、软件SPI结构体 以及 SPI模式枚举
typedef enum
{
MODE_0 = 0, /* CPOL = 0, CPHA = 0; */
MODE_1, /* CPOL = 0, CPHA = 1; */
MODE_2, /* CPOL = 1, CPHA = 0; */
MODE_3, /* CPOL = 1, CPHA = 1; */
}exSPIModeEnum;
typedef struct SoftSPIHandle
{
GPIO_TypeDef * NSS_Port;
GPIO_TypeDef * SCK_Port;
GPIO_TypeDef * MOSI_Port;
GPIO_TypeDef * MISO_Port;
uint16_t NSS_Pin;
uint16_t SCK_Pin;
uint16_t MOSI_Pin;
uint16_t MISO_Pin;
exSPIModeEnum mode;
void (* SetHigh)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (* SetLow)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (* SetMode)(struct SoftSPIHandle xSoftSPIHandle, exSPIModeEnum eMode);
uint8_t (* ReadPin)(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
void (* SoftSPIWriteData)(struct SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength);
void (* SoftSPIReadData)(struct SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength);
}SoftSPIHandle;
2、SPI初始化函数
void vSoftSPIInit(exSPIModeEnum eMode)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* 管脚初始化 */
SOFT_SPI_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB14 ------> SPI1_NSS
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = SOFT_SPI_NSS_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(SOFT_SPI_NSS_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SOFT_SPI_SCK_PIN;
HAL_GPIO_Init(SOFT_SPI_SCK_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SOFT_SPI_MOSI_PIN;
HAL_GPIO_Init(SOFT_SPI_MOSI_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SOFT_SPI_MISO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(SOFT_SPI_MISO_PORT, &GPIO_InitStruct);
/* 初始化Soft SPI 句柄 */
ex_SoftSPIHandle.NSS_Port = ex_SoftSPIHandle.SCK_Port = ex_SoftSPIHandle.MISO_Port = ex_SoftSPIHandle.MOSI_Port = GPIOB;
ex_SoftSPIHandle.NSS_Pin = SOFT_SPI_NSS_PIN;
ex_SoftSPIHandle.SCK_Pin = SOFT_SPI_SCK_PIN;
ex_SoftSPIHandle.MOSI_Pin = SOFT_SPI_MOSI_PIN;
ex_SoftSPIHandle.MISO_Pin = SOFT_SPI_MISO_PIN;
/* 注册Soft SPI 函数 */
ex_SoftSPIHandle.SetHigh = vSetHigh;
ex_SoftSPIHandle.SetLow = vSetLow;
ex_SoftSPIHandle.SetMode = vSetMode;
ex_SoftSPIHandle.ReadPin = HAL_GPIO_ReadPin;
ex_SoftSPIHandle.SoftSPIWriteData = vSoftSPIWriteData;
ex_SoftSPIHandle.SoftSPIReadData = vSoftSPIReadData;
/* 设置对应SPI模式 */
ex_SoftSPIHandle.mode = eMode;
if(eMode & MODE_0)
{
/* CPOL = 0 ; CPHA = 0 */
/* SPI 默认模式0 处于空闲状态 */
SOFT_SPI_NSS_High();
SOFT_SPI_SCK_Low();
}
else if(eMode & MODE_1)
{
/* CPOL = 0 ; CPHA = 1 */
/* SPI 默认模式1 处于空闲状态 */
SOFT_SPI_NSS_High();
SOFT_SPI_SCK_Low();
}
else if(eMode & MODE_2)
{
/* CPOL = 1 ; CPHA = 0 */
/* SPI 默认模式2 处于空闲状态 */
SOFT_SPI_NSS_High();
SOFT_SPI_SCK_High();
}
else
{
/* CPOL = 1 ; CPHA = 1 */
/* SPI 默认模式3 处于空闲状态 */
SOFT_SPI_NSS_High();
SOFT_SPI_SCK_High();
}
}
3、SPI读写函数
static void vSoftSPIWriteData(SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength)
{
for(uint32_t i = 0; i < uiLength; i++)
ucSoftSPIReadWriteByte(xSoftSPIHandle, pData[i]);
}
static void vSoftSPIReadData(SoftSPIHandle xSoftSPIHandle, uint8_t *pData, uint32_t uiLength)
{
for(uint32_t i = 0; i < uiLength; i++)
pData[i] = ucSoftSPIReadWriteByte(xSoftSPIHandle, DummyByte);
}
static uint8_t ucSoftSPIReadWriteByte(SoftSPIHandle xSoftSPIHandle, uint8_t ucData)
{
uint8_t ucRecvData = 0;
if(xSoftSPIHandle.mode == MODE_0)
{
/* CPOL = 0 ; CPHA = 0 */
/* 数据传输 */
for(uint8_t i = 0; i < 8; i++)
{
/* 发送数据 */
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
if( ucData & 0x80 )
xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
else
xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
ucData <<= 1;
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
/* 接收数据 */
ucRecvData <<= 1;
if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
ucRecvData |= 1;
}
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
}
else if(xSoftSPIHandle.mode == MODE_1)
{
/* CPOL = 0 ; CPHA = 1 */
/* 数据传输 */
for(uint8_t i = 0; i < 8; i++)
{
/* 发送数据 */
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
if( ucData & 0x80 )
xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
else
xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
ucData <<= 1;
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
/* 接收数据 */
ucRecvData <<= 1;
if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
ucRecvData |= 1;
}
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
}
else if(xSoftSPIHandle.mode == MODE_2)
{
/* CPOL = 1 ; CPHA = 0 */
/* 数据传输 */
for(uint8_t i = 0; i < 8; i++)
{
/* 发送数据 */
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
if( ucData & 0x80 )
xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
else
xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
ucData <<= 1;
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
/* 接收数据 */
ucRecvData <<= 1;
if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
ucRecvData |= 1;
}
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
}
else
{
/* CPOL = 1 ; CPHA = 1 */
/* 数据传输 */
for(uint8_t i = 0; i < 8; i++)
{
/* 发送数据 */
xSoftSPIHandle.SetLow(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
if( ucData & 0x80 )
xSoftSPIHandle.SetHigh(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
else
xSoftSPIHandle.SetLow(xSoftSPIHandle.MOSI_Port, xSoftSPIHandle.MOSI_Pin);
ucData <<= 1;
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
vDelayus(2);
/* 接收数据 */
ucRecvData <<= 1;
if(xSoftSPIHandle.ReadPin(xSoftSPIHandle.MISO_Port, xSoftSPIHandle.MISO_Pin))
ucRecvData |= 1;
}
xSoftSPIHandle.SetHigh(xSoftSPIHandle.SCK_Port, xSoftSPIHandle.SCK_Pin);
}
return ucRecvData;
}
三、 STM32-SPI 外设使用示例(HAL库)
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* SPI1 clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
PB14 ------> SPI1_NSS
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SPI1_NSS_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(SPI1_NSS_Port, &GPIO_InitStruct);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* spiHandle)
{
if(spiHandle->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PB3 ------> SPI1_SCK
PB4 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* USER CODE END SPI1_MspDeInit 1 */
}
}
void vSPI1Init(void)
{
ex_SPI1Handle.Instance = SPI1;
ex_SPI1Handle.Init.Mode = SPI_MODE_MASTER;
ex_SPI1Handle.Init.Direction = SPI_DIRECTION_2LINES;
ex_SPI1Handle.Init.DataSize = SPI_DATASIZE_8BIT;
ex_SPI1Handle.Init.CLKPolarity = SPI_POLARITY_LOW;
ex_SPI1Handle.Init.CLKPhase = SPI_PHASE_1EDGE;
ex_SPI1Handle.Init.NSS = SPI_NSS_SOFT;
ex_SPI1Handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
ex_SPI1Handle.Init.FirstBit = SPI_FIRSTBIT_MSB;
ex_SPI1Handle.Init.TIMode = SPI_TIMODE_DISABLE;
ex_SPI1Handle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
ex_SPI1Handle.Init.CRCPolynomial = 10;
HAL_SPI_Init(&ex_SPI1Handle);
}
int8_t cSPIWriteData(SPI_HandleTypeDef *ex_SPIhandle, uint8_t *pData, uint16_t usLength)
{
int8_t status = HAL_ERROR;
status = HAL_SPI_Transmit(ex_SPIhandle, pData, usLength, 1000);
return status;
}
int8_t cSPIReadData(SPI_HandleTypeDef *ex_SPIhandle, uint8_t *pData, uint16_t usLength)
{
int8_t status = HAL_ERROR;
status = HAL_SPI_Receive(ex_SPIhandle, pData, usLength, 1000);
return status;
}
四、SPI常规调试手段
1、如:SPI FLASH 可以发送 读取芯片ID的指令查看是否能够正确返回芯片的ID
参考资料为:
【野火】《STM32 HAL库开发实战指南-基于F407》
【正点原子】《STM32F4开发指南(HAL库版)》