STM32F0_HAL库驱动描述——基于F1的USART串口IT中断实现解析

从原子F103 HAL库基础串口例程来看HAL程序结构;

 

从main函数开始,首先是HAL库两个函数的初始化:

  •        HAL_Init();
  •        Stm32_Clock_Init(RCC_PLL_MUL9);

 

 

解析HAL_Init()

分为四个部分:

       A:启用FLASH预取缓存区;

       B:设置中断组优先级(由于F0是M0系列的,因此没有组优先级一说);

       C:配置SYSTICK时钟;

       D:初始化低等级的硬件;

 

HAL_StatusTypeDef HAL_Init(void)
{
  /* Configure Flash prefetch */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

  /* Prefetch buffer is not available on value line devices */
  __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */

  /* Set Interrupt Group Priority */
  HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

  /* Use systick as time base source and configure 1ms tick (default clock after Reset is MSI) */
  HAL_InitTick(TICK_INT_PRIORITY);

  /* Init the low level hardware */
  HAL_MspInit();

  /* Return function status */
  return HAL_OK;
}

 

 

A:启用预取缓存区

       预取缓存区通常用于通过ICode总线进行指令提取,且在复位后启用;

/**
  * @brief  Enable the FLASH prefetch buffer.
  * @retval None
  */ 
#define __HAL_FLASH_PREFETCH_BUFFER_ENABLE()    (FLASH->ACR |= FLASH_ACR_PRFTBE)

 

B:设置中断优先级组

       通常PRIORITY GROUP存在于M3和M4以上的内核中,采用M0的话不存在该项设置;

/**
  * @brief  Sets the priority grouping field (pre-emption priority and subpriority)
  *         using the required unlock sequence.
  * @param  PriorityGroup: The priority grouping bits length. 
  *         This parameter can be one of the following values:
  *         @arg NVIC_PRIORITYGROUP_0: 0 bits for pre-emption priority
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bits for pre-emption priority
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for pre-emption priority
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for pre-emption priority
  *                                    1 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for pre-emption priority
  *                                    0 bits for subpriority
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible. 
  *         The pending IRQ priority will be managed only by the subpriority. 
  * @retval None
  */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
  NVIC_SetPriorityGrouping(PriorityGroup);
}

C:配置SYSTICK时钟

/**
  * @brief This function configures the source of the time base. 
  *        The time source is configured  to have 1ms time base with a dedicated 
  *        Tick interrupt priority.
  * @note This function is called  automatically at the beginning of program after
  *       reset by HAL_Init() or at any time when clock is reconfigured  by HAL_RCC_ClockConfig(). 
  * @note In the default implementation, SysTick timer is the source of time base. 
  *       It is used to generate interrupts at regular time intervals. 
  *       Care must be taken if HAL_Delay() is called from a peripheral ISR process, 
  *       The the SysTick interrupt must have higher priority (numerically lower) 
  *       than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
  *       The function is declared as __Weak  to be overwritten  in case of other
  *       implementation  in user file.
  * @param TickPriority: Tick interrupt priority.
  * @retval HAL status
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /*Configure the SysTick to have interrupt in 1ms time basis*/
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  /*Configure the SysTick IRQ priority */
  HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);

   /* Return function status */
  return HAL_OK;
}

 

解析:HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  通过层层拆解,可以看到SYSTICK的配置是通过设置寄存器LOAD、VAL、CTRL和NVIC的SCB_SHP、NVIC_IP来实现的;

/**
  * @brief  Initializes the System Timer and its interrupt, and starts the System Tick Timer.
  *         Counter is in free running mode to generate periodic interrupts.
  * @param  TicksNumb: Specifies the ticks Number of ticks between two interrupts.
  * @retval status:  - 0  Function succeeded.
  *                  - 1  Function failed.
  */
uint32_t HAL_SYSTICK_Config(uint32_t TicksNumb)
{
   return SysTick_Config(TicksNumb);
}

/**
  \brief   System Tick Configuration
  \details Initializes the System Timer and its interrupt, and starts the System Tick Timer.
           Counter is in free running mode to generate periodic interrupts.
  \param [in]  ticks  Number of ticks between two interrupts.
  \return          0  Function succeeded.
  \return          1  Function failed.
  \note    When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
           function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
           must contain a vendor-specific implementation of this function.
 */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

