MATLAB :【11】一文带你读懂serialport串口收发原理与实现

这篇具有很好参考价值的文章主要介绍了MATLAB :【11】一文带你读懂serialport串口收发原理与实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

碎碎念:

这周的主要工作还是集中于FOC中,因为羡慕稚晖君做出的漂亮Qt面板,因此在利用MATLAB复刻过程中,学习了一下serialport的使用。FOC的GUI部分就在加班加点写作中啦,同时最近打算开一个新坑,大家可以期待一下哈哈哈。

欢迎大佬们点赞+收藏+关注~ o(* ̄▽ ̄*)ブ

目录

1 串口接收

2 串口发送


考虑到互联网中对MATLAB中最新的serialport的使用案例有些混乱,并且很多都是基于已经被淘汰的serial库,严重缺乏易用性,因此在本文中给出简单的串口收发模板,特别是串口回调函数的使用案例。

1 串口接收

串口接收是指,开发板将数据发送给电脑,电脑读取数据并进行数据分析处理的过程。

想弄清楚怎么接收串口的数据,那你首先就需要知道串口的数据是怎么发送出来的

试想这样的应用场景,我的开发板上安装了一个温度传感器,温度传感器采集的数据长度是3字节(24比特);我需要将开发板采集到的温度信息实时显示在屏幕上,我需要怎么做?

这其中需要注意的有下面几点:

  1. 温度传感器是3字节的,如何确定接收到的某一个字节位于三个字节中的哪个位置?
  2. 实时显示要求我需要对每次发送过来的数据做出响应,这种响应需要怎么做?

针对问题1:

其实这也是初学者常遇到的问题,有时候串口发送的数据就像一个堵不住的水管,完全不知道要怎么处理。

由于串口协议的限定,导致其每次发送的只能是一个字节,对于多字节的数据【ABC】来说,就只能通过三个字节【A】、【B】、【C】来发送,如下图所示(最左端为最先接收到的字节):

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

这就显然会遇到问题,在任意一个时刻,我没办法确定接收到的数据到底处于【ABC】的哪一个位置;更致命的是,由于物理介质的影响,甚至可能会造成数据的丢失,这就更给数据的接受造成了影响。

如何解决这一问题呢?人们开始想到了“打包”的方式,也可以理解为我们常说的“帧”的概念。只要在每组数据的开头加一些标志,表示出这是数据的最开始位置不就好了,即为下图所示(最左端为最先接收到的字节):

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

假设我们设置的这个标志为【FF、FF】,当上位机检测到连续的两个【FF】时,就表示之后的三个字节分别为【A】、【B】、【C】。

这其实就解决了这个问题1,实现了对一帧中每一个字节的位置确定。

针对问题2:

解决问题1后,我们当然可以利用顺序执行的方式,来实现对串口数据的一次读取以及数据处理。但是如何实现当每一次检测到特定信号,就调用一次数据处理函数呢?

这就要先理解一下MATLAB中serialportlist的使用逻辑了,整体来说serialportlist是对serial的升级版本(在帮助页面也有提到),其通过构建SerialObject对象的方式,来实现串口参数的设置以及读写。

具体细节可以参考MATLAB文档serialport,太全面的参数设置过于冗余,不在本文讨论范围内。这里主要介绍两个比较重要的概念缓冲区以及回调函数。

缓冲区:

在serialport中,缓冲区是自动存在于SerialObject对象中,但是有时使用时(如本文)不需要针对性设置缓冲区的大小。可以理解为一个长度固定的FIFO队列,当检测到特定信号的时候,将串口传入的每一个字节的数据,按顺序保存在里面,当长度满了之后,就不再继续在里面添加新的数据了。

可能会使用到的函数为

flush(SerialObj)

可以用来清空缓冲区,常常用在串口对象初始化的时候。

回调函数:

