RK3568串口调试

目录

前言

一、代码位置

二、硬件原理图

三、设备树配置

四、设备注册

五、串口调试功能

六、驱动调试

 总结


前言

本文主要讲解如何移植RK3568的串口并且测试连通性

一、代码位置

Linux kernel 中,使用8250串口通用驱动,以下为主要驱动文件:

drivers/tty/serial/8250/8250_core.c # 8250串口驱动核心

drivers/tty/serial/8250/8250_dw.c # Synopsis DesignWare 8250串口驱动

drivers/tty/serial/8250/8250_dma.c # 8250串口DMA驱动

drivers/tty/serial/8250/8250_port.c # 8250串口端口操作

drivers/tty/serial/8250/8250_early.c # 8250串口early console驱动

在SDK代码下面的kernel目录下输入make ARCH=arm64 menuconfig进入到图形化配置页面,在界面输入“/”,在框内输入8250然后回车,会看下如下页面:

输入对应的数字2可以去8250的配置页面,由于Rockchip SDK中提供的UART默认配置已经使用了8250驱动我们就不需要修改。

二、硬件原理图

如下是硬件上通过高低电平转换将uart转化为rs232,rs232与网口进行连接。

三、设备树配置

rk平台的设备树修改路径都是在kernel\arch\arm64\boot\dts\rockchip下面,具体哪个文件根据对应开发板来决定,通常描述设备硬件配置在rkxxxx.dtsi中,比如在rk3568.dtsi中:

uart4: serial@fe680000 {

compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";

reg = <0x0 0xfe680000 0x0 0x100>;

interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;

clocks = <&cru SCLK_UART4>, <&cru PCLK_UART4>;

clock-names = "baudclk", "apb_pclk";

reg-shift = <2>;

reg-io-width = <4>;

dmas = <&dmac0 8>, <&dmac0 9>;

pinctrl-names = "default";

pinctrl-0 = <&uart4m0_xfer>;

status = "disabled";

};

我们需要修改的参数是pinctrl-0和status
uart4有两组数据线

uart4 {

  uart4m0_xfer: uart4m0-xfer {

   rockchip,pins =

    <1 4 2 &pcfg_pull_up>,

    <1 6 2 &pcfg_pull_up>;

  };

  uart4m1_xfer: uart4m1-xfer {

   rockchip,pins =

    <3 9 4 &pcfg_pull_up>,

    <3 10 4 &pcfg_pull_up>;

  };

 };

我们根据硬件的接法来选择对应的数据线,最后设备树的修改是:

&uart4 {

status = "okay";

pinctrl-names = "default";

pinctrl-0 = <&uart4m1_xfer>;

};

四、设备注册

dts中使能UART后,能在系统启动的log中看到以下对应的打印,表示设备正常注册:

root@linaro-alip:/# dmesg |grep ttyS4

[    0.235933] fe680000.serial: ttyS4 at MMIO 0xfe680000 (irq = 58, base_baud = 1500000) is a 16550A

普通串口设备将会根据dts中的aliase来对串口进行编号,对应注册成ttySx设备。注册的节点为/dev/ttyS4,命名规则是通过dts中的aliases来的。

aliases {

serial0 = &uart0;

serial1 = &uart1;

serial2 = &uart2;

serial3 = &uart3;

}

对应uart0注册为ttyS0,uart0注册为ttyS1,如果需要把uart3注册成ttyS1,可以进行以下修改

serial1 = &uart3;  

serial3 = &uart1;

五、串口调试功能

Rockchip UART作为控制台,使用fiq_debugger流程。Rockchip SDK一般会将uart2配置为ttyFIQ0设备。使用以下驱动文件:

drivers/staging/android/fiq_debugger/fiq_debugger.c drivers/soc/rockchip/rk_fiq_debugger.c  

arch/arm/mach-rockchip/rk_fiq_debugger.c

dtsfiq_debugger节点配置如下。由于fiq_debugger和普通串口互斥,

在使能fiq_debugger节点后必须禁用对应的普通串口uart节点。c

hosen: chosen {

bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0";

};

fiq-debugger {

compatible = "rockchip,fiq-debugger";

rockchip,serial-id = <2>;

rockchip,wake-irq = <0>;

/* If enable uart uses irq instead of fiq */

rockchip,irq-mode-enable = <1>;

rockchip,baudrate = <1500000>; /* Only 115200 and 1500000 */

interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_LOW>;

pinctrl-names = "default";

pinctrl-0 = <&uart2m0_xfer>;

status = "okay";

};

&uart2 {

status = "disabled";

};

