RK3568串口调试

这篇具有很好参考价值的文章主要介绍了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然后回车,会看下如下页面:

/dev/ttys4,驱动开发,linux,嵌入式硬件

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

二、硬件原理图

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

/dev/ttys4,驱动开发,linux,嵌入式硬件

/dev/ttys4,驱动开发,linux,嵌入式硬件

三、设备树配置

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短接之后你会看到如下打印:

/dev/ttys4,驱动开发,linux,嵌入式硬件

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

/dev/ttys4,驱动开发,linux,嵌入式硬件

 总结

串口的调试并不复杂,都是在SDK上已有的基础上进行修改,网上的例子也有很多,有兴趣的朋友可以在开源的基础上自己写一份串口测试的应用程序,这个过程才会有点收获,这里只是简单记录一下整个调试和测试过程。文章来源地址https://www.toymoban.com/news/detail-594753.html

到了这里,关于RK3568串口调试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RK3568驱动OV13850摄像头模组调试过程

    品牌:Omnivision 型号:CMK-OV13850 接口: MIPI 像素:1320W OV13850彩色图像传感器是一款低电压、高性能1/3.06英寸1320万像素 CMOS图像传感器 ,使用OmniBSI+?技术提供了单-1320万像素(4224×3136)摄像头的功能。通过串行摄像头控制总线(SCCB)接口的控制,它提供了全帧、下采样、开窗的

    2023年04月27日
    浏览(41)
  • Linux驱动开发一、RK3568把hello编译到Linux内核中运行。‘rk_vendor_read’未定义的引用

    1、在字符设备目录下建立hello目录 ~/Linux/rk356x_linux/kernel/drivers/char/hello 2、进入hello目录,新建hello.c、Makefile、Kconfig三个文件 3、Kconfig是打开make menuconfig配置界面是后的选项,这Kconfig是在字符设备下的。 config后面的HELLO就是对应配置后在kernel目录下的**.config中的CONFIG_HELLO配置

    2024年02月11日
    浏览(52)
  • rk3568驱动开发之mipi屏

    屏是嵌入式驱动开发中常见的设备,一般的带屏项目中最开始要调试的,简单记录一下自己在项目开发中的经验过程。所用平台是rockchip的rk3568,android11。 硬件原理图主要看接的是哪个mipi接口,屏的电源控制io,背光控制io,这些需要在设备树中配置的要仔细核对。 PS:以上

    2024年02月12日
    浏览(28)
  • RK3568平台开发系列讲解(调试篇)如何跟踪系统事件

    沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本文我们要介绍 Linux 上两个非常有用的工具: ltrace 和 strace 。在分析软件的运行过程、调试疑难 Bug 、执行性能分析和调优等方面,它们都是非常有用的辅助工具。另外,我们还会介绍与之相关的一个功能强大的系统调

    2023年04月18日
    浏览(48)
  • RK系列(RK3568) MIPI屏适配 PWM背光 驱动开发

    平台:Android12 SOC:RK3568 一般拿到MIPI屏 厂家会给一个跑的配置文件  例如我的: 那么可以配置设备树 由于我接的是DSI0 backlight = backlight; 设置背光

    2024年02月07日
    浏览(28)
  • RK3568平台开发系列讲解(调试篇)常见的性能优化手段

    🚀返回专栏总目录 沉淀、分享、成长#

    2023年04月11日
    浏览(59)
  • 迅为RK3568开发板驱动开发指南-输入子系统

    《iTOP-RK3568开发板驱动开发指南》 更新,本次更新内容对应的是驱动 (第十三篇 输入子系统) 视频,帮助用户快速入门,大大提升研发速度。 第13篇-输入子系统 目录 第1篇 驱动基础篇 第2篇 字符设备基础 第3篇 并发与竞争 第4篇 高级字符设备进阶 第5篇 中断 第6篇 平台总

    2024年03月26日
    浏览(35)
  • rk3568_linux5.10 调试6275p pcie wifi

    a : config 部分必须选择 Device Drivers --- Network device support --- Wireless LAN --- Rockchip Wireless LAN support --- [*] build wifi ko modules [*] Broadcom Wireless Device Driver Support --- 选择好路径: (/system/etc/firmware/fw_bcm43752a2_pcie_ag.bin) Firmware path (/system/etc/firmware/nvram_ap6275p.txt) NVRAM path Enable Chip Interface (

    2024年02月08日
    浏览(46)
  • RK3568平台开发系列讲解(驱动基础篇)自动创建设备节点

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢自动创建设备节点分为两个步骤: 步骤一:使用 class_create 函数创建一个类。 步骤二:使用 device_create 函数在我们创建的类下面创建一个设备。 Linux 驱动实验中,当我们通过 insmod 命令加载模块后,

    2023年04月12日
    浏览(44)
  • RK3568平台开发系列讲解(调试篇)如何控制普通进程的优先级

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在本篇将介绍 Linux 中影响进程被内核调度的参数,并介绍控制这些参数的系统调用及它们的使用方法。深入理解它们的作用和底层的实现机理 如果没有特别指定,在 Linux 中创建的进程都是普通进程

    2023年04月18日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包