4、K210串口外设:UART控制器、引脚映射、寄存器配置及MaixPy中串口API的使用
K210作为一款面向AIoT应用的RISC-V双核芯片,其串口(UART)外设是连接AI视觉识别结果与外部单片机控制系统(如STM32、ESP32、Arduino)的核心桥梁。在“AI识别结果发送到单片机控制系统”这一课程主题下,理解K210的UART硬件架构、引脚映射规则以及MaixPy软件层面的API封装,是完成数据链路打通的关键前提。本章将从硬件控制器原理出发,逐步深入到寄存器配置逻辑,最后落脚于MaixPy的实用编程接口,帮助读者建立从底层寄存器到上层应用的完整知识体系。
上图展示了K210串口通信的三层架构。硬件层提供了物理基础,寄存器层是软件与硬件交互的接口,而MaixPy API则是对寄存器操作的封装与简化。在实际开发中,我们通常直接使用MaixPy的API,但理解底层寄存器有助于排查复杂问题,例如波特率误差、FIFO溢出或中断丢失等。
4.1 K210 UART控制器核心原理
K210的UART控制器是一个完整的硬件通信外设,支持全双工异步串行通信。与许多MCU不同,K210的UART在设计上具有以下显著特点:
- 多实例支持:K210内部集成了3个独立的UART控制器(UART0、UART1、UART2),每个控制器均可独立配置波特率、数据格式和中断。这意味着可以同时与多个外部设备通信,例如一个串口连接调试器,另一个串口连接单片机控制系统。
- FPIOA灵活引脚映射:这是K210最具特色的功能之一。芯片内部有一个功能可编程IO阵列(FPIOA),允许将任意GPIO引脚配置为UART的TX或RX功能。这极大地方便了PCB布局和系统集成,不再受限于固定的引脚复用表。
- 硬件FIFO缓冲:每个UART控制器内置了32字节的发送FIFO和32字节的接收FIFO。FIFO的存在减少了CPU中断频率,允许CPU批量处理数据,提升了通信效率。当FIFO半满或超时时,可触发中断通知CPU取走数据。
- 可编程中断:支持发送完成中断、接收数据可用中断、接收超时中断、奇偶校验错误中断等多种中断源。通过合理配置中断,可以实现高效的异步通信。
在AI识别场景中,K210通常作为协处理器,将识别到的目标类别、坐标、置信度等信息通过UART发送给主控单片机。由于AI推理速度通常在几十毫秒到几百毫秒不等,而串口波特率一般设置为115200或更高,因此UART的硬件FIFO和中断机制能够确保数据不丢失,即使CPU正在忙于神经网络计算。
4.2 引脚映射与FPIOA配置
K210的FPIOA(Field Programmable IO Array)是理解引脚映射的关键。芯片共有48个GPIO引脚,每个引脚都可以通过软件配置为多种功能之一。对于UART而言,我们需要将某个GPIO配置为UART_TX或UART_RX功能。
在MaixPy中,引脚映射通常通过fm.register()函数完成。该函数接受引脚编号、功能名称和复用配置三个参数。以下是一个典型的引脚映射示例:
from Maix import GPIO, fm
from machine import UART
# 将GPIO10配置为UART2的TX引脚
fm.register(10, fm.fpioa.UART2_TX)
# 将GPIO11配置为UART2的RX引脚
fm.register(11, fm.fpioa.UART2_RX)
# 创建UART2对象,波特率115200
uart = UART(UART.UART2, 115200, 8, None, 1, timeout=1000)
需要注意的是,fm.fpioa.UART2_TX和fm.fpioa.UART2_RX是MaixPy预定义的功能枚举值。每个UART控制器都有对应的TX和RX功能编号。下表列出了常用的UART引脚映射功能:
| UART实例 | 功能枚举(TX) | 功能枚举(RX) | 推荐GPIO | 备注 |
|---|---|---|---|---|
| UART0 | UART0_TX | UART0_RX | GPIO4/GPIO5 | 通常用于调试串口 |
| UART1 | UART1_TX | UART1_RX | GPIO6/GPIO7 | 可连接蓝牙模块 |
| UART2 | UART2_TX | UART2_RX | GPIO10/GPIO11 | 推荐用于与单片机通信 |
在硬件连接时,务必注意交叉连接:K210的TX应连接外部设备的RX,K210的RX应连接外部设备的TX,同时共地。如果使用开发板,请查阅原理图确认默认的UART引脚是否已被其他外设占用。
4.3 寄存器配置与波特率计算
虽然MaixPy封装了寄存器操作,但理解寄存器配置有助于深入诊断通信问题。K210的UART寄存器基地址如下:UART0位于0x38000000,UART1位于0x38003000,UART2位于0x38006000。每个控制器内部包含多个寄存器,其中关键的寄存器包括:
- UART_CTRL (偏移0x00):控制寄存器,用于使能UART、设置数据位长度(5/6/7/8位)、停止位数量(1/2位)、校验模式(无/奇/偶校验)。
- UART_CLK_DIV (偏移0x04):时钟分频寄存器,决定波特率。计算公式为:
波特率 = 时钟频率 / (分频系数 × 16)。K210的UART时钟源通常为26MHz(APB时钟),因此分频系数 = 26000000 / (波特率 × 16)。例如,要获得115200波特率,分频系数 = 26000000 / (115200 × 16) ≈ 14.1,取整为14,实际波特率约为116071,误差约0.75%,在可接受范围内。 - UART_TX_FIFO (偏移0x08):发送FIFO数据寄存器,写入即发送。
- UART_RX_FIFO (偏移0x0C):接收FIFO数据寄存器,读取即接收。
- UART_IE (偏移0x10):中断使能寄存器,可分别使能接收中断、发送中断、错误中断等。
- UART_IIR (偏移0x14):中断状态寄存器,用于判断当前中断类型。
在MaixPy中,我们无需直接操作这些寄存器,但可以通过uart.init()方法间接配置。以下代码展示了完整的初始化流程:
from machine import UART
# 初始化UART2,波特率115200,8位数据,无校验,1位停止位
uart = UART(UART.UART2, 115200, 8, None, 1, timeout=1000)
# 参数说明:UART实例、波特率、数据位、校验位(None/0/1)、停止位、超时时间(ms)
# 发送数据
uart.write(b'Hello STM32!')
# 读取数据(阻塞,最多读取64字节)
data = uart.read(64)
if data:
print("Received:", data)
在配置时,需要特别注意波特率误差。当通信双方波特率误差超过2%时,数据帧极易出错。对于K210而言,当目标波特率不是26MHz的整数分频时,会产生一定误差。下表列出了常见波特率的实际分频系数与误差:
| 目标波特率 | 分频系数(取整) | 实际波特率 | 误差 | 是否推荐 |
|---|---|---|---|---|
| 9600 | 169 | 9615 | 0.16% | ✅ 推荐 |
| 57600 | 28 | 58036 | 0.76% | ✅ 推荐 |
| 115200 | 14 | 116071 | 0.76% | ✅ 推荐 |
| 250000 | 6 | 270833 | 8.33% | ❌ 不推荐 |
| 1000000 | 1 | 1625000 | 62.5% | ❌ 不可用 |
从表中可以看出,K210的UART时钟分频器精度有限,对于超过250000的高波特率,误差急剧增大。因此,在与单片机通信时,建议使用115200或57600波特率,既保证速度又确保可靠性。
4.4 MaixPy串口API实战:AI识别结果发送
在掌握了硬件和寄存器知识后,我们进入实际应用场景。假设我们使用K210运行YOLOv2模型进行目标检测,需要将识别到的物体类别和坐标通过UART发送给STM32单片机。以下是一个完整的代码框架:
from Maix import GPIO, fm
from machine import UART
import sensor, image, lcd, time
from maix import KPU
# 1. 引脚映射
fm.register(10, fm.fpioa.UART2_TX)
fm.register(11, fm.fpioa.UART2_RX)
# 2. 初始化UART
uart = UART(UART.UART2, 115200, 8, None, 1, timeout=1000)
# 3. 初始化摄像头和LCD
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
lcd.init()
# 4. 加载KPU模型(假设已转换)
kpu = KPU()
kpu.load_kmodel("/sd/yolov2.kmodel")
# 5. 定义目标类别
labels = ["person", "car", "dog", "cat"]
while True:
img = sensor.snapshot()
# 运行AI推理
kpu.run_yolo2(img)
detections = kpu.region_yolo2()
if detections:
for det in detections:
x, y, w, h, score, class_id = det
# 构造发送数据帧:帧头+类别ID+坐标+置信度+校验
# 格式:0xAA 0x55 [class_id] [x_high] [x_low] [y_high] [y_low] [score] [checksum]
data = bytearray(9)
data[0] = 0xAA
data[1] = 0x55
data[2] = class_id
data[3] = (x >> 8) & 0xFF
data[4] = x & 0xFF
data[5] = (y >> 8) & 0xFF
data[6] = y & 0xFF
data[7] = int(score * 100)
# 简单校验:所有字节求和取低8位
checksum = sum(data[:8]) & 0xFF
data[8] = checksum
uart.write(data)
print("Sent:", class_id, x, y, score)
# 显示图像
lcd.display(img)
time.sleep_ms(50)
在上述代码中,我们定义了一个简单的通信协议:帧头(0xAA 0x55)、类别ID、坐标(16位)、置信度(0-100)和校验和。这种协议设计具有以下优点:
- 帧头同步:接收方可以通过检测帧头来识别数据帧的起始位置,避免数据错位。
- 定长帧:每帧固定9字节,便于单片机使用状态机解析。
- 校验和:简单的累加校验可以检测大部分传输错误。
在实际部署中,还需要考虑以下问题:
4.5 常见误区与调试技巧
在K210串口开发中,初学者常遇到以下问题,这里逐一分析并提供解决方案:
| 常见误区 | 现象 | 原因分析 | 解决方案 |
|---|---|---|---|
| 引脚映射冲突 | 串口无输出或输出乱码 | GPIO被其他外设占用,或TX/RX接反 | 使用fm.help()查看引脚占用,检查硬件连接 |
| 波特率误差过大 | 通信不稳定,偶发丢字节 | 选择了非标准波特率(如250000) | 改用115200或57600,或调整单片机侧波特率 |
| FIFO溢出 | 接收数据丢失 | CPU处理不及时,接收FIFO满后新数据被丢弃 | 启用接收超时中断,或使用DMA传输 |
| 未处理粘包 | 单片机解析数据错乱 | 发送频率过高,接收方未及时读取 | 在协议中加入帧头帧尾,接收方使用状态机解析 |
| 电平不匹配 | 通信完全无响应 | K210为3.3V电平,单片机为5V电平 | 使用电平转换芯片(如MAX3232)或分压电路 |
调试串口问题时,建议使用逻辑分析仪或USB转TTL工具监听总线数据。在MaixPy中,可以通过以下代码快速验证UART是否工作正常:
# 回环测试:将TX与RX短接
from machine import UART
uart = UART(UART.UART2, 115200, 8, None, 1, timeout=1000)
uart.write(b'Test')
data = uart.read(4)
print("Echo:", data) # 应输出 b'Test'
如果回环测试通过,说明UART硬件和初始化配置正确,问题可能出在外部连接或协议解析上。如果回环测试失败,则需要检查引脚映射、波特率设置以及是否有其他外设干扰。