自定义的串口通信协议

2023-05-16

自定义的通信协议

  • 自定义一主多从串口通讯_1
    • 硬件基础
          • 两个从机的 Tx 是相互连接的,导致一个从机在需要发送数据时发不出去了
    • 协议思路
            • 数据包封装和解封装
  • 树莓派python串口的使用注意
    • 更改树莓派串口设备驱动
    • 关闭控制台功能
    • python串口的使用
  • 通讯应答测试

自定义一主多从串口通讯_1

这是一个类似开发日志的小记录,这篇主要是记录自己在毕设里自定义的一种基于串口通讯的通讯协议,至于为什么没有使用modbus只是因为我没有用过。

硬件基础

我的毕设大体上需要通讯的主要有三个模块分别是:树莓派当做主要的控制板、传感器采集板动力驱动板。后面两个是自己用stm32f103rct6做的板子,以后有机会我也会展示一下

在连接上,我们知道树莓派只有一个串口是引脚引出的,所以我希望可以通过一个串口同时控制两个stm32的板子,所以一主多从的自定义串口通讯协议想法就这样出来了。

首先要解决连线问题:我们知道两个设备的串口在连线时Rx、Tx是需要反接的,但是在两个从机的时候就需要解决一个小问题:

两个从机的 Tx 是相互连接的,导致一个从机在需要发送数据时发不出去了

通过查资料我们知道,想mcu的串口都是以低电平为有效值,所以问题这就转换成为 当从机1的Tx变为低电平时如何能不受从机2 Tx高电平的影响。
显而易见,用一个二极管应该就可以解决,我直接上框图应该就清楚了:
从机1 发送数据时Tx低电平会拉低主机Rx,同时从机2 Tx保持高电平不影响通讯。
在这里插入图片描述

协议思路

我的协议主要包含以下6个内容:

  1. 起始标志:我这里是直接用 '>' 的值来表示
  2. 目标地址:一共有三个模块所以地址也有三个
enum ModuleAddrees {
    kRaspberryAddr = 0x1,
    kChuanGanQiAddr = 0x2,
    kQuDongBanAddr = 0x3,
    kReseve1        = 0x4,
    kReseve2        = 0x5,
    kReseve3        = 0x6,
    kReseve4        = 0x7,
};   ///< 标记模块地址
  1. 消息类型:用来标记当前数据包属于那种数据类型,目前只想到了以下几个
enum DataTypeId {
    kAck_dti = 0x0,
    kYaw_dti = 0x1,
    kCio_dti = 0x2,
    kAdc_dti = 0x3,
    kEncoder_dti = 0x4,
    kMoto_dti = 0x5,
    kServe_dti = 0x6,
    kPower_dti = 0x7,

    kReseveLast_dti = 0x1f, //0001 1111
};   ///< 标记数据类型
  1. 内容长度 :就是简单记录消息内容的长度,主要是想用来校验,后来好像没用上,太懒了直接没写校验
  2. 内容:就是要携带的主要内容
  3. 结束标志:惯用换行组合 “\r\n”,对应数组就是[13 10]
数据包封装和解封装

基本上用一个数组把上面的内容全部接下来就可以了,不过我为了能少一个byte把目标地址消息类型的值通过移位放到了一个8位数据里了(高3位的bit表示目标地址, 低5位的bit表示消息类型)。我就直接上代码了,这样简单直接

/**
 * @brief 封装数据包
 * @param package 数据包
 * @param detAddr 目标模块地址
 * @param dti   数据类型标号
 * @param len   数据长度
 * @param srcData 原始数据(组)
 * @return 数据包长度, 构造失败返回-1
 */
int MES_PackageData(char *package, enum ModuleAddrees detAddr, enum DataTypeId dti,
                    unsigned char len, void *srcData)
{
    int i;
    int result = -1;
    char *p = package;
    unsigned char addrTid = (((unsigned char)detAddr) << 5) | ((unsigned char)dti);

    // 起始标志 ‘>’
    *p = '>';
    p++;
    // 模块号(3bit)+数据类型号(5bit)
    *p = addrTid;
    p++;
    // 数据(组)长度,单位(byte)
    *p = len;
    p++;
    // 数据(组)
    for (i = 0; i < len; i++, p++) {
        *p = ((char *)srcData)[i];
    }
    // 结束标志 “\r\n”
    *p = '\r'; p++; *p = '\n';

    // 计算包长度
    result = p - package + 1;

    return result;
}                    
/**
 * @brief 拆解数据包
 * @param package 数据包
 * @param selfAddr 本机模块号
 * @param res 获得数据
 * @param dti 数据类型编号
 * @return 数据长度
 */