这个是解决问题2的关键,回调函数可以理解为一个开关被触发后需要进行的操作(或者简单理解为单片机的中断处理函数);我们可以通过SerialObject的对象设置,来设置检测到什么信号(这个信号是作为一帧的结尾的时候,执行回调函数。

举个方案A作为例子,我们可以设置检测到【FF FF】信号的时候,执行三个字节的数据读取。(尽管不这样用,后面会说为什么)

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

如上图所示,当我们按照上面方案A的方式,设置回调函数的触发条件,有什么问题呢?每当检测到【FF FF】的时候,就会触发回调函数。

看似没问题,但是此时一帧的组合已经从【FF FF A B C】变成了【A B C FF FF】,因为我们提到回调函数敏感的是一帧的结尾。检测到【FF FF】时,下一个字节显然就是【A】。这其实是不规范的,我们不能理所当然地认为每一帧都是传输正确的。

举个例子:

【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】

中间红色的ABC表示因为数据线接触不良导致的传输错误,如果具有固定帧头的话,或许帧头也会出现错误,从而直接跳过这一帧错误的信号【 A B C FF FF】。

因此必须通过固定的帧头来确定此时传输的是否是完整的数据。

这就需要我们进一步对一帧的结构,进行修改了,让其完整地包含“帧头”与“帧尾”。在MATLAB中给出了configureTerminator的方法,可以编辑SerialObject需要检测到的帧尾信号。详细解释可以看configureTerminator官方文档,其中有这样的介绍:

configureTerminator(t,terminator) defines the terminator for both read and write communications with the remote host specified by the TCP/IP client t. Allowed terminator values are "LF" (default), "CR", "CR/LF", and integer values from 0 to 255. The syntax sets the Terminator property of t.

这里提到,我们可以设置需要检测帧尾信号为“LF”、“CR”、“CR/LF”或一个0-255的整数(刚好对应了8位无符号数,也就是一字节)。

按照上面的说明,我们可以对之前的帧进行下图的修改,加上帧尾(最左端为最先接收到的字节):

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

这样,我们就可以利用检测帧尾(橙色部分),来实现对回调函数的调用啦。但是新的疑问又诞生了:我理解0-255的数字怎么发送,但是这毕竟是单字节的,会不会造成数据读取混乱?上文提到的“LF”、“CR”、“CR/LF”这三个又是什么?(这也是困扰了我一段时间的问题)

“LF”、“CR”、“CR/LF”概念解释:

引用自:CR,LF详解_Berwyn丶的博客-CSDN博客_cr的16进制

从起源上来说,在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33,Linux/Unix下的tty概念也来自于此)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打完一行换行的时候,要用去0.2秒正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。

于是,研制人员想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,告诉打字机把纸向下移一行。这就是“换行”和“回车”的来历,从它们的英语名字上也可以看出一二。

后来,计算机发明了,这两个概念也就被般到了计算机上。那时,存储器很贵,一些科学家认为在每行结尾加两个字符太浪费了,加一个就可以。于是,就出现了分歧。在不同的系统中,就出现了下面的状况:

系统 符号 名称 十六进制(ASCII)
Linux ’\n’ LF 0x0A
Mac ’\r’ CR 0x0D
Windows ’\r\n’ CR/LF 【0x0D 0x0A】
注:这里并不是说在Windows系统中只能使用CR/LF作为帧尾,表格里说的是对应系统本文编辑器中的默认换行符。

是不是感觉豁然开朗?那我们就可以理所当然的将之前的图改为下面的样子(最左端为最先接收到的字节):

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

读到这里,我想读者朋友们已经逐渐理解了最开始所说的:想弄清楚怎么接收串口的数据,那你首先就需要知道串口的数据是怎么发送出来的。回想一下我们的思路,因为要实现多字节读取,所以需要给一个固定的帧头用来确定每个字节的位置;为了提供一个可以激活回调函数的信号,并且不影响帧头的存在,我们需要添加一个帧尾。结合configureTerminator中的设置信号,我们发现可以使用“LF”、“CR”、“CR/LF”或者0-255的数字作为帧尾让回调函数激活,通过查阅原来前面的三个“LF”、“CR”、“CR/LF”说的是换行符的ASCII码,我们可以使用开发板让他们发出对应的十六进制数据来表示。

