STM32 HAL库 SPI发送循环分析及使用示例

1 简介

  1.1 SPI和UART一样是一种串行数据收发硬件协议,不同点是SPI是在时钟线SCK的同步作用下对数据移进移除,UART是靠双方的波特率(1bit所占的时间)来确定。

  1.2 HAL库对两者在收发总线的错误处理都一样,都提供了用于收发错误处理的回调函数 hspi->ErrorCallback(hspi);huart->ErrorCallback(huart);以UART为例,有

极性错误,帧错误,噪声错误,帧过载,所有的接收错误都放在错误回调函数中进行处理,但是有错误在帧CRC校验会检查出来,此处可以不用处理。

SPI也一样把相关接收错误都注释掉。并且在收发函数里,不打开错误中断。就像用标准外设库或者寄存器来操作芯片一样,只设置有用的中断。

  1.3  NSS pulse mode
This mode is activated by the NSSP bit in the SPIx_CR2 register and it takes effect only if
the SPI interface is configured as Motorola SPI master (FRF=0) with capture on the first
edge (SPIx_CR1 CPHA = 0, CPOL setting is ignored). When activated, an NSS pulse is
generated between two consecutive data frame transfers when NSS stays at high level for
the duration of one clock period at least. This mode allows the slave to latch data. NSSP
pulse mode is designed for applications with a single master-slave pair.

  /* Enable TXE and ERR interrupt */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_TXE /*| SPI_IT_ERR*/));

2 库收发函数说明

详见相关注释

HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));

  /* Process Locked */
//  __HAL_LOCK(hspi);

//  if ((pData == NULL) || (Size == 0U))
//  {
//    errorcode = HAL_ERROR;
//    goto error;
//  }

//  if (hspi->State != HAL_SPI_STATE_READY)
//  {
//    errorcode = HAL_BUSY;
//    goto error;
//  }

  /* Set the transaction information */
  hspi->State       = HAL_SPI_STATE_BUSY_TX;
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pTxBuffPtr  = (uint8_t *)pData;
  hspi->TxXferSize  = Size;
  hspi->TxXferCount = Size;

  /* Init field not used in handle to zero */
//  hspi->pRxBuffPtr  = (uint8_t *)NULL;
//  hspi->RxXferSize  = 0U;
//  hspi->RxXferCount = 0U;
//  hspi->RxISR       = NULL;

  /* Set the function for IT treatment */
//  if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
//  {
//    hspi->TxISR = SPI_TxISR_16BIT;
//  }
//  else
//  {
    hspi->TxISR = SPI_TxISR_8BIT;   /*指定发送中断回调函数*/
//  }

  /* Configure communication direction : 1Line */
//  if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
//  {
//    SPI_1LINE_TX(hspi);
//  }

#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

  /* Enable TXE and ERR interrupt */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_TXE /*| SPI_IT_ERR*/));
  /*打开发送中断,如果外设使能后会立马进入发送中断程序,进行发送-中断-发送下一个数据的循环*/

  /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }

error :
//  __HAL_UNLOCK(hspi);
  return errorcode;
}
void HAL_SPI_IRQHandler(SPI_HandleTypeDef *hspi)
{
  uint32_t itsource = hspi->Instance->CR2;
  uint32_t itflag   = hspi->Instance->SR;

  /* SPI in mode Receiver ----------------------------------------------------*/
//  if ((SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) == RESET) &&
//      (SPI_CHECK_FLAG(itflag, SPI_FLAG_RXNE) != RESET) && (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_RXNE) != RESET))
//  {
//    hspi->RxISR(hspi);
//    return;
//  }

  /* SPI in mode Transmitter -------------------------------------------------*/
  if ((SPI_CHECK_FLAG(itflag, SPI_FLAG_TXE) != RESET) && (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_TXE) != RESET))
  {
    hspi->TxISR(hspi);
    return;
  }

