在嵌入式系统中,串口既可以用于输出日志进行系统调试,又可以用于短距离低速通信,是一种非常实用的通信端口。
本文基于RockPI 4A
单板Linux 4.4
内核介绍RK3399 UART
功能。
一、UART介绍
UART(Universal Asynchronous Receiver/Transmitter)
:通用异步收发器,适用于短距离、低速、串行和全双工数据传输。
在PC
中,串口一般指COM
口,即串行通讯端口。有9个引脚,使用RS-232
电平。
在嵌入式中,串口一般指UART
口。一般使用3个引脚,使用TTL
电平。
TTL/RS-232/RS-485
是指电平标准,差别如下:
电平标准 | 逻辑0 | 逻辑1 | 传输方式 |
---|---|---|---|
TTL 电平 |
0 ~ 0.4V |
2.4 ~ 5V |
全双工 |
RS-232 电平 |
3 ~ 15V |
-15 ~ -3V |
全双工 |
RS-485 电平 |
-6 ~ -2V |
2 ~ 6V |
半双工(差分传输,距离更长) |
在嵌入式单板调试时,可以选择USB转TTL
模块,实现PC
和单板之间的串口通讯,见下图:
RK3399 UART
控制器特性:
1、支持5路串口。
2、支持DMA
或中断传输模式。
3、支持2个64字节的发送和接收FIFO
。
4、支持5/6/7/8bit
串行数据发送或接收。
5、支持启动、停止和奇偶校验等标准异步通信位。
6、最大可支持到4Mbps
的时钟波特率。
7、UART0/3
支持自动流控模式。
RK3399 UART
引脚描述见下图:
二、UART连接
ROCKPi 4A
单板有个40个引脚的扩展口,引用radxa
图片,见下图:
RockPI 4A
单板的UART2
作为调试串口,和USB
转TTL
串口的引脚连接方式如下:
RockPI4A单板 | USB转TTL串口 |
---|---|
PIN8(UART2_TXD) | RXD |
PIN9(GND) | GND |
PIN10(UART2_RXD) | TXD |
RockPI 4A
单板的调试串口配置见下图:
连接串口时,必须先保证GND
连接正确,其次查看串口引脚电平是否兼容,串口参数配置是否正确,否则容易出现串口不可用或乱码。
三、UART配置
以ROCKPI 4A
单板为例,介绍RK3399 DTS
中UART
配置。
3.1、串口别名
普通串口设备会根据dts
中aliases
对串口进行编号,将serialx
注册成对应的ttySx
设备。
配置文件:arch/arm64/boot/dts/rockchip/rk3399.dtsi
。
RK3399 DTS
中aliases
定义如下:
aliases { ... serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; serial3 = &uart3; serial4 = &uart4; };
如果将UART4
修改注册成ttyS1
,可以进行如下修改:
aliases { ... serial0 = &uart0; serial1 = &uart4; ## 使用uart4替换uart1 ... serial4 = &uart1; };
3.2、串口配置
配置文件:arch/arm64/boot/dts/rockchip/rk3399.dtsi
。
UART0 dts
配置如下:
uart0: serial@ff180000 { compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart"; reg = <0x0 0xff180000 0x0 0x100>; ## uart0寄存器地址0xff180000和映射大小0x100 clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>; ## uart0使用的时钟 clock-names = "baudclk", "apb_pclk"; interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH 0>; ## uart0使用SPI中断,中断号131(99+32),没有使用DMA模式 reg-shift = <2>; ## 寄存器地址偏移2位,即offset+4 reg-io-width = <4>; ## 寄存器位宽,即32位。 pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>; ## uart0使用流控引脚 status = "disabled"; ## 默认关闭状态 };
UART0 pinmux
配置,其中包括流控引脚cts/rts
。
uart0 { uart0_xfer: uart0-xfer { rockchip,pins = <2 16 RK_FUNC_1 &pcfg_pull_up>, <2 17 RK_FUNC_1 &pcfg_pull_none>; }; uart0_cts: uart0-cts { rockchip,pins = <2 18 RK_FUNC_1 &pcfg_pull_none>; }; uart0_rts: uart0-rts { rockchip,pins = <2 19 RK_FUNC_1 &pcfg_pull_none>; }; };
注:
RK3399 UART0
中断号为131。SPI
中断号从32开始,dts
中的SPI
中断号默认从0开始,所以UART0
配置的中断号为:131-32,即99。
系统启动后,可查看串口中断(131和132):
root@xiaotianbsp:/# cat proc/interrupts CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 14: 0 0 0 0 0 0 GICv3 29 Edge arch_timer ... 35: 16 0 0 0 0 0 GICv3 131 Level serial ... 222: 301 0 0 0 0 0 GICv3 132 Level debug ... Err: 0
3.3、串口使能
配置文件:arch/arm64/boot/dts/rockchip/rockpi-4-linux.dtsi
。
UART0 dts
使能配置如下:
&uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_xfer &uart0_cts>; status = "okay"; ## okay:打开串口功能; disabled:关闭该串口功能。 }; &uart2 { status = "okay"; ## 使能串口2 };
注:
status = "okay"
或"ok"
,而不能用enable
。
内核解析代码如下:
static bool __of_device_is_available(const struct device_node *device) { ... status = __of_get_property(device, "status", &statlen); ... if (statlen > 0) { if (!strcmp(status, "okay") || !strcmp(status, "ok")) return true; } return false; }
3.4、ttyFIQ0
系统使用/dev/ttyFIQ0
作为console
设备。
配置文件:arch/arm64/boot/dts/rockchip/rockpi-4-linux.dtsi
,内容如下:
fiq_debugger: fiq-debugger { status = "disabled"; compatible = "rockchip,fiq-debugger"; rockchip,serial-id = <2>; ## 使用串口号(UART2),修改该属性的值,切换串口,同时切换串口的pinmux rockchip,signal-irq = <182>; rockchip,wake-irq = <0>; rockchip,irq-mode-enable = <1>; /* If enable uart uses irq instead of fiq */ rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */ pinctrl-names = "default"; pinctrl-0 = <&uart2c_xfer>; ## pinmux必须和serial-id对应 };
RockPI 4A
单板使用的Debian
系统,在配置文件extlinux.conf
中设置console
参数。
root@xiaotianbsp:/boot/extlinux# cat extlinux.conf timeout 10 menu title select kernel label kernel-4.4.154-90-rockchip-ga14f6502e045 kernel /vmlinuz-4.4.154-90-rockchip-ga14f6502e045 devicetreedir /dtbs/4.4.154-90-rockchip-ga14f6502e045 ## 使能early printk,ttyFIQ0做为console设备,串口波特率1.5M,8个数据位,1个停止位 append earlyprintk console=ttyFIQ0,1500000n8 init=/sbin/init root=PARTUUID=b921b045-1d rw rootwait rootfstype=ext4
系统启动后,可通过cmdline
查看。
root@xiaotianbsp:~# cat /proc/cmdline earlyprintk console=ttyFIQ0,1500000n8 init=/sbin/init root=PARTUUID=b921b045-1d rw rootwait rootfstype=ext4
四、UART驱动
RK3399 Linux4.4
内核UART
驱动采用8250通用驱动,类型是16550A
。主要实现文件:
drivers/tty/serial/8250/8250_dma.c ## UART dma实现 drivers/tty/serial/8250/8250_dw.c ## Synopsys DesignWare 8250串口驱动 drivers/tty/serial/8250/8250_early.c ## early console实现 drivers/tty/serial/8250/8250_port.c ## UART端口配置的相关接口
UART
驱动和调试后续介绍。
五、内核日志调试
在嵌入式Linux
系统中,最常见的就是使用串口输出内核日志进行功能调试。
5.1、printk
在Linux
内核中,可用函数printk()
将内核信息输出到内核信息缓冲区中。
内核日志输出分不同的等级,定义文件:include/linux/kern_levels.h
,包括:
#define LOGLEVEL_EMERG 0 /* system is unusable */ #define LOGLEVEL_ALERT 1 /* action must be taken immediately */ #define LOGLEVEL_CRIT 2 /* critical conditions */ #define LOGLEVEL_ERR 3 /* error conditions */ #define LOGLEVEL_WARNING 4 /* warning conditions */ #define LOGLEVEL_NOTICE 5 /* normal but significant condition */ #define LOGLEVEL_INFO 6 /* informational */ #define LOGLEVEL_DEBUG 7 /* debug-level messages */
除printk()
函数外,还可以使用pr_**()
和dev_**()
。
pr_**
定义文件:include/linux/printk.h
,宏定义如下:
#define pr_emerg(fmt, ...) \ printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) #define pr_alert(fmt, ...) \ printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) #define pr_crit(fmt, ...) \ printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) #define pr_err(fmt, ...) \ printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) #define pr_warn pr_warning #define pr_notice(fmt, ...) \ printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
dev_**
定义文件:drivers/base/core.c
,函数定义如下:
#define define_dev_printk_level(func, kern_level) \ void func(const struct device *dev, const char *fmt, ...) \ { \ struct va_format vaf; \ va_list args; \ \ va_start(args, fmt); \ \ vaf.fmt = fmt; \ vaf.va = &args; \ \ __dev_printk(kern_level, dev, &vaf); \ \ va_end(args); \ } \ EXPORT_SYMBOL(func); define_dev_printk_level(dev_emerg, KERN_EMERG); define_dev_printk_level(dev_alert, KERN_ALERT); define_dev_printk_level(dev_crit, KERN_CRIT); define_dev_printk_level(dev_err, KERN_ERR); define_dev_printk_level(dev_warn, KERN_WARNING); define_dev_printk_level(dev_notice, KERN_NOTICE); define_dev_printk_level(_dev_info, KERN_INFO);
5.1.1、日志输出等级
通过修改loglevel
或/proc/sys/kernel/printk
的值可以调整内核日志输出等级。
在系统启动前,通过配置loglevel
,可以调整串口日志输出等。
root@xiaotianbsp:~# cat /boot/extlinux/extlinux.conf ... label kernel-debug kernel /debug/Image fdt /debug/rk3399-rock-pi-4a.dtb ## 修改loglevel调整串口输出日志等级 append earlyprintk console=ttyFIQ0,1500000n8 loglevel=4 init=/sbin/init root=PARTUUID=b921b045-1d rw rootwait rootfstype=ext4 no_console_suspend initcall_debug
其他系统(如:Ubuntun、Buildroot
或Android
),一般在bootargs
中修改loglevel
。
注:
no_console_suspend
用于Linux系统电源管理调试,表示系统休眠(suspend)后,串口不休眠,仍可输出。
在系统启动后,也可动态调整串口日志输出等级。
root@xiaotianbsp:/proc/sys/kernel# cat printk 7 4 1 7 root@xiaotianbsp:/proc/sys/kernel# echo 4 > printk root@xiaotianbsp:/proc/sys/kernel# cat printk 4 4 1 7
printk
中的数字对应不同的日志级别,只要修改console
口的日志级别即可。
int console_printk[4] = { CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */ ## 控制台的日志级别 MESSAGE_LOGLEVEL_DEFAULT, /* default_message_loglevel */ ## 默认消息日志级别 CONSOLE_LOGLEVEL_MIN, /* minimum_console_loglevel */ ## 最小控制台日志级别 CONSOLE_LOGLEVEL_DEFAULT, /* default_console_loglevel */ ## 默认控制台日志级别 };
5.1.2、日志时间戳
在系统启动后,可以动态调整内核日志时间戳的显示。
## 1.time值为Y,表示显示时间戳 root@xiaotianbsp:/# cat /sys/module/printk/parameters/time Y ## 2.此时日志显示有时间戳 root@xiaotianbsp:/# find . -name time [ 1719.836194] FAT-fs (sda4): error, invalid access to FAT (entry 0x07b03538) [ 1719.836874] FAT-fs (sda4): error, invalid access to FAT (entry 0x07b03538) ## 3.设置time值为N root@xiaotianbsp:/# echo N > /sys/module/printk/parameters/time ## 4.此时日志显示有时间戳 root@xiaotianbsp:/# find . -name time FAT-fs (sda4): error, invalid access to FAT (entry 0x07b03538) FAT-fs (sda4): error, invalid access to FAT (entry 0x07b03538)
5.2、dmesg
在系统启动后,如果已经错过内核启动阶段或使用非串口(例:adb/ssh
登录)连接调试板,可以使用dmesg
查看内核日志。
dmesg
用法如下:
root@xiaotianbsp:/# dmesg -h Usage: dmesg [options] Display or control the kernel ring buffer. Options: -C, --clear clear the kernel ring buffer -c, --read-clear read and clear all messages -D, --console-off disable printing messages to console -E, --console-on enable printing messages to console -F, --file <file> use the file instead of the kernel log buffer -f, --facility <list> restrict output to defined facilities -H, --human human readable output -k, --kernel display kernel messages -L, --color[=<when>] colorize messages (auto, always or never) colors are enabled by default -l, --level <list> restrict output to defined levels -n, --console-level <level> set level of messages printed to console -P, --nopager do not pipe output into a pager -r, --raw print the raw message buffer -S, --syslog force to use syslog(2) rather than /dev/kmsg -s, --buffer-size <size> buffer size to query the kernel ring buffer -u, --userspace display userspace messages -w, --follow wait for new messages -x, --decode decode facility and level to readable string -d, --show-delta show time delta between printed messages -e, --reltime show local time and time delta in readable format -T, --ctime show human-readable timestamp (may be inaccurate!) -t, --notime don't show any timestamp with messages --time-format <format> show timestamp using the given format: [delta|reltime|ctime|notime|iso] Suspending/resume will make ctime and iso timestamps inaccurate. -h, --help display this help and exit -V, --version output version information and exit Supported log facilities: kern - kernel messages user - random user-level messages mail - mail system daemon - system daemons auth - security/authorization messages syslog - messages generated internally by syslogd lpr - line printer subsystem news - network news subsystem Supported log levels (priorities): emerg - system is unusable alert - action must be taken immediately crit - critical conditions err - error conditions warn - warning conditions notice - normal but significant condition info - informational debug - debug-level messages For more details see dmesg(1).
5.2.1、显示内核日志
root@xiaotianbsp:/# dmesg [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys cpu [ 0.000000] Initializing cgroup subsys cpuacct [ 0.000000] Linux version 4.4.154-90-rockchip-ga14f6502e045 (root@2705a206000b) (gcc version 7.3.1 20180425 [linaro-7.3-2018.05 revision d29120a424ecfbc167ef90065c0eeb7f91977701] (Linaro GCC 7.3-2018.05) ) #22 SMP Tue Jul 30 10:32:28 UTC 2019
5.2.2、限制日志输出等级
## 仅输出error信息 root@xiaotianbsp:/# dmesg -l err [ 2.170152] rockchip-pcie f8000000.pcie: PCIe link training gen1 timeout! [ 2.175658] rk-vcodec ff650000.vpu_service: could not find power_model node [ 2.180010] rk-vcodec ff660000.rkvdec: could not find power_model node [ 2.200359] rockchip-vop ff900000.vop: missing rockchip,grf property [ 2.201913] rockchip-vop ff8f0000.vop: missing rockchip,grf property [ 2.203632] i2c i2c-9: of_i2c: modalias failure on /hdmi@ff940000/ports [ 2.240381] mali ff9a0000.gpu: Failed to get regulator [ 2.240839] mali ff9a0000.gpu: Power control initialization failed [ 2.260317] rk_gmac-dwmac fe300000.ethernet: cannot get clock clk_mac_speed
## 同时输出error和warning信息 root@xiaotianbsp:/# dmesg -l err,warn [ 0.000000] rockchip_clk_register_frac_branch: could not find dclk_vop0_frac as parent of dclk_vop0, rate changes may not work [ 0.000000] rockchip_clk_register_frac_branch: could not find dclk_vop1_frac as parent of dclk_vop1, rate changes may not work [ 0.000000] rockchip_cpuclk_pre_rate_change: limiting alt-divider 33 to 31 [ 1.589058] thermal thermal_zone1: power_allocator: sustainable_power will be estimated [ 1.637902] phy phy-ff770000.syscon:[email protected]: Failed to get VBUS supply regulator [ 1.639962] phy phy-ff770000.syscon:[email protected]: Failed to get VBUS supply regulator [ 2.170152] rockchip-pcie f8000000.pcie: PCIe link training gen1 timeout!
5.2.3、查找某条信息
root@xiaotianbsp:~# dmesg | grep rockchip-vop [ 2.197270] rockchip-vop ff900000.vop: missing rockchip,grf property [ 2.198853] rockchip-vop ff8f0000.vop: missing rockchip,grf property root@xiaotianbsp:~# dmesg | grep xiaotianbsp root@xiaotianbsp:~#
5.2.4、清空环形缓冲区信息
root@xiaotianbsp:/# dmesg -c [ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Initializing cgroup subsys cpuset [ 0.000000] Initializing cgroup subsys cpu [ 0.000000] Initializing cgroup subsys cpuacct ... root@xiaotianbsp:/# dmesg root@xiaotianbsp:/#
dmesg
更多用法大家可以自行测试。
注:转载请注明作者和出处。
RustDesk 由于诈骗猖獗,暂停国内服务 淘宝 (taobao.com) 重启网页版优化工作 Apple 发布 M4 芯片 高中生自创开源编程语言作为成人礼——网友锐评:依托答辩 云风从阿里离职,未来计划制作 Windows 平台的独立游戏 程序员的归宿 Visual Studio Code 1.89 发布 华为官宣余承东职务调整 15 年前被钉在“FFmpeg 耻辱柱”,今天他却得谢谢咱——腾讯QQ影音一雪前耻? 华中科技大学开源镜像站正式开放外网访问