天天看點

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庫中它已經很好用了。

繼續閱讀