全网最适合入门的面向对象编程教程:04 类和对象的 Python 实现-为自定义类添加方法(PySerial 库接收串口数据)

摘要:

本文我们主要讲解了如何为自定义类添加方法,pyseria 库的基本使用(串口数据收发、serial.Serial 类的属性和方法),VSPDPro 虚拟串口软件使用方法等,并使用自定义的串口类和 PC 主机进行串口数据收发。

往期推荐:

学嵌入式的你,还不会面向对象??!

全网最适合入门的面向对象编程教程:00 面向对象设计方法导论

全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念

全网最适合入门的面向对象编程教程:02 类和对象的 Python 实现-使用 Python 创建类

全网最适合入门的面向对象编程教程:03 类和对象的 Python 实现-为自定义类添加属性

更多精彩内容可看:

给你的 Python 加加速:一文速通 Python 并行计算

一文搞懂 CM3 单片机调试原理

肝了半个月,嵌入式技术栈大汇总出炉

电子计算机类比赛的“武林秘籍”

一个MicroPython的开源项目集锦:awesome-micropython,包含各个方面的Micropython工具库

文档和代码获取

可访问如下链接进行对文档下载:

https://github.com/leezisheng/Doc

本文档主要介绍如何使用 Python 进行面向对象编程,需要读者对 Python 语法和单片机开发具有基本了解。相比其他讲解 Python 面向对象编程的博客或书籍而言,本文档更加详细、侧重于嵌入式上位机应用,以上位机和下位机的常见串口数据收发、数据处理、动态图绘制等为应用实例,同时使用 Sourcetrail 代码软件对代码进行可视化阅读便于读者理解。

相关示例代码获取链接如下:https://github.com/leezisheng/Python-OOP-Demo

正文

可以看到上一小节,我们为 SerialClass 类添加了串口设备名、波特率、数据位等串口相关的属性,但是注意到面向对象编程的重点在于不同对象之间的交互。我们感兴趣的是,触发某些行为可以使属性发生变化或与外界产生交互。在类中定义函数就相当于定义类的方法,回想一下我们在单片机上使用串口时往往进行打开串口、发送数据、接收数据和关闭串口等操作,在 PC 端串口操作与单片机上类似。这里我们先为类添加方法,具体实现先省略,代码如下:

class SerialClass:

    _# 注意:特殊方法“__init__”前后分别有两个下划线!!!_
def __init__(self,port,baudrate,bytesize,parity,stopbits):
self.devport = port
self.devbaudrate = baudrate
self.devbytesize = bytesize
self.devparity = parity
self.devstopbits = stopbits _# 打开串口_
def OpenSerial(self):
_# __TODO:__打开串口方法待完成_
pass _# 关闭串口_
def CloseSerial(self):
_# __TODO:__打开串口方法待完成_
pass _# 串口读取_
def ReadSerial(self):
_# __TODO:__串口读取方法待完成_
pass _# 串口写入_
def WriteSerial(self):
_# __TODO:__串口写入方法待完成_
pass

这时,我们可以利用 dir(obj)方法获得类的对象实例的所有属性和方法名,dir(obj)返回一个 list。我们使用 for 循环打印,代码如下:

_# serdev是SerialClass类的一个实例化对象_
for item in dir(serdev):
print(item)

可以看到,我们已经给串口类添加了具体方法,这里,你可能会问,那这些方法具体该怎么实现呢?难道说要我们调操作系统的驱动函数读取串口、造轮子对数据解析?当然不可能,Python 强大的第三方库什么都有,这不,它来了:

pyserial,一个实用的串口通信 python 库

pySerial 是 Python 中用于操作串口的第三方模块,它支持 Windows、Linux、OSX、BSD 等多个平台。如果要使用 pySerial 模块,首先必须保证 Python 版本高于 Python 2.7 或者 Python 3.4。另外,如果你是用的是 Windows 系统,那必须使用 Win7 及以上的版本。

pySerial 的安装很简单,只需要执行一条命令:pip install pyserial。安装完成后,只需要在 Python 代码中使用** import serial **语句导入该模块即可。

pySerial 中主要的类就是 class serial.Serial,官方文档说明如下:

常用参数含义如下:

