STM32串口通讯实现任意长度数据的收发(包含帧头、帧尾校验,附开源程序)

一、串口的基本概念

  • 【数据组成】串口的通讯协议由开始位,数据位,校验位,结束位构成。
  • 【数据结构】一般以一个低电平作为一帧数据的起始,接着跟随 8 位或者 9 位数据位,之后为校验位,分为奇校验,偶校验和无校验,最后以一个先高后低的脉冲表示结束位,长度可以设置为 0.5,1,1.5 或 2 位长度。
  • 【奇偶校验原理】统计发送数据中高电平即’1’的奇偶,将结果记录在奇偶校验位中发送给接收方,接收方收到奇偶校验位后和自己收到的数据进行对比,如果奇偶性一致就接受这帧数据,否则认为这帧数据出错。
  • 如下图所示:一个 8 位数据位,1 位奇偶校验位,1 位结束位的串口数据帧。

    上图的解释如下:


    注意事项:

    1. 一般进行串口通讯时,收发双方要保证遵守同样的协议才能正确的完成收发,除了协议要一致之外,还有一个非常重要的要素要保持一致,那就是通讯的速率,即波特率。
    2. 波特率是指发送数据的速率,单位为波特每秒,一般串口常用的波特率有 115200,38400,9600 等。
    3. 串口的波特率和总线时钟周期(clock)成倒数关系,即总线时钟周期越短,单位时间内发送的码元数量越多,串口波特率就越高。

    二、使用 STM32 标准库 实现任意长度数据的收发

    示例程序功能说明:首先,通过串口一实现对任意长度数据的接收,接收到数据之后,将其存放到一个数组当中,方便后续调用;其次将STM32接收到的数据通过串口一发送出去。核心程序如下:(完整版程序详见文末链接)

    // 串口发送部分核心代码如下:
    int main(void)
    {	
    	delay_init();	    //延时函数初始化
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    	uart_init(115200);	 //串口初始化为115200	
    	
    //	USART_SendData(USART1, 0x11);  
    //	while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
    
    	while(1)
    	{
    		if(receive_flag == 1) //串口接收完成标志位
    		{
    				for(int i=0;i<8;i++)
    				{
    					USART_SendData(USART1, Receive_Data[i]);//向串口1发送数据
    					while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束				
    				}	
    				receive_flag = 0;  //一帧数据发送完毕之后,标志位置0		
    		}
    	}		
    }
    
    // 串口接收部分核心代码如下:
    void USART1_IRQHandler(void)
    {	
    	static u8 Count=0;  //定义计数静态变量
    	u8 Usart_Receive;   //定义中间传递变量
    
    	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断是否接收到数据
    	{
    			Usart_Receive = USART_ReceiveData(USART1); //读取数据
    		
    			if(receive_flag == 0)
    			{
    					//串口数据填入数组
    					Receive_Data[Count]=Usart_Receive;
    					
    					//确保数组第一个数据为FRAME_HEADER(帧头)
    					if(Usart_Receive == FRAME_HEADER||Count>0) 
    						Count++; 
    					else 
    						Count=0;
    					
    					if (Count == 8) //验证数据包的长度
    					{   
    						Count=0; //为串口数据重新填入数组做准备
    						
    						if(Receive_Data[7] == FRAME_TAIL) //验证数据包的帧尾
    						{
    							//接收到完整的一帧数据之后,在这里面进行处理
    							receive_flag = 1;												
    						}
    					}					
    			}			
    	}
    }
    

    程序关键:

    1. 通过微小调整,可以实现任意长度数据的收发,方便使用;
    2. 串口接收鲁棒性高,采用校验帧头、帧尾以及数据长度的方式,确保接收数据的稳定性。

    STM32 标准库 串口收发实验展示(实验中,帧头设置为0x01,帧尾设置为0x02,数据长度为8个字节)

    【实验结论】如上图所示,发送端的数据,接收端会完好收到。下面进行干扰性测试:

    【实验结论】如上图所示,在引入帧头、帧尾以及长度校验后,程序的抗干扰能力较强。

    三、使用 STM32 HAL库 实现任意长度数据的收发

    HAL库程序使用CUBE MX生成

    // 串口发送部分核心代码如下:
    int main(void)
    {
      /* USER CODE BEGIN 1 */
      /* USER CODE END 1 */
      /* MCU Configuration--------------------------------------------------------*/
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
      /* USER CODE BEGIN Init */
      /* USER CODE END Init */
      /* Configure the system clock */
      SystemClock_Config();
      /* USER CODE BEGIN SysInit */
      /* USER CODE END SysInit */
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      //__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
      HAL_UART_Receive_IT(&huart1,(uint8_t *)Usart_Receive,1); //这句话极为重要
      /* USER CODE END 2 */
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    		if(receive_flag == 1)
    		{
    			HAL_UART_Transmit(&huart1,Receive_Data,8,100);			
    			receive_flag = 0;			
    		}				
      }
      /* USER CODE END 3 */
    }
    
    // 串口接收部分核心代码如下:
    void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
    {
    	static uint8_t Count=0;  //定义计数静态变量
    	
      if(huart->Instance==USART1)
      {
          HAL_UART_Receive_IT(&huart1,(uint8_t *)Usart_Receive,1);
    		
    			if(receive_flag == 0)
    			{
    					//串口数据填入数组
    					Receive_Data[Count]=Usart_Receive[0];
    					
    					//确保数组第一个数据为FRAME_HEADER(帧头)
    					if(Usart_Receive[0] == FRAME_HEADER||Count>0) 
    						Count++; 
    					else 
    						Count=0;
    					
    					if (Count == 8) //验证数据包的长度
    					{   
    						Count=0; //为串口数据重新填入数组做准备
    						
    						if(Receive_Data[7] == FRAME_TAIL) //验证数据包的帧尾
    						{
    							//接收到完整的一帧数据之后,在这里面进行处理
    							receive_flag = 1;												
    						}
    					}					
    			}					
      }
    }
    

    STM32 HAL库 串口收发实验展示(实验中,帧头设置为0x01,帧尾设置为0x02,数据长度为8个字节)

    四、相关程序及总结

    1.上述代码已开源,开源链接:

    https://e.coding.net/ehangtest01/stm32_explore/STM32_Explore.git

    文件夹内容如下:

    2.不管是使用标准库还是HAL库,都需要对数据进行校验,这样才能保证数据传输的稳定性。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32串口通讯实现任意长度数据的收发(包含帧头、帧尾校验,附开源程序)

    发表评论