串口数据帧同步

串口数据帧同步

介绍

串口是十分常用的一个资源, 每次需要进行处理串口数据, 但是 PC 上的串口存在缓冲区机制, 导致串口跟我们在单片机中的使用方式可能有所不同,
每次拿到缓冲区数据之后,判断是否满足帧头,进而开始新的一帧数据计数存储, 最终满足一帧数据之后 将数据发送出来.

原理

我们使用自定义的存储结果 作为自己的存储结果, 一般来说在项目中使用就是帧数据长度是一致的, 我们的程序也是基于这个原理的

class SerialData
{
    public:
    uchar *data_;   ///< 帧数据
    uchar *head_;   ///< 帧头数据
    int dat_len_;   ///< 数据帧长度
}

我们需要根据自己的协议初始化数据长度和数据帧头内容 假设我们的数据为 14 个字节 包括 一般使用串口通信的数据都是按照这种格式进行的数据发送,

[帧头] [长度] [地址] [指令] [数据] [校验]
00 01 02 03 04 05 06 07 08 09 10 11 12 13
68 0D 00 84 00 04 09 11 79 45 00 81 13 01

frame_len = 14;
data_ = new uchar[frame_len];               ///< 存储数据
head_ = new uchar[4]{ 0x68,0x0D,0x00,0x84 }; ///< 旧版协议头

实际上我们确定通信协议之后,PC不存在命令之类的解析的话,可以把前面四个字节处理帧头,
每次接收到帧头四个字节的时候表示数据已经重新开始了一帧,将收集到的数据存储相应的流程即可,继续处理后续数据

  1. 如果数据正常, 读取到帧头数据之后,假设帧头数据一致, 数据依次往后填充数据,等到数组填满,会再次获取到帧头数据
  2. 根据上一帧的状态码, 确定当前值, 如果满足设定的四个值, 满足帧头数据, 处理后续字节
  3. 将数据与帧头数组依次比较,使用 $flg_status$ 记录上一次匹配的位置, 如果不一致, 置零, 下次重新开始,
  4. 每次判断四个字节一致之后, $flg_status$ 累加到了 4 , 这时候进行 数据校验, 根据结果,将当前数据帧的值 压入队列中,
  5. 以上方法会导致第一帧校验失败,但是后续的帧处理结果可以保证不会出现问题, 相当于将四个字符作为了帧尾处理
    static int data_cnt = 0;
    static int flg_status = 0;  // 0 普通数据,与数据头1 比较, 1 数据头1,2 数据头2, 3 数据头3,
                                // 每次将缓冲区 数据全部读出来,
                                // 判断上一帧读取到了什么状态, 数据分为 前4个字节的帧头数据和 后面的数据
                                // 1.读取到了帧头 // 2. 读取到了一般数据帧
                                // 每次计数, 读取到帧头的时候, 判断数据是否已经满了, 已经满了进行数据校验, 满足就加入数据
                                // 数据未满, 则可能数据未满, 
    QByteArray recv = serial_->readAll();   // QSerial  读取的数据类型是 QByteArrray
    int N = recv.size();
    if (recv.isEmpty())
    {
        Sleep(5);   // 休眠5毫秒
        LError("recv data is empty");
        return false;
    }
    // 遍历得到的数据数组
    for (int i = 0; i < N; i++)
    {
        uchar tmp = char2uchar(recv[i]);

        // 每次来了数据与上次的置位比较, 如果一致, 比较后一位, 
        //  如果前面4位都一致,表示已经开始了新的一帧, 将数据依次复制
        //    如果数据已经满足帧头, 将之前的数据 校验满足之后加入队列中, 其他线程处理
        if (tmp == m_Frame.head_[flg_status])
        {
            flg_status++;
            if (flg_status == 4)
            {
                // 由于存在每次存入数据的时候存在上一帧的数据 问题 所以这里校验可能失败
                m_read_queue_->Push(m_Frame);
                // 更新前面4个字节的帧头数据
                for (int i = 0; i < 4; i++)
                    m_Frame.data_[i] = m_Frame.head_[i];
                // 匹配重新置零, 同时将数据前面数据复制过去
                flg_status = 0;
            }
        }
        else
        {
            flg_status = 0; // 如果前面的匹配上了, 后面匹配不上, 则重新开始进行匹配
        }
        m_Frame.data_[data_cnt++] = tmp;    // 移动指针, 将数据存储到数组中
        // 超过设定值, 减去一个长度,避免越界  // 与取与操作 % 处理一致 
        // 重新开始的位置 从可以刷新的数据开始
        if (data_cnt == m_Frame.dat_len_)
            data_cnt = 0;
    }

更多

这个程序是自己目前在使用的方法,简单有效, 适用于串口通信的数据处理,十分好用, 这个算是项目文档 的一部分,后续可能再拓展, 但是基本框架不变, 测试起来也很好用

自己测试过程可以使用 VSPD虚拟串口 6.9 汉化版 模拟出来两个相连的串口,初始设置一下就好, 然后使用普通的串口调试软件打开两个串口即可进行通信,十分方便,

参考链接

猜你喜欢

转载自www.cnblogs.com/hugochen1024/p/12570783.html