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);
}
运行效果如下:


相关文章:
Banana Pi BPI-W3(Armsom W3)RK3588开当板之调试UART
前言 本文主要讲解如何关于RK3588开发板UART的使用和调试方法,包括UART作为普通串口和控制台两种不同使用场景 一. 功能特点 Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准,完整模块支持以下功能: 支…...
LeetCode88——合并两个有序数组
LeetCode88——合并两个有序数组 1.题目描述: 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减…...
C++ BinarySercahTree recursion version
for循环版本的:C BinarySercahTree for version-CSDN博客 Inorder()在c BinarySerschTree for verison写了。 还是按照那种嵌套的方式来写递归。 现在来写查找 FindR() bool FindR(){return _FindR(_root);}然后_FindR()函数写递归具体实现: 假设要…...
兑换码生成与解析-个人笔记(java)
1.需求分析 兑换码长度为10字符,包含24个大写字母和8个数字。兑换码需要保证唯一性,不可重复兑换。需要防止猜测和爆刷攻击。兑换码生成和验证的算法需要高效,避免对数据库带来较大的压力。 导航 1.需求分析2.实现方案3.加密过程4.解密过程5…...
2023/10/25MySQL学习
外键约束 在子表添加外键后,不能在主表删除或更新记录,因为存在外键关联 删除外键,注意外键名称时我们添加外键时起的名称 使用cascade操作后,可以操作主表数据,并且子表的外键也会对应改变 set null的话,删除主表对应主键信息后,子表对应外键信息变为空 多表关系 创建中间表 可…...
网络协议--Ping程序
7.1 引言 “ping”这个名字源于声纳定位操作。Ping程序由Mike Muuss编写,目的是为了测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显应答(图6-3列出了所有的ICMP报文类型)。 一般来说&#x…...
如何在 Azure 容器应用程序上部署具有 Elastic Observability 的 Hello World Web 应用程序
作者:Jonathan Simon Elastic Observability 是提供对正在运行的 Web 应用程序的可见性的最佳工具。 Microsoft Azure 容器应用程序是一个完全托管的环境,使你能够在无服务器平台上运行容器化应用程序,以便你的应用程序可以扩展和缩减。 这使…...
JAVA排序
再看各种排序前我们先了解一下什么叫 稳定性 比如一组数据arr[i]下标与arr[j下标]相等,arr[i]在前面,arr[j]在arr[i]后面,排序后这两个数据仍然是arr[i]在arr[j]前面,arr[j]在arr[i]后面,这就叫稳定 插入排序: 优势: 越有序查找速度越快 时间复杂度: O(N^2) 空间复…...
opencalib中lidar2camera安装记录
目录 一、opencalib安装 二、lidar2camera的安装 三、测试运行 四、出现过的问题 一、opencalib安装 代码地址:https://github.com/PJLab-ADG/SensorsCalibration/blob/master/README.md # pull docker image sudo docker pull scllovewkf/opencalib:v1 # Aft…...
整个自动驾驶小车001:概述
材料: 1,树梅派4b,作为主控,这个东西有linux系统,方便 2,HC-S104超声波模块,我有多个,不少于4个,我可以前后左右四个方向都搞一个 3,l298n模块,…...
windows本地搭建mmlspark分布式机器平台流程
文章目录 windows本地搭建mmlspark分布式机器平台流程安装环境pyspark环境spark环境java环境hadoop环境1.修改hadoop配置文件下的jdk地址为自己的实际地址2.修改bin文件离线环境jar包环境1mmlsprk第三方包jar包环境2参考代码我有话说其他问题记录概要参考文献windows本地搭建mm…...
深入探究 Next.js 中的 getServerSideProps 和 getStaticProps 用法及区别
引言: Next.js 是一个流行的 React 框架,它提供了许多强大的功能来简化服务器渲染(SSR)和静态生成(SSG)的过程。其中,getServerSideProps 和 getStaticProps 是两个重要的函数,用于在…...
餐饮业如何高效经营?赶紧闭眼抄这个方法!
在现代社会,餐饮业已经成为人们日常生活中不可或缺的一部分。为了提高食堂运营效率,满足不断增长的客户需求,智慧收银系统应运而生。 智慧收银系统帮助食堂业主更好地理解其客户,提高服务质量,优化库存管理,…...
餐饮外卖小程序商城的作用是什么
随着互联网及线上餐饮的发展趋势,行业洗牌正在加速,并且对餐饮连锁门店提出更高要求,餐饮数字化转型加快,积极发展线上经营是不少餐饮商家的首选。这其中,餐饮外卖商城成为很多餐饮品牌的线上经营品牌,也是…...
nRF52832 SDK15.3.0 基于ble_app_uart demo FreeRTOS移植
参考资料:Nrf52832 freeOS系统移植_nrf5283操作系统-CSDN博客 这里把移植经验记录下来,供有需要的同学参考,有不对的地方也请大家批评指正。 把FreeRTOS移植到 nRF5_SDK_15.3.0_59ac345\examples\ble_peripheral\ble_app_uart工程ÿ…...
电厂数据可视化三维大屏展示平台加强企业安全防范
园区可视化大屏是一种新型的信息化手段,能够将园区内各项数据信息以图像的形式直观呈现在大屏幕上,便于管理员和员工进行实时监控、分析和决策。本文将从以下几个方面介绍园区可视化大屏的作用和应用。 VR数字孪生园区系统是通过将实际园区的各种数据和信…...
2246: 【区赛】【宁波32届小学生】最佳交换
目录 题目描述 输入 输出 样例输入 样例输出 提示 代码 题目描述 星星小朋友和 N-1 个小伙伴一起玩了一上午的纸牌游戏,星星总是能赢,气焰嚣张, 小伙伴们决定出道纸牌问题难倒星星,让他别再狂妄自大了,问题是这…...
Java面试记录
文章目录 1、final关键字2、synchronized关键字(1)synchronized的功能:(2)synchronized的底层实现原理: 3、Java中线程同步的实现方法(1). 使用synchronized关键字:&…...
【数据库】聚集函数
聚集函数 聚集函数一览AVG() 函数COUNT() 函数MAX() 函数MIN() 函数SUM() 函数 组合聚集函数 聚集函数一览 我们需要汇总数据而不是实际检索,此时我们使用聚集函数进行处理; 聚集函数一览表如下: 函数说明AVG()返回平均值COUNT()返回数量总…...
【单元测试】--编写单元测试
一、编写第一个单元测试 编写第一个单元测试通常包括以下步骤。以下示例以C#和NUnit为例: 创建测试项目: 在Visual Studio中,创建一个新的Class Library项目,这将是你的单元测试项目。在解决方案资源管理器中,右键点…...
Xshell远程连接Kali(默认 | 私钥)Note版
前言:xshell远程连接,私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