/**
  \brief   Set Interrupt Priority
  \details Sets the priority of an interrupt.
  \note    The priority cannot be set for every core interrupt.
  \param [in]      IRQn  Interrupt number.
  \param [in]  priority  Priority to set.
 */
__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if ((int32_t)(IRQn) < 0)
  {
    SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  }
  else
  {
    NVIC->IP[((uint32_t)(int32_t)IRQn)]               = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
  }
}

 

关于SysTick寄存器的介绍在M3权威指南中有详细的介绍;

关于NVIC这两个寄存器的配置

  SCB->SHP系统处理程序优先级寄存器用来控制异常处理程序的优先级;

  NVIC->IP中断优先级控制寄存器组设置响应优先级和抢占优先级;

 

解析:HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority ,0);

  其中NVIC_SetPriority()的函数同上SysTick_Config()中设置的NVIC函数,都是配置中断的配置优先级组、抢占优先级和响应优先级;

/**
  * @brief  Sets the priority of an interrupt.
  * @param  IRQn: External interrupt number
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xxx.h))
  * @param  PreemptPriority: The pre-emption priority for the IRQn channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority 
  * @param  SubPriority: the subpriority level for the IRQ channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority.          
  * @retval None
  */
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
{
  uint32_t prioritygroup = 0x00;
  
  /* Check the parameters */
  assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));
  assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
  
  prioritygroup = NVIC_GetPriorityGrouping();
  
  NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));
}

 

 

D:配置初始化的硬件,保持为空;

/**
  * @brief  Initializes the Global MSP.
  * @param  None
  * @retval None
  */
void HAL_MspInit(void)
{
}

 

 

解析Stm32_Clock_Init(RCC_PLL_MUL9);

主要是实现了两个配置:

  • 时钟源配置
  • 系统时钟SYSCLK配置

 

void Stm32_Clock_Init(u32 PLL)
{
    HAL_StatusTypeDef ret = HAL_OK;
    RCC_OscInitTypeDef RCC_OscInitStructure; 
    RCC_ClkInitTypeDef RCC_ClkInitStructure;
    
    RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; 
    RCC_OscInitStructure.HSEState=RCC_HSE_ON; 
    RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON;
    RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE;    
    RCC_OscInitStructure.PLL.PLLMUL=PLL; 
    ret=HAL_RCC_OscConfig(&RCC_OscInitStructure);
if(ret!=HAL_OK) while(1);
  RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
    RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;    
    RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;            
    RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2;             
    RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1;
    ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2);
if(ret!=HAL_OK) while(1);
}

 

  RCC_OscInitStructure用来设置时钟源、使能相应的时钟源、时钟源分频和PLL时钟配置;

  RCC_ClkInitStructure用来配置SYSCLK的HCLK、PCLK1和PCLK2;

这个与系统自带常规的时钟配置不同,采用的是手动的时钟配置;

 

解析delay_init(u8 SYSCLK);

  主要是选择SysTick时钟是HCLK还是HCLK/8;通常选取SysTick=HCLK;

void delay_init(u8 SYSCLK)
{
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    fac_us=SYSCLK;
}

/**
  * @brief  Configures the SysTick clock source.
  * @param  CLKSource: specifies the SysTick clock source.
  *         This parameter can be one of the following values:
  *             @arg SYSTICK_CLKSOURCE_HCLK_DIV8: AHB clock divided by 8 selected as SysTick clock source.
  *             @arg SYSTICK_CLKSOURCE_HCLK: AHB clock selected as SysTick clock source.
  * @retval None
  */
void HAL_SYSTICK_CLKSourceConfig(uint32_t CLKSource)
{
  /* Check the parameters */
  assert_param(IS_SYSTICK_CLK_SOURCE(CLKSource));
  if (CLKSource == SYSTICK_CLKSOURCE_HCLK)
  {
    SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK;
  }
  else
  {
    SysTick->CTRL &= ~SYSTICK_CLKSOURCE_HCLK;
  }
}

 

 

解析uart_init(115200);