至此,我们知道了数据从开发板上发送出来时的结构。对比四种帧尾,只有“CR/LF”是两个字节的,对于温度这种未知的数据信号来说,是最稳妥的,可以更好的避免出现雷同情况,导致读取错误。

举个例子:

当我们发送的数据是:【FF FF A B C 帧尾】。

当帧尾是1字节很有可能出现【C】与【帧尾】相同的情况,如果【帧尾】是两字节,【B C】与之雷同的情况则会概率减小很多。

因此我们选择在开发板中按照下图的方式来发送数据给上位机(最左端为最先接收到的字节),这需要先在开发板中定义好,本文默认读者已经完成了这部分,如果有需要的话,读者也可以留言给我,我会单独出一篇文章进行讲解:

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

那么现在就可以开始激动人心(bushi)的MATLAB编程环节啦,基于MATLAB文档serialport,下面给出一个简单的模板:

Port_List = serialportlist("available");
SerialObj = serialport("COM7",115200);
configureTerminator(SerialObj,"CR/LF");
flush(SerialObj);
SerialObj.UserData = struct("Data",[]);
configureCallback(SerialObj,"terminator",@readSerialData);

% 回调函数
function readSerialData(src, ~)
    data = read(src,7,"uint8");
    src.UserData.Data = data;
    ShowTemp(src);
end

% 温度数据处理与展示
function ShowTemp(src)
    if(src.UserData.Data(1:2) == [0xFF 0xFF])
        Temperature = src.UserData.Data(3)*256*256 + src.UserData.Data(4)*256 + src.UserData.Data(5);
        disp(Temperature);
    end
end

下面对代码进行一下讲解:

Port_List = serialportlist("available");

展示出当前系统中可用的串口列表,与电脑设备管理器中的端口是对应的。


SerialObj = serialport("COM7",115200);

利用serialport函数来构造一个串口对象SerialObj,设定对应的端口是COM7端口,波特率是115200。


configureTerminator(SerialObj,"CR/LF");

设置需要检测到的帧尾是"CR/LF"。


flush(SerialObj);

清空串口对象的接收缓冲区。


SerialObj.UserData = struct("Data",[]);

通过查看SerialObj对象的属性,可以看到其中存在一个属性叫做UserData,可以用来存储数据,这里我们将其定义为一个结构体,里面自行定义只有一个叫做Data的数据。


configureCallback(SerialObj,"terminator",@readSerialData);

指定回调函数,也就是第三个属性提到的readSerialData函数,表示检测到帧尾后需要进行的操作。“terminator”参数的意思是检测结束符,读者只需要修改最后一个参数readSerialData即可。


function readSerialData(src, ~)

定义回调函数,src表示自定传入的对象,因此不需要进行修改。


    data = read(src,7,"uint8");

read函数表示从串口对象中读取7字节的数据,因为是从检测到结束符后面开始的也就是【FF FF A B C 0D 0A】这7个字节的内容。“uint8”表示读取的是8位无符号数。值得注意的是,这部分还有其他的函数可以使用,例如用来读取一行字符的readline函数,同样在MATLAB文档serialport有明确介绍。

这里其实就可以进一步理解CR/LF之所以是换行符的原因了,从一个换行符读取到另一个换行符之间,不就是读取一行(readline)的含义吗?


    src.UserData.Data = data;

将读取到的数据data存储到对象属性UserData里面的结构体下的Data中,实现数据的存储。数据的存储方式是一个长度为7的数组,可以直接利用索引1-7进行调用。

    ShowTemp(src);

调用数据处理的函数,用来预处理和显示接收到的数据。


end


function ShowTemp(src)

定义数据处理函数


    if(src.UserData.Data(1:2) == [0xFF 0xFF])

使用if语句,判断数据头是否是【FF FF】,确定是否有传输错误。


        Temperature = src.UserData.Data(3)*256*256 + src.UserData.Data(4)*256 + src.UserData.Data(5);

之后的三个字节是【A B C】,每个是8比特,因此要乘以它们的权值进行计算,获得原始的数据。


        disp(Temperature);

