对嵌入式开发者来说,Linux串口驱动是个绕不开的坎。串口虽然古老,但在调试设备、连接传感器、与工业模块通信时依然不可或缺。很多人刚接触时会被设备树、波特率、中断处理搞得一头雾水,甚至分不清ttyS和ttyUSB到底有什么区别。其实串口驱动没那么玄乎,无非就是硬件层怎么收发数据,内核怎么把它抽象成文件操作,以及应用层怎么调用read和write。

串口在Linux里到底是个什么角色

Linux把一切外设都抽象成文件linux的串口驱动,串口也不例外。当你插入一个USB转串口模块,系统会创建类似/dev/ttyUSB0的设备节点,然后你就可以像读写普通文件一样操作它。这个抽象层非常强大,它把硬件细节全部隐藏了,但你如果写驱动,就必须理解背后的机制。

串口驱动本质上是一组函数指针,它们挂在tty驱动框架下面。内核里有一套标准的tty核心代码,负责处理行规程、缓冲、流量控制这些通用逻辑。硬件相关的部分才交给具体的驱动去实现,比如设置波特率、配置数据位、处理中断。这就是分层设计的好处——写一个新串口驱动,你只需要关注底层硬件怎么读写寄存器,上层的接口都不用动。

你可能会问,那设备树是干嘛用的?设备树就是告诉内核,这块芯片上哪个引脚是TX、哪个是RX,用哪路时钟,中断号是多少。没有设备树,驱动根本不知道去操作哪块硬件。比如最常见的UART控制器,它的寄存器基地址、时钟频率、DMA通道这些信息,都要从设备树里读出来。

怎么写一个最简单的串口驱动

linux485串口驱动_linux串口驱动程序_linux的串口驱动

别被“驱动”两个字吓到,其实很多串口驱动都是照着模板改出来的。内核源码的drivers/tty/serial/目录下有大量现成的例子,你找个和自己芯片架构类似的,复制一份,改寄存器操作就行。

核心要实现的函数就这么几个:serial8250_startup负责初始化硬件,设置中断和波特率;serial8250_shutdown负责关闭串口;serial8250_set_termios用来配置串口参数。还有serial8250_tx_chars和serial8250_rx_chars,它们分别在发送和接收中断里被调用,负责往FIFO里写数据或从FIFO里读数据。

举个例子,很多国产MCU的串口都兼容16550标准。那你直接复用8250驱动就可以了,只需要定义好端口号、寄存器偏移、时钟源这些参数。内核里有个earlycon机制,连中断都不需要,靠轮询就能在系统启动早期输出调试信息,这个对排查硬件初始化问题特别有用。

linux串口驱动程序_linux485串口驱动_linux的串口驱动

要注意的是,现在的串口驱动大多支持DMA模式。如果数据量很大,比如每秒几百KB的串口通信,中断方式会被频繁触发,CPU占用率飙升。DMA模式可以把数据在内存和FIFO之间直接搬运,CPU只要在传输完成时处理一个中断就行。但DMA的配置比较复杂,需要处理缓存一致性、DMA描述符链等问题。

串口驱动常见的坑怎么排查

写串口驱动最头疼的就是数据收发不正常。要么发出去的数据完全不对,要么接收丢包,要么波特率不准。这些问题大部分出在时钟配置上。串口的波特率发生器需要根据时钟频率计算分频系数,如果你的时钟源和实际用的不一致linux重启命令,算出来的波特率就差很远了。用示波器量一下TX脚的波形,看位宽对不对,这是最直接的方法。

linux的串口驱动_linux串口驱动程序_linux485串口驱动

还有个经典问题:中断不触发。有时候中断注册成功了,但就是进不了中断处理函数。这时候先查中断号对不对,再查中断控制器有没有使能。有些SoC的串口中断是挂在GIC上的,GIC的配置不对,串口的中断就被屏蔽了。你可以在驱动里临时用轮询模式代替中断,如果轮询能正常收发,那就说明问题出在中断通路。

接收丢包多半和FIFO阈值有关。串口FIFO满了之后,如果CPU没来得及读取,新数据就会覆盖旧数据。你可以把FIFO触发阈值调低,比如设置为1字节就触发中断,但这样中断太频繁,CPU受不了。更好的做法是用DMA加环形缓冲区,让DMA自动把FIFO里的数据搬到内存,驱动再从环形缓冲区里取。

用户空间工具怎么配合驱动调试

驱动写好了linux 软件,总得验证一下能不能用。很多人在内核态折腾半天,其实用应用层的工具就能定位大部分问题。stty命令可以查看和设置串口参数,比如stty -F /dev/ttyS0 115200 cs8就把波特率设为115200,数据位8位。cat和echo可以直接读写串口,cat /dev/ttyS0能看到接收到的数据,echo “hello” > /dev/ttyS0能发送数据。

linux485串口驱动_linux的串口驱动_linux串口驱动程序

如果想模拟一个串口设备,可以用socat创建一个虚拟串口对。这在开发驱动时很有用,你不需要真的接两个设备,就能测试收发逻辑。socat PTY,link=/dev/ttyV0 PTY,link=/dev/ttyV1会创建一对互联的虚拟串口,往一个写,另一个就能读到。

更高级的工具是jtag和逻辑分析仪。当驱动完全不正常时,逻辑分析仪抓一下TX/RX的波形,看有没有电平跳变,数据格式对不对。有些SoC的串口引脚复用配置错了,管脚电平根本不会变化,这时候光看软件日志是查不出来的。

串口驱动看起来琐碎linux的串口驱动,但理解透了,其他外设驱动的套路也就通了。无非是寄存器读写加中断处理,再加上Linux内核那一套抽象框架。只要把时钟和中断这两个基础点搞明白,遇到问题知道从哪里下手,串口驱动就不再是难题。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复