rockchip,serial-id:使用的UART编号。修改serial-id到不同UARTfiq_debugger设备也会注册成

ttyFIQ0设备。

pinctrl-0:修改为对应的串口数据脚。

六、驱动调试

现在我们已经得到的串口四的驱动节点,这之后要测试硬件的连通性,比较简单直观的测试方法是对硬件上的TX、RX进行短接,然后输入命令 cat /dev/ttyS3 & 后台监控串口,收到会立即打印输出现在执行命令 echo “helloworld” > /dev/ttyS3如果观察到执行完命令 立即输出helloworld 则表明收发都是正常的

还有一种通用的测试方法是在开发板上跑一套应用程序,可以发送数据,可以接收数据。比如如下测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <termio.h>
#include <time.h>
#include <pthread.h>

int read_data(int fd, void *buf, int len);
int write_data(int fd, void *buf, int len);
int setup_port(int fd, int baud, int databits, int parity, int stopbits);
void print_usage(char *program_name);

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
int data_available = 0;

void *read_thread(void *arg) {
    int fd = *(int *)arg;
    char buffer[1024]; // 存储读取的数据

    while (1) {
        int bytes_read = read_data(fd, buffer, sizeof(buffer));
        if (bytes_read > 0) {
            printf("Read Thread: Read %d bytes: %s\n", bytes_read, buffer);
        } else {
            // 处理读取错误或设备关闭的情况
            break;
        }
    }
    
    pthread_exit(NULL);
}

void *write_thread(void *arg) {
    int fd = *(int *)arg;
	char input[1024]; // 存储用户输入的数据

    while (1) {
        printf("Enter data to write (or 'q' to quit): ");
        fgets(input, sizeof(input), stdin);

        if (strcmp(input, "q\n") == 0 || strcmp(input, "Q\n") == 0) {
            // 用户输入 'q' 或 'Q',退出循环
            break;
        }

        int len = strlen(input);
        int bytes_written = write_data(fd, input, len);
        if (bytes_written > 0) {
            printf("Write Thread: Wrote %d bytes: %s\n", bytes_written, input);
        }
    }
    
    pthread_exit(NULL);
}

int main(int argc, char *argv[]) //./a.out /dev/ttyS4 115200 8 0 1
{
    int fd;
    int baud;
    int len;
    int count;
    int i;
    int databits;
    int stopbits;
    int parity;

    if (argc != 6) {
        print_usage(argv[0]);
        return 1;
    }
 
    baud = atoi(argv[2]);
    if ((baud < 0) || (baud > 921600)) {
        fprintf(stderr, "Invalid baudrate!\n");
        return 1;
    }
 
    databits = atoi(argv[3]);
    if ((databits < 5) || (databits > 8)) {
        fprintf(stderr, "Invalid databits!\n");
        return 1;
    }
 
    parity = atoi(argv[4]);
    if ((parity < 0) || (parity > 2)) {
        fprintf(stderr, "Invalid parity!\n");
        return 1;
    }
 
    stopbits = atoi(argv[5]);
    if ((stopbits < 1) || (stopbits > 2)) {
        fprintf(stderr, "Invalid stopbits!\n");
        return 1;
    }
 
 
    fd = open(argv[1], O_RDWR, 0);
    if (fd < 0) {
        fprintf(stderr, "open <%s> error %s\n", argv[1], strerror(errno));
        return 1;
    }
 
    if (setup_port(fd, baud, databits, parity, stopbits)) {
        fprintf(stderr, "setup_port error %s\n", strerror(errno));
        close(fd);
        return 1;
    }
	pthread_t read_tid, write_tid;
    int ret;

    // 创建读取线程
    ret = pthread_create(&read_tid, NULL, read_thread, &fd);
    if (ret != 0) {
        fprintf(stderr, "Failed to create read thread\n");
        return 1;
    }

    // 创建写入线程
    ret = pthread_create(&write_tid, NULL, write_thread, &fd);
    if (ret != 0) {
        fprintf(stderr, "Failed to create write thread\n");
        return 1;
    }

    // 等待读取线程和写入线程结束
    pthread_join(read_tid, NULL);
    pthread_join(write_tid, NULL);
	
    close(fd);
 
    return 0;
}

static int baudflag_arr[] = {
    B921600, B460800, B230400, B115200, B57600, B38400,
    B19200,  B9600,   B4800,   B2400,   B1800,  B1200,
    B600,    B300,    B150,    B110,    B75,    B50
};
static int speed_arr[] = {
    921600,  460800,  230400,  115200,  57600,  38400,
    19200,   9600,    4800,    2400,    1800,   1200,
    600,     300,     150,     110,     75,     50
};

