1. SPI基础
- 物理层
- 片选线 :选中拉低
- SCK: 时钟线
- MOSI:主出从入
- MISO:主入从出
- 协议层
CPOL:时钟极性:空闲电平高低
CPHA:时钟相位:第一个还是第二个边沿采样
2. 示例SPI-W25Q16 (见模组分类下文章)
3. SPI+DMA
//返回当前DMA通道传输中剩余数据单元的数量
/*** @brief Return the number of remaining data units in the current DMA Channel transfer.* @param __HANDLE__ DMA handle* @retval The number of remaining data units in the current DMA Channel transfer.*/
#define __HAL_DMA_GET_COUNTER(__HANDLE__) ((__HANDLE__)->Instance->CNDTR)//判断DMA中数据传输是否完成
while(__HAL_DMA_GET_COUNTER(&hdma_spi1_tx) != 0);
- SPI使用软件控制片选,使用DMA,一定等传输完成之后才能拉高片选
请看下面的代码举例:
while (1){/* USER CODE END WHILE */HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);HAL_SPI_TransmitReceive_DMA(&hspi1,txbuff,rxbuff,8);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);}
发现现象: SPI数据还没有传输完成,但片选已经拉高了。。。
- 解决思路1:判断DMA传输是否完成
//拉低片选
//SPI-DMA传输
//传输完成
//拉高片选
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_RESET);
HAL_SPI_TransmitReceive_DMA(&hspi1,txbuff,rxbuff,8);
while(__HAL_DMA_GET_COUNTER(&hdma_spi1_rx)!=0);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15,GPIO_PIN_SET);
- 解决思路2:在发送完成中断回调中拉高片选
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{LOG_OUT("SPI tx finish\r\n");//拉高片选
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{LOG_OUT("SPI rx finish\r\n");//拉高片选
}
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{LOG_OUT("SPI rx finish\r\n");//拉高片选
}
- SPI,中断,DMA相关的函数接口如下
/** @addtogroup SPI_Exported_Functions_Group2* @{*/
/* I/O operation functions ***************************************************/
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout);
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_IT(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_Receive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef HAL_SPI_TransmitReceive_DMA(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData,uint16_t Size);
HAL_StatusTypeDef HAL_SPI_DMAPause(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAResume(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_DMAStop(SPI_HandleTypeDef *hspi);
/* Transfer Abort functions */
HAL_StatusTypeDef HAL_SPI_Abort(SPI_HandleTypeDef *hspi);
HAL_StatusTypeDef HAL_SPI_Abort_IT(SPI_HandleTypeDef *hspi);void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_RxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_TxRxHalfCpltCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi);
void HAL_SPI_AbortCpltCallback(SPI_HandleTypeDef *hspi);
网友链接
4. W25Q16-SPI-DMA示例
void spi_dma_wait_tx_end(uint32_t time_t)
{uint32_t ret = 0;uint32_t time = 0;do{SPI_Delay(1);ret = __HAL_DMA_GET_COUNTER(&hdma_spi1_tx);time++;}while(ret !=0 && time < time_t);LOG_OUT("t=%d %d\r\n",ret,time);
}
void spi_dma_wait_rx_end(uint32_t time_t)
{uint32_t ret = 0;uint32_t time = 0;do{SPI_Delay(1);ret = __HAL_DMA_GET_COUNTER(&hdma_spi1_rx);time++;}while(ret !=0 && time < time_t);LOG_OUT("r=%d %d\r\n",ret,time);
}/*
函数参数: SPI-DMA1、T_pData:发送数据缓冲区中取出数据发送出去2、T_Size :需要发送的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Transmit(uint8_t * T_pData, uint16_t T_Size)
{
#if USE_SPI_DMAuint8_t ret = HAL_SPI_Transmit_DMA(&W25Q_SPI, T_pData, T_Size);spi_dma_wait_tx_end(0xFFFF);return ret;
#elsereturn HAL_SPI_Transmit(&W25Q_SPI, T_pData, T_Size, 0xFFFF);
#endif
}/*
函数参数: SPI-DMA1、R_pData:接收数据并放置到接收数据缓冲区中2、R_Size :需要接收的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Receive(uint8_t * R_pData, uint16_t R_Size)
{
#if USE_SPI_DMAuint8_t ret = HAL_SPI_Receive_DMA(&W25Q_SPI, R_pData, R_Size);spi_dma_wait_rx_end(0xFFFF);return ret;
#elsereturn HAL_SPI_Receive(&W25Q_SPI, R_pData, R_Size, 0xFFFF);
#endif
}
/*
读ID描述:读3个字节分别是生产厂家、存储器类型、容量
*/
HAL_StatusTypeDef Bsp_Read_Jedec_ID(uint8_t * R_Jedec_ID)
{uint8_t read_id[3] = {0};uint8_t cmd = W25Q_JEDEC_ID;HAL_StatusTypeDef STD = HAL_ERROR;Bsp_Judge_Busy();W25Q_CS_Level(0);SPI_Delay(1);if(bsp_w25q_Transmit(&cmd, 1) == HAL_OK){if(bsp_w25q_Receive(read_id, 3) == HAL_OK){STD = HAL_OK;}}W25Q_CS_Level(1);if(STD == HAL_OK){LOG_OUT("read_id= %x %x %x\r\n", read_id[0], read_id[1], read_id[2]);}else{LOG_OUT("read_id= err\r\n");}return STD;
}