1.

串口驱动入门:从概念到框架
大家好,我是老张,在嵌入式Linux驱动开发这块摸爬滚打了十几年,从早期的ARM9到现在的Cortex-A系列,串口驱动可以说是最基础、最常用,但也最容易踩坑的外设之一。
今天我就以IMX6ULL开发板为例,带大家完整走一遍UART驱动从RS232到RS485的移植与测试过程。
串口到底是什么?简单来说,它就是设备之间“说话”的通道。
在嵌入式世界里,串口就像开发板的“嘴巴”和“耳朵”,既能输出调试信息让你知道系统在干什么,也能接收指令控制外设。
根据电平标准不同,常见的有TTL(3.3V/5V)、RS232(±12V)和RS485(差分信号)。
虽然电平不同,但它们在Linux内核中的驱动框架是统一的,都是通过UART子系统来管理。
我刚开始接触Linux驱动时,最头疼的就是搞不清内核里那一堆结构体的关系。
后来发现,理解UART驱动框架,关键就是抓住三个核心结构体:uart_driver、uart_port和uart_ops。
你可以把uart_driver想象成一家快递公司的总部,负责品牌注册和整体管理;uart_port就是分布在各个城市的快递站点,每个串口对应一个站点;而uart_ops则是站点里快递员的具体操作规范,比如怎么收件、怎么派件。
在IMX6ULL这类芯片上,NXP官方其实已经把最底层的驱动写好了,文件就是drivers/tty/serial/imx.c。
我们实际要做的,大部分情况下并不是从头写驱动,而是正确地配置设备树,让内核知道“哦,这个板子上用了哪个串口,引脚接在哪里,现在要启用它”。
这就像你买了台新电脑,不需要自己写USB驱动,只要插上设备,系统就能识别一样。
不过,知道怎么配置只是第一步。
在实际项目中,特别是工业控制领域,RS232往往不够用,需要用到传输距离更远、抗干扰能力更强的RS485。
这时候就需要在驱动层面做一些调整,比如控制方向引脚(DE/RE)。
我遇到过不少项目,RS485通信不稳定,最后发现都是方向切换时序没处理好。
接下来,我就从最基础的驱动框架分析开始,一步步带你搞定这些实际问题。
2.Linux
uart_driver:驱动的“身份证”与“户口本”
在Linux内核里,任何一个驱动想要“上户口”,都得先向内核注册自己。
对于串口驱动来说,这个“户口本”就是struct
uart_driver。
我们来看看它在include/linux/serial_core.h里的关键定义:
structuart_driver
驱动名称,比如"imx-uart"
const
设备节点名,比如"ttymxc"
int
};
这几个字段我一个个解释下。
owner就是声明这个驱动模块属于谁,写THIS_MODULE就行。
driver_name是给内核看的名字,而dev_name才是出现在/dev。
- 编译安装:
start="3">
make-j4
start="4">
拷贝到根文件系统: sudolib/*
start="5">
设置环境变量:在开发板的/etc/profile里添加 exportTERM=vt100
TERMINFO=/usr/share/terminfo
我遇到过的一个坑是terminfo数据库路径不对,导致minicom启动时提示“terminal
type
unknown”。
确保/usr/share/terminfo下有没有rs485相关文件
配置RS485参数: #查看当前配置
/sys/class/tty/ttymxc2/rs485/enabled
start="3">
- 写一个测试程序,交替发送和接收
- 用示波器或逻辑分析仪观察DE引脚和TXD的时序
常见问题:
- 数据丢失:方向切换太早或太晚,调整delay参数
- 只能发不能收:DE引脚控制逻辑反了,检查active-high还是active-low
- 通信距离短:检查终端电阻,线材质量,波特率是否过高
6.6
压力测试与稳定性验证
最后要做压力测试,确保驱动稳定:
长时间测试:
#发送端
-l
运行几个小时,看有没有丢数据。
大数据量测试:
#发送大量数据
/dev/ttymxc2
高波特率测试:测试最高能到多少波特率。
IMX6ULL理论上支持5Mbps,但实际受硬件限制,一般到1Mbps没问题。
中断和DMA测试:查看中断统计:
cat/proc/interrupts
uart
如果发送大量数据时CPU占用率很高,可以考虑启用DMA。
我在实际项目中遇到过最奇怪的问题是:串口工作几分钟后就卡死。
最后发现是驱动里的一个bug,在特定波特率下,FIFO阈值设置不合理导致溢出。
解决方法是在设备树里调整FIFO触发阈值。
7.
DMA传输:解放CPU的利器
当串口波特率很高或数据量很大时,用中断方式每个字节都要进一次中断,CPU占用率会很高。
IMX6ULL的UART支持DMA,可以大大减轻CPU负担。
设备树里已经配置了DMA通道:
dmas=
"tx";
但要真正启用DMA,还需要在驱动里做更多工作。
NXP的驱动默认可能没开DMA,需要检查配置。
启用DMA后,数据收发不经过CPU,直接在外设和内存之间传输。
测试DMA是否工作:
- 发送大量数据,用
top命令看CPU占用率 - 查看DMA统计信息:
cat/proc/dma
- 对比启用DMA前后的性能差异
DMA的缺点是增加了系统复杂性,而且小数据量时可能不如中断效率高。
一般建议在115200以上波特率或连续传输时启用。
7.2
硬件流控:可靠传输的保障
RS232除了TXD和RXD,还有RTS和CTS引脚用于硬件流控。
当接收方缓冲区快满时,拉高CTS告诉发送方“暂停发送”。
这可以防止数据丢失,特别是在高速通信时。
启用硬件流控:
- 设备树里配置RTS和CTS引脚
- 驱动里设置相应的标志
- 应用程序也要支持
测试硬件流控:
#查看当前设置
crtscts
然后用高速发送数据,观察CTS引脚的电平变化。
7.3
自定义波特率与特殊模式
有些设备需要非标准波特率,比如187500、250000等。
IMX6ULL的UART支持小数分频,可以实现任意波特率。
设置自定义波特率:
structtermios
&options);
驱动里需要在set_termios函数里处理BOTHER情况,计算合适的分频系数。
除了标准UART模式,IMX6ULL还支持:
- 红外模式(IrDA):用于红外通信
- 智能卡模式:用于SIM卡、银行卡等
- LIN总线模式:汽车电子常用
这些模式通过配置UART的特定寄存器启用,一般应用场景较少,但知道有这个功能。
7.4
驱动调试技巧
调试驱动时,我常用的方法:
打印调试信息:
//在驱动里添加
sport->port.membase);
需要开启内核的DEBUG选项。
查看硬件寄存器:
#安装devmem2工具
查看UART3控制寄存器
使用ftrace跟踪函数调用:
echofunction
/sys/kernel/debug/tracing/current_tracer
echo
/sys/kernel/debug/tracing/set_ftrace_filter
echo
/sys/kernel/debug/tracing/tracing_on
操作串口
/sys/kernel/debug/tracing/tracing_on
cat
/sys/kernel/debug/tracing/trace
分析Oops信息:如果驱动崩溃,保存/proc/vmcore或dmesg输出,用addr2line工具定位代码位置。
7.5
性能优化建议
根据我的经验,优化串口性能可以从这几个方面入手:
- 调整FIFO阈值:默认可能不是最优值,根据数据特点调整
- 使用DMA:大数据量时必用
- 优化中断处理:中断处理函数里尽量少做事情
- 内存对齐:DMA缓冲区要按cache
line对齐
- 电源管理:不用时进入低功耗模式
- 批处理:应用程序尽量一次读写多个字节
可以写个简单的性能测试程序:
structtimeval
计算吞吐量
对比不同优化措施的效果。
8.
从理论到实践:一个真实项目案例
最后我想分享一个实际项目的经验,可能对大家有启发。
那是几年前的一个工业控制器项目,需要同时连接多个RS485设备(温控器、电机驱动器、传感器等)。
需求分析:
- 主控:IMX6ULL
- 通信接口:4个RS485,1个RS232用于调试
- 通信协议:Modbus
RTU
- 要求:实时性高,不能丢数据
硬件设计:
- UART1用作调试口(RS232)
- UART2、UART3、UART4、UART5用作RS485
- 每个RS485用单独的GPIO控制方向
- 所有RS485接口加隔离保护
软件实现:
- 设备树配置:每个UART单独配置,RS485的GPIO用pinctrl绑定
- 驱动修改:标准驱动不支持多个GPIO方向控制,参考RK的补丁修改
- 应用层:用libmodbus库,每个端口一个线程
遇到的问题和解决:
问题1:多个RS485同时通信时互相干扰分析:GPIO切换时的毛刺干扰了其他端口解决:在GPIO控制函数里加微小延时,错开切换时间
问题2:高波特率(921600)时丢数据分析:CPU处理不过来,中断太频繁解决:启用DMA,调整FIFO触发阈值从1/8改为1/4
问题3:长时间运行后死机分析:内存泄漏,每次收发都申请DMA缓冲区但没释放解决:改用预分配的循环缓冲区
问题4:RS485最远距离达不到标称的1200米分析:线路阻抗不匹配,信号反射解决:启用终端电阻,降低波特率到19200
最终方案:
- 驱动层:修改imx.c,添加多GPIO方向控制支持
- 系统层:调整内核调度策略,给串口线程更高优先级
- 应用层:实现超时重传、数据校验、链路检测
这个项目让我深刻体会到,串口驱动不是配好设备树就完事了。
实际应用中要考虑抗干扰、实时性、稳定性、功耗等多方面因素。
特别是工业环境,电磁干扰强,温湿度变化大,对驱动稳定性要求极高。
我建议大家在学完基础后,一定要动手做项目。
可以从简单的开始,比如用串口控制一个继电器,读取一个温湿度传感器。
遇到问题不要怕,Linux驱动开发就是这样,大部分时间都在调试和查资料。
但当你最终调通的那一刻,那种成就感是无与伦比的。
好了,关于Linux
UART驱动从RS232到RS485的移植与测试,我就分享到这里。
内容比较多,从框架分析到代码实现,从工具移植到问题排查,基本上覆盖了实际开发的全流程。
每个项目情况不同,可能遇到的问题也不一样,但解决问题的思路是相通的:先理解原理,再动手实践,遇到问题耐心分析,善用调试工具。


