概要

本文写自正在做的项目,需要使用串口2处理EasyModBus传输的报文,原本采用中断处理的方式,在屏幕,按键,感应器同时传输下,产生了丢包现象,偶发性的死机问题,所以改用消息队列进行缓存,逐条处理。

整体流程

  • 创建队列
  • 串口中断接收报文,简易判别
  • 添加入队列
  • 解包任务,从队列中取出报文
  • 解包做相应处理

具体实现

  • 创建队列
    结构体
#define QUEUE_LENGTH 	20

struct CommData
{
	uint8_t data[50];
	uint8_t length;
};

struct CommQueue
{
	uint8_t head;
	uint8_t tail;
	uint8_t isEmpty;
	struct CommData data[QUEUE_LENGTH];
};

初始化----主函数调用

struct CommQueue sendQueue,recvQueue;

void QUEUE_init(struct CommQueue *queue)
{
	queue->head = 0;
	queue->tail = 0;
	queue->isEmpty = 1;
}
QUEUE_init(&recvQueue);
QUEUE_init(&sendQueue);
  • 添加到队列
uint8_t QUEUE_add(struct CommQueue *queue,struct CommData data)
{
	uint8_t rtl = 1;

	__disable_irq();

	if(queue->isEmpty)
	{
		memcpy(&queue->data[queue->tail],&data,sizeof(data));
		queue->tail ++;
		queue->tail %= QUEUE_LENGTH;
		queue->isEmpty = 0;
	}
	else
	{
		if(queue->tail == queue->head)
		{
			__enable_irq();
			return 0;
		}

		memcpy(&queue->data[queue->tail],&data,sizeof(data));
		queue->tail ++;
		queue->tail %= QUEUE_LENGTH;
	}

	__enable_irq();

	return rtl;
}
  • 从队列中取出
uint8_t QUEUE_get(struct CommQueue *queue,struct CommData *data)
{
	uint8_t rtl=1;

	__disable_irq();

	if(queue->isEmpty)
	{
		__enable_irq();
		return 0;
	}
	else
	{
		memcpy(data,&queue->data[queue->head],sizeof(struct CommData));
		queue->head ++;
		queue->head %= QUEUE_LENGTH;

		if(queue->head == queue->tail)
		{
			queue->isEmpty = 1;
		}
	}

	__enable_irq();

	return rtl;
}
  • 中断接收报文-进行判断
void USART2_IRQHandler(void)
{
	struct CommData recvData_U2;
	unsigned char data;
	static uint8_t count = 0;
	static unsigned char check_data ; //记录第一位数据

	data = USART2->DR;
	recvData_U2.data[count] = data;
	count++;
	if(count==1)
	{//报文头
		if(recvData_U2.data[0] != 0x0A && recvData_U2.data[0] != 0x0E)
		{
			count = 0;
			return ;
		}
	}
   //简易校验-确保接收个数
	if(count == 2) 
		check_data = recvData_U2.data[1];
	if(count == check_data) //接受数据位数和记录的一致表示此组数据接收完成
	{
	    //报文尾判断
		if(recvData_U2.data[count-1]!=0x0D)
		{
			count = 0;
			return ;
		}
		else
		{
			recvData_U2.length = count;
			count = 0;
		}
		if((recvData_U2.data[0]==0x0A && recvData_U2.data[2]== 0))
		{//需要及时处理的--直接调用相应函数,不用入队
			StopModeMB();
		}
		else
		{//入队
			QUEUE_add(&recvQueue,recvData_U2);
		}
	}
}
  • 队列中取出数据解包 --请根据自己的实际进行修改
struct CommData recvData_U2H;
void Usart2_ParameterHandler()
{
	uint8_t i;
	unsigned char check_code=0 ;
	while(QUEUE_get(&recvQueue, &recvData_U2H))
	{
		for(i=0;i<recvData_U2H.length-2;i++)
		{
			check_code += recvData_U2H.data[i];
		}
		switch (recvData_U2H.data[0])
		{
		case 0x0A:
			if( recvData_U2H.data[1] == 0x0A && recvData_U2H.data[recvData_U2H.length-2] == check_code)
			{
				if(recvData_U2H.data[4] == 0x00 && recvData_U2H.data[5] == 0x00 )
				{
					Send_ParameterInit();
				}
				else
					Updata_Parameter();
			}
			break;
		case 0x0E:
			if( recvData_U2H.data[1] == 0x06 && recvData_U2H.data[recvData_U2H.length-2] == check_code)
			{//动作执行
				sysPara.SL = recvData_U2H.data[2];
			}
			break;
		default :
			//  解包失败 --执行响应动作
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14,GPIO_PIN_RESET);
			HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15,GPIO_PIN_RESET);
			break;
		}
	}
}

小结

使用队列后,解决丢包现象。可靠性得到进一步保证。

03-27 08:14