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-DMA
1、T_pData:发送数据缓冲区中取出数据发送出去
2、T_Size :需要发送的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Transmit(uint8_t * T_pData, uint16_t T_Size)
{
#if USE_SPI_DMA
uint8_t ret = HAL_SPI_Transmit_DMA(&W25Q_SPI, T_pData, T_Size);
spi_dma_wait_tx_end(0xFFFF);
return ret;
#else
return HAL_SPI_Transmit(&W25Q_SPI, T_pData, T_Size, 0xFFFF);
#endif
}
/*
函数参数: SPI-DMA
1、R_pData:接收数据并放置到接收数据缓冲区中
2、R_Size :需要接收的数据的长度
*/
static HAL_StatusTypeDef bsp_w25q_Receive(uint8_t * R_pData, uint16_t R_Size)
{
#if USE_SPI_DMA
uint8_t ret = HAL_SPI_Receive_DMA(&W25Q_SPI, R_pData, R_Size);
spi_dma_wait_rx_end(0xFFFF);
return ret;
#else
return 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;
}