//  /* SPI in Error Treatment --------------------------------------------------*/
//  if (((SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET) || (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET) || (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)) && (SPI_CHECK_IT_SOURCE(itsource, SPI_IT_ERR) != RESET))
//  {
//    /* SPI Overrun error interrupt occurred ----------------------------------*/
//    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_OVR) != RESET)
//    {
//      if (hspi->State != HAL_SPI_STATE_BUSY_TX)
//      {
//        SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_OVR);
//        __HAL_SPI_CLEAR_OVRFLAG(hspi);
//      }
//      else
//      {
//        __HAL_SPI_CLEAR_OVRFLAG(hspi);
//        return;
//      }
//    }

//    /* SPI Mode Fault error interrupt occurred -------------------------------*/
//    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_MODF) != RESET)
//    {
//      SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_MODF);
//      __HAL_SPI_CLEAR_MODFFLAG(hspi);
//    }

//    /* SPI Frame error interrupt occurred ------------------------------------*/
//    if (SPI_CHECK_FLAG(itflag, SPI_FLAG_FRE) != RESET)
//    {
//      SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FRE);
//      __HAL_SPI_CLEAR_FREFLAG(hspi);
//    }

//    if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
//    {
//      /* Disable all interrupts */
//      __HAL_SPI_DISABLE_IT(hspi, SPI_IT_RXNE | SPI_IT_TXE | SPI_IT_ERR);

//      hspi->State = HAL_SPI_STATE_READY;
//      /* Disable the SPI DMA requests if enabled */
//      if ((HAL_IS_BIT_SET(itsource, SPI_CR2_TXDMAEN)) || (HAL_IS_BIT_SET(itsource, SPI_CR2_RXDMAEN)))
//      {
//        CLEAR_BIT(hspi->Instance->CR2, (SPI_CR2_TXDMAEN | SPI_CR2_RXDMAEN));

//        /* Abort the SPI DMA Rx channel */
//        if (hspi->hdmarx != NULL)
//        {
//          /* Set the SPI DMA Abort callback :
//          will lead to call HAL_SPI_ErrorCallback() at end of DMA abort procedure */
//          hspi->hdmarx->XferAbortCallback = SPI_DMAAbortOnError;
//          if (HAL_OK != HAL_DMA_Abort_IT(hspi->hdmarx))
//          {
//            SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_ABORT);
//          }
//        }
//        /* Abort the SPI DMA Tx channel */
//        if (hspi->hdmatx != NULL)
//        {
//          /* Set the SPI DMA Abort callback :
//          will lead to call HAL_SPI_ErrorCallback() at end of DMA abort procedure */
//          hspi->hdmatx->XferAbortCallback = SPI_DMAAbortOnError;
//          if (HAL_OK != HAL_DMA_Abort_IT(hspi->hdmatx))
//          {
//            SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_ABORT);
//          }
//        }
//      }
//      else
//      {
//        /* Call user error callback */
//#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
//        hspi->ErrorCallback(hspi);
//#else
//        HAL_SPI_ErrorCallback(hspi);
//#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
//      }
//    }
//    return;
//  }
}
/**
  * @brief  Handle the data 8-bit transmit in Interrupt mode.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @retval None
  */
static void SPI_TxISR_8BIT(struct __SPI_HandleTypeDef *hspi)
{
  *(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);
  hspi->pTxBuffPtr++;
  hspi->TxXferCount--;

  if (hspi->TxXferCount == 0U)
  {
#if (USE_SPI_CRC != 0U)
    if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
    {
      /* Enable CRC Transmission */
      SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
    }
#endif /* USE_SPI_CRC */
    SPI_CloseTx_ISR(hspi);
  }
}
/**
  * @brief  Handle the end of the TX transaction.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @retval None
  */
