1. AXI UART16550 测试环境搭建与原理
在嵌入式Linux开发中,UART16550是一种广泛使用的串行通信控制器IP核。它通过AXI总线与处理器连接,提供稳定的串行数据传输能力。本次测试基于Xilinx Zynq平台,使用自定义的AXI UART16550 IP核进行功能验证。
1.1 硬件平台配置
测试平台采用Xilinx Zynq-7000系列SoC,主要硬件配置如下:
- 主频:650MHz ARM Cortex-A9双核处理器
- 外设:AXI UART16550控制器(基地址0x43c10000)
- 时钟:100MHz AXI总线时钟
- 内存:1GB DDR3
硬件设计中需要注意几个关键点:
- 地址映射必须正确配置,确保CPU能通过AXI总线访问UART寄存器
- 中断信号需要正确连接到PS的中断控制器
- 参考时钟频率直接影响波特率精度
实际项目中遇到过因地址映射错误导致无法访问寄存器的问题,建议在Vivado中仔细检查地址编辑器(auto assign)生成的地址范围。
1.2 UART16550寄存器详解
UART16550的核心是通过寄存器控制通信过程,主要寄存器及其功能如下:
| 寄存器偏移 | 名称 | 读/写 | 功能描述 |
|---|---|---|---|
| 0x00 | RBR/THR | R/W | 接收缓冲/发送保持 |
| 0x04 | IER | R/W | 中断使能 |
| 0x08 | FCR | W | FIFO控制 |
| 0x0C | LCR | R/W | 线路控制 |
| 0x14 | LSR | R | 线路状态 |
| 0x1C | SCR | R/W | 暂存寄存器 |
特别需要注意的是LCR寄存器的bit7(DLAB位),它控制着对波特率分频器的访问。当DLAB=1时,偏移0x00和0x04分别对应DLL和DLM寄存器。
2. 底层寄存器级测试
2.1 波特率配置原理与实现
UART通信的核心参数是波特率,计算公式为:
code复制波特率 = 系统时钟频率 / (16 × 分频系数)
对于100MHz系统时钟,115200波特率对应的分频系数计算过程:
code复制分频系数 = 100,000,000 / (16 × 115200) ≈ 54.253
取整后 DIVISOR = 54
实际波特率 = 100,000,000 / (16 × 54) ≈ 115740.7 (误差0.47%)
对应的寄存器配置脚本关键部分:
bash复制# 进入波特率设置模式
lcr=$(devmem $((UART_BASE+LCR)) 32)
devmem $((UART_BASE+LCR)) 32 $((lcr | 0x80))
# 设置分频系数
DLL=$((DIVISOR & 0xFF))
DLM=$(( (DIVISOR >> 8) & 0xFF ))
devmem $((UART_BASE+0x00)) 32 $DLL # DLL寄存器
devmem $((UART_BASE+0x04)) 32 $DLM # DLM寄存器
# 退出波特率设置模式
devmem $((UART_BASE+LCR)) 32 $((lcr & ~0x80))
2.2 FIFO初始化与数据收发
现代UART16550实现通常包含16字节的FIFO缓冲区,初始化时需要正确配置:
bash复制# FIFO使能 + 收发FIFO复位
devmem $((UART_BASE+FCR)) 32 0x07
数据发送时需要检查LSR寄存器的THRE位(bit5),确保发送保持寄存器就绪:
bash复制while true; do
lsr=$(devmem $((UART_BASE+LSR)) 32)
if (( (lsr & 0x20) != 0 )); then
break
fi
done
devmem $((UART_BASE+THR)) 32 "$(printf '0x%x' "'$char")"
3. Linux驱动集成与测试
3.1 设备树配置关键点
在Zynq平台上,AXI UART16550的设备树配置有几个特殊要求:
- 必须删除默认的reg-offset属性
- 需要指定reg-shift和reg-io-width
- 对于测试用途可以禁用回环测试
典型配置如下:
dts复制&axi_uart16550_0 {
/delete-property/ reg-offset;
reg-shift = <2>;
reg-io-width = <4>;
no-loopback-test;
};
实际项目中遇到过因reg-shift设置错误导致无法通信的问题,建议与IP核的寄存器宽度保持一致。
3.2 内核驱动加载验证
驱动成功加载后,dmesg中应出现类似信息:
code复制43c10000.serial: ttyS2 at MMIO 0x43c10000 (irq = 48, base_baud = 6250000) is a 16550A
关键检查点:
- 正确的物理地址映射(0x43c10000)
- 中断号分配正确(irq = 48)
- 设备节点创建成功(/dev/ttyS2)
3.3 用户空间测试方法
基本发送测试:
bash复制echo "test message" > /dev/ttyS2
接收测试(需连接另一终端):
bash复制cat /dev/ttyS2
高级测试工具推荐:
bash复制# 使用screen进行交互测试
screen /dev/ttyS2 115200
# 使用minicom进行专业测试
minicom -D /dev/ttyS2 -b 115200
4. 常见问题与调试技巧
4.1 FIFO死锁问题
在回环测试模式下,UART16550可能出现RX FIFO锁死的情况。典型症状是:
- 能发送数据但无法接收
- LSR寄存器值异常
- 系统日志中出现"16550A rx FIFO lockup"警告
解决方案:
- 设备树中添加no-loopback-test属性
- 软件初始化时明确复位FIFO(FCR[1:0]=11)
- 避免在硬件设计中使能自动流控
4.2 中断工作异常排查步骤
当UART中断不工作时,建议按以下流程排查:
- 检查设备树中断号是否正确
bash复制cat /proc/interrupts | grep uart
- 验证IER寄存器配置
bash复制devmem $((UART_BASE+IER)) 32
- 检查GIC中断控制器状态
bash复制cat /proc/irq/48/spurious
- 确认驱动中断处理函数已注册
bash复制cat /proc/interrupts
4.3 性能优化建议
对于高速UART通信,建议:
- 启用DMA传输(如果IP支持)
dts复制dmas = <&axi_dma_0 0>, <&axi_dma_0 1>;
dma-names = "tx", "rx";
- 调整内核串口缓冲区大小
bash复制stty -F /dev/ttyS2 ospeed 3000000
- 使用RT-Preempt内核减少延迟
5. 进阶测试与自动化
5.1 压力测试脚本
bash复制#!/bin/bash
DEV=/dev/ttyS2
BAUD=115200
# 配置串口
stty -F $DEV $BAUD -echo -onlcr
# 启动后台接收进程
cat $DEV > receive.log &
# 发送测试数据
for i in {1..1000}; do
echo "Test packet $i" > $DEV
sleep 0.1
done
# 比较收发数据
diff send.log receive.log
5.2 自动化测试框架集成
建议将UART测试集成到自动化测试框架中,例如:
- 使用Python的pyserial库
python复制import serial
ser = serial.Serial('/dev/ttyS2', 115200, timeout=1)
ser.write(b'Test message')
response = ser.readline()
- 结合expect脚本实现交互测试
expect复制spawn screen /dev/ttyS2 115200
expect "login:"
send "root\r"
- 使用Jenkins或GitLab CI实现持续集成
在长期项目实践中,发现UART16550的稳定性高度依赖于时钟质量和PCB布局。曾遇到因时钟抖动导致的偶发通信错误,最终通过以下措施解决:
- 在时钟线上增加RC滤波
- 缩短UART IP核与PS的连接距离
- 在软件中增加重传机制