使用串口的空闲中断和接收中断进行串口数据的不定长接收
(1)芯片STM32F756VGT6
(2)IAR软件环境
(3)使用芯片的串口6,和外接的RS485收发模块一起用做RS485通讯
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 */
}
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)
#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
#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;
}
图片就不发了,说说实际测试现象。
1.当接收字节数较少时,该方式能很好的解决不定长数据接收,且接收数据无错误,长度一致。
2.当接收字节数较长时,发现会有单个字节出错,实际检查发现有可能是硬件本身接收这块出现问题,空闲中断会发生在接收字节的中途,这就导致了当处理时数据某一字节丢失。但本身这种机制保证了数据传输过来,你能做出接收到数据的判断,并且在对数据内容使用前,加上一个合适的延时,也一样能够保证数据的完整性。因为这时候就算后面继续来数据,接收中断那边仍能够继续正常的工作,即使是标志位已经置位。实际项目使用中,我用该代码测试接收串口摄像头传来的图片数据,一张图12K,每次传输字节数512Byte,每次使用数据比较前加了一个2ms的延时,保证数据完整接收,测试时间1星期,接收数据没有发生错误。