static void SPI_CloseTx_ISR(SPI_HandleTypeDef *hspi)
{
  uint32_t tickstart;

  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();

  /* Disable TXE and ERR interrupt */
  __HAL_SPI_DISABLE_IT(hspi, (SPI_IT_TXE /*| SPI_IT_ERR*/));
                                                                 /*数组发送完毕关闭发送中断*/
  /* Check the end of the transaction */
  if (SPI_EndRxTxTransaction(hspi, SPI_DEFAULT_TIMEOUT, tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
  }

  /* Clear overrun flag in 2 Lines communication mode because received is not read */
//  if (hspi->Init.Direction == SPI_DIRECTION_2LINES)
//  {
//    __HAL_SPI_CLEAR_OVRFLAG(hspi);
//  }

  hspi->State = HAL_SPI_STATE_READY;
//  if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
//  {
//    /* Call user error callback */
//#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
//    hspi->ErrorCallback(hspi);
//#else
//    HAL_SPI_ErrorCallback(hspi);
//#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
//  }
//  else
//  {
    /* Call user Rx complete callback */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
    hspi->TxCpltCallback(hspi);
#else
    HAL_SPI_TxCpltCallback(hspi);     /*自己可以重写该回调函数,如TM1620,发送一帧显示数据后,在该回答函数里手动设置NSS信号*/     
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
//  }
}
/**
  * @brief  Handle the check of the RXTX or TX transaction complete.
  * @param  hspi SPI handle
  * @param  Timeout Timeout duration
  * @param  Tickstart tick start value
  * @retval HAL status
  */
static HAL_StatusTypeDef SPI_EndRxTxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart)
{
  /* Control if the TX fifo is empty */
  if (SPI_WaitFifoStateUntilTimeout(hspi, SPI_FLAG_FTLVL, SPI_FTLVL_EMPTY, Timeout, Tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    return HAL_TIMEOUT;
  }                                                    /*等待发送寄存器空,最后一个数据已经移出发送寄存器DR*/

  /* Control the BSY flag */
  if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_BSY, RESET, Timeout, Tickstart) != HAL_OK)
  {
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    return HAL_TIMEOUT;                                /*等待BUSY硬件清楚BUSY标志*/
     /*The BSY flag can be used in certain modes to detect the end of a transfer so that the
software can disable the SPI or its peripheral clock before entering a low-power mode which
does not provide a clock for the peripheral. This avoids corrupting the last transfer.*/
  }

  /* Control if the RX fifo is empty */
//  if (SPI_WaitFifoStateUntilTimeout(hspi, SPI_FLAG_FRLVL, SPI_FRLVL_EMPTY, Timeout, Tickstart) != HAL_OK)
//  {
//    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
//    return HAL_TIMEOUT;
//  }

  return HAL_OK;
}
/**
  * @brief  Handle SPI FIFO Communication Timeout.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *              the configuration information for SPI module.
  * @param  Fifo Fifo to check
  * @param  State Fifo state to check
  * @param  Timeout Timeout duration
  * @param  Tickstart tick start value
  * @retval HAL status
  */
static HAL_StatusTypeDef SPI_WaitFifoStateUntilTimeout(SPI_HandleTypeDef *hspi, uint32_t Fifo, uint32_t State,
                                                       uint32_t Timeout, uint32_t Tickstart)
{
  while ((hspi->Instance->SR & Fifo) != State)  /*判断标志位*/
  {
//    if ((Fifo == SPI_SR_FRLVL) && (State == SPI_FRLVL_EMPTY))
//    {
//      /* Read 8bit CRC to flush Data Register */
//      READ_REG(*((__IO uint8_t *)&hspi->Instance->DR));
//    }

    if (Timeout != HAL_MAX_DELAY)
    {
      if (((HAL_GetTick() - Tickstart) >= Timeout) || (Timeout == 0U))
      {
        /* Disable the SPI and reset the CRC: the CRC value should be cleared
           on both master and slave sides in order to resynchronize the master
           and slave for their respective CRC calculation */

        /* Disable TXE, RXNE and ERR interrupts for the interrupt process */
        __HAL_SPI_DISABLE_IT(hspi, (SPI_IT_TXE | SPI_IT_RXNE | SPI_IT_ERR));

        if ((hspi->Init.Mode == SPI_MODE_MASTER) && ((hspi->Init.Direction == SPI_DIRECTION_1LINE)
                                                     || (hspi->Init.Direction == SPI_DIRECTION_2LINES_RXONLY)))
        {
          /* Disable SPI peripheral */
          __HAL_SPI_DISABLE(hspi);
        }

        /* Reset CRC Calculation */
        if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
        {
          SPI_RESET_CRC(hspi);
        }

        hspi->State = HAL_SPI_STATE_READY;

        /* Process Unlocked */
        __HAL_UNLOCK(hspi);

        return HAL_TIMEOUT;
      }
    }
  }

  return HAL_OK;
}

 3  初始化函数