参数名称 含义
port 定义设备名称
baudrate 波特率:波特率是指串口通信中每秒钟传输的符号数,单位是波特(baud)。它决定了数据传输的速度和效率。在串口通信中,收发双方必须使用相同的波特率才能正常通信。典型值为:9600, 19200, 38400, 57600, 115200。
bytesize 数据位:数据位是指在每个数据包中传输的实际数据位数。它表示每个数据包中携带的有效信息量。常见的数据位长度有 5 位、7 位和 8 位。典型值为: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS。
parity 设置校验位,奇偶校验是一种错误检测方式,用于检查数据传输过程中是否出现错误,可选择偶校验或奇校验。典型值为:PARITY_NONE, PARITY_EVEN, PARITY_ODD PARITY_MARK, PARITY_SPACE。
stopbits 停止位是指在数据包的末尾添加的一个额外的位,用于标识一个数据包的结束。停止位的长度可以是 1 位、1.5 位或 2 位。典型值为: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO。
timeout 设置读取超时值(以秒为单位):可选参数为-0、None、x。
write_timeout 设置写入超时值(以秒为单位):可选参数为-0、None、x。timeout = None:永远等待/直到收到请求的字节数。timeout = 0:非阻塞模式,任何情况下立即返回,返回零个或多个,最多可达请求的字节数。

serial.Serial 类常用方法如下:

方法名称 作用
serial.open() 打开串口
serial.close() 关闭串口
serial.isOpen() 判断串口是否打开
serial.write(data) 写串口数据 data
serial.read(size) 读串口 size 个数据
serial.flushInput() 清除输入缓冲区数据
serial.flushOutput() 中止当前输出并清除输出缓冲区数据
serial.inWaiting() 判断当前接收的数据
serial.readline() 读一行数据,以/n 结束,要是没有/n 就一直读,阻塞

接下来看我们的示例代码:

class SerialClass:
def __init__(self,devport,devbaudrate,devbytesize,devparity,devstopbits):
_# 直接传入serial.Serial()类_
self.dev = serial.Serial()
self.dev.port = devport
self.dev.baudrate = devbaudrate
self.dev.bytesize = devbytesize
self.dev.parity = devparity
self.dev.stopbits = devstopbits def OpenSerial(self):
self.dev.open() def CloseSerial(self):
self.dev.close() def ReadSerial(self):
_# 非阻塞方式按行读取_
data = self.dev.readline()
_# 收到为二进制数据,用utf-8编码将二进制数据解码为unicode字符串_
_# 字符串转为int类型_
data = int(data.decode('utf-8', 'replace'))
return data def WriteSerial(self,write_data):
_# 非阻塞方式写入_
self.dev.write(write_data.encode())
_# write的输入参数必须是bytes 格式,_
_# 字符串数据需要encode()函数将其编码为二进制数据_
_# \r\n表示换行回车_
self.dev.write('\r\n'.encode())

这里可以看到,为方便讲解,我们直接调用 pyserial 库中函数完成串口的打开关闭和读写等功能:

  • 在初始化函数中,我们定义 self.dev 属性为串口设备,用其接收串口对象,相当于 self.dev 就是 serial.Serial 类的实例对象,其后用其他入参给 self.dev 设置串口通信参数,完成串口设备对象的创建;
  • 在打开串口和关闭串口函数中,我们使用 self.dev.open()、self.dev.close()语句,相当于直接调用 serial.open()和 serial.close()方法;
  • 重点就在收发函数中,需要特别注意的有两点:

① 串口接收到的是二进制数据,如果接收到的 data 全是英文,就需要用 utf-8 编码将二进制数据解码为 unicode 字符串。如果 data 里包含中文,则最好以 gb18030 编码将二进制数据解码为 unicode 字符串。同时,有时候由于带中文,或者由于串口的传输线缆出现接触不良等原因,会产生错误或者乱码,如果直接解码,就会报错,为了能够顺畅的解码串口打印,避免这种情况发送,decode 的参数里加上“replace”即可。它实现的作用是,如果解码的过程中遇到错误,会自动以问号?代替解码失败的字符。

② 在串口发送中,pyserial 的文档注明了,write 的输入参数必须是 bytes 格式的(也就是二进制数据),python3 里对字符串和二进制数据流有明确的区分,文本总是 unicode 编码储存的,由 str 类型表示。二进制数据则由 bytes 类型表示,所以字符串数据需要 encode()函数将其编码为二进制数据,然后才可以顺利发送

串口类的方法已然写好,接下来如果进行测试呢?我们需要买一个单片机和 usb 转 ttl 模块连接电脑,进行串口类的方法测试?(当然不是,开个玩笑)