其配置串口和传统的库函数配置相似,从波特率、数据格式、极性、流处理和模式;

  重点在最后一句话对IT的配置;

void uart_init(u32 bound)
{    
    UART1_Handler.Instance=USART1;
    UART1_Handler.Init.BaudRate=bound;
    UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B;
    UART1_Handler.Init.StopBits=UART_STOPBITS_1;    
    UART1_Handler.Init.Parity=UART_PARITY_NONE;    
    UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE;
    UART1_Handler.Init.Mode=UART_MODE_TX_RX;    
    HAL_UART_Init(&UART1_Handler);                        
    
    HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE); 
  
}

 

 

根据注释给出的,该函数会开启接收中断,设置标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量;

  1.        获取此时对应的串口状态;
  2.        若此时串口空闲或串口正在处理发送,进入下一步判断;
  3.        将数据放入接收缓冲中,并将接收的数据长度放入相应的接收判断中;
  4.        置位无错误标志位;
  5.        如果此时在发送则为发送和接收同时进行标志,若总线空闲则为接收进行标志;
  6.   使能串口中断:UART_IT_PE、UART_IT_ERR和UART_IT_RXNE
/**
  * @brief  Receives an amount of data in non blocking mode 
  * @param  huart: Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @param  pData: Pointer to data buffer
  * @param  Size: Amount of data to be received
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  uint32_t tmp_state = 0;
  
  tmp_state = huart->State;
  if((tmp_state == HAL_UART_STATE_READY) || (tmp_state == HAL_UART_STATE_BUSY_TX))
  {
    if((pData == NULL ) || (Size == 0))
    {
      return HAL_ERROR;
    }

    /* Process Locked */
    __HAL_LOCK(huart);

    huart->pRxBuffPtr = pData;
    huart->RxXferSize = Size;
    huart->RxXferCount = Size;

    huart->ErrorCode = HAL_UART_ERROR_NONE;
    /* Check if a transmit process is ongoing or not */
    if(huart->State == HAL_UART_STATE_BUSY_TX)
    {
      huart->State = HAL_UART_STATE_BUSY_TX_RX;
    }
    else
    {
      huart->State = HAL_UART_STATE_BUSY_RX;
    }

    /* Process Unlocked */
    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error Interrupt */
    __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

    /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
    __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

    /* Enable the UART Data Register not empty Interrupt */__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

 

 

串口初始化有包含对应的硬件初始化:HAL_UART_MspInit(huart);

  •        配置的核心在UART_SetConfig(huart);这句话中;
  •        HAL_UART_MspInit(huart);会在判断串口复位状态后进行重新初始化;
/**
  * @brief  Initializes the UART mode according to the specified parameters in
  *         the UART_InitTypeDef and create the associated handle.
  * @param  huart: Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
  /* Check the UART handle allocation */
  if(huart == NULL)
  {
    return HAL_ERROR;
  }

  /* Check the parameters */
  if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE)
  {
    /* The hardware flow control is available only for USART1, USART2, USART3 */
    assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));
    assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));
  }
  else
  {
    assert_param(IS_UART_INSTANCE(huart->Instance));
  }
  assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));
  assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling));
  
  if(huart->State == HAL_UART_STATE_RESET)
  {  
    /* Allocate lock resource and initialize it */
    huart->Lock = HAL_UNLOCKED;
    
    /* Init the low level hardware */HAL_UART_MspInit(huart);
  }

  huart->State = HAL_UART_STATE_BUSY;

  /* Disable the peripheral */
  __HAL_UART_DISABLE(huart);
  
  /* Set the UART Communication parameters */UART_SetConfig(huart);
/* In asynchronous mode, the following bits must be kept cleared: 
     - LINEN and CLKEN bits in the USART_CR2 register,
     - SCEN, HDSEL and IREN  bits in the USART_CR3 register.*/
  CLEAR_BIT(huart->Instance->CR2, (USART_CR2_LINEN | USART_CR2_CLKEN));
  CLEAR_BIT(huart->Instance->CR3, (USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN));
  
  /* Enable the peripheral */
  __HAL_UART_ENABLE(huart);
  
  /* Initialize the UART state */
  huart->ErrorCode = HAL_UART_ERROR_NONE;
  huart->State= HAL_UART_STATE_READY;
  
  return HAL_OK;
}

 

 

具体的硬件配置:GPIO

  •        若USART1配置存在,则开启对应引脚GPIOA时钟、外设时钟、复位时钟;
  •        配置对应的GPIOA;
  •        同时若开启了USART1的接收打开了,则开启串口1的中断,并设置该中断优先级;
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    if(huart->Instance==USART1)
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();
        __HAL_RCC_USART1_CLK_ENABLE();    
        __HAL_RCC_AFIO_CLK_ENABLE();
    
        GPIO_Initure.Pin=GPIO_PIN_9;
        GPIO_Initure.Mode=GPIO_MODE_AF_PP;
        GPIO_Initure.Pull=GPIO_PULLUP;
        GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);

        GPIO_Initure.Pin=GPIO_PIN_10;    
        GPIO_Initure.Mode=GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA,&GPIO_Initure);
        
#if EN_USART1_RX
        HAL_NVIC_EnableIRQ(USART1_IRQn);
        HAL_NVIC_SetPriority(USART1_IRQn,3,3);
#endif    
    }
}

 

 

解析USART1_IRQHandler(void):

这段函数主要是两个部分:

  •        对中断标志位判断后处理;
  •        超时检测:
void USART1_IRQHandler(void)                    
{ 
    u32 timeout=0;    
    HAL_UART_IRQHandler(&UART1_Handler);
    
    timeout=0;
    while (HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)
    {
     timeout++;
     if(timeout>HAL_MAX_DELAY) break;    
    
    }
     
    timeout=0;
    while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
    {
     timeout++; 
     if(timeout>HAL_MAX_DELAY) break;
    }
}

 

 

不同于传统的采用HAL库或库函数对中断标志的判断后进行处理,HAL库自带的中断处理会帮我们做这件事,并在判断完中断标志位后引用回调处理函数;

       核心判断的方式是通过__HAL_UART_GET_FLAG()和__HAL_UART_GET_IT_SOURCE()这两个函数来获取此时的串口状态和中断标志位;

       前面有很大一部分是对中断极性错误PE、格式错误FE、噪声错误NE、溢出错误ORE极性判断并置位串口句柄中错误标志位;

       串口数据缓冲接收到数据、空闲线路检测以及传输完成三个判断是处理核心,分别对应UART_Receive_IT(huart);和UART_Transmit_IT(huart);以及UART_EndTransmit_IT(huart);

/**
  * @brief  This function handles UART interrupt request.
  * @param  huart: Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t tmp_flag = 0, tmp_it_source = 0;

  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_PE);
  tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_PE);  
  /* UART parity error interrupt occurred ------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    huart->ErrorCode |= HAL_UART_ERROR_PE;
  }
  
  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_FE);
  tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_ERR);
  /* UART frame error interrupt occurred -------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    huart->ErrorCode |= HAL_UART_ERROR_FE;
  }
  
  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_NE);
  /* UART noise error interrupt occurred -------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    huart->ErrorCode |= HAL_UART_ERROR_NE;
  }
  
  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_ORE);
  /* UART Over-Run interrupt occurred ----------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    huart->ErrorCode |= HAL_UART_ERROR_ORE;
  }
  
  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_RXNE);
  tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE);
  /* UART in mode Receiver ---------------------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  { 
    UART_Receive_IT(huart);
  }
  
  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TXE);
  tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE);
  /* UART in mode Transmitter ------------------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  {
    UART_Transmit_IT(huart);
  }

  tmp_flag = __HAL_UART_GET_FLAG(huart, UART_FLAG_TC);
  tmp_it_source = __HAL_UART_GET_IT_SOURCE(huart, UART_IT_TC);
  /* UART in mode Transmitter end --------------------------------------------*/
  if((tmp_flag != RESET) && (tmp_it_source != RESET))
  {
    UART_EndTransmit_IT(huart);
  }  

  if(huart->ErrorCode != HAL_UART_ERROR_NONE)
  {
    /* Clear all the error flag at once */
    __HAL_UART_CLEAR_PEFLAG(huart);
    
    /* Set the UART state ready to be able to start again the process */
    huart->State = HAL_UART_STATE_READY;
    
    HAL_UART_ErrorCallback(huart);
  }  
}

 

 

