RoboMaster视觉教程(8)串口通讯_robotmaster c板串口

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

在打开串口时需要提供串口设备的文件地址类似于/dev/ttyUSB0

如果使用妙算的话可以使用妙算自带的GPIO上的几个串口。

如果使用USB串口,在插拔的过程中有可能会出现串口号变化的情况,比如上次是ttyUSB0然后程序挂了或串口出错了,插拔usb转串口之后串口号可能变成ttyUSB1。对于这种情况可以先将当前系统中有效的串口找出来然后再打开串口,可以参考stakoverflow中

https://stackoverflow.com/questions/2530096/how-to-find-all-serial-devices-ttys-ttyusb-on-linux-without-opening-them

串口通信为保证上下位机数据准确需要制定一个通信协议,我最开始做比赛的时候是用字符串来对发送数据进行描述的,类似于“Y010P020"来代表yaw轴转10度pitch轴转20度,在下位机stm32上用最原始的加法和乘法把字符组合成数据。

这种方式不仅不好看效率也很低也很容易出错,合理的方法是定义发送帧和接收帧,用帧头和帧尾来校验帧是否正确,帧头和帧尾中间放数据。

这是DJI开源代码中定义的上位机向下位机传输的帧的格式,可以看到中间的数据部分是用两个字节组合成一个16位的整数。

frame1

串口发送时一般是发送一段unsigned char数组,其中的每一个字节都可以通过强制类型转换来表示其他的类型。这有点类似于c语言中的union用一段内存来表示不同的数据类型。

以上述帧为例第一个字节为帧头0xFF,中间6个字节组成三个16位整数,就可以像下面这样写。

unsigned char data[] = { 0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFE};
\*(short\*)(data+1)=(short)value1;
\*(short\*)(data+3)=(short)value2;
\*(short\*)(data+5)=(short)value3;

下位机在接收的时候首先找0xFF找到后再接收7个字节,对比最后一个字节是否是0xFE若不是则丢弃,若是则本次数据有效,之后同样可以采用强制类型转换的方式来提取数据。

如果想发送其他类型的数据的话同样可以用这种方式,但要根据类型的大小分配好需要的字符数组大小以防越界。

大疆开源代码中下位机对上位机的帧的格式如下:

frame2.png)

对应的串口接收和命令解析由void RemoteController::praseData(const char * data, int size)函数完成。

这个函数中比较有意思的一段是

case 2:{                        // pitch angle
    int a = 0;
    a |= (0x7f & cmd2);
    a = (0x80 & (cmd2)) == 0 ? a : a | 0xffffff80;
    other_param->angle_pitch = (int)a / 4.0;
    //std::cout << "angle\_pitch:" << other\_param->angle\_pitch << '\n';
    break;
}

a |= (0x7f & cmd2);这行干啥的呢?就是将cmd2这个字节的后7位赋值给a。而下一句a = (0x80 & (cmd2)) == 0 ? a : a | 0xffffff80;则是判断cmd2是否是负数如果是就把a的剩余位数都赋1变成负数。

我觉得这段代码写得不好,明明有更好看易懂的写法,直接int a=*(char*)&cmd2不就好啦 ^_^

另外串口波特率需要设置合理,如果波特率太高则误码率会增加,波特率太低则发送速度太慢。

以波特率115200为例,它表示每秒发送115200位,换算成字节每秒是11520(不加校验位)也就是除以十,按上例每次发送的数据为8字节,则除8得到每秒最大可发送指令1440次,这样对于100多帧的摄像头来说是够用的(我觉得串口的发送速度至少要比摄像头的帧率大10倍以上)。

如果想每次多传些数据,那就需要提高波特率了,在东南大学的开源代码中他们把波特率设置为460800也就是115200的4倍,他们定义的帧每次发送需要传输16字节接收需要20字节在这个波特率下可以满足性能需要。

东南大学开源代码串口部分

东南大学的串口部分的开源代码兼顾了调试需要,对一些异常情况也考虑的比较周到。最值得称赞的地方就是他们定义的帧格式考虑很周全,这样在设计自己的通信协议时极大地减少了工作量。

/\*
 \* @Brief: 控制战车帧结构体
 \*/
struct ControlFrame
{
    uint8\_t  SOF;
    uint8\_t  frame_seq;
    uint16\_t shoot_mode;
    float    pitch_dev;
    float    yaw_dev;
    int16\_t  rail_speed;
    uint8\_t  gimbal_mode;
    uint8\_t  EOF;
}_controlFrame;

/\*
 \* @Brief: 战车回传数据帧结构体
 \*/
struct FeedBackFrame
{
    uint8\_t  SOF;
    uint8\_t  frame_seq;
    uint8\_t  task_mode;
    uint8\_t  bullet_speed;
    uint8\_t  rail_pos;
    uint8\_t  shot_armor;
    uint16\_t remain_HP;
    uint8\_t  reserved[11];
    uint8\_t  EOF;
}_feedBackFrame;

/\*
 \* @Brief: 比赛红蓝方
 \*/
enum TeamName
{
    BLUE_TEAM       =   (uint16\_t)0xDDDD,
    RED_TEAM        =   (uint16\_t)0xEEEE
};