在写与单片机通信的上位机软件时,如果使用单片机的串口来实际调试,那么我们至少还需要一个 USB 转串口,这样才能让单片机和电脑串口通讯,接着我们还需要在单片机上运行程序和串口相关的程序,以便我们知道数据传输的状态,这无疑加大的开发的难度。这里,我们使用虚拟串口软件即可,虚拟串口软件是一种模拟物理串行接口的软件,它完全复制了硬件 COM 接口的功能,并且将被操作系统和串行应用程序识别为真实端口。现实生活中,虚拟串口用处很多。比如:你的应用程序检测串行输入数据的时候,方便调试。还比如:多个有应用程序之间使用串口通信。

这里我们使用 VSPDProv6.9 软件,创建虚拟串口,软件下载安装教程点击链接:https://www.xue51.com/soft/9349.html

这里我们创建两个相互连接的串口 com11 和 com17,虚拟的串口需要成对创建,来指明他们的连接关系,就好比各种连接线的公投和母头一样。我们建立好虚拟串口连接以后,就可以使用它们来通讯了,我们可以选择任意一个串口助手软件来与我们写的 python 串口类进行通信。这里,我们选择 xcom 软件进行测试,xcom 软件的安装下载教程可以点击链接: https://blog.csdn.net/qq_41573860/article/details/103796913

打开 xcom 软件,我们先进行相关串口参数的配置,具体如下图所示:

此时,我们在 Python 程序中,创建串口设备实例,连接 com17,代码如下:

_# 生成串口类的实例_
serdev = SerialClass(devport = "COM17",
devbaudrate = 115200,
devbytesize = serial.EIGHTBITS, _# 数据位长度为8位_
devparity = serial.PARITY_NONE, _# 无奇偶校验_
devstopbits = serial.STOPBITS_ONE _# 1位停止位_
)

我们首先尝试使用 Python 完成串口发送功能,这里我们定义了一个 count 计数变量,初始值为 0,每次发送完成后递增,循环 100 次后关闭串口,代码如下:

serdev.OpenSerial()
count = 0
while True:
# 必须使用全局变量,不然每次循环都是一个新的count
# 关于原因可以自行查看变量的生存期和作用域相关知识点
count = count + 1
serdev.WriteSerial(str(count))
if(count == 100):
break
serdev.CloseSerial()
print("device close")

运行结果如下:

接下来,我们尝试使用 Python 完成串口接收功能,我们在 xcom 中定时发送,在 Python 中轮询接收并打印接收的数据,这里我们导入 time 库,打印接收时间。time 库是 Python 中处理时间的标准库,是最基础的时间处理库。time 库主要用于计时和获取系统时间,time.ctime()函数用于获取当前世界统一时间,形式为“星期-月份-当月号-时-分-秒-年份”

示例代码和配置如下:

import time
while True:
data = serdev.ReadSerial()
print("serdev recieve %d"%data)
print("recieve time:"+str(time.ctime()))

运行结果如下:

