您的位置:首页 >资讯 >

RK3588-UART

2023-06-11 00:59:25    来源:jf_30051736

前言

专栏总目录本文主要讲解如何关于RK3588开发板UART的使用和调试方法,包括UART作为普通串口和控制台两种不同使用场景

一. 功能特点

RockchipUART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准,完整模块支持以下功能:

支持5、6、7、8 bits数据位。支持1、1.5、2 bits停止位。支持奇校验和偶校验,不支持mark校验和space校验。支持接收FIFO和发送FIFO,一般为32字节或者64字节。支持最高4M波特率,实际支持波特率需要芯片时钟分频策略配合。支持中断传输模式和DMA传输模式。 支持硬件自动流控,RTS+CTS。

二、代码位置

Linuxkernel 中,使用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中提供的UART默认配置已经使用了8250驱动我们就不需要修改

三、硬件原理图

串口功能的硬件上比较简单,这是只附上调试串口的原理图

四、设备树配置

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

uart2: serial@feb50000 {compatible = "rockchip,rk3588-uart", "snps,dw-apb-uart";reg = < 0x0 0xfeb50000 0x0 0x100 >;interrupts = < GIC_SPI 333 IRQ_TYPE_LEVEL_HIGH >;clocks = < &cru SCLK_UART2 >, < &cru PCLK_UART2 >;clock-names = "baudclk", "apb_pclk";reg-shift = < 2 >;reg-io-width = < 4 >;dmas = < &dmac0 10 >, < &dmac0 11 >;pinctrl-names = "default";pinctrl-0 = < &uart2m1_xfer >;status = "disabled";};

4.1作为普通串口

假入我们想使用w3开发板上40PIN上的uart7

我们在dts可以使用如下配置打开

&uart7 {status = "okay";pinctrl-names = "default";pinctrl-0 = < &uart7m1_xfer >;};

4.2作为调试串口

Rockchip UART作为控制台,使用fiq_debugger流程。

在dts中fiq_debugger节点配置如下。由于fiq_debugger和普通串口互斥,在使能fiq_debugger节点后必须禁用对应的普通串口uart节点。

chosen: 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 insteadof 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到不同UART,fiq_debugger设备也会注册成ttyFIQ0设备。 rockchip,irq-mode-enable:配置为1使用irq中断,配置为0使用fiq中断。interrupts:配置的辅助中断,保持默认即可。pinctrl-0:使用的串口引脚rockchip,baudrate:波特率配置

五、串口相关问题

5.1设备注册

普通串口设备将会根据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;

5.2控制台打印相关

Rockchip UART打印通常包括DDR阶段、Miniloader阶段、TF-A (Trusted Firmware-A)阶段、OP-TEE阶段、Uboot阶段和Kernel阶段,我们平时主要关注的是uboot阶段和kernel阶段的打印,在这两个阶段我们可以尝试关闭所有打印或切换所有打印到其他UART,RK平台默认的调试串口是uart2_m0这一组引脚,假如现在我将打印换成其他串口,可以尝试以下做法。

5.2.1DDR Loader修改方法

DDR Loader中关闭或切换打印,需要修改DDR Loader中的UART打印配置,修改文件rkbin/tools/ddrbin_param.txt中的以下参数:

uart id= # UART控制器id,配置为0xf为关闭打印

uart iomux= # 复用的IOMUX引脚 uart

baudrate= # 115200 or 1500000

修改完成后,使用以下命令重新生成ddr.bin固件。

./ddrbin_tool ddrbin_param.txt rk3588_ddr_lp4_2112MHz_lp5_2736MHz_v1.09.bin

5.2.2Uboot修改方法

Uboot中关闭打印,需要在menuconfig中,打开配CONFIG_DISABLE_CONSOLE,保存到.config文件

Uboot中切换打印,由传参机制决定,不需要进行额外修改。uboot解析传参机制相关代码在arch/arm/mach-rockchip/board.c的board_init_f_init_serial()函数中。

5.2.3kernel修改方法

去掉打印需要在menuconfig中,关闭配置CONFIG_SERIAL_8250_CONSOLE。

Device Drivers --->

Character devices --->

Serial drivers --->

[ ]Console on 8250/16550 and compatible serial port

在dts配置中找到类似以下内容,并去掉UART基地址和console相关配置参数

chosen: chosen {bootargs = "earlycon=uart8250,mmio32,0xfeb50000 console=ttyFIQ0 irqchip.gicv3_pseudo_nmi=0 root=PARTUUID=614e0000-0000 rw rootwait";};

将0xfeb50000 console=ttyFIQ0 去掉,然后找到fiq-debugger节点,修改serial-id为0xffffffff,去掉UART引脚复用相关配置。注意,需要保持fiqdebugger节点使能,保持fiq-debugger流程系统才能正常启动