/\*
 \* @Brief: control frame mode
 \*/
enum ControlMode
{
    SET_UP          =   (uint16\_t)0xCCCC,
    RECORD_ANGLE    =   (uint16\_t)0xFFFF,
    REQUEST_TRANS   =   (uint16\_t)0xBBBB
};

/\*
 \* @Brief: 发射方式
 \*/
enum ShootMode
{
    NO_FIRE         =   (uint16\_t)(0x00<<8),//不发射
    SINGLE_FIRE     =   (uint16\_t)(0x01<<8),//点射
    BURST_FIRE      =   (uint16\_t)(0x02<<8) //连发
};

/\*
 \* @Brief: 发射速度
 \*/
enum BulletSpeed
{
    HIGH_SPEED      =   (uint16\_t)(0x01),   //高速
    LOW_SPEED       =   (uint16\_t)(0x02)    //低速
};

/\*
 \* @Breif:所需控制模式
 \*/
enum TaskMode
{
    NO_TASK         =   (uint8\_t)(0x00),    //手动控制
    SMALL_BUFF      =   (uint8\_t)(0x01),    //小符模式
    BIG_BUFF        =   (uint8\_t)(0x02),    //大符模式
    AUTO_SHOOT      =   (uint8\_t)(0x03)     //自动射击
};

/\*
 \* @Brief: 哨兵云台工作模式
 \*/
enum GimbalMode
{
    PATROL_AROUND   =   (uint8\_t)(0x01),    //旋转巡逻
    PATROL_ARMOR_0  =   (uint8\_t)(0x02),    //巡逻装甲板0
    PATROL_ARMOR_1  =   (uint8\_t)(0x03),    //巡逻装甲板1
    SERVO_MODE      =   (uint8\_t)(0x04)     //伺服打击
};

/\* @Brief:
 \* SYSTEM\_ERROR: System error catched. May be caused by wrong port number,
 \* fragile connection between Jetson and STM, STM shutting
 \* down during communicating or the sockets being suddenly
 \* plugged out.
 \* OJBK: Everything all right
 \* PORT\_OCCUPIED: Fail to close the serial port
 \* READ\_WRITE\_ERROR: Fail to write to or read from the port
 \* CORRUPTED\_FRAME: Wrong frame format
 \* TIME\_OUT: Receiving time out
 \*/
enum ErrorCode
{
    SYSTEM_ERROR    = 1,
    OJBK            = 0,
    PORT_OCCUPIED   = -1,
    READ_WRITE_ERROR= -2,
    CORRUPTED_FRAME = -3,
    TIME_OUT        = -4
};

Qt编写串口助手

Qt是非常好用的跨平台开源的GUI程序开发库,我非常喜欢Qt。Qt有详细的文档和大量的示例程序,很多示例程序只需要稍微改一改就可以写出我们想要的功能,对比之下用GTK开发就困难多了。

这里我们就来用Qt自带的串口终端的例子来实现一个串口助手,Qt编写的代码是跨平台的也就是三大主流系统Windows、Linux、macOS都可以用一套代码实现相同的功能,这个例子我在Windows和Linux下测试都是好用的。

打开Qt Designer 在Welcome界面中点击Example 在搜索框中搜索 terminal 就能找到串口终端的例子

/8/qt1.png)/8/qt2.png)双击后选择复制项目并打开会进入配置界面,按默认配置即可

/8/qt3.png)
![/qt4.png)]](https://img-blog.csdnimg.cn/20190821204151891.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA3NTAxMzc=,size_16,color_FFFFFF,t_70)
在弹出配置界面的同时也会弹出该例子的说明帮助,Qt的帮助文档都写得非常规范,读了会很有收获
![qt5.png)]](https://img-blog.csdnimg.cn/20190821204211465.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA3NTAxMzc=,size_16,color_FFFFFF,t_70)
这个例子直接编译运行就能得到一个简易的串口助手。
![/8/qt6.png)]](https://img-blog.csdnimg.cn/20190821204228633.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA3NTAxMzc=,size_16,color_FFFFFF,t_70)
点击齿轮按钮可以打开串口配置窗口,左边列出了目前系统中存在的串口,右边是波特率校验位等的设置。配置好后点击连接按钮就可以打开串口了。
![/8/qt8.png)]](https://img-blog.csdnimg.cn/20190821204252305.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTA3NTAxMzc=,size_16,color_FFFFFF,t_70)
这个示例程序实现了数据发送和接收功能,在黑框中可以直接输入要发送的数据,同时接收数据也会传到黑框中。

数据的接收和发送就是调用read和write函数

void MainWindow::writeData(const QByteArray &data)
{
    m_serial->write(data);
}
void MainWindow::readData()
{
    const QByteArray data = m_serial->readAll();
    m_console->putData(data);
}


![img](https://img-blog.csdnimg.cn/img_convert/261a76c54ca76ce619a1cc6281169389.png)
![img](https://img-blog.csdnimg.cn/img_convert/e4d866fe9af265dcb080ba048f0e2bc0.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

02018209)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值