串口——通用数据分包解析

使用空闲中断IDLE+DMA+列队方式。

串口数据因为是按字节连续发送的,如果数据包发送的间隔很小,或者处理不及时,很容易存在丢包、粘包的问题。最近项目开发中发现一个比较通用的方法来防止丢包,就是使用队列的方式。

原理:就是把接收的数据按顺序压入队列(队列一般是先进先出),然后处理的时候,从队列里取最早压入的数据,在应用层里一个字节一个字节的取出来,取出一个,队列就空出来一个,找到完整的数据包,然后进行处理,这样来了数据就压入队列,两不耽误。

接收方式可以按字节,也可以用DMA中断或空闲中断、或DMA+空闲中断,随意。

主要就是两个函数:向队列压入数据、从队列取出完整的数包(需要根据帧头、帧尾来找)。

/* 压入数据 */
void queue_push(uint8_t _data, p_QUEUE _que)
{
    uint16_t pos = (_que->_head + 1) % QUEUE_MAX_SIZE;
    if(pos!=_que->_tail) 
    {
        _que->_data[_que->_head] = _data;
        _que->_head = pos;
    }
}
/* 取出数据 */
static void queue_pop(uint8_t * _data, p_QUEUE _que)
{
    if(_que->_tail!=_que->_head)
    {
        *_data = _que->_data[_que->_tail];
        _que->_tail = (_que->_tail + 1) % QUEUE_MAX_SIZE;
    }
}

下面的代码就是从队列里一个字节一个字节的找到帧头、帧尾,组成一帧完整的数据包。 

uint16_t queue_find_cmd_rs485(uint8_t *buffer, uint16_t buf_len, p_QUEUE _que, uint8_t cmdHead, uint32_t cmdEnd)
{
    uint16_t cmd_size = 0;
    uint8_t _data = 0;
	static uint32_t cmd_state = 0; //队列帧尾检测状态
    static qsize cmd_pos = 0; //当前指令指针位置

    while(queue_size(_que)>0)
    {
        //取一个数据
        queue_pop(&_data, _que);
        if(cmd_pos==0&&_data!=cmdHead) //指令第一个字节必须是帧头,否则跳过
        {					
            continue;
        }

        if(cmd_pos<buf_len) //防止缓冲区溢出
            buffer[cmd_pos++] = _data;

        cmd_state = ((cmd_state<<8)|_data); //拼接最后4个字节,组成一个32位整数

        //最后4个字节与帧尾匹配,得到完整帧
        if(cmd_state==cmdEnd)
        {
            cmd_size = cmd_pos; //指令字节长度
            cmd_state = 0; //重新检测帧尾巴
            cmd_pos = 0; //复位指令指针

            return cmd_size;
        }
    }
    return 0; //没有形成完整的一帧
}

遇到的问题:

因为队列开辟了一块缓存,所以导致程序运行出现点问题,最后通过调整.s文件的堆栈大小解决了。

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值