fiq_debugger: fiq-debugger {compatible = "rockchip,fiq-debugger";rockchip,serial-id = < 0xffffffff >;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 423 IRQ_TYPE_LEVEL_LOW >;status = "okay";};

切换打印串口例如将Kernel打印从UART2切换到UART3,在dts配置中找到类似以下内容,将UART基地址由UART2改为UART3.

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

0xfe670000是UART3基地址,然后找到fiq-debugger节点,修改serial-id为3,修改UART3引脚复用配置pinctrl-0 = <&uart3m0_xfer>。注意,同时需要将切换为打印串口的UART3作为普通串口的节点禁用。

六、串口测试

在开发板上跑一套应用程序,可以发送数据,可以接收数据,测试方法可以短接TX_RX

#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           );}

运行效果如下:

审核编辑:汤梓红

标签:

相关阅读

国际档案日:侨批漫画述乡愁 “迁台记忆”连两岸 当前快看

香港岭南大学分数线河北(香港岭南大学分数线)

张怡宁将执教印度乒乓球队?假的!

每日快讯!霍尊前女友被移交检方,为900万搭上一生,小作文以后不好使了?

入党谈心谈话记录_与入党积极分子谈话记录|全球热闻

放开那三国(放开)

国铁:今起12306试行在线选铺服务 想要下铺以后要拼手速啦!

天天观天下!上海嘉定一货车在运输途中发生爆燃,火已扑灭无人员伤亡

每日看点!血腥风格银河恶魔城新作《Cookie Cutter》实机预告赏

暗黑4索命陷阱技能有什么作用效果

暗黑4暗夜凝望裤子有什么特点-天天观焦点

【当前热闻】手机奇迹mu单机版哪个好 手机奇迹mu单机版大全前十

暗黑4高效率获得通用类威能方法介绍

焦点关注:开箱子csgo网站茄子安全吗 八大最便宜csgo国外开箱子网站排名

暗黑4天生领袖巅峰效果介绍 天天新视野

暗黑4bd威能流程介绍

《匹诺曹的谎言》公开demo上线steam!参与活动赢官方游戏周边_世界观察

科幻恐怖FPS新作《RIPOUT》预告 展示危险场景 今日热文

【天天聚看点】高爆率传奇刀刀切割版手游大全 高爆率传奇刀刀切割版推荐

英国监管机构阻止微软收购动视暴雪 后者获准参与上诉程序

欧洲月榜:受塞尔达影响 Switch销量5月提升39%

暗黑4超强电流之威能介绍

66skins开箱官网哪个优惠 八大最正规csgo箱子开箱网站合集

苹果Epic对决或重燃:双方均申请重新审议判决

《潜行者2》确认不参加6月的Xbox发布会

最新奇迹私服哪个最靠谱 最新奇迹私服十大推荐一览

【世界新要闻】暗黑4吞噬火焰之威能介绍

暗黑4冰川威能介绍_焦点讯息

环球即时:出手迅速!俄罗斯黑客宣布破解 《暗黑破坏神4》

buff开箱网正规吗 十大最专业csgo外国开箱子网站排名|全球报资讯

《底特律:化身为人》发行商公布独立发行厂牌

天天热点!网传任天堂将与环球影业合作推出《塞尔达传说大电影》

暗黑4好运之威能介绍

csgo开箱网哪个便宜 五大最专业开箱子csgo网站模拟器大全 全球简讯

老胖秃的深海大冒险 《潜水员戴夫》正式版月底发售

暗黑4不破电索之威能介绍

观速讯丨csgo开箱网站007网址 十大最安全csgo国服开箱子网站介绍

今日精选:夏日游戏节缺乏女性身影 遭多位外媒记者和推特大V批评

《刺客信条:幻景》加入怀旧滤镜 可让画面更像1代|世界视讯

农业战略格局示意图(七区二十三带农业战略格局) 速看

焦点热文:索尼将不会参加今年的德国科隆游戏展

暗黑4绞杀者的威能介绍|焦点消息

MMORPG《王权与自由》公开全新宣传视频 全球测试遥遥无期

《刺客信条:幻景》幕后视频1:回归系列本源

PC新作《匹诺曹的谎言》9月19日正式上线 现已推出体验版

个人社保账户查询官网(如何查询个人失业保险账户)

每日热点:《FF7:重生》官方游戏介绍 新游戏截图公布:爱丽丝绝美

【速看料】《暗黑破坏神4》最细剧情详解之第三章:不死之身伊莱亚斯

魔兽故事:另一条时间线?暴雪多元宇宙故事再拓展,瓦里安国王死而复活! 今日精选

正惊推游:暗黑系列中最大女魔头回归《暗黑4》,为什么玩家却纷纷喊妈妈? 天天快播

头条:怀旧周报:《暗黑4》成为搬砖党噩梦?《天下贰》复活引发玩家热议!