全网最适合入门的面向对象编程教程:04 类和对象的 Python 实现-为自定义类添加方法(PySerial 库接收串口数据)的更多相关文章

  1. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  2. 最适合入门的Laravel中级教程(一)

    Laravel 是一个全栈框架: 我们使用 Laravel 开发业务常见有 3 个方向: 前端页面和后端逻辑混合的应用 主要是面向对 SEO 有需求的项目: 比如说新闻资讯博客文章等: 一般在控制器中 ...

  3. Python入门之面向对象编程(一)面向对象概念及优点

    概念 谈到面向对象,很多程序员会抛出三个词:封装.继承和多态:或者说抽象.一切都是对象之类的话,然而这会让初学者更加疑惑.下面我想通过一个小例子来说明一下 面向对象一般是和面向过程做对比的,下面是一个 ...

  4. JavaScript基础入门12 - 面向对象编程

    目录 JavaScript 面向对象编程 前言 构造函数创建对象 instanceof constructor 返回值 原型对象 关于对象的属性查找 in hasOwnProperty() JS当中实 ...

  5. [Java入门笔记] 面向对象编程基础(一):类和对象

    什么是面向对象编程? 我们先来看看几个概念: 面向过程程序设计 面向过程,是根据事情发展的步骤,按进行的顺序过程划分,面向过程其实是最为实际的一种思考方式,可以说面向过程是一种基础的方法,它考虑的是实 ...

  6. Python入门之面向对象编程(四)Python描述器详解

    本文分为如下部分 引言——用@property批量使用的例子来引出描述器的功能 描述器的基本理论及简单实例 描述器的调用机制 描述器的细节 实例方法.静态方法和类方法的描述器原理 property装饰 ...

  7. 一.OC基础之:1,OC语言的前世今生 ,2,OC语言入门,3,OC语言与C的差异,4,面向对象,5,类和对象的抽象关系,6,类的代码创建,7,类的成员组成及访问

    1,OC语言的前世今生 , 一, 在20世纪80年代早期,布莱德.麦克(Brad Cox)设计了OC语言,它在C语言的基础上增加了一层,这意味着对C进行了扩展,从而创造出一门新的程序设计语言,支持对象 ...

  8. PythonI/O进阶学习笔记_3.2面向对象编程_python的继承(多继承/super/MRO/抽象基类/mixin模式)

    前言: 本篇相关内容分为3篇多态.继承.封装,这篇为第二篇 继承. 本篇内容围绕 python基础教程这段: 在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法.使 ...

  9. JavaScript中的面向对象编程,详解原型对象及prototype,constructor,proto,内含面向对象编程详细案例(烟花案例)

    面向对象编程:   面向:以什么为主,基于什么模式 对象:由键值对组成,可以用来描述事物,存储数据的一种数据格式 编程:使用代码解决需求   面向过程编程:         按照我们分析好的步骤,按步 ...

  10. 1.面向过程编程 2.面向对象编程 3.类和对象 4.python 创建类和对象 如何使用对象 5.属性的查找顺序 6.初始化函数 7.绑定方法 与非绑定方法

    1.面向过程编程 面向过程:一种编程思想在编写代码时 要时刻想着过程这个两个字过程指的是什么? 解决问题的步骤 流程,即第一步干什么 第二步干什么,其目的是将一个复杂的问题,拆分为若干的小的问题,按照 ...

随机推荐

  1. 『手撕Vue-CLI』编码规范检查

    前言 这篇为什么是编码规范检查呢?因为这是一个很重要的环节,一个好的编码规范可以让代码更加清晰易读,在官方的 VUE-CLI 也是有着很好的编码规范的,所以我也要加入这个环节. 其实不管在哪个项目中, ...

  2. python连接redis,mongodb以及简单命令使用

    redis 环境如下: [root@mcw01 ~/msRedis]$ ps -ef|grep -v grep|grep redis root 46061 1 0 14:28 ? 00:00:45 r ...

  3. 【题解】A18537.我心中珍藏的游戏

    题目跳转 思路: 题目问最多可以获得的额外伤害,其实就是询问在这些技能中,如何怎样选取一个最优的发动技能顺序使得攻击加成最大.我们可以把每一个技能看作成一个图的顶点,把每一个攻击加成看作图的边,权制为 ...

  4. 高分辨率食道测压(HRM)

    高分辨率测压(High resolution Manometry) HRM的优势 高分辨率食管测压不但实现了从咽部到胃部的全程功能监测,而且插管无需牵拉,操作十分方便.更为重要的是,临床医生经过简单的 ...

  5. 【前端】css js 全屏 esc退出全屏 滚动条隐藏 兼容火狐,文字超出容器长度省略号显示

    全屏 if (docElm.requestFullscreen) { docElm.requestFullscreen(); } else if (docElm.msRequestFullscreen ...

  6. 慢查询SQL优化

    记一次慢查询的SQL优化 测试表结构 MariaDB [shoppings]> desc login_userinfo; +------------+-------------+------+- ...

  7. 如何启动?win11下的Linux子系统【4种方法】

    实验室的开发环境在Linux操作系统下,时不时就需要打开Linux环境去操作,而且需要本地编译或者远程SSH.这时候window和Linux切换很不方便.本科的做法就是window+虚拟机的Linux ...

  8. .NET桌面程序混合开发之二:在原生WinFrom程序中使用WebView2

    本文将介绍如何在WinForms中嵌入WebView2,并讲到WebView2的主要特征.点击了解更多WebView2的API. 1. 准备 Visual Studio 2017 及以上版本 WebV ...

  9. linux下基于官方源码编译ipopt

    linux下基于官方源码编译ipopt 1.C++依赖项安装升级 由于需要编译c++所以需要安装一系列的依赖: apt-get update apt-get -y upgrade apt instal ...

  10. OpenCV笔记(5) Rect类

    看项目代码时,发现了Rect的神奇用法,rect = rect + point.于是了解了一下Rect类. 1. 构造函数 public Rect(Point location, Size size) ...