STM32如何接收不定长数据?
在Modbus协议中经常返回的数据的长度是不同的,或者在使用串口通讯的一些模块的时候发送不同的命令返回的一帧数据的长度也是不同,因此在接收的时候我们需要准确判断一帧数据是否已经传输完成,传输完成后再对数据进行分析。判断一帧数据是否传输完成有两种方法,第一种是使用定时器,定时一个字节的数据传输的时间,当进入定时器中断,表示在一个字节的传输时间内未收到数据,表示一帧数据传输完成,同时在串口接收中断中需不断重新开始定时器计时,该方法比较麻烦,还占用定时器的资源。第二种是使用串口的空闲中断判断一帧数据是否传输完成。
串口的空闲中断
检测到接收数据后,在数据总线上的一个字节时间内,没有接收到数据触发空闲中断。RXNE置位一次,空闲总线就检测一次!上电后未触发串口接收中断即未接收到数据时不会进入空闲中断。
HAL库代码
注意:初始化的时候需要打开空闲中断和接收中断(勿使用HAL_UART_Received_IT()使能中断)
初始化:
/*Enable USART1 idle interrupt*/ __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); /*Enable USART1 received interrupt*/ __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE); 串口接收中断: /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /*Enter USART1 received interrupt*/ if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET) { /*Clear USART1 received interrupt flag*/ __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE); /*Save the USART1 received data to Rec_Buff[]*/ Rec_Buff[Rec_Cnt] = USART1 -> DR; SEGGER_RTT_printf(0, "Enter usart1 received interrupt! Rec_Buff[%d] = %x\n", Rec_Cnt, Rec_Buff[Rec_Cnt]); Rec_Cnt++; if(Rec_Cnt > 31) { Rec_Cnt = 0; } } /*Enter USART1 idle interrupt*/ else if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) { /*Clear USART1 idle interrupt flag*/ __HAL_UART_CLEAR_IDLEFLAG(&huart1); /*Enter USART1 idle interrupt clear Rec_Cnt, Ready to receive another frame of data*/ Rec_Cnt = 0; SEGGER_RTT_printf(0, "Enter usart1 idle interrupt! Rec_Cnt = %d\n", Rec_Cnt); } /* USER CODE END USART1_IRQn 1 */ }
标准库代码
初始化:
1. USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能接收中断 2. USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); //使能接收空闲中断
串口接收中断:
/*串口接接收中断服务函数*/ void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //触发空闲中断,表示一帧数据传输完成 { volatile uint8_t Clear = 0; Idle_Flag = 1; USART_ClearITPendingBit(USART1, USART_IT_IDLE); //清除空闲中断标志位 Clear = (USART1 -> DR); //测试发现无该语句无法每次都能进入空闲状态 Rx_Cnt = 0; } if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除接收中断标志位 Rec_Buff[Rx_Cnt] = (USART1 -> DR); //接收到的字节保存,数组地址加1 SEGGER_RTT_printf(0, "Rec_Buff[%d] = %d\n", Rx_Cnt, Rec_Buff[Rx_Cnt]); Rx_Cnt++; Rec_Data = Rx_Cnt; SEGGER_RTT_printf(0, "Rx_Cnt = %d, Rec_Data = %d\n", Rx_Cnt, Rec_Data); if(Rx_Cnt == 3) { Rx_Cnt = 0; } } }
注意:使用标准库清除空闲中断标志位一定要加上“Clear = (USART1 -> DR);”该语句,否则会存在异常。
以此作为日常学习的记录,有不对之处欢迎大家指正!