展示当前的数据到控制台。


    end
end

2 串口发送

串口发送是指,电脑将需要发送的数据(一般是指令或者参数设置信息)整合好,发送给开发板的过程。

相信有了前面串口接收的基础,这对大家来说就非常简单了,在这里,我们还是假设一个应用场景来进行讲解,由于很对读者会使用到GUI进行串口发送的测试,这里我们就以GUI中的文本输入框的数据格式为例。

在GUI中,我需要将一个十六进制字符串“FF 01 02 03 04”发送给开发板,我需要怎么做?GUI如下图所示,是“文本区域”类型的模块:

MATLAB :【11】一文带你读懂serialport串口收发原理与实现

这里需要注意下面的问题:

  1. 如何从GUI中获取数据(仅限于GUI使用时,如果是脚本文件,则需要按照字符串来进行处理)。
  2. GUI中获取到的数据,实际上是cell类型,而不是单纯的字符串类型(仅限于GUI使用时,如果是脚本文件,则需要按照字符串来进行处理)。
  3. 如何将数据进行分割并发送。

这里由于三个问题相当明确且容易解决,因此我直接给出串口发送函数write的使用案例:

Port_List = serialportlist("available");
SerialObj = serialport("COM7",115200);
send_data = get(app.TextAreaTabSend, "value");
HEX       = hex2dec(strsplit(cell2mat(send_data), " "));
write(app.SerialObject,HEX,"uint8");

下面对代码进行一下讲解:

Port_List = serialportlist("available");

展示出当前系统中可用的串口列表,与电脑设备管理器中的端口是对应的。


SerialObj = serialport("COM7",115200);

利用serialport函数来构造一个串口对象SerialObj,设定对应的端口是COM7端口,波特率是115200。


send_data = get(app.TextAreaTabSend, "value");

从GUI中获取当前TextArea中的值信息,返回的时cell类型的数据。


HEX       = hex2dec(strsplit(cell2mat(send_data), " "));

从内层到外层,依次完成cell2mat()将cell类型转为mat类型;strsplit()将mat类型按照空格进行分割;hex2dec()将字符串视为hex类型的数据转为十进制进行传输。

如果是单纯的字符串操作,则换为下面的函数即可:

HEX       = hex2dec(strsplit(send_str, " "));

将字符串先进行分割,然后转为十进制的数组。

注意,这两种写法我都是默认,发送的信息必须每个字节之间使用空格进行分割处理,因为使用的时write函数,并且是uint8类型。


write(SerialObj ,HEX,"uint8");

将数据HEX发送给SerialObj对象,实现发送。这里使用的是write函数,其实还有另一个函数writeline,读者可以参考MATLAB文档serialport进行查阅。

至此,就完成了全部的数据收发任务啦,当需要关闭串口时,只需要使用下面的函数,删除创建的对象即可。

delete(SerialObj);                        %通过删除对象来断开串口

最后再提及一下,为什么我都是使用的write以及read的uint8类型呢?一方面我们的应用环境还是数字的传输为主,字符串的传输这里并没有怎么涉及到。另一方面,逐个字节的收发,在我看来是更方便理解其中串口协议原理的,并且ASCII本身就是8位无符号数。


首次尝试这样的写作方式,希望本篇文章能够给读者一些帮助,同时由于本人水平有限,如果有一些问题的话,请务必留言指出,我一定虚心接受!

这就是本期的全部内容啦,如果你喜欢我的文章,不要忘了点赞+收藏+关注,分享给身边的朋友哇~文章来源地址https://www.toymoban.com/news/detail-417068.html