初始化应根据芯片与具体外设的通信方式来设置,主要设置 .Mode .Direction

void MX_SPI2_Init(void)
{

  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;    /*根据外设通信方式来设置*/                 
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;    /*根据外设通信方式来设置*/        
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
  hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_LSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }

}

 其中CLKPolarity和CLKPhase对比被驱动芯片的datasheet以及下图来设置(摘抄自ST Reference manual):

 

4 使用示例

  用STM32的SPI操作LED显存芯片TM1620,该芯片显存大小为12字节,显存地址为00H-0BH,

  该芯片控制命令主要有: 显示模式命令,设置成几位,几段来控制。

                                         数据命令设置,设置芯片是数据模式还是地址模式。

 TM1620接收数据格式如下:该格式决定SPI的时钟极性、相位等配置。

 TM1620设置成地址自动加1模式时的数据传输如下:该格式主要注意片选信号STB变高、变低的

规律,决定怎么来组织驱动程序。该芯片地址自动增加模式的规律是接收命令时每个字节片选芯片

STB会有一个周期变高,但设置完显存单元地址后,一直到后面的数据结束,中间不需要拉高STB。

所以初始化芯片时先设置好芯片,后面再次刷新显示时,只要在发送函数HAL_SPI_Transmit_IT()

前,拉低STB,发送中断程序的回调函数里,拉高STB,即可出现符合要求的时序。

  

  代码如下:

/* Includes ------------------------------------------------------------------*/
#include "spi_tm1620.h"
/* USER CODE BEGIN 0 */
#include "spi.h"
/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

static  uint8_t cmd1 =0x02;/*6位8段*/
static  uint8_t cmd2 =0x8A;/*显示脉冲宽度及显示打开*/
static  uint8_t cmd3 =0x40;/*写数据,地址自动增加模式*/


/* USER CODE END 1 */

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
 

/* USER CODE BEGIN 2 */

static void delay(uint16_t t)
{
    while(t--)
        ;
}

void Bsp_Tm1620_Init(void)
{    
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &cmd1 ,1,10);
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_SET);
    delay(10);
    
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &cmd2 ,1,10);
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_SET);
    delay(10);
    
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi2, &cmd3 ,1,10);
    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_SET);
    delay(10);

}

void Bsp_Tm1620_UpdDispBuff (DISP_BUFF *pdb)
{
    
//    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_RESET);发送前拉低STB
 
    CS_GPIO_Port->BRR = (uint32_t)CS_Pin;
  
    
    HAL_SPI_Transmit_IT(&hspi2,(uint8_t*)pdb,sizeof(DISP_BUFF));

    
}

void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
//    HAL_GPIO_WritePin(CS_GPIO_Port,CS_Pin,GPIO_PIN_SET); 传输完成拉高STB
    
    CS_GPIO_Port->BSRR = (uint32_t)CS_Pin;
    
}

 

posted @ 2023-10-23 15:58  okyihu  阅读(549)  评论(0编辑  收藏  举报