天天看点

STM32的HAL库SPI操作(Slave模式)Slave模式设置收发数据函数总结

Slave模式设置

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库中要我们干两件事:

  1. 重写它的回调函数;
  2. 在初始化完SPI的时候调用一次HAL_SPI_TransmitReceive_IT();

    好了,我们看代码:

// 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端要怎么做?

  1. 第一步,发一个字节的数据;[这个是用来占位的,把我们在初始化中那个字节用掉]
  2. 第二步,可以开始循环读8个字节来转回double型了
  3. 或者想干嘛干嘛,两边要统一就行。I am The King in this World!

总结

总体来说,没用的时候感觉很没方向,没注意spi的特点。其实在HAL库中它已经很好用了。

继续阅读