到了这里,关于MATLAB :【11】一文带你读懂serialport串口收发原理与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 抖音小店无货源处罚全解,一文带你读懂官方规则,合理规避风险

    大家好,我是电商年年 根据官方解释, “无货源店铺” ,指店铺经营者没有现货,在网上找产品找货源,在未取得货源方授权的情况下,把货源信息“搬运”到自己的店铺中,等有消费者下单,店铺经营者再去 货源方店铺下 单,由货源方向消费者派单发货。   那官方对于

    2024年02月03日
    浏览(66)
  • 一口气带你读懂跨境电商出海模式

    作为一名跨境电商卖家,掌握正确的“出海模式”至关重要。今天,就让我们一起揭开跨境电商的神秘面纱,探索它的各种出海模式,为你的电商之旅提供指引。 1️⃣ 国际站模式 ✅起步门槛:10W及以内 ✅利润:高 ✅客单价:较高(以 B2B 大单为主) ✅运营难度:中低,

    2024年02月21日
    浏览(53)
  • Linux - 一篇带你读懂 Curl Proxy 代理模式

    curl 是一个很有名的处理网络请求的 类Unix 工具。出于某种原因,我们进行网络请求,需要设置代理。本文讲全面介绍如何为 curl 设置代理 设置代理参数 基本用法 设置 HTTP 代理 下面两种设置代理的方式是可以的 由于代理地址的默认协议为  HTTP, 所以可以省略,按照下面的

    2024年02月05日
    浏览(69)
  • 工时管理为何对项目如此重要?8Manage 带你读懂!

    “时间就是金钱”,相信作为管理者都已经听腻了这话,但在项目管理中确实是真理。你要知道项目工时是会直接影响到项目费用成本的,不论项目工作是按小时还是按固定费用计费和付款,在一段工时内完成的工作越多,说明效率就越高,而费用成本也就越低。而且员工工

    2024年02月16日
    浏览(49)
  • 1.4W字!让我带你读懂springmvc的世界!

    目录 一.前提了解 1.tomcat和servlet的关系? 2.springmvc想要实现web开发必须满足的条件是什么? 二.什么是SpringMVC 三.基于SpringMVC创建web项目 ①创建项目并选择依赖  ②设置热部署(部分代码改动不需要手动重新run即可生效) 四.理解前后端分离的开发过程 五.SpringMVC实现web开发

    2024年02月06日
    浏览(63)
  • 图论:一文教你读懂常见的图遍历算法

    深度优先搜索(DFS): 从一个起始节点开始,访问该节点并将其标记为已访问。 递归地访问所有与当前节点直接相连且未被访问过的节点。 重复上述步骤,直到所有节点都被访问过或没有未访问的节点。 广度优先搜索(BFS): 从一个起始节点开始,将其放入队列中,并标

    2024年04月25日
    浏览(35)
  • 3D智能相机:带你读懂3D机器视觉如何实时扫描构建物体的3D模型

    作者:御剑飞行  3D智能相机是一种能够捕捉三维空间中物体形状和位置信息的相机。它可以在一个瞬间同时捕捉到物体的深度和颜色信息,并用这些数据创建一个三维模型。 3D智能相机工作方式示意图,图源@御剑飞行 3D智能相机能够捕获物体的更详细信息,包括其尺寸、形

    2024年04月09日
    浏览(56)
  • 一文带你如何用SpringBoot+RabbitMQ方式来收发消息

    预告了本篇的内容:利用RabbitTemplate和注解进行收发消息,还有一个我临时加上的内容:消息的序列化转换。 本篇会和SpringBoot做整合,采用自动配置的方式进行开发,我们只需要声明RabbitMQ地址就可以了,关于各种创建连接关闭连接的事都由Spring帮我们了~ 交给Spring帮我们管

    2024年02月09日
    浏览(41)
  • Matlab上位机——串口收发、接收转为图像实时显示、图像放大缩小等功能

    原例程代码可以进行串口接收发送,加载与存储数据 本人在例程代码的基础上添加了共三个功能 1.加载文件数据,三通道同时显示波形 2.放大缩小 3.均值滤波 4.将接收到的数据以固定位数转换为实时波形 布局 加载文件数据后(excel的csv格式) 滤波效果 实时波形采集当时忘了

    2024年02月02日
    浏览(48)
  • 数学建模 | 一文读懂:支持向量机(matlab源码)

    ===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 =====================================================

    2024年02月01日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包