ipc认证是什么意思(IPC是什么意思) 天天视讯

《原神》可以快速抓晶蝶的点位有哪些 晶蝶传送点速刷攻略分享

世界速看:《波斯王子:失落的王冠》受到大量负面评价 不喜欢占多数

《命运2》终局活动日落PvE武器怎么选 终局活动日落PvE武器推荐

《铁拳8》即将开启封测 7月28日至31日

《蛋仔派对》一周年庆典即将开启 大神APP邀你预约庆典赢大奖!|环球消息

《命运2》深岩墓室称号怎么获得 深岩墓室称号成就攻略

焦点速看:中广欧特斯中央空调怎么样_中广欧特斯

《崩坏星穹铁道》1.1版本全角色攻略大全 全角色光锥遗器搭配推荐一览 全球要闻

朋友圈怎么只发文字的文案(朋友圈怎么只发文字) 热资讯

全国青少年:夏游贵州免景区门票 在“惠游”中有所学

为带来更好的游戏体验 《消逝的光芒2》DLC延期至明年

七日无理由退货=七日免费试穿(用)?江苏省消保委:无理由并不等于无限制_全球即时看

AMD RX7900XT欧洲价格暴跌 现已不到800欧元_当前速讯

《漫威蜘蛛侠2》毒液并不是Eddie Brock

传SE重制《最终幻想10》 计划2026年发售|天天播资讯

6月份Steam新品节公开 6月19日至26日 世界热讯

王者荣耀花木兰图片q版_王者荣耀花木兰图片|每日速看

全球即时看!1.8l的车型有哪些(1.8l的家用轿车有哪些?)

《暗黑4》联动时装惊艳全场!来《暗黑破坏神:不朽》一秒变身莉莉丝

天天关注:没有充值的奇迹手游十大排行榜 没有充值的奇迹手游有哪些

《我在修仙世界御剑除魔》值得体验的修仙题材割草游戏

世界观焦点:《战锤40K:星际战士2》全新合作战役模式预告公布

不充钱的奇迹手游有哪些 不充钱的奇迹手游十大排行榜

《火力苏打》宝箱机制是什么 宝箱机制介绍|世界看热讯

原神V3.2完美一键端剧情完整无措做到底,深渊和秘境能刷|焦点速看

全球资讯:没有充值的奇迹手游合集 没有充值的奇迹手游十大推荐

暗黑4急速烟尘之威能介绍

最实用的CSGO开箱网站有哪些 CSGO公认最好的开箱网站分享2023

《重启》普通生物战斗能力测试开始 加入光荣的进化吧!-当前头条

天天观热点:《漫威蜘蛛侠2》10月20日发售 6月16日开启预售

环球观察:人气乙女游戏续作《失忆症 Amnesia: Later x Crowd》 发售日期正式解禁!

每日短讯:人气「友尽」游戏《胡闹搬家2(Moving Out 2)》即将推出!

塞尔达传说王国之泪蔬菜浓汤怎么做

狐与蛙之旅有什么背景剧情

当前热文:《英雄联盟》将新增段位“翡翠” 取消晋级赛

csgo最好的皮肤交易平台是哪个 十大最火的CSGO皮肤交易平台推荐

最资讯丨csgo网站开箱如何取货 即开即取超高爆率的CSGO开箱网站分享

另一款奇幻游戏需要什么配置

【播资讯】俄罗斯黑客出手 《暗黑破坏神4》被迅速攻破了

环球资讯:青丘物语需要什么配置

焦点速看:《超侦探事件簿 雾雨谜宫》推出最新Web CM「幽玛篇」!

奇迹卓越高爆版手游合集 奇迹卓越高爆版手游推荐 要闻速递

除了赛1000,钱江的“赛650”也要登场了

Ultros有什么背景剧情_世界微动态

【世界速看料】mu奇迹手游正规版本合集 mu奇迹手游正规版本盘点

GTAOL的体验改进即将到来 每日短讯

奇迹手游超级变态版合集 奇迹手游超级变态版手游推荐

《逍遥情缘》手游人人趋之若鹜的特殊技能,到底有多好用?

《逍遥情缘》可爱珍兽要这么入手 从今天开始苦练招财进宝吧|全球观焦点

环球观察:2023LPL夏季赛战报:全线碾压不给机会 NIP击败AL获胜

失忆症地堡极端的捕鼠者怎么解锁

礼包丰厚CSGO开箱价格低网站 csgo最好的皮肤交易平台是哪个?

对萌新友好的CSGO开箱网站 安全性最高的CSGO皮肤饰品交易平台介绍

失忆症地堡弹震症怎么解锁_全球讯息

手续费低的csgo租刀平台有吗 十大最好用的CSGO饰品租赁平台榜单 全球热消息

茄子力推CSGO开箱网站秒到库存 CSGO开箱教程2023最新分享