int speed_to_flag(int speed)
{
    int i;
 
    for (i = 0;  i < sizeof(speed_arr)/sizeof(int);  i++) {
        if (speed == speed_arr[i]) {
            return baudflag_arr[i];
        }
    }
 
    fprintf(stderr, "Unsupported baudrate, use 9600 instead!\n");
    return B9600;
}

static struct termio oterm_attr;

int setup_port(int fd, int baud, int databits, int parity, int stopbits)
{
    struct termio term_attr;
 
    
    if (ioctl(fd, TCGETA, &term_attr) < 0) {
        return -1;
    }
 
    
    memcpy(&oterm_attr, &term_attr, sizeof(struct termio));
 
    term_attr.c_iflag &= ~(INLCR | IGNCR | ICRNL | ISTRIP);
    term_attr.c_oflag &= ~(OPOST | ONLCR | OCRNL);
    term_attr.c_lflag &= ~(ISIG | ECHO | ICANON | NOFLSH);
    term_attr.c_cflag &= ~CBAUD;
    term_attr.c_cflag |= CREAD | speed_to_flag(baud);
 
    
    term_attr.c_cflag &= ~(CSIZE);
    switch (databits) {
        case 5:
            term_attr.c_cflag |= CS5;
            break;
 
        case 6:
            term_attr.c_cflag |= CS6;
            break;
 
        case 7:
            term_attr.c_cflag |= CS7;
            break;
 
        case 8:
        default:
            term_attr.c_cflag |= CS8;
            break;
    }
 
    
    switch (parity) {
        case 1:  
            term_attr.c_cflag |= (PARENB | PARODD);
            break;
 
        case 2:  
            term_attr.c_cflag |= PARENB;
            term_attr.c_cflag &= ~(PARODD);
            break;
 
        case 0:  
        default:
            term_attr.c_cflag &= ~(PARENB);
            break;
    }
 
 
    
    switch (stopbits) {
        case 2:  
            term_attr.c_cflag |= CSTOPB;
            break;
 
        case 1:  
        default:
            term_attr.c_cflag &= ~CSTOPB;
            break;
    }
 
    term_attr.c_cc[VMIN] = 1;
    term_attr.c_cc[VTIME] = 0;
 
    if (ioctl(fd, TCSETAW, &term_attr) < 0) {
        return -1;
    }
 
    if (ioctl(fd, TCFLSH, 2) < 0) {
        return -1;
    }
 
    return 0;
}
 
 
int read_data(int fd, void *buf, int len)
{
    int count;
    int ret;
 
    ret = 0;
    count = 0;
 
    //while (len > 0) {
 
    ret = read(fd, (char*)buf + count, len);
    if (ret < 1) {
        fprintf(stderr, "Read error %s\n", strerror(errno));
        //break;
    }
 
    count += ret;
    len = len - ret;
 
    //}
 
    *((char*)buf + count) = 0;
    return count;
}
 
 
int write_data(int fd, void *buf, int len)
{
    int count;
    int ret;
 
    ret = 0;
    count = 0;
 
    while (len > 0) {
 
        ret = write(fd, (char*)buf + count, len);
        if (ret < 1) {
            fprintf(stderr, "Write error %s\n", strerror(errno));
            break;
        }
 
        count += ret;
        len = len - ret;
    }
 
    return count;
}

void print_usage(char *program_name)
{
    fprintf(stderr,
            "*************************************\n"
            "  A Simple Serial Port Test Utility\n"
            "*************************************\n\n"
            "Usage:\n  %s <device> <baud> <databits> <parity> <stopbits> \n"
            "       databits: 5, 6, 7, 8\n"
            "       parity: 0(None), 1(Odd), 2(Even)\n"
            "       stopbits: 1, 2\n"
            "Example:\n  %s /dev/ttyS4 115200 8 0 1\n\n",
            program_name, program_name
           );
}

使用交叉编译将可执行程序拷贝进开发板

prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc tx_rx.c -pthread

然后执行如下命令测试:

chmod 777 a.out

./a.out /dev/ttyS4 115200 8 0 1

这是一个自发自收程序,这样就不用与PC端建立连接了,对于测试连通性还是没问题的,当你将tx、rx短接之后你会看到如下打印:

而不进行短接就是如下打印:

 总结

串口的调试并不复杂,都是在SDK上已有的基础上进行修改,网上的例子也有很多,有兴趣的朋友可以在开源的基础上自己写一份串口测试的应用程序,这个过程才会有点收获,这里只是简单记录一下整个调试和测试过程。

  • 20
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

于山巅相见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值