嵌入式开发中,串口驱动移植是一个常见但容易出错的环节。很多开发者把官方驱动复制过去就以为完事了,实际上一上路就会发现各种问题——设备无法识别、波特率不准、中断不响应。我这些年踩过不少坑,今天就把核心步骤和常见问题拆开讲清楚,希望能帮有需要的朋友省点调试时间。

移植前要确认哪些硬件信息

串口驱动移植的第一步不是写代码,而是理顺硬件关系。很多移植失败的根源在于物理层和芯片手册没看透。

你得先搞清楚开发板上用的是哪种串口控制器。常见的有16550兼容UART、三星的S5PV210、全志的H3/H5、Rockchip的RK3288/RK3399,以及NXP的i.MX系列。它们在寄存器布局、FIFO深度、中断机制上差别不小。如果拿着全志的驱动直接往i.MX上套,十有八九是跑不起来的。

linux串口转usb驱动_linux串口驱动_linux串口驱动移植

第二个要确认的是时钟树。串口波特率由外部晶振或PLL分频而来,如果串口模块的时钟源没有使能linux操作系统培训,或者分频参数设错了,数据就会乱成一团。我见过一个案例,开发者移植Allwinner H5的串口驱动,波特率怎么配都偏了,最后发现是PLL的默认输出频率和驱动里写的不一致。

另外,GPIO的复用配置也容易忽略。很多SoC的串口引脚是和GPIO、SPI等功能复用的,必须通过pinctrl或寄存器把引脚功能切换到UART模式,否则信号根本出不来。

内核驱动结构怎么搭

Linux内核里串口驱动通常分为三部分:底层硬件操作、串口核心框架、以及TTY层。移植时最需要改动的是底层硬件部分。

linux串口转usb驱动_linux串口驱动_linux串口驱动移植

驱动框架建议直接用8250或者serial core。8250是内核内置的通用串口驱动框架,支持大多数标准UART,适合16550兼容的控制器。如果芯片不是标准的,就得写自己的serial驱动硬盘安装linux,注册到serial core里。这一步的关键是实现ops结构体中的startup、shutdown、set_termios、tx_empty、stop_tx、start_tx等回调。

举个例子,有些国产芯片的串口寄存器偏移和标准16550不一样,比如全志的UART寄存器地址是偏移0x0到0x1C,而标准的是0x0到0x7。这种情况下,你得重写读/写寄存器的函数,而不是直接套用8250_port的默认操作。

还有一点,中断处理函数要按照硬件的中断标志位来写。有些芯片支持TX FIFO空、RX FIFO满、线路状态错误等多个中断源,你要在IRQ handler里逐一判断并处理。漏掉一个,整个串口可能就卡死了。

设备树节点该怎么配

linux串口转usb驱动_linux串口驱动_linux串口驱动移植

设备树是当前最主流的硬件描述方式,串口驱动的移植基本都依赖设备树来传递地址、中断、时钟等参数。

最基础的配置是compatible、reg、interrupts三个属性。compatible要写对,比如全志的串口是“allwinner,sun8i-h3-uart”linux串口驱动移植,Rockchip的可能是“rockchip,rk3399-uart”。这个字符串要和驱动里match_table中的一致,否则匹配不上。

接着是clocks和clock-names。串口多绑在一路时钟上,需要指定时钟源和频率。很多开发者在这里漏了,结果串口工作不稳定。比如RK3399的UART2,时钟要配成“clk_uart2”和“pclk_uart2”,不然驱动跑一半就崩了。

pinctrl也要配好。设备树里要定义uart2_pins节点,指定哪些引脚用于TX、RX,以及是否开启上拉、驱动强度等。pinctrl-names一般是“default”,对应芯片内部的引脚复用配置。

linux串口驱动移植_linux串口驱动_linux串口转usb驱动

还有些属性如dma-names、auto-flow-control,如果不是必须,建议不要配。没配好在某些内核版本上会导致驱动注册失败。

编译调试中常见问题怎么解决

移植完成后,编译和运行时都会遇到问题,我挑几个常见的说说。

第一个是内核编译报“undefined reference to xxx”。这是最常见的问题,说明驱动里调用了某个函数但没链接进来。比如用了8250框架但没选CONFIG_SERIAL_8250,或者选了但没选CONFIG_SERIAL_8250_CONSOLE。回到menuconfig里把相关选项勾上,一般能解决。

linux串口驱动_linux串口转usb驱动_linux串口驱动移植

第二个是串口设备节点没生成。用ls /dev/ttyS或ttyS看不到设备linux串口驱动移植,大多是设备树匹配不上或者驱动probe失败了。可以看看内核启动日志,grep下“uart”或设备名,如果看到“failed to get clock”或“no matching node”,回去改设备树。

第三个是数据收发错乱。比如波特率对不上、数据丢包。可以先用逻辑分析仪抓一下波形,看波特率是不是符合预期。如果偏了,调时钟分频。如果数据帧格式不对,检查set_termios里是否正确设置了位数、校验位。

还有个坑是控制台乱码。如果串口同时用作console,要确认CONFIG_SERIAL_CONSOLE和串口索引号配对了。有的板子ttyS0是调试串口,但console设成了ttyS1,结果打印全跑偏了。

串口驱动移植说难不难,说简单也不简单,关键是把硬件手册读透、设备树写对、驱动框架选对。每块芯片都有自己的脾气,移植时耐心一点,先跑通一个串口再扩展到其他。多抓几帧波形也比靠猜强。希望这篇对正在做移植的你有所帮助。

Tagged:
Author

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

刘遄

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

发表回复