宝子们!有没有想过,开发板和 PC、传感器之间是怎么 “对话” 的?其实靠的就是串口通信(UART) 这个 “聊天工具”!今天就带大家吃透 QuecPython 的串口通信 —— 从原理到实操,连小白都能跟着做,最后还能实现 “PC 发指令,LED 亮灭” 的小 demo,超有成就感~
一、先搞懂:UART 是怎么让设备 “聊天” 的?
咱们先把专业术语 “翻译” 成大白话,毕竟原理懂了,后面实操才不慌~
1.1 异步通信:不用 “统一闹钟” 也能聊
UART 的核心是 “异步”—— 就像两个人聊天,不用盯着同一个时钟说话,靠 “开场白” 和 “结束语” 判断一句话的开始和结束:
**● 空闲态:**没说话时,线路是高电平(相当于 “沉默”);
● 起始位:要说话了,先发 1 个低电平(“喂,我要讲了!”);
**● 数据位:**接着发 5-9 位数据(比如 “hello” 的二进制),注意是从最低位(LSB)开始传;
**● 校验位:**可选 0 或 1 位(“查错小助手”,比如奇偶校验,确保数据没传错);
**● 停止位:**说完了,发 1-2 位高电平(“好,这句话结束啦”)。
还有个小知识点:双方 “语速”(波特率)可以有误差,但不能超过 10%!常用的 “语速” 有 4800、9600、115200bps(115200 最常用,传得快~)。
1.2 硬件架构:设备 “聊天” 的 “内部部门”
就像公司有收发快递的部门,UART 也有专门的 “组件分工”,保证数据不丢、不错:
发送端(发消息的设备)
● **发送保持寄存器:**临时存 CPU 要发的数据(比如 “快递暂存台”);
**● 移位寄存器:**把 CPU 给的 “并行数据”(一堆数据一起)转成 “串行数据”(一个一个按顺序发),相当于 “打包成一串”;
**● 波特率发生器:**生成精准的 “时钟”,比如 115200bps 对应每个 bit 的时间约 8.68μs(确保 “语速” 稳定)。
接收端(收消息的设备)
**● 施密特触发器:**过滤线路上的 “噪音”(比如电压波动),确保读对电平;
**● FIFO 缓冲区:**硬件 FIFO 有 128 字节,软件 FIFO 有 4KB(相当于 “快递柜”,暂时存数据,避免 CPU 忙不过来导致数据丢失);
**● 中断控制器:**数据到了(RX_ARRIVED)、柜子满了(RX_OVERFLOW)都会 “喊 CPU”,不用 CPU 一直盯着。
1.3 数据传输的 “小细节”:别踩电压坑!
● 电平匹配:模块默认是 1.8V TTL 电平(0V = 低,1.8V = 高),移芯平台能切 3.3V。要是接 3.3V/5V 设备(比如 Arduino),必须加电平转换器,不然会烧模块!
**● 驱动能力:**单端输出电流≤8mA,线长超过 10 米的话,记得外接 “驱动模块”,不然信号会变弱;
**● 灵活性:**数据位(5-8 位)、停止位(1-2 位)、校验位都能调,比如传 ASCII 字符常用 8 位数据位 + 1 位停止位 + 无校验。
二、让 “聊天” 更高效:FIFO、DMA 和流控
设备聊得频繁了,容易 “堵单”(数据丢了)或 “占着 CPU 不放手”,这时候就得靠这三个 “神器”!
2.1 FIFO:数据的 “临时仓库”
FIFO 就像快递柜,分 “硬件小柜” 和 “软件大仓”:
**● 硬件 FIFO:**收发各 128 字节,先填满小柜,满了再往软件 FIFO(4KB)里放,避免数据 “堆门口”;
**● 接收逻辑:**用 DMA(后面说)搬数据,每满 64 字节触发一次中断(不用 CPU 每次接 1 个字节,省力气);
**● 溢出处理:**软件 FIFO 也满了?会触发 RX_OVERFLOW 中断,赶紧处理旧数据!
2.2 DMA:“自动快递员”,解放 CPU
没有 DMA 时,CPU 得亲自 “搬数据”(比如从 UART 往内存搬),占满 100% 算力;有了 DMA,相当于雇了 “自动快递员”:
1.UART 喊 “要搬数据啦(DMA 请求)”;
2.DMA 控制器接手,直接读写内存,不用 CPU 插手;
3.搬完了再通知 CPU。
效果超明显:传输速度快 3-5 倍,CPU 占用率直接降到 5% 以下,再也不用 “专职搬快递” 了!
2.3 硬件流控(RTS/CTS):避免 “强行塞件”
当设备 A 发得太快,设备 B 的 FIFO 满了,怎么办?靠 RTS/CTS “喊停”:
**● RTS(请求发送):**设备 A 的 RTS 变高 → “我柜子满了,别发了!”;
**● CTS(清除发送):**设备 B 看到 A 的 RTS 高 → 暂停发送,等 A 的 RTS 变低再继续。
**接线要注意:**交叉接!A 的 RTS 连 B 的 CTS,B 的 RTS 连 A 的 CTS,接反了就 “喊不停” 啦~
三、动手实操:用串口控制 LED 亮灭!
光说不练假把式,咱们用 EC800M 开发板做个小 demo:模组通过 UART2 和 PC 聊天,PC 发 “on” LED 亮,发 “off” LED 灭,超简单!
3.1 先搭好 “聊天通道”:参数配置
要创建串口对象,得先确定 “聊天参数”,用machine.UART类:
from machine import UART
class machine.UART(UARTn, baudrate, databits, parity, stopbits, flowctl)
构造串口对象:UART2,波特率115200,8数据位,无校验,1停止位,无流控
uart = UART(UART.UART2, 115200, 8, 0, 1, 0)
硬件引脚映射
可参考官网说明:
勾选右边型号选择,就可以查看到对应型号的串口标识,需要注意的是,关于串口的引脚选择,一致上都以官网为准,而不参考数据手册,目前quecpython并没有专门的硬件设计手册,由于QuecPython和QuecOpen存在一定差异,例如有哪些引脚可以作为GPIO不能直接参考QuecOpen的文档,包括串口也是同理,其他部分和open保持一致。
3.2 核心方法:收发消息
常用的串口方法,记这几个就够了:
**1.uart.any():**查接收缓冲区有多少未读字节(比如返回 20→有 20 字节没读);
**2.uart.read(nbytes):**读n字节数据,返回 bytes 类型(比如uart.read(5)读 5 字节,没数据返回 None);
**3.uart.write(data):**发数据,要传 bytes(字符串转一下:“hello”.encode()),返回成功发送的字节数;
**4.uart.set_callback(fun):**设置回调函数,收到数据自动触发(不用老盯着缓冲区)。
3.3 完整代码:控制 LED 的 “聊天脚本”
import utime
import log
from machine import UART
import pm
import sys_bus
utime.sleep(5)
import _thread
pm.autosleep(1)
from machine import Pin
gpio1 = Pin(Pin.GPIO21, Pin.OUT, Pin.PULL_DISABLE, 0)
# 设置日志输出级别
log.basicConfig(level=log.INFO)
uart_log = log.getLogger("UART")
def callback_A(topic, msg):
global gpio1
print("topic = {} msg = {}".format(topic, msg))
if(msg=="on"):
gpio1.write(1)
print("on")
elif(msg=='off'):
gpio1.write(0)
print("off")
def thread_entry_A(id):
sys_bus.subscribe("sysbus/thread_A", callback_A)
while True:
utime.sleep(5)
class Example_uart(object):
def __init__(self, no=UART.UART2, bate=115200, data_bits=8, parity=0, stop_bits=1, flow_control=0):
self.uart = UART(no, bate, data_bits, parity, stop_bits, flow_control)
self.uart.set_callback(self.callback)
# self.uart.control_485(UART.GPIO24, 1)
def callback(self, para):
# uart_log.info("call para:{}".format(para))
# # print("the any is",self.uart.any())
# print("the time is:",utime.ticks_diff(utime.ticks_ms(), start))
# start = utime.ticks_ms()
if(0 == para[0]):
s=self.uartRead(para[2])
# with open('usr/a.txt','a+') as f:
# f.write(s)
def uartWrite(self, msg):
uart_log.info("write msg:{}".format(msg))
self.uart.write(msg)
def uartRead(self, len):
msg = self.uart.read(len)
utf8_msg = msg.decode()
sys_bus.publish_sync("sysbus/thread_A", utf8_msg)
uart_log.info("UartRead msg: {}".format(utf8_msg))
return utf8_msg
def uartWrite_test(self):
for i in range(10):
write_msg = "Hello count={}".format(i)
self.uartWrite(write_msg)
utime.sleep(1)
if __name__ == "__main__":
_thread.start_new_thread(thread_entry_A, ('A',))
uart_test = Example_uart()
# start = utime.ticks_ms()
uart_test.uartWrite_test()
3.4 运行效果:眼见为实!
**1.模组发消息给 PC:**打开串口工具(比如 QCOM),选对 COM 口和 115200 波特率,会看到模组发 “Hello count=0” 到 “count=9”(图 1);
**2.PC 发 “on”→LED 亮:**在串口工具输入 “on” 发送,模组收到后,GPIO21 变高,D3 灯亮起(图 3),日志里会显示 “收到消息:on”;
**3.PC 发 “off”→LED 灭:**输入 “off” 发送,GPIO21 变低,D3 灯熄灭(图 5),日志同步更新。
四、避坑指南:别让这些问题卡你进度!
调串口时最容易踩坑,宝子们记好这些 “排坑技巧”:
4.1 硬件连接:别接错、别烧板!
**● 电平不匹配:**1.8V 模块接 3.3V 设备,一定要加电平转换器!直接接可能会烧模块的 UART 引脚;
**● 接线反了:**TX 接 TX、RX 接 RX→肯定聊不上!要交叉接:模组 TX 接 PC(USB 转 TTL)RX,模组 RX 接 PC TX;
**● 长距离干扰:**线超过 10 米,用 RS485 总线,TX/RX 线远离电源线(比如 220V 线),不然会有乱码。
4.2 软件调试:常见问题怎么解?
4.3 多线程注意:别 “抢快递”!
如果多个线程同时读 / 写串口(比如一个线程发数据,一个线程读数据),会导致数据混乱!记得加互斥锁(参考之前的多线程攻略),保证同一时间只有一个线程操作串口。
赶紧动手试试吧!










