python串口提高实时性

问题描述

今天在设计python串口代码时遇见一个问题,接收到的数据打印出来,一直都是两三组连在一起,即我的目标数据是:
7e307d02087d01557e
但是我把接收到的数据经过处理打印出来后是:7e307d02087d01557e7e307d02087d01557e
或者打印结果是:7e307d02087d01557e7e307d02087d01557e7e307d02087d01557e
额。。。这显然不是我需要的结果了。

解决方式

先说下我的思路,首先引入getopt库,实现串口的串口号和波特率可以由命令行参数改变,这点说实话有些鸡肋。

test_param = {"com": "com1", "baudrate": "3000000"}

    # 尝试获取命令行参数,c->指定的com口,b->指定的波特率,s->secret_file,t->test_file
    try:
        options, argv = getopt.getopt(sys.argv[1:], "c:b:")
    except getopt.GetoptError:
        sys.exit()

    # 根据命令行的参数内容,调整字典中键值对应的value
    for option, value in options:
        if option in ("-c", "--comname"):
            test_param['com'] = value
            print("com: {0}".format(test_param['com']))

        if option in ("-b", "--baudrate"):
            test_param['baudrate'] = value
            print("baudate : {0}".format(test_param['baudrate']))

    uartmanage = TUartManage(test_param["com"], test_param['baudrate'])

其次是构建python的pyserial串口功能类TUartManage,主要是有两个参数,串口号和波特率。
在进行类的初始化时,主要是建立了一个**bytearray()**数组,因为这个串口程序最终是要服务于协议栈使用,所以根据需求决定的,但是影响说实话不大,因为python想要做一些类型、格式等等的改变真的是太容易了。

class TUartManage(object):
    def __init__(self, com, baudrate):
        self.__com = com
        self.__baudrate = baudrate
        self.__read_flag = False
        self.data_bytes = bytearray()
        self.__com_uart = serial.Serial(port=self.__com, baudrate=self.__baudrate, timeout=0.1)
        if self.__com_uart.is_open:
            print('open ' + self.__com + ' success !!!')

此外,self.__com_uart是建立的串口对象,在这里是打开串口一,并给定了一个波特率,波特率最后说给了多少。
看下下面的实现程序:

    def __read_data(self):
        '''
        在线程内循环接收数据,数据发送端选用的正点原子的串口调试工具,最大支持3000K的波特率,所以
        本程序实时性实测在波特率为3000ke时接收正常。
        :return: None
        :note 数据缓存在self.data_bytes中,可由外界直接调用获取
        '''
        while self.__com_uart.isOpen() and self.__read_flag:
            if self.__com_uart.in_waiting:
                self.data_bytes = self.__com_uart.read(self.__com_uart.in_waiting)
                if self.data_bytes[0] == 0x7e:
                    data_end = self.data_bytes.find(0x7e, 1)
                    self.data_bytes = self.data_bytes[0:data_end+1].hex()
                    self.__com_uart.reset_input_buffer()

    def com_start_read(self, rx_size=1*1024*1024):
        """
        设置接收buf的size,并创建一个线程,专门用来实时接收数据
        :param rx_size: 默认设置1M的接收缓存
        :return: None
        """
        self.__com_uart.set_buffer_size(rx_size=rx_size)
        self.__read_flag = True
        read_thread = threading.Thread(target=self.__read_data)
        read_thread.setDaemon(True) #守护进程,自动运行结束退出,或者主进程结束时,自动结束
        read_thread.start()

先来说下额外的知识点:

如果在一个类中,想要将一些参数、函数设置为私有属性,即类似于C语言的static变量、函数这样子的,只可以在本文件内使用,外界无法查看与调用,这时可以采用在首字母前加两个下划线的形式,在本文程序内均有涉及,关于私有属性的知识点,最近会整理下,感兴趣可以翻一下博客。

继续来说下上面的程序,如果想读取数据时可以调用com_start_read函数,可以通过此函数设置serial的接收缓存大小,默认为1M。此函数建立一个带守护属性的线程,即此线程退出方式有两种,一是函数运行完成,自然退出,但是考虑到串口读取函数一定是需要一直读取的,所以退出时一般是利用他的守护属性,即当主线程退出时,也会结束守护线程。

真正的串口读取函数其实是:__read_data函数,但是你也可以发现,他是一个具有私有属性的函数,所以外界调用时是看不见的。此函数是以:

while self.__com_uart.isOpen() and self.__read_flag:

来作为循环判断依据的,所以此函数正常退出时,会有两种方式,一是串口被关闭,条件不成立,会立即结束循环,二是将允许读串口标志位self.__read_flag变为False,这部分贴上来的代码上没有,因为我还没写,其实就是再建立一个com_stop_read函数,在里面将允许读串口标志位self.__read_flag变为False。
至于这么做的目的当时是为了适应协议在不同时刻需要设置不同的缓存size问题,还有就是移植性会比较好的。

最后再来说一下最初的问题,怎么提高串口的实时性,其实我当时犯的错误是,每次都从接收缓存内读取数据,但是一直没有对其进行清除,所以会带着上次的数据,造成实时性不好的假象,所以使用:

self.__com_uart.read(self.__com_uart.in_waiting)