这里在串口数据缓存寄存器接收到数据时使用了UART_Receive_IT(huart);来调用接收数据的回调处理函数;

  •        这个函数的核心在于HAL_UART_RxCpltCallback(huart);这句调用回调处理函数的函数;
  •        除此之外都在对接收缓存区中的数据进行处理;
  •        判断传输计数后关闭中断,接受完成中断状态更新,并关闭PE和ERR中断,最后执行回调函数;
/**
  * @brief  Receives an amount of data in non blocking mode 
  * @param  huart: Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval HAL status
  */
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
  uint16_t* tmp;
  uint32_t tmp_state = 0;
  
  tmp_state = huart->State; 
  if((tmp_state == HAL_UART_STATE_BUSY_RX) || (tmp_state == HAL_UART_STATE_BUSY_TX_RX))
  {
    if(huart->Init.WordLength == UART_WORDLENGTH_9B)
    {
      tmp = (uint16_t*) huart->pRxBuffPtr;
      if(huart->Init.Parity == UART_PARITY_NONE)
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);
        huart->pRxBuffPtr += 2;
      }
      else
      {
        *tmp = (uint16_t)(huart->Instance->DR & (uint16_t)0x00FF);
        huart->pRxBuffPtr += 1;
      }
    }
    else
    {
      if(huart->Init.Parity == UART_PARITY_NONE)
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
      }
      else
      {
        *huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
      }
    }

    if(--huart->RxXferCount == 0)
    {
      __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);

      /* Check if a transmit process is ongoing or not */
      if(huart->State == HAL_UART_STATE_BUSY_TX_RX) 
      {
        huart->State = HAL_UART_STATE_BUSY_TX;
      }
      else
      {
        /* Disable the UART Parity Error Interrupt */
        __HAL_UART_DISABLE_IT(huart, UART_IT_PE);

        /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
        __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);

        huart->State = HAL_UART_STATE_READY;
      }
      HAL_UART_RxCpltCallback(huart);

      return HAL_OK;
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY; 
  }
}

 

关于状态检测:

 

HAL_UART_STATE_READY是外设已初始化并可以使用标志位,通过检测这个标志位来确定向外设接收数据完成;

timeout = 0;
while(HAL_UART_GetState(&UART1_Handler) != HAL_UART_STATE_READY)
{
  timeout++;
  if(timeout>HAL_MAX_DELAY) break;
}

 

 

完成一次接收处理后,再次调用一次HAL_UART_Receive_IT来重新启动串口接收中断,并设置RxXferCount为1作为初始值;

timeout=0;
while(HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)
{
  timeout++; 
  if(timeout>HAL_MAX_DELAY) break;   
}

 

解析HAL_UART_RxCpltCallback()

其功能实现很简单,就是判断结束标志是否是回车符(0x0d 0x0a),同时将数据缓存接收到缓存数组中;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance==USART1)
    {
        if((USART_RX_STA&0x8000)==0)
        {
            if(USART_RX_STA&0x4000)
            {
                if(aRxBuffer[0]!=0x0a)USART_RX_STA=0;
                else USART_RX_STA|=0x8000; 
            }
            else
            {    
                if(aRxBuffer[0]==0x0d)USART_RX_STA|=0x4000;
                else
                {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=aRxBuffer[0] ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; 
                }         
            }
        }
    }
}

 

 

解析主函数main():

主要是通过判断串口标志位USART_RX_STA来实现

  判断串口接收完成标志位;

  获取数据长度;

  将缓存数组中的数据传输出去;

  等待传输完成;

  清空串口接收完成标志位;

while(1)
{    
       if(USART_RX_STA&0x8000)
        { 
            len=USART_RX_STA&0x3fff;
            HAL_UART_Transmit(&UART1_Handler,(uint8_t*)USART_RX_BUF,len,1000);
            while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);
            USART_RX_STA=0;
        } 
}

 

posted @ 2019-06-17 15:31  dil  阅读(2955)  评论(0编辑  收藏  举报