int MES_UnpackageData(char *package, enum ModuleAddrees selfAddr,
                    char *res, enum DataTypeId *dti)
{
    int result = -1;
    char *p = package;
    unsigned char temp;
    int i;

    // 判断起始标志
    if (*p != '>') {
        return result;
    }
    p++;
    // 判断模块号(3bit)+获取数据类型号(5bit)
    temp = *p;
    if ((temp >> 5) != (unsigned char)selfAddr) {
        return result;
    }
    *dti = temp & 0x1f;
    p++;
    // 获取数据
    result = *p;
    p++;
    for (i = 0; i < result; i++, p++) {
        res[i] = *p;
    }

    return result;
}

以上是stm32从机的代码,下面是在树莓派上写的python代码

def __packData(self, detAddr = ModuleAddrees.kNone,
                   dti = DataTypeId.kAdc_dti,
                   len = 0, srcData = np.zeros(0,dtype = np.uint8)):
        '''
        :breif 打包数据
        :param detAddr:  目标地址
        :param dti:   数据类型
        :param len: 参数长度
        :param srcData: 参数列表
        :return: 完整数据包 np.ndarray
        '''
        pack = np.zeros(len + 5, dtype=np.uint8)
        pack[0] = ord('>') # begin flag
        pack[1] = np.uint8(detAddr << 5) | np.uint8(dti & 0x1f)
        pack[2] = np.uint8(len)
        pack[-2] = ord('\r')
        pack[-1] = ord('\n')

        i = 0
        while i < len:
            pack[i+3] = np.uint8(srcData[i])
            i += 1

        return pack

def __Unpackage(self, pack = '', len = 0):
        '''
        解开数据包
        :param pack:
        :param len:
        :return: 内容   长度   类型
        '''
        pck = np.array(list(pack), dtype=np.uint8)
        if pack.__len__() < 6:
            dataContent = []
            dataType = -1
            dataSize = -1
            return (dataContent, dataSize, dataType)

        dataContent = pck[3:-2]
        dataSize = pck[2]
        dataType = DataTypeId(pck[1] & 0x1f)

        return (dataContent, dataSize, dataType)

'''
这两函数是一个类方法,不能直接用,能看出来流程就行。

到这我的协议内容基本就结束了,后面主要是记录在第一次使用树莓派串口时候要做的工作和注意事项
'''

树莓派python串口的使用注意

更改树莓派串口设备驱动

我用的是树莓派4b,芯片内部有两个串口:一个称之为硬件串口(/dev/ttyAMA0),一个称之为mini串口(/dev/ttyS0)。两个区别是ttyAMA0是外设串口有单独的时钟,而ttyS0是简易的串口由内核提供时钟,所以AMA0会相对稳定,ttyS0会受到内核时钟的影响。

通过指令ls -l /dev ,可以看出目前引脚引出的串口是ttyS0,所以第一步的工作就是要修改这个映射关系
在这里插入图片描述
参考博客:修改树莓派串口设备映射
主要内容是找到相关文件 我这里是 “/boot/overlays/miniuart-bt.dtbo” ,不同版本系统多少会有点不一样
在这里插入图片描述
然后编辑 /boot/config.txt
在该文件中增加一行代码 dtoverlay=“对应的文件名”
在这里插入图片描述
然后保存文件,重启树莓派使之生效。
在此查看设备,对应关系就改过来了
在这里插入图片描述

关闭控制台功能

起初为了能通过串口进入系统,打开了控制台功能,这里需要关闭,两个指令即可

sudo systemctl stop serial-getty@ttyAMA0.service
sudo systemctl disable serial-getty@ttyAMA0.service

我看的博客都有讲到还需要修改 /boot/cmdline.txt
把里面关于串口的内容都删掉,但是我发现我的里面直接就已经没有了,所以有需要的转置上面的超链接

再次重启就可以正常使用了

python串口的使用

我没有使用WiringPi库,而是直接用的PySerial。在边学边用的过程中发现,python的串口数据在解析的时候不像C语言的那么方便(应该是我太菜了),我总是在纠结python如何对数据进行声明,如何对数据强制转换。
目前为止,我发现通过串口发送数组类型是相对方便的所以在python中我的串口数据包都是以数组的形式进行传递(dtype=np.uint8)。
可能是因为python中万物皆对象的概念导致我没办法实现类似C语言中union这样的相同内存不同数据类型的数据结构(应该还是我太菜了)
所以我准备后面用c语言写个静态库,python调用库来实现这样的功能,希望确保数据的准确

通讯应答测试

