【温酒笔记】SPI

1. SPI基础

  1. 物理层

在这里插入图片描述

  • 片选线 :选中拉低
  • SCK: 时钟线
  • MOSI:主出从入
  • MISO:主入从出
  1. 协议层
    在这里插入图片描述
    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);
  1. 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值