python+嵌入式 |
您所在的位置:网站首页 › python读取电脑串口数据 › python+嵌入式 |
目录
前言安装pyserialpyserial大致概括整体流程硬件连接例子(简单版)
详细使用serial初始化参数发包收包收包
检查包并解包python struct模块结语
前言
这几年,自己也做了一些嵌入式机器人。在整个开发的过程中,调通信通常会花费一段比较长的时间,串口通信就是这样的一个部分。 而现在在百度上进行搜索,发现对python串口通信的博客讲解,都有点太笼统了,这其中,应该与python在处理硬件底层速度较慢导致用的人少有关系。 这里把python串口通信的部分进行一下个人使用过程中的总结。既是自我总结,也让未来开发更快。 文章参考官方文档: https://pyserial.readthedocs.io/ python进行串口通信,依赖的包就是pyserial,因此,本文是基于这个包进行总结。 总结过程中难免有理解不到位,或者错误的地方,欢迎大家提出问题并一起交流 安装pyserialpip安装 python -m pip install pyserial # 或者直接pip安装 pip install pyserialconda安装 conda install pyserial #or conda install -c conda-forge pyserial pyserial大致概括 整体流程对于串口通信,我们的大致流程如下: 硬件连接 存在 不存在 将串口设备与电脑连接 查看串口端口号 代码收发包 寻找原因并连接发包 导入pyserial库 打开串口并设置串口参数 发送数据 关闭 or 循环发送收包 导入pyserial库 打开串口并设置串口参数 python读取数据至缓存区 从缓存区读取数据 解包转换为自己需要的数据 关闭串口 or 循环读取 硬件连接首先需要将串口设备与电脑相连,并查看自己的串口的端口号是多少 串口的端口号就是将你的串口命名,让程序或者系统能够快捷的寻找 对于windows设备 打开设备管理器 -> 端口 -> 寻找你的设备号 对于Windows来说,串口设备端口号一般都是COMn,这里COM8使用CH340串口芯片来进行通信 对于Linux设备(这里以Ubuntu为例) ls /dev/ttyUSB*返回的就是你所有串口设备的设备号,如果你具有多个设备,则可以通过拔插之后,查看哪个设备增删来确定你的设备名 问题 针对Ubuntu22.04可能出现找不到设备端口号的问题,解决方法如下:Ubuntu22.04没有ttyUSB(无法访问 ‘/dev/ttyUSB‘: 没有那个文件或目录)问题解决 针对windows找不到端口,一般来说都是驱动问题,解决方法可参考下面博客笔记本W10找不到端口(com口)及单片机串口连接的问题(附51开发板的CH340串口芯片的驱动程序安装包) 例子(简单版)找到了设备的端口号之后,就可以使用python来进行通信了。 我贴一个csdn上搜python串口通信的的第一个博客的代码例子。 代码来源: https://blog.csdn.net/weixin_43217958/article/details/109782000 import serial#导入串口通信库 from time import sleep ser = serial.Serial() def port_open_recv():#对串口的参数进行配置 ser.port='com3' ser.baudrate=9600 ser.bytesize=8 ser.stopbits=1 ser.parity="N"#奇偶校验位 ser.open() if(ser.isOpen()): print("串口打开成功!") else: print("串口打开失败!") #isOpen()函数来查看串口的开闭状态 def port_close(): ser.close() if(ser.isOpen()): print("串口关闭失败!") else: print("串口关闭成功!") def send(send_data): if(ser.isOpen()): ser.write(send_data.encode('utf-8'))#编码 print("发送成功",send_data) else: print("发送失败!") if __name__ == '__main__': port_open_recv() while True: a=input("输入要发送的数据:") send(a) sleep(0.5)#起到一个延时的效果,这里如果不加上一个while True,程序执行一次就自动跳出了但是,这个例子,并不适合直接拿来到嵌入式中进一步开发,因为一些重要的参数和解包的方法并没有说明出来,并且在python中,涉及一些数据类型的转换也是比较复杂的(也是让我无数次抓狂的地方😡)。 详细使用针对串口通信,我们可以将其分为发包和收包。而想要比较方便的使用python来实现通信,我们需要使用到python一个非常强大的功能包struct,使用的讲解流程,我将按照官网参数文档介绍 - > 例子这样的顺序来解释。 serial初始化参数class serial.Serial init(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None, exclusive=None) 参数 port – 串口名字(COMn或者/dev/ttyUSB)或者Nonebaudrate (int) – 波特率,比如9600或者115200bytesize – 数据位数,可能的参数值有: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITSparity – 奇偶校验,可能的参数值: PARITY_NONE, PARITY_EVEN, PARITY_ODD ,PARITY_MARK, PARITY_SPACEstopbits – 停止位的比特数. 可能的参数值: STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWOtimeout (float) – 设置pyserial持续读取数据的最长时间(s)xonxoff (bool) – 是否启动软件流控制rtscts (bool) – 是否启动硬件(RTS/CTS)流控制dsrdtr (bool) – 是否启动硬件(DSR/DTR)流控制write_timeout (float) – 设置pyserial最长写入串口数据的时间(s)inter_byte_timeout (float) – 字符间超时, 没有则禁止(默认禁止).exclusive (bool) – 设置独占访问模式(仅POSIX)。 如果端口已经以独占访问模式打开,则不能以独占访问模式打开端口。异常退出 ValueError –如果一些参数不在允许参数内,则返回ValueErro,比如波特率设置SerialException – 如果设备无法被找到或者被设置,则返回SerialException说明 当我们初始化串口的时候,open()函数会被调用,串口就会被打开。 timeout参数会影响到read()函数的使用,这个timeout参数非常重要,直接影响到我们对串口数据的读取。 timeout = None: 一直等待,直到设置的接收字节数满后退出 timeout = 0: 非阻塞模式,在任何情况下都立即返回,返回零或更多,最多为请求的字节数 timeout = x:当请求的字节数可用时,将timeout设置为x秒(允许浮动)立即返回,否则等待超时到期,并返回在此之前收到的所有字节。 而对于wrtie()(发包函数)而言,默认为阻塞,除非设置了write_timeout。 针对硬件流控制而言,这个得观察嵌入式设备了,我之前使用stm32和python通信的时候使用过一次,得需要结合硬件连接和原理图说明,但是我并没有完全搞透,且其他时候用的也比较少,这里就不展开叙述了。 发包在嵌入式中,我们使用发包,一般是将我们的状态数据,或者是控制指令通过转码为符合设备的通信协议的格式后,将其发出。 因此,我们在编写发包函数前,需要先熟读通信协议,并理解我们需要发送什么样的指令,一般协议是16进制的一串数据。 pyserial中发包函数为write() write(data): 参数: 需要发送的数据 返回值: 写入的字节数 返回值类型: int 异常值返回 – 如果为端口配置了写入超时并且超过了时间。 将字节数据写入端口。这应该是字节类型(或兼容的,如bytearray,memoryview)。必须对Unicode字符串进行编码(例如“hello”.encode(“utf-8”)。 下面是参考实例 # Usart Library import serial import struct import binascii # Init serial port Usart = serial.Serial( port = '/dev/ttyUSB0', # 串口 baudrate=115200, # 波特率 timeout = 0.001 ) # 判断串口是否打开成功 if Usart.isOpen(): print("open success") else: print("open failed") # 使用优雅的方式发送串口数据 # 这里的数据可以根据你的需求进行修改 send_data = [0xA4,0x03,0x08,0x23,0xD2] #需要发送的串口包 send_data=struct.pack("%dB"%(len(send_data)),*send_data) #解析成16进制 print(send_data) Usart.write(send_data) #发送 收包针对收包,我们一般流程就是 收包 -> 检查包 -> 解包,先查看官网中收包的函数文档 read(size=1) 参数: size – 读取字节数 返回值: 串口读取得到的字节 返回值类型: bytes 从串行端口读取字节。如果设置了超时,则返回的字符数可能少于请求的字符数。如果没有超时,它将阻塞,直到读取请求的字节数。 read_until(expected=LF, size=None) 参数: expected – 预期需要的字节。 size – 读多少字节。返回值: 串口读取到的字节数 返回值类型: bytes 读取数据,直到找到预期的序列(默认为“\n”)、超过大小或超时。如果设置了超时,则返回的字符可能少于请求的字符。如果没有超时,它将阻塞,直到读取请求的字节数。 in_waiting() 获得input buffer中缓存字节数 返回值类型: int 收包我们首先需要从串口中读取缓存数据 # Usart Library import serial import struct import binascii # Init serial port Usart = serial.Serial( port = '/dev/ttyUSB0', # 串口 baudrate=115200, # 波特率 timeout = 0.001 ) # 由于后续使用read在未收全数据的时候,会按照一个timeout周期时间读取数据 # 波特率115200返回数据时间大概是1ms,9600下大概是10ms # 所以读取时间设置0.001s # 判断串口是否打开成功 if Usart.isOpen(): print("open success") else: print("open failed") # ----读取串口数据----------------------------------- try: count = serial.inWaiting() if count > 0: # 初始化数据 Read_buffer = [] # 接收数据至缓存区 Read_buffer=serial.read(40) # 我们需要读取的是40个寄存器数据,即40个字节 # Read_data() # 前面两行可以注释,换成后面这个函数 except KeyboardInterrupt: if serial != None: print("close serial port") serial.close() #--------------------------------------------------------这里如果没有在0.001s的时间内读到40字节的包,就会退出。因此我们需要结合通信协议来判断我们收到的包是否正确。 检查包并解包这里根据我之前使用的一款IMU(GY-95T)为例子,来演示用状态机判断包是否正确。 同时在函数中,我也将解包函数放入,后面再说明 def Read_data(self): ''' Author: Liu Yuxiang Time: 2022.12.13 description: 读取串口数据 ''' # 初始化数据 counter = 0 Recv_flag = 0 Read_buffer = [] # 接收数据至缓存区 Read_buffer=serial.read(40) # 我们需要读取的是40个寄存器数据,即40个字节 # 状态机判断收包数据是否准确 while(1): # 第1帧是否是帧头ID 0xA4 if (counter == 0): if(Read_buffer[0] != 0xA4): break # 第2帧是否是读功能码 0x03 elif (counter == 1): if(Read_buffer[1] != 0x03): counter=0 break # 第3帧判断起始帧 elif (counter == 2): if(Read_buffer[2] >> b'\x00\x9c@c'意思就是说,使用>代表的字节顺序即big-endian(网络序),来将后面的数据转化成I代表的数据类型(4字节无符号整型) (官网例子) >>>from struct import * >>>pack(">bhl", 1, 2, 3) b'\x01\x00\x02\x00\x00\x00\x03' >>>unpack('>bhl', b'\x01\x00\x02\x00\x00\x00\x03') (1, 2, 3) >>>calcsize('>bhl') 7这里和前面的例子是一个道理,使用>对齐的方式,来将后面三个数据依次打包成b,h,l所代表的数据,解包也是这样,将一串byte数据按照>对齐格式转换成b h l对对应的数据。 注意:可以使用b4这样的写法来代表有一个数组 计算所选择的数据格式有多少字节 struct.calcsize(format) Return the size of the struct (and hence of the bytes object produced by pack(format, ...)) corresponding to the format string format. 结语差不多这样就可以比较优雅的利用python接收和读取串口数据了。如有问题,欢迎评论区提出。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |