概要
本文写自正在做的项目,需要使用串口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;
}
}
}
小结
使用队列后,解决丢包现象。可靠性得到进一步保证。