SPI的使用,Master端的很多,Slave端的不好找,也很少,能参考的也很少,后面具体来看一下:
Slave端的初始化程序和Master端的只有一行不同
hspi1.Init.Mode = **SPI_MODE_SLAVE;**其它完全一致。
初始化代码:
/* SPI1 init function */
void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_SLAVE;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_INPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
Error_Handler();
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* spiHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(spiHandle->Instance==SPI1)
{
/* SPI1 clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA4 ------> SPI1_NSS
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PA7 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = CHX_NSS_Pin|CHX_SCLK_Pin|CHX_MOSI_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = CHX_MISO_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(CHX_MISO_GPIO_Port, &GPIO_InitStruct);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
}
}
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
要注意的是,数据收和数据发其实也同时进行了对应的数据发和数据收工作,只是没关注,所以没拿数据;
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
有点乱对不对,实际就都只用第一个就好了,传数的时候同时进行收发,那么回调就只要重写收发回调函数,其它4个不用理它,我们的主要数据收发工作就在这个回调函数中了。如果硬要用其它那几个,那么就要一一对应着用,用了收就要写收数据回调,否测它是不会跳去收发数据回调的。发送数据同样的。
回到我们的应用中来,因为我用的是中断方式,HAL库中要我们干两件事:
// SPI先发生一次调用,把00放入缓冲区,待主机来取
void SPI1_init_IT(void)
{
cmd_flag = false;
cmd = 0;
memset(spi_txbuff,0,32);
memset(spi_rxbuff,0,32);
HAL_SPI_TransmitReceive_IT(&hspi1,spi_txbuff,spi_rxbuff,1);
}
初始化完SPI接口后,进行一次收发调用,这个函数是不阻塞的,但是数据已经放到缓冲区了,因为从机没有SPI时钟,所以这里数据是不会立刻传给主机的,只有等主机来通讯的时候,这1个字节才会送到主机的接叫缓冲区中,也就是说,这里的数据,将在下次通讯的时候发送出去。
这里长度可以是任意,因为我打算只1个传个标志,让主机知道接口正常所以就是个0,
再看回调函数
// SPI收发回调函数,收发结束回调到这里,因为实际是在中断里的,所以快速操作早点结束
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
double rx = 3.1415926;
// 暂时只处理spi1的
if(hspi==&hspi1)
{
// 如果逻辑简单,就在这里处理收到的数据,数据会自动存在spi_rxbuff中,也就是你上次调用
// HAL_SPI_TransmitReceive_IT 的时候指定的缓冲区中。
// 如果逻辑复杂,就设置一个标志,再到主循环中去处理,当然,如果有跑操作系统,就去相应的任务中处理
// 在这里,我们要传一个double型的数给Master端,stm32里面,它是8个字节的;
HAL_SPI_TransmitReceive_IT(&hspi1, (uint8_t *)&rx, spi_rxbuff,sizeof(double));
}
}
这里只发一个double型的数,PI回去。那么Master端要怎么做?
总体来说,没用的时候感觉很没方向,没注意spi的特点。其实在HAL库中它已经很好用了。