来读串口数据时,每次读完一定要在最后对其进行清零,可别在最开始清零,不然你容易得到一个空的数据。
还有就是测试时,使用的是正点原子的XCOM串口工具,最高支持3000K波特率,所以本程序测试时给定的波特率为3000K,这实时性还不够好吗。
以前在校园时最常用的是9600和115200,目前看见的实用的也不过250K和500K,所以3000K够快啦。
代码还没有完善,没做com_stop_read函数,不过也是非常简单的,我把代码贴在最后,感兴趣可以自己看下,有好的建议也可以沟通交流。

额外的:

安装serial时,经常理所当然的使用:

pip install serial

但是运行时会发现提示serial中没有Serial,这是因为按的不对,真正的该执行的应该是:

pip install pyserial

祝君好运,python串口玩得愉快
代码如下:

# coding=utf-8
# @Time : 2020.12.19
# @Author : QiQiuyang
# @File : uart_manage.py
# @note : 实现python端串口多线程实时收发

import serial   #pip install pyserial
import sys

import getopt
import threading

class TUartManage(object):
    def __init__(self, com, baudrate):
        self.__com = com
        self.__baudrate = baudrate
        self.__read_flag = False
        self.data_bytes = bytearray()
        self.__com_uart = serial.Serial(port=self.__com, baudrate=self.__baudrate, timeout=0.1)
        if self.__com_uart.is_open:
            print('open ' + self.__com + ' success !!!')


    def __read_data(self):
        '''
        在线程内循环接收数据,数据发送端选用的正点原子的串口调试工具XCOM,最大支持3000K的波特率,所以
        本程序实时性实测在波特率为3000ke时接收正常。
        :return: None
        :note 数据缓存在self.data_bytes中,可由外界直接调用获取
        '''
        while self.__com_uart.isOpen() and self.__read_flag:
            if self.__com_uart.in_waiting:
                self.data_bytes = self.__com_uart.read(self.__com_uart.in_waiting)
                if self.data_bytes[0] == 0x7e:
                    data_end = self.data_bytes.find(0x7e, 1)
                    self.data_bytes = self.data_bytes[0:data_end+1].hex()
                    self.__com_uart.reset_input_buffer()

    def com_start_read(self, rx_size=1*1024*1024):
        """
        设置接收buf的size,并创建一个线程,专门用来实时接收数据
        :param rx_size: 默认设置1M的接收缓存
        :return: None
        """
        self.__com_uart.set_buffer_size(rx_size=rx_size)
        self.__read_flag = True
        read_thread = threading.Thread(target=self.__read_data)
        read_thread.setDaemon(True) #守护进程,自动运行结束退出,或者主进程结束时,自动结束
        read_thread.start()

    def com_stop_read(self):
        '''
        将允许读串口的标志位置False,这个操作会使read_data函数,自然结束读取数据,退出线程
        :return: None
        '''

        self.__read_flag = False




if __name__ == "__main__":
    # 字典
    test_param = {"com": "com1", "baudrate": "3000000"}

    # 尝试获取命令行参数,c->指定的com口,b->指定的波特率,s->secret_file,t->test_file
    try:
        options, argv = getopt.getopt(sys.argv[1:], "c:b:")
    except getopt.GetoptError:
        sys.exit()

    # 根据命令行的参数内容,调整字典中键值对应的value
    for option, value in options:
        if option in ("-c", "--comname"):
            test_param['com'] = value
            print("com: {0}".format(test_param['com']))

        if option in ("-b", "--baudrate"):
            test_param['baudrate'] = value
            print("baudate : {0}".format(test_param['baudrate']))

    uartmanage = TUartManage(test_param["com"], test_param['baudrate'])
    uartmanage.com_start_read()
    while True:
        print(uartmanage.data_bytes)






  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Python串口助手是一个用Python编写的工具,用于和串口进行通信。它通过串口连接电脑和其他设备,实现数据的收发和控制。 Python串口助手具有以下特点和功能: 1. 界面友好简洁:Python串口助手提供了直观的用户界面,可以轻松选择串口、波特率等参数,并显示串口通信的相关信息,使操作更加方便和直观。 2. 实时监测和显示:它可以实时监测串口数据的收发情况,并将收到的数据以十进制或十六进制的形式显示出来,方便用户进行数据的实时查看和分析。 3. 数据收发功能:Python串口助手可以实现串口数据的发送和接收,用户可以自定义发送的数据,并实时查看接收到的数据,以便进行数据的交互和处理。 4. 自动发送功能:它还支持自动发送功能,可以设定发送的时间间隔和发送的数据内容,达到自动化测试和数据采集的目的。 5. 数据记录和保存:Python串口助手可以将接收到和发送的数据保存为文件,方便用户进行数据的回放和分析,也可以方便地与其他软件进行数据交互。 6. 多平台支持:它可以在多种操作系统上运行,包括Windows、Linux和MacOS等,适用于不同的开发环境和应用场景。 7. 扩展性强:Python串口助手基于Python编写,具有丰富的库和模块,用户可以根据自己的需求进行功能的拓展和定制,更加灵活和强大。 总之,Python串口助手是一个功能强大、易于使用的工具,可用于串口通信和数据处理,帮助用户快速进行串口设备的开发和测试工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值