为了方便模拟消息的应答,从机我是直接插到了串口助手上,这样我就可以控制从机发送。
测试思路是这样的,树莓派向从机发送应答命令包从机回复,如果树莓派可以正常发送和接收在控制台上会有消息显示,如果正常即为成功

  1. 编写测试demo

def __task_enter(self,argm = []):
        '''
        对接收到的数据进行分析并且做出响应
        :return:
        '''
        lock = threading.Lock()
        try:
            while self.__TaskIsRun:
                # step.1 接收数据包
                (self.r_data, self.r_len) = self.ReadLine()
                # step.2 解析数据包
                lock.acquire()
                (cont, size, type) = self.__Unpackage(self.r_data, self.r_len)
                lock.release()
                # step.3 分析数据
                if type == DataTypeId.kAck_dti:  #解析应答回复内容
                    print("get kAck_dti\n")
                    if cont[0] == ModuleAddrees.kChuanGanQiAddr.value:
                        self.dataApi.ack_CGQ = 1
                        self.ipc.cmdAckCGQ.set()
                    elif cont[0] == ModuleAddrees.kQuDongBanAddr.value:
                        self.dataApi.ack_QDB = 1
                        self.ipc.cmdAckQDB.set()
                    else:
                        print("ack pack cont error\n")
                    pass
                elif type == DataTypeId.kMoto_dti:
                    print("get kMoto_dti\n")
                    pass
                continue
        except:
            print("The thread is end >> " + self.task.getName())
            return



if __name__ == "__main__":
    prl = SpcProtocol("protocolTest", "/dev/ttyAMA0", 115200)

    prl.StartTask()
    while True:
        key = input("输入cmd: ")
        print(key, end = '\n')
        if key == 'ack':
            prl.SendCmdAck(ModuleAddrees.kQuDongBanAddr)  # 发送应答命令
        print("wait for ack\n")
        prl.ipc.cmdAckQDB.wait()
        print('get it from QuDongBan\n')
        continue


  1. 输入指令发送并检查
    在这里插入图片描述
    在这里插入图片描述

  2. 查看控制台回复内容
    在这里插入图片描述
    在这里插入图片描述
    另外也测试了其他错误的数据回复包,在设定中的错误控制台也会做出对应的反映。
    至此,自定义的通讯协议初步完成了点对点的实验,明天开始测试对两个从机的通讯响应。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

自定义的串口通信协议 的相关文章

  • #include<chrono>

    include lt chrono gt 是C 43 43 标准库中用于处理时间和持续时间的头文件 chrono库提供了一系列用于表示时间点 时间段和时钟的类和函数 以下是chrono库中一些常用接口和用法 xff1a 持续时间 xff08
  • 【#include<opencv2/core/core.hpp>】

    include lt opencv2 core core hpp gt 是OpenCV库中的一个核心模块头文件 xff0c 它包含了许多用于处理图像和计算的基本数据结构 函数和类 以下是opencv2 core core hpp中的一些常用
  • 逐函数详细讲解ORB_SLAM2算法和C++语法|LoadImages|2-1

    完整可执行代码 https github com xiaoqiuslam orb2 视频讲解 逐函数讲解ORB SLAM2源码 xff5c 1 加载Euroc数据集图像 逐函数讲解ORB SLAM2源码 1 加载Euroc数据集图像 逐函数
  • Makefile介绍

    概述 什么是makefile xff1f 或许很多Windows的程序员都不知道这个东西 xff0c 因为那些Windows的集成开发环境 xff08 integrated development environment xff0c IDE
  • 逐函数详细讲解ORB_SLAM2算法和C++语法|System|2-2

    span class token comment Create SLAM system It initializes all system threads and gets ready to process frames span ORB
  • c_cpp_properties.json vscode ubuntu18.04

    在 Ubuntu 18 04 上使用 Visual Studio Code 时 xff0c 你可以根据以下示例配置 c cpp properties json 文件 假设你已经安装了 GCC 和 G 43 43 编译器 xff0c 这个示例
  • 激光SLAM 算法匹配原理

    文章目录 1 数据获取 xff1a 2 数据关联 xff1a 3 匹配 xff1a 4 位姿更新 xff1a 5 地图更新 xff1a 6 循环 xff1a 数据关联二维激光局部定位算法原理 激光SLAM xff08 Simultaneou
  • 【ORB_SLAM2 CMakeLists.txt 文件详解】

    文章目录 ORB SLAM2 CMakeLists txt 文件详解 set CMAKE LIBRARY OUTPUT DIRECTORY PROJECT SOURCE DIR lib add library PROJECT NAME SH
  • 数据结构知识体系框架图

  • [TM4C123单片机实践] 配置SSI并驱动DAC7811显示正弦波

    这几天做电赛学习了TM4C123 单片机 总得来说 结合官方例程与参考手册 加上一个好的示波器 效率会高很多 TI的SSI 实际上就是SPI 我门先熟悉一下SPI SPI 就是在主机与从机之间用来传输数据的 通过TX RX传输数据 通过CS
  • 压力BMP180传感器时序详解

    BMP180是一种高精度数字压力传感器 xff0c BMP180的超低功耗 xff0c 低电压电子元件经过优化 xff0c 适用于移动电话 xff0c PDA xff0c GPS导航设备和户外设备 UP 61 压力数据 xff08 16到1
  • C3D行为识别(一):UCF101视频数据集预处理

    行为识别 xff08 一 xff09 xff1a UCF101视频数据集预处理 文章目录 行为识别 xff08 一 xff09 xff1a UCF101视频数据集预处理1 数据集介绍1 1 UCF101 2 UCF101预处理2 1 划分t
  • Linux学习-坑多就慢慢填

    Linux学习 坑多就慢慢填 刚开始想找个资料丰富的开发板学习Linux xff0c 那时正点原子和野火都上架了NXP芯片的开发板 xff0c 因为经常用正点原子的硬件 教程和论坛 xff0c 毅然选择了正点原子的NAND版 xff0c 实
  • 树莓派 I2C通信,控制多个I2C从设备

    1 需要安装 i2c tools工具 在黑框输入 xff1a sudo apt get install i2c tools 2 后续编程使用python库 在黑框输入 xff1a sudo apt get install python sm
  • 软件框架详解

    软件框架至少包含以下组成部分 xff1a xff08 1 xff09 一系列完成计算的模块 xff0c 在此称为构件 xff08 2 xff09 构件之间的关系与交互机制 xff08 3 xff09 一系列可变点 xff08 也称热点 xf
  • oauth2四种模式的流程图

    参照https www ruanyifeng com blog 2019 04 oauth grant types html 以下为自己的理解整理 xff0c 授权码和密码模式是用户使用第三方平台账号授权给客户端应用 xff0c 来获取受第
  • Linux 应用开发 - 必须掌握的 5 个底层 IO 函数

    底层输入输出 xff08 Low Level Input Output xff09 这篇博客主要介绍 Linux 原生的 IO 操作 xff08 Low IO xff09 xff0c 你可能会想不是有跨平台的 ANSI C 可以使用么 xf
  • 为什么选择嵌入式方向

    本文出自 同济大学软件学院院长谈 嵌入式 方向选择 xff0c 个人感觉不错 xff0c 故转之 嵌入式系统无疑是当前最热门最有发展前途的 IT应用领域之一 嵌入式系统用在一些特定专用设备上 xff0c 通常这些设备的 硬件资源 xff08
  • 关于Apache的目录浏览功能

    近日 xff0c 公司有个需求需要使用到Apache的目录浏览功能 但是遇到了一点问题 xff0c 所以出现了这篇文章 linux安装Apache很简单 xff0c 使用yum安装即可 命令 xff1a yum y install http

