STM32F401RET6 LQFP64 (Nucleo-F401RE) SPI通信(主从双机SPI通信)
1.1 SPI总线介绍
SPI 通讯使用 3 条总线及片选线,3 条总线分别为 SCK、MOSI、MISO,片选线为NSS(CS)
NSS 信号线由高变低 ,是 SPI 通讯的起始信号 。 NSS 是每个从机各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选 中了,开始准备与主机通讯。在图中的标号处, NSS 信号由低变高 ,是 SPI 通讯的停止 信号 ,表示本次通讯结束,从机的选中状态被取消。
SPI 使用 MOSI 及 MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。 MOSI 及 MISO 数据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。
SPI 一共有四种通讯模 式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此 引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。
时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时, SCK 信号线的电平信号(即 SPI 通 讯开始前、 NSS 线为高电平时 SCK 的状态)。 CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时, MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿” 被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿” 采样。
1.1.1 CPHA=0 时的 SPI 通讯模式
1.1.2 CPHA=1 时的 SPI 通讯模式
1.2 注意事项
-
SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备;
-
部分MCU SPI模块一旦使能。SCK一直输出。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mWZBDnNf-1684574929468)(D:\tool_install\st mcu\STM32F401RET6 LQFP64 (Nucleo-F401RE)] SPI通信(主从双机SPI通信).assets\v2-2fdc62a4f8d7ae6038ca4678c6241b5c_1440w.webp)
1.3 SPI模式编号
SPI的时钟极性和相位的配置通常称为 SPI模式,所有可能的模式都遵循以下约定;具体如下表所示;
SPI Mode | CPOL | CPHA |
---|---|---|
0 [00] | 0 | 0 |
1 [01] | 0 | 1 |
2 [10] | 1 | 0 |
3 [11] | 1 | 1 |
1.4 MCU 与MCU 之间主从通信连接
连线是一一对应的,不能将MOSI接上MISO,且两个设备的配置参数速率、相位、极性、CRC和传输方向及位数要相同,切记,一定要共地 。
1.4.1 SPI 初始化CLK以及PIN 复用
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI1)
{
/* SPI2 clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI1 GPIO Configuration
D13 SCK PA5
D12 MISO PA6
D11 MOSI PA7
D10 CS PB6
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull=GPIO_PULLUP ;//GPIO_NOPULL;
GPIO_InitStruct.Alternate=GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
printf("spi pin init\r\n");
// GPIO_InitStruct.Pin = ADC_MISO_Pin;
// GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
// GPIO_InitStruct.Pull = GPIO_NOPULL;
// HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
1.4.2 SPI控制器初始化
/*##-1- Configure the SPI peripheral #######################################*/
/* Set the SPI parameters */
SpiHandle.Instance = SPIx;
SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
SpiHandle.Init.Direction = SPI_DIRECTION_2LINES;
// SpiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
// SpiHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
//#ifdef MASTER_BOARD
SpiHandle.Init.CLKPhase = SPI_PHASE_2EDGE;
//#else
//SpiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
//#endif
SpiHandle.Init.CLKPolarity = SPI_POLARITY_HIGH;
SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SpiHandle.Init.CRCPolynomial = 7;
SpiHandle.Init.DataSize = SPI_DATASIZE_8BIT;
SpiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
#ifdef MASTER_BOARD
SpiHandle.Init.NSS = SPI_NSS_SOFT;
#else
SpiHandle.Init.NSS = SPI_NSS_HARD_INPUT;
#endif
SpiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
#ifdef MASTER_BOARD
SpiHandle.Init.Mode = SPI_MODE_MASTER;
printf("SPI MASTER_BOARD\r\n");
#else
SpiHandle.Init.Mode = SPI_MODE_SLAVE;
printf("SPI slaver_BOARD\r\n");
#endif /* MASTER_BOARD */
printf("HAL_SPI_Init start \r\n");
if(HAL_SPI_Init(&SpiHandle) != HAL_OK)
{
/* Initialization Error */
//Error_Handler();
printf("Initialization Error \r\n");
while(1);
}
dle) != HAL_OK)
{
/* Initialization Error */
//Error_Handler();
printf(“Initialization Error \r\n”);
while(1);
}