因项目中的上位机与单片机之间的通信协议采用非标准协议,上位机发送的数据长度不定,因此在网上查询相关的解决方法,但查询的结果,网上讨论的利用空闲模式接收不定长数据的方法,都是基于DMA接收的,个人项目因为有低功耗需要,平时是在停机模式,在唤醒后才能接收数据,所以考虑非DMA接收方式,经过调试,试验成功,将代码记录下来。
实现的功能
实验完成的代码功能包括STM32L051的低功耗串口LPUART1初始化、中断接收,Printf打印函数,利用空闲中断实现不定长数据接收;RTC时钟配置、闹钟配置等功能
开发环境
利用ST的STM32CubeMX向导,生成初始化函数,使用KEIL编绎:
部分函数修改
因为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_DATEWEEKDAY
和RTC_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.测试结果
链接: 完整工程下载.
手机扫一扫
移动阅读更方便
你可能感兴趣的文章