STM32L051 HAL库非DMA模式使用空闲中断接收不定长数据
阅读原文时间:2021年04月20日阅读:1

STM32L051 HAL库非DMA模式使用空闲中断接收不定长数据

因项目中的上位机与单片机之间的通信协议采用非标准协议,上位机发送的数据长度不定,因此在网上查询相关的解决方法,但查询的结果,网上讨论的利用空闲模式接收不定长数据的方法,都是基于DMA接收的,个人项目因为有低功耗需要,平时是在停机模式,在唤醒后才能接收数据,所以考虑非DMA接收方式,经过调试,试验成功,将代码记录下来。

  • 实现的功能
    实验完成的代码功能包括STM32L051的低功耗串口LPUART1初始化、中断接收,Printf打印函数,利用空闲中断实现不定长数据接收;RTC时钟配置、闹钟配置等功能

  • 开发环境
    利用STSTM32CubeMX向导,生成初始化函数,使用KEIL编绎:

  1. 系统时钟配置:RTC使用外部LSE晶振,主时钟使用内部HSI,开启PLL,SYSCLK配置为32MHz,LPUART时钟源使用LSE。
  2. RTC配置:开启RTC,开启闹钟A,外部校准关闭,RTC唤醒关闭。
  3. LPUART配置:
  4. 代码向导输出配置,使用HAL库:
  • 部分函数修改
    因为STM32CubeMX向导生成的初始化函数默认是没开启空闲中断功能的,所以需要修改部分初始化函数。
    1.LPUART初始化函数中,增加开启接收中断和空闲中断

    /**

    • @brief UART MSP Initialization

    • This function configures the hardware resources used in this example

    • @param huart: UART handle pointer

    • @retval None
      / void HAL_UART_MspInit(UART_HandleTypeDef huart)
      {
      GPIO_InitTypeDef GPIO_InitStruct = {0};
      if(huart->Instance==LPUART1)
      {
      /* USER CODE BEGIN LPUART1_MspInit 0 */

      /* USER CODE END LPUART1_MspInit 0 / / Peripheral clock enable / __HAL_RCC_LPUART1_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); /*LPUART1 GPIO Configuration
      PC4 ------> LPUART1_TX
      PC5 ------> LPUART1_RX
      */
      GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5;
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStruct.Alternate = GPIO_AF2_LPUART1;
      HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

      /* LPUART1 interrupt Init / HAL_NVIC_SetPriority(LPUART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(LPUART1_IRQn); / USER CODE BEGIN LPUART1_MspInit 1 / __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE); __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); / USER CODE END LPUART1_MspInit 1 */
      }
      }

2.修改LPUART中断接收函数,屏蔽原向导生成的中断处理函数 HAL_UART_IRQHandler(&hlpuart1)

/**
  * @brief This function handles LPUART1 global interrupt / LPUART1 wake-up interrupt through EXTI line 28.
  */
void LPUART1_IRQHandler(void)
{
  /* USER CODE BEGIN LPUART1_IRQn 0 */
  /* USER CODE END LPUART1_IRQn 0 */
//  HAL_UART_IRQHandler(&hlpuart1);
  /* USER CODE BEGIN LPUART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_RXNE) != RESET)  
    { 
        __HAL_UART_CLEAR_FLAG(&hlpuart1, UART_CLEAR_OREF | UART_CLEAR_NEF | UART_CLEAR_PEF | UART_CLEAR_FEF);
            if(LPUart1_End_flage == 0)
            {
              LPUart1_RX_BUF[LPUart1_REC_Cnt] = LPUART1->ISR;    
        LPUart1_RX_BUF[LPUart1_REC_Cnt] = LPUART1->RDR;    
        LPUart1_REC_Cnt++;
                if(LPUart1_REC_Cnt > LPUart1_BUF_SIZE) 
                    LPUart1_REC_Cnt =0; 
            }
  }
  else if(__HAL_UART_GET_FLAG(&hlpuart1,UART_FLAG_IDLE) == SET)
    {
             __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
//           printf("UART IT For IDEL \r\n");
        LPUart1_End_flage = 1;
        LPUart1_REC_SIZE = LPUart1_REC_Cnt;
        LPUart1_REC_Cnt = 0;
   }        
  /* USER CODE END LPUART1_IRQn 1 */
}

3.修改闹钟配置函数,这里我设置为闹钟每小时激活一次。
**注意**这个闹钟设置:AlarmMask的意思是屏蔽相应的参数为无效,代码里闹钟时间设置成了1:30:20,如果要在每天的1:30:20闹钟动作,则sAlarm.AlarmMask = RTC_ALARMMASK_NONE,表示闹钟时间严格匹配设置的时间,因为我这里设置每小时的30分20秒闹钟动作,所以需要屏蔽RTC_ALARMMASK_DATEWEEKDAYRTC_ALARMMASK_HOURS,代码修改为sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS

/** Enable the Alarm A 
  */
  sAlarm.AlarmTime.Hours = 1;
  sAlarm.AlarmTime.Minutes = 30;
  sAlarm.AlarmTime.Seconds = 20;
  sAlarm.AlarmTime.SubSeconds = 0;
  sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
  sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY|RTC_ALARMMASK_HOURS;//|RTC_ALARMMASK_MINUTES|RTC_ALARMMASK_SECONDS;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_NONE;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
  sAlarm.AlarmDateWeekDay = 1;
  sAlarm.Alarm = RTC_ALARM_A;
  if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }

4.测试代码

while (1)
  {
        if(LPUart1_End_flage == 1)
        {
            HAL_RTC_GetTime(&hrtc, &stimestructure, RTC_FORMAT_BIN);
            HAL_RTC_GetDate(&hrtc, &sdatestructure, RTC_FORMAT_BIN);
//            printf("\r\n%02d/%02d/%02d\r\n",2000 + sdatestructure.Year, sdatestructure.Month, sdatestructure.Date);
//      printf("%02d:%02d:%02d\r\n",stimestructure.Hours, stimestructure.Minutes, stimestructure.Seconds);
            for(i=0;i< LPUart1_REC_SIZE;i++)
            printf("%.2x",LPUart1_RX_BUF[i]);
//            printf("\r\n");
            LPUart1_End_flage = 0;
        }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

5.测试结果

完整工程下载

链接: 完整工程下载.