美文网首页
Linux内核UART

Linux内核UART

作者: 小田BSP | 来源:发表于2022-07-24 22:52 被阅读0次

    在嵌入式系统中,串口既可以用于输出日志进行系统调试,又可以用于短距离低速通信,是一种非常实用的通信端口。

    本文基于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和单板之间的串口通讯,见下图:

    image.png
    RK3399 UART控制器特性:

    1、支持5路串口。

    2、支持DMA或中断传输模式。

    3、支持2个64字节的发送和接收FIFO

    4、支持5/6/7/8bit串行数据发送或接收。

    5、支持启动、停止和奇偶校验等标准异步通信位。

    6、最大可支持到4Mbps的时钟波特率。

    7、UART0/3支持自动流控模式。

    RK3399 UART引脚描述见下图:

    image.png

    二、UART连接

    ROCKPi 4A单板有个40个引脚的扩展口,引用radxa图片,见下图:

    image.png
    RockPI 4A单板的UART2作为调试串口,和USBTTL串口的引脚连接方式如下:
    RockPI4A单板 USB转TTL串口
    PIN8(UART2_TXD) RXD
    PIN9(GND) GND
    PIN10(UART2_RXD) TXD

    RockPI 4A单板的调试串口配置见下图:

    image.png
    连接串口时,必须先保证GND连接正确,其次查看串口引脚电平是否兼容,串口参数配置是否正确,否则容易出现串口不可用或乱码。

    三、UART配置

    ROCKPI 4A单板为例,介绍RK3399 DTSUART配置。

    3.1、串口别名

    普通串口设备会根据dtsaliases对串口进行编号,将serialx注册成对应的ttySx设备。

    配置文件:arch/arm64/boot/dts/rockchip/rk3399.dtsi

    RK3399 DTSaliases定义如下:

        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。

    image.png
    系统启动后,可查看串口中断(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、BuildrootAndroid),一般在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:usb2-phy@e450.1: Failed to get VBUS supply regulator
    [    1.639962] phy phy-ff770000.syscon:usb2-phy@e460.3: 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更多用法大家可以自行测试。

    注:转载请注明作者和出处。

    相关文章

      网友评论

          本文标题:Linux内核UART

          本文链接:https://www.haomeiwen.com/subject/oebwirtx.html