Python串口通信详解:从基础到高级

说白了,Python的串口通信其实很简单,但门道可不少。
首先,得确保你安装了pyserial模块,这个模块是进行串口通信的基石。
去年我们跑的那个项目,大概3 000量级,全靠这个模块才搞定了与硬件设备的通信。

展开来说,先说最重要的,打开和关闭串口。
比如,ser = serial.Serial('COM1 ', 9 6 00)这行代码就打开了名为COM1 的串口,波特率为9 6 00。
然后,通过ser.write(b'Hello,Serial!')可以写入数据,而data = ser.readline()则是读取数据。
其实,这个过程听起来简单,但有个细节挺关键的,就是波特率,这决定了数据传输的速度。

我一开始也以为只要配置好波特率就行,后来发现不对,还得考虑校验位和停止位。
比如,ser.parity = serial.PARITY_ODD设置了奇校验位,ser.stopbits = serial.STOPBITS_TWO设置了两个停止位。

异步读写操作是提高效率的关键。
比如,你可以用threading模块来异步读取数据,这样主线程就不会被阻塞了。
等等,还有个事,就是使用上下文管理器可以自动关闭串口,这样就不用担心忘记关闭它了。

错误处理与异常也是必不可少的。
比如,如果串口打开失败,serial.SerialException会抛出异常。
这个点很多人没注意,但很重要。

再比如,与Arduino通信时,你可能需要解析数据。
这里有个例子,parse_serial_data(data)函数可以解析从串口接收的数据。

可视化串口数据是个好方法,可以帮助你实时监控数据流。
用matplotlib库可以轻松实现,就像plot_serial_data(data)函数那样。

最后,多线程和队列的应用可以让串口通信与其他任务并行执行,提高了程序的灵活性和实时性。
我觉得值得试试,尤其是当你需要同时处理多个任务时。

总的来说,Python串口通信虽然简单,但要玩转它还是需要掌握一些关键点。
记得,先从基础做起,慢慢深入,最后别忘记测试和异常处理。

Python线程和进程

行吧,这事儿得说说。
Python里头,线程和进程确实不一样。

线程,说白了,就是一个进程里头跑的小任务。
一个进程能开好几个线程。
它们共享同一个内存空间,这个挺重要的。
所以,线程之间说话、传数据就方便多了,不用像进程那样绕弯子。

特点呢,有几个: 1 . 轻便。
线程创建、切换比进程快,为啥?因为它们用一套内存,不用重新分地盘。
2 . 共享资源。
线程能直接用进程里的全局变量、数据啥的,不用拷贝。
3 . 有个坑,叫GIL。
在CPython(就是常见的Python那种)里,这个全局解释器锁,让同一时间只能有 个线程跑Python代码。
所以,想同时用多核CPU干CPU密集活儿,多线程帮不上大忙。

用在哪? 1 . I/O密集型。
比如你跑网络请求、读写文件啥的。
等你等网络、等硬盘的时候,别的线程能跑起来,效率就上去了。
2 . 频繁通信。
因为共享内存,线程间传个消息很简单,不用搞那些复杂的进程间通信(IPC)玩意儿。

代码例子,就简单两个线程,打印点东西,还睡会儿: python import threading import time
def my_function(name, sleep_time): print(f"线程{name}开始") time.sleep(sleep_time) print(f"线程{name}结束")
thread1 = threading.Thread(target=my_function, args=("A", 2 )) thread2 = threading.Thread(target=my_function, args=("B", 3 )) thread1 .start() thread2 .start() thread1 .join() thread2 .join() print("所有线程已完成")
你看,开两个线程,一个睡2 秒,一个睡3 秒。

再说说进程。
进程就是一个正在跑的程序实例。
每个进程有自己独立的内存空间,系统资源也是独立的。

特点: 1 . 独立。
进程之间内存不共享,想通信得用进程间通信(IPC),比线程复杂点。
2 . 资源开销大。
开进程、关进程比开线程费劲,得重新分内存、搞资源啥的。
3 . 跳过GIL。
每个进程有自己的Python解释器、内存,所以不受GIL限制,能真·利用多核CPU跑并行计算。

用在哪? 1 . CPU密集型。
比如你搞数学运算、数据处理啥的。
多进程可以利用多核CPU,性能提升明显。
2 . 需要隔离。
任务之间得完全隔开,用多进程挺好。

代码例子,用进程池搞几个数字的平方: python import multiprocessing
def square(number): return number number
def calculate_squares(numbers): pool = multiprocessing.Pool(processes=multiprocessing.cpu_count()) result = pool.map(square, numbers) pool.close() pool.join() return result
if __name__ == "__main__": numbers = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] squares = calculate_squares(numbers) print("数字的平方:", squares)
你看,利用进程池,每个数字跑一个进程,算平方。

然后是线程同步。
多线程一起跑的时候,得保证它们按规矩办事,不然数据乱套、出问题。

方法有: 1 . 锁(Lock)。
最简单的,保护个共享资源,谁用谁先拿,用完了放回去。
2 . 条件变量(Condition)。
线程等条件满足再跑。
3 . 信号量(Semaphore)。
控制几个线程能同时用共享资源。
4 . 事件(Event)。
线程间通信、同步用。

锁的例子: python import threading lock = threading.Lock() shared_resource = 0
def increment(): global shared_resource for _ in range(1 00000): lock.acquire() try: shared_resource += 1 finally: lock.release()
def decrement(): global shared_resource for _ in range(1 00000): lock.acquire() try: shared_resource -= 1 finally: lock.release()
t1 = threading.Thread(target=increment) t2 = threading.Thread(target=decrement) t1 .start() t2 .start() t1 .join() t2 .join() print(f"Final value of shared resource: {shared_resource}")
你看,两个线程对一个变量加1 、减1 ,用锁保证同时只能一个跑,最后结果理论上还是0。

总结一下吧:
线程适合I/O密集、频繁通信的任务,但受GIL限制,CPU密集活儿跑不快。

进程适合CPU密集、需要隔离的任务,能绕过GIL,真并行。

线程同步很重要,保证多线程不乱跑,数据不出错。