串口不定长接收数据--空闲中断方式测试

1.问题描述:

    使用串口的空闲中断和接收中断进行串口数据的不定长接收

2.测试平台:

    (1)芯片STM32F756VGT6

    (2)IAR软件环境

    (3)使用芯片的串口6,和外接的RS485收发模块一起用做RS485通讯

3.实际操作:

    (1)串口初始化:

void MX_UART6_Init(void)
{
  
  huart6.Instance = USART6;
  huart6.Init.BaudRate = 115200;
  huart6.Init.WordLength = UART_WORDLENGTH_8B;
  huart6.Init.StopBits = UART_STOPBITS_1;
  huart6.Init.Parity = UART_PARITY_NONE;
  huart6.Init.Mode = UART_MODE_TX_RX;
  huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart6.Init.OverSampling = UART_OVERSAMPLING_16;
  huart6.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart6.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart6) != HAL_OK)
  {
    // _Error_Handler(__FILE__, __LINE__);
  }
}
if(uartHandle->Instance==USART6)
 {
    /* USER CODE BEGIN USART6_MspInit 0 */
    
    /* USER CODE END USART6_MspInit 0 */
    /* USART6 clock enable */
    __HAL_RCC_USART6_CLK_ENABLE();
    
    /**USART6 GPIO Configuration    
    PC6     ------> USART6_TX
    PC7     ------> USART6_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    
    /* USART6 interrupt Init */
    HAL_NVIC_SetPriority(USART6_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(USART6_IRQn);
    /* USER CODE BEGIN USART6_MspInit 1 */
    __HAL_UART_ENABLE_IT(uartHandle,UART_IT_RXNE);
    __HAL_UART_ENABLE_IT(uartHandle,UART_IT_IDLE);
    /* USER CODE END USART6_MspInit 1 */
 }

(2)中断回调函数内部关于串口6部分的处理:

if(huart->Instance == USART6)
  {
    if(__HAL_UART_GET_FLAG(huart,UART_FLAG_RXNE) != 0)
    {
      temp = (uint8_t)huart->Instance->RDR;
      if((temp != 0) || (Rs485_Driver.ReciveBufferLen > 0))//根据自身算法过滤无用数据
      {	    
        Rs485_Driver.ReciveBuffer[Rs485_Driver.ReciveBufferLen++] = temp;
        Rs485_Driver.ReciveBuffer[Rs485_Driver.ReciveBufferLen] = 0; //根据实际项目部分算法需求,后一位数据清0,可不要
      }//if((temp != 0) || (receiveBufferPointUart1 > 0))
    }//if(__HAL_UART_GET_FLAG(huart,UART_FLAG_RXNE) != 0)
    else if(__HAL_UART_GET_FLAG(huart,UART_FLAG_ORE) != 0)
    {
      __HAL_UART_CLEAR_OREFLAG(huart);
    }//else if(__HAL_UART_GET_FLAG(huart6,UART_FLAG_ORE) != 0)
    else if(__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != 0)
    {
      Rs485_Driver.ReciveFlag = 1;//接收完成,进入空闲终端,接收标志位置位
      __HAL_UART_CLEAR_IDLEFLAG(huart);
    }//else if(__HAL_UART_GET_FLAG(huart6,UART_FLAG_IDLE) != 0)
  }//else if(huart->Instance == USART6)

(3)对应收发函数处理写法:

  I.头文件:

#ifndef _RS485_h_
#define _RS485_h_

#include "usart.h"
#include "FreeRTOS.h"
#include "cmsis_os.h"
//#include "SYSTEM_Manage.h"

#define RS485_BUFFER_SIZE 512

typedef void FUNCTION(unsigned int);

typedef struct _RS485_STRU
{
	uint8_t               ReciveBuffer[RS485_BUFFER_SIZE];   //接收缓存
	uint8_t               ReciveFlag;                        //接收完成标志   
	uint8_t               OverTimeCount;                     //超时计数最大值,和实际函数配合
        uint32_t              ReciveBufferLen;                   //接收长度
	FUNCTION              *Delay;                            //函数指针,用于指向延时函数
	UART_HandleTypeDef    *UsartHandle;                      //串口句柄
}_rs485_stru;

extern _rs485_stru Rs485_Driver;

void Rs485_Transmit(_rs485_stru *Rs485Driver,uint8_t *Buffer,uint16_t Size);
uint8_t Rs485_Transmit_Recive(_rs485_stru *Rs485Driver,uint8_t *Buffer,uint16_t Size);

#endif

II.源文件:

#include "rs485.h"

_rs485_stru Rs485_Driver = 
{
  {0},
  0,
  50,
  0,
  &osDelayTask,        //实际项目使用FreeRtos,如果裸跑可以换成自己的延时函数
  &huart6,
};

//可用宏函数替代,实际未使用
void Rs485_Transmit(_rs485_stru *Rs485Driver,uint8_t *Buffer,uint16_t Size)
{
	HAL_UART_Transmit(Rs485Driver->UsartHandle,Buffer,Size,0xFFFF);
	while(__HAL_UART_GET_FLAG(Rs485Driver->UsartHandle,UART_FLAG_TC)!=SET); 
}

uint8_t Rs485_Transmit_Recive(_rs485_stru *Rs485Driver,uint8_t *Buffer,uint16_t Size)
{
	uint8_t counter = 0;
	//接收准备,初始化
	Rs485Driver->ReciveFlag = 0;
	Rs485Driver->ReciveBufferLen = 0;
	//发送数据帧,一般为读取报文帧
	HAL_UART_Transmit(Rs485Driver->UsartHandle,Buffer,Size,0xFFFF);
	while(__HAL_UART_GET_FLAG(Rs485Driver->UsartHandle,UART_FLAG_TC)!=SET); 
	//接收判断
	while((!Rs485Driver->ReciveFlag)&&(counter < Rs485Driver->OverTimeCount))//超时检测,超时计数可放到函数参数中,节省空间
	{
		Rs485Driver->Delay(20);
		counter++;
	}
	
	if(counter == Rs485Driver->OverTimeCount)//接收失败返回0,否则返回1
		return 0;
	
	return 1;
}
 
  

4.测试结果:

    图片就不发了,说说实际测试现象。

    1.当接收字节数较少时,该方式能很好的解决不定长数据接收,且接收数据无错误,长度一致。

    2.当接收字节数较长时,发现会有单个字节出错,实际检查发现有可能是硬件本身接收这块出现问题,空闲中断会发生在接收字节的中途,这就导致了当处理时数据某一字节丢失。但本身这种机制保证了数据传输过来,你能做出接收到数据的判断,并且在对数据内容使用前,加上一个合适的延时,也一样能够保证数据的完整性。因为这时候就算后面继续来数据,接收中断那边仍能够继续正常的工作,即使是标志位已经置位。实际项目使用中,我用该代码测试接收串口摄像头传来的图片数据,一张图12K,每次传输字节数512Byte,每次使用数据比较前加了一个2ms的延时,保证数据完整接收,测试时间1星期,接收数据没有发生错误。



你可能感兴趣的:(stm32)