随机推荐

  • Linux 下的make命令与Makefile

    概述 博客内容包含linux下make命令的使用与makefile的书写规则等 xff0c 希望通过本文档使读者对make命令makefile文件有进一步了解 xff0c 由于鄙人经验学识有限文档中会有描述不准确以及理解偏差 xff0c 欢
  • 人工智能会取代程序员吗?

    多亏了人工智能 xff08 AI xff09 xff0c 软件将在未来自行编写 至少 xff0c 这就是谷歌首席执行官桑达尔 皮查伊 xff08 Sundar Pichai xff09 对软件开发未来的看法 他是对的 xff01 这并不能使
  • 人工智能和5G在无人机技术中的作用

    技术为我们提供了许多引人入胜的设备和发明 xff0c 无人机就是其中之一 无人机 xff0c 在技术术语中也称为无人机 xff08 UAV xff09 xff0c 自2007年首次出现以来越来越受欢迎 这些小工具最初是手动和远程控制的 无人
  • 惠普暗影精灵笔记本开机自动打开小键盘,冷热启动均支持

  • STM32F4 使用结构体配置功能

    1 IIC配置 void IIC Mode Config void I2C InitTypeDef I2C InitStructure I2C InitStructure I2C Mode 61 I2C Mode I2C IIC模式 I2C
  • 执行 sudo apt-get update 报错:仓库 xxx 没有release文件 / 不再含有release文件

    输入命令 sudo apt get update 报错 这里可以看到有两个问题 xff0c 一个是 ubuntu自己的源连不上了 第二三个红框框 xff0c 一个是 vmware 这个软件 第一个红框框 首先解决第一个问题 archive
  • C++ 发送HTTP请求

    HTTPRequest HTTPRequest是一个用于发出HTTP请求的单头C 43 43 库 您可以将它包含在项目中并使用它 HTTPRequest在macOS Windows Haiku BSD和GNU Linux上进行了测试 xff
  • 小程序、公众号开发报code been used(40163)或invalid code(40029)错误,解决方案--之--搞清楚微信的登录机制,保存登录状态!!!

    x1f4d6 前言 做微信小程序或公众号开发 xff0c 有时我们为了管理用户 xff0c 需要获取用户的openid xff0c unionId等信息 这时会用到微信提供的接口 xff1a code2Session code2Sessio
  • 使用git在vscode中进行版本控制

    版本控制是一件非常cool的事 xff0c 可以将我们的代码按照版本存储 1 首先我们选择一个版本控制工具 xff0c 这里我们选择git xff0c 点击下载安装 xff0c 安装时选择vscode编辑器 2 安装好了之后打开vscode
  • 2.3.1 参数服务器理论模型

    ROS入门 2 3 1 参数服务器理论模型 ROS入门 理论与实践 视频教程镇楼 参数服务器实现是最为简单的 xff0c 该模型如下图所示 该模型中涉及到三个角色 ROS Master 管理者 Talker 参数设置者 Listener 参
  • 4.6.3 编码设置参数

    ROS入门 4 6 3 编码设置参数 ROS入门 理论与实践 视频教程镇楼 编码的方式可以更方便的设置 全局 相对与私有参数 1 C 43 43 实现 在 C 43 43 中 xff0c 可以使用 ros param 或者 ros Node
  • 5.1 TF坐标变换

    ROS入门 5 1 TF坐标变换 ROS入门 理论与实践 视频教程镇楼 机器人系统上 xff0c 有多个传感器 xff0c 如激光雷达 摄像头等 xff0c 有的传感器是可以感知机器人周边的物体方位 或者称之为 坐标 xff0c 横向 纵向
  • 10.1.3 action通信自定义action文件调用(Python)

    ROS入门 10 1 3 action通信自定义action文件调用 Python ROS入门 理论与实践 视频教程镇楼 需求 创建两个ROS 节点 xff0c 服务器和客户端 xff0c 客户端可以向服务器发送目标数据N 一个整型数据 服
  • rosdep

    rosdep 初始化时异常解决方案 rosdep 初始化时异常解决 视频教程 安装构建依赖 在 noetic 最初发布时 xff0c 和其他历史版本稍有差异的是 没有安装构建依赖这一步骤 随着 noetic 不断完善 xff0c 官方补齐了
  • springboot应用集成prometheus监控

    环境参数 xff1a 运行命令 xff1a 1 uname xff0d a xff08 Linux查看版本当前操作系统内核信息 xff09 2 cat proc version xff08 Linux查看当前操作系统版本信息 xff09 3
  • [NVIDIA Jetson Xavier Nx]从刷机烧录到环境配置 记录

    目录 前言一 开机烧录二 环境配置Cuda环境变量配置更新源python环境配置安装Jtop 内存 CPU GPU等等资源监视工具 前言 对进行NVIDIA Jetson Xavier Nx环境配置进行记录 一 开机烧录 参考下面的博客 x
  • 最近处理的报错 -DCMAKE_BUILD_TYPE=Debug

    1 error 39 nullptr 39 was not declared in this scope 解决方法 使用的是QTcreator的pro文件 然后缺少相应关于c 43 43 11的设置 点pro文件中加载的东西如下 QMAKE
  • Halcon 单相机标定

    原文链接 xff1a https blog csdn net weixin 43197380 article details 90438976 comments 13104885 一 理论 为什么要进行单相机标定 xff1f 广义 xff1
  • 将图片嵌入Markdown文档

    将图片嵌入Markdown文档中是一个比较难受的事情 一般大家都会将图片存入本地某个路径或者网络存储空间 xff0c 使用URL链接的形式插入图片 image url to image 将图片放到本地的时候如果想将文档分享给朋友或者换台电脑
  • 自定义的串口通信协议

    自定义的通信协议 自定义一主多从串口通讯 1硬件基础两个从机的 Tx 是相互连接的 xff0c 导致一个从机在需要发送数据时发不出去了 协议思路数据包封装和解封装 树莓派python串口的使用注意更改树莓派串口设备驱动关闭控制台功能pyth