Banana Pi BPI-W3(Armsom W3)RK3588开当板之调试UART

前言

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

一. 功能特点

Rockchip UART (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。

二、代码位置

在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中提供的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 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到不同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引脚复用相关配置。注意,需要保持fiq�debugger节点使能,保持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);
}

运行效果如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117852.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

gRPC之gateway集成swagger

1、gateway集成swagger 1、为了简化实战过程&#xff0c;gRPC-Gateway暴露的服务并未使用https&#xff0c;而是http&#xff0c;但是swagger-ui提供的调用服 务却是https的&#xff0c;因此要在proto文件中指定swagger以http调用服务&#xff0c;指定的时候会用到文件 prot…

深入浅出Dart》函数

函数 Dart语言作为一门面向对象的语言&#xff0c;函数在其中占据了非常重要的位置。本文将详细解析如何定义和调用函数&#xff0c;以及箭头语法&#xff0c;高阶函数和闭包等内容。 1. 函数的定义和调用 在Dart中&#xff0c;函数可以定义为一段实现特定功能的代码块&…

Pytorch使用torchvision.datasets.ImageFolder读取数据集,数据集的内容排列状况

当使用torchvision.datasets.ImageFolder读取猫狗数据集时,dataset中存的图片是 猫狗猫狗猫狗猫狗 还是 猫猫猫猫狗狗狗狗 呢? 数据集文件的存放路径如下图 测试代码如下 import torch import torchvisiontransform torchvision.transforms.Compose([torchvision.transform…

易点易动设备管理系统:提升企业备件管理和维修效率的智能解决方案

在当今竞争激烈的商业环境中&#xff0c;大型生产制造企业面临着日益复杂的设备管理挑战。为了提高生产效率和降低成本&#xff0c;企业需要一种高效的设备管理系统。易点易动设备管理系统是一款智能化的解决方案&#xff0c;旨在帮助企业提升备件管理和维修效率。本文将详细介…

C++笔记之初始化二维矩阵的方法

C笔记之初始化二维矩阵的方法 —— 2023年5月20日 上海 code review! 文章目录 C笔记之初始化二维矩阵的方法一.常见方法1. 使用数组2. 使用向量3. 使用数组的动态分配4. 使用嵌套的 std::vector 并使用resize方法5. 初始化固定大小的 std::array 二.C中使用vector初始化二维矩…

Django token 认证原理与实战

概述 cookie、session 与token 的区别 Cookie的作用 cookie的存储量很小&#xff0c;一般不超过4Kcookie并不会保存很多信息&#xff0c;一般用来存储登录状态cookie是以键值对进行表示的(keyvalue),例如nameli,表示cookie的名字是name,cookie携带的值是licookie的存储分为会…

蓝桥杯双周赛算法心得——摆玩具(分段的差不计入结果)

大家好&#xff0c;我是晴天学长&#xff0c;这是一道可以牢记的数学技巧题。 1) .摆玩具 2) .算法思路 1.创建一个Scanner对象以从用户读取输入。 2.从用户读取n和k的值&#xff0c;分别表示数组的大小和k的值。 3.创建一个大小为n的整数数组a&#xff0c;用于存储输入的数…

Django实现音乐网站 (22)

使用Python Django框架做一个音乐网站&#xff0c; 本篇音乐播放器功能完善&#xff1a;顺序播放、设置播放数、歌词滚动等功能。 目录 顺序播放 设置顺序播放 单曲播放数 添加路由 视图处理 模板处理 歌词滚动 视图内容返回修改 样式设置 模板内容 歌词滚动脚本 歌…

uniapp开发小程序—根据生日日期计算年龄 周岁

0、需求 在UniApp开发小程序中&#xff0c;将接口返回的出生日期转化为年龄&#xff1b;判断接口返回的年龄是否是周岁 可以使用JavaScript的日期处理方法来实现。 一、第一种方式&#xff08;示例代码&#xff09;&#xff1a; //javascript // 假设接口返回的年龄为生日的…

TCP通信实战案例-即时通信

即时通信是什么含义&#xff0c;要实现怎么样的设计&#xff1f; 即时通信&#xff0c;是指一个客户端的消息发出去&#xff0c;其他客户端可以接收到。 即时通信需要进行端口转发的设计思想。 服务端需要把在线的Socket管道存储起来。 一旦收到一个消息要推送给其他管道。…

Json字符串转换小工具

下载【免费】Json字符串格式化和压缩&#xff0c;支持数组元素的不换行且能转换成16进制资源-CSDN文库 推荐理由&#xff1a; 离线使用支持json字符串的数据格式化和压缩&#xff0c;如&#xff1a;图1支持数组元素的16进制转换&#xff0c;如&#xff1a;图2支持数组元素不换…

小主机折腾记18

这个月冲动消费了小两千块钱…… 1.880g5twr 由于四根2400t的内存条没有用出去&#xff0c;我又把它们装回了惠普的800g5twr&#xff1b; 看到pdd有400块钱的9350K&#xff0c;于是想着给他上一个9350k 在参考了pdd、咸鱼以及淘宝的价格后&#xff0c;我发现400块钱的9350k都…

Autojs 利用OpenCV识别棋子之天天象棋你马没了

本例子通过代码像你介绍利用OpenCV实现霍尔找圆的方法定位棋子位置 通过autojs脚本实现自动点击棋子 开源地址 https://github.com/Liberations/TtxqYourHorseIsGone/blob/master/main.js AutoXJs https://github.com/kkevsekk1/AutoX/releasesauto() //安卓版本高于Android 9…

Plex踩坑——plex web无法找到媒体服务器

现象&#xff1a;之前安装过plex server&#xff0c;然后卸载了。再次重装后&#xff0c;plex web无法找到媒体服务器。 原因&#xff1a;卸载plex server时需要手动将plex的注册表删除&#xff1a;HKEY_CURRENT_USER\Software\Plex, Inc. 原文链接Uninstall Plex Media Serve…

wf-docker集群搭建(未完结)

系列文章目录 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、redis集群二、mysql集群三、nacos集群1. 环境要求2. 拉取镜像2.1. 拉取镜像方式配置集群2.2. 自定义nacos镜像配置集群 3 自定义…

C语言 sizeof 函数内部进行计算

直接看代码 #include <stdio.h> int main() {int i 2;int j;j sizeof(i i);printf("i %d, j %d", i ,j);return 0; }执行结果&#xff1a; 可以看到 i的值一直是没有变的&#xff0c; j 是int类型下 sizeof占用的大小为 4个字节&#xff0c;不是i的 22…

NewStarCTF2023week4-More Fast(GC回收)

打开链接&#xff0c;存在很多个类&#xff0c;很明显是php反序列化漏洞利用&#xff0c;需要构造pop链 &#xff0c; 关于pop链构造的详细步骤教学&#xff0c;请参考我之前的博客&#xff0c;真的讲得很详细也容易理解&#xff1a; http://t.csdnimg.cn/wMYNB 如果你是刚接…

代碼隨想錄算法訓練營|第四十五天|1049. 最后一块石头的重量 II、494. 目标和、474.一和零。刷题心得(c++)

目录 讀題 1049. 最后一块石头的重量 II 自己看到题目的第一想法 看完代码随想录之后的想法 494. 目标和 自己看到题目的第一想法 看完代码随想录之后的想法 474.一和零 自己看到题目的第一想法 看完代码随想录之后的想法 1049. 最后一块石头的重量 II - 實作 思路 …

宏电5G RedCap工业智能网关获首个中国移动5G物联网开放实验室5G及轻量化产品能力认证

10月21日&#xff0c;2023世界物联网博览会——中国移动物联网开发者大会暨物联网产业论坛在无锡圆满举行。宏电股份参与中国移动5G物联网开放实验室5G及轻量化产品能力认证成果授牌仪式&#xff0c;并获得认证证书。 此次认证主要对产品功能、产品性能、RedCap网络兼容性进行测…

sw型材利用父子关系找最新特征

利用父子关系可以找到设计树最新的编辑