前言
通过io 操作实现点亮LED灯操作。 但是IO操作的都是寄存器地址,所以先搞清楚寄存器知识点。
难点
- 对于部分嵌入式同学来说,都是很基础的家常便饭,但是对于上层应用转嵌入式、驱动工程师来说,比较难。
- 不理解寄存器基本知识点、看操作手册比较困难,无法理解基础知识点等 。
参考资料
GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO
RK3568驱动指南|第十二篇 GPIO子系统-第129章 GPIO控制和操作实验
使用C程序通过sysfs文件系统控制gpio
rk3588写入寄存器
linux IO指令 读写GPIO口电平实例
- 这里目标是LED灯点亮实验,核心知识点是认识寄存器地址并进行操作。
- 了解、学习, 寄存器相关知识点、IO指令操作。
一、寄存器知识点-复用寄存器-方向寄存器-数据寄存器
需求是通过寄存器地址,直接控制LED亮灭! 那么复用、方向、数据这些其实我们之前就已经了解过。 GPIO 来实现外设LED灯的亮灭,那么几个基本知识点:
- 复用配置:配置pin脚给GPIO功能【本身pin脚复用中有很多功能可以复用,如果实现LED灯亮灭,那么你这个pin脚第一步要实现 复用给GPIO口来用】
- 方向配置:方向就是极性、输入输出一说。如果控制外设,那么就是输出 out;如果是接收信号 来实现事件监听,比如判断高低电平变化、按键是否被按下,那么就是输入 in。
- 数据:切实就是配置高低电平,配置的底层逻辑就是配置寄存器的值。
上面分析下其实就是不同阶段对应的不同寄存器。
区别和联系
| 寄存器 | 比喻 | 作用 | 在点亮LED实验中的操作 |
|---|---|---|---|
| 复用寄存器 | 选择水龙头出口的模式 | 配置引脚的基本功能(GPIO?UART?) | 设置为 GPIO 功能 |
| 方向寄存器 | 决定水流方向(流出/吸入) | 配置GPIO引脚为输入或输出 | 设置为 输出 模式 |
| 数据寄存器 | 拧开水龙头开关 | 设置输出电平的高低 写入 ‘1’ 或 ‘0’ | 来点亮或熄灭 |
实现LED灯亮灭步骤-操作寄存器步骤
它们之间的联系和工作流程(点亮LED的编程步骤):
这是一个严格的、有逻辑依赖关系的配置顺序:
-
1、先配置【复用寄存器】:告诉芯片:“这个引脚我要用来做普通的GPIO,请不要把它用作其他用途(比如串口)。”
-
2、再配置【方向寄存器】:告诉GPIO:“你现在处于输出模式,你要去驱动外部设备(LED),而不是读取外部信号。”
-
3、最后操作【数据寄存器】:给GPIO一个明确的指令:“输出高电平(1)”或者“输出低电平(0)”,从而控制LED的亮灭。
简单来说:
复用寄存器决定了引脚是不是一个GPIO。
方向寄存器决定了这个GPIO是输出还是输入。
数据寄存器决定了这个输出型GPIO的电平状态。
二、查找复用寄存器
查找复用寄存器的方法
为什么要查找,就是要看一下默认的复用值是不是目前需求需要的,当然 也可以通过寄存器地址 来更改为复用需要的值。
简要思路
要确定一个Pin脚(例如GPIO2_A2)的复用寄存器地址,你需要查阅官方文档《Rockchip RK3568 TRM Part1》 或者比如 Rockchip RK3568 TRM Part1 V1.1-20210301.pdf。具体步骤如下:
找到寄存器偏移地址:在TRM手册中搜索该Pin脚的名字(如"GPIO2_A2"),你会找到其IOMUX控制寄存器的偏移地址。例如,GPIO2_A2的偏移地址可能是0xE020。
确定寄存器基地址:RK3568的GPIO控制器分为多个BANK(GPIO0-GPIO4),它们位于不同的基地址上:
-
PMUGRF:负责GPIO0,基地址一般为 0xFDC20000。
-
GRF:负责GPIO1, GPIO2, GPIO3, GPIO4,基地址一般为 0xFDC60000。
计算完整寄存器地址:将基地址与偏移地址相加,得到完整的物理地址。例如,GPIO2_A2的完整地址就是 0xFDC60000 + 0xE020 = 0xFDC6E020
根据pin脚名字找对应偏移地址
比如:我们用到的pin脚标号 GPIO0B7 ,那就在 参考数据手册 Rockchip RK3568 TRM Part1 V1.1-20210301.pdf 中找偏移地址。
看 第三章 PMU_GRF_Register_Description-Registers Summary

PMU_GRF、PMU_GRF_GPIO0B7_IOMUX_L 和 PMU_GRF_GPIO0B7_IOMUX_H 名词理解
| 术语名称 | 全称/解释 | 主要作用 | 在 RK3568 中的补充说明 |
|---|---|---|---|
| PMU_GRF | Power Management Unit General Register Files | 电源管理单元通用寄存器文件:包含与电源管理、时钟、复位及部分GPIO(如GPIO0)控制相关的寄存器。 | 基地址通常为 0xFDC20000。GPIO0 Bank 属于 PMU分组,因此其配置寄存器位于 PMU_GRF 内。 |
| PMU_GRF_GPIO0B7_IOMUX_L | PMU_GRF GPIO0B IOMUX Low for pin 7 | 配置 GPIO0_B7 引脚 低两位 的复用功能。 | 具体控制引脚功能的低位部分,例如选择功能0、1、2等。 |
| PMU_GRF_GPIO0B7_IOMUX_H | PMU_GRF GPIO0B IOMUX High for pin 7 | 配置 GPIO0_B7 引脚 高两位 的复用功能。 | 具体控制引脚功能的高位部分,与低位组合形成完整的4位功能选择码。 |
GPIO 分组与 IOMUX 概念
理解上述寄存器,还需要了解 RK3568 的 GPIO 组织方式和 IOMUX 概念:
GPIO 分组结构:RK3568 的 GPIO 分为 5 个 Bank (GPIO0~GPIO4),GPIO0 属于 PMU 分组。每个 Bank 内部分为 4 个 Group (A, B, C, D),每个 Group 有 8 个引脚。因此,GPIO0B7 指的就是 GPIO0 Bank, Group B, pin 7。
引脚复用(IOMUX):芯片引脚有限,一个物理引脚往往可以复用为多种不同功能(如 GPIO、UART、I2C 等)。通过配置 IOMUX 寄存器,你可以选择这些引脚在当前场景下的具体角色。这种配置也常被称为 管脚功能选择 或 Pin multiplexing。
问题-为什么GPIO0B7 对应的偏移地址在PMU_GRF_GPIO0B_IOMUX_H
在Rockchip(RK)平台的开发中,区分GPIO控制寄存器是使用 PMU_GRF_GPIO0B7_IOMUX_H 还是 PMU_GRF_GPIO0B7_IOMUX_L,关键在于理解Rockchip的寄存器组织方式。下面这个表格清晰地展示了GPIO0B组引脚与寄存器位的对应关系:
| GPIO 引脚 | 所属寄存器位 | 对应寄存器宏后缀 |
|---|---|---|
| GPIO0B0 | 低16位 | _L |
| GPIO0B1 | 低16位 | _L |
| GPIO0B2 | 低16位 | _L |
| GPIO0B3 | 低16位 | _L |
| GPIO0B4 | 高16位 | _H |
| GPIO0B5 | 高16位 | _H |
| GPIO0B6 | 高16位 | _H |
| GPIO0B7 | 高16位 | _H |
_L和_H 区分规则与原理
PMU_GRF_GPIO0B7_IOMUX_H 和 PMU_GRF_GPIO0B7_IOMUX_L,其区分依据主要来自Rockchip芯片的以下设计:
- 32位寄存器与高低位分配:Rockchip的GRF(通用寄存器文件)和PMU_GRF(电源管理单元GRF)中的IOMUX(输入输出复用)寄存器通常是32位的。为了高效利用地址空间,一个32位的IOMUX寄存器会被划分为两部分:
高16位:用于控制某个特定的GPIO引脚(例如GPIO0B7)。
低16位:用于控制与该引脚在同一个Bank内的另一个GPIO引脚。
因此,通过 _H 和 _L 后缀,就能明确指定当前操作的是寄存器的高16位还是低16位。
- 引脚编号与寄存器位的关联:在一个GPIO Bank内部,引脚的编号顺序决定了它在复合寄存器中的位置。以GPIO0B组为例,它包含8个引脚(B0到B7)。根据这个规则,通常是较低的引脚编号(如B0-B3)对应寄存器的低16位(使用 _L 后缀),较高的引脚编号(如B4-B7)对应寄存器的高16位(使用 _H 后缀)。你提到的 GPIO0B7 正处于较高的编号,因此它由 PMU_GRF_GPIO0B7_IOMUX_H 的高16位控制。
如何查找复用寄存器
如上,已经找到了偏移量地址:

继续找一下高位的相关信息描述:PMU_GRF_GPIO0B_IOMUX_H

现在偏移量地址已经找到了,偏移量地址高位 数据单元功能也已经知道了,为了获取到寄存器复用地址 还需要的就是复用基地址:

哈哈哈,寄存器文件中都有描述的,那么得到的PMU_GRF复用寄存器基地址就是:0xFDC20000
所以复用寄存器地址=基地址+偏移地址=0xFDC2000C 。 使用 io 命令查看此寄存器的地址:
就是:复用寄存器地址=0xFDC20000+0x000C
基础知识点:十六进制格式表示
复用寄存器地址=0xFDC20000+0x000C,你会发现基地址和偏移地址 格式其实不一样的,如下分析:
案例分析0xFDC20000 0x000C
0xFDC20000
-
这是一个完整的8位十六进制数,代表一个32位的值。
-
它很可能是一个内存地址、一个较大的常量值或者一个硬件寄存器的配置值。它的高字节(FD, C2)都是有意义的数字,所以不能省略。
0x000C
-
这是一个4位十六进制数。在32位的上下文中,它等同于 0x0000000C。
-
它通常表示一个较小的数值,或者在一个16位系统/语境中(16位用4个十六进制数表示)。在32位语境下写成 0x000C 而不是 0xC,可能是为了:
在代码或文档中与其他32位数值对齐,保持美观。
明确指示这个值将被放入一个16位或32位的存储空间中。
在数据手册或协议中,这个值可能对应一个16位的字段。
使用 io 命令查看此寄存器的地址
如上:复用寄存器地址=0xFDC20000+0x000C= 0xFDC2000C
[root@topeet:/]# io -r -4 0xFDC2000C
fdc2000c: 00000001
[root@topeet:/]#
所以得到的GPIO0B7 口的复用寄存值是 00000001

这里侧重于查找gpio口寄存器复用默认功能,设置功能暂不讲解,通过io 口设置一次而已。
二、查找方向寄存器
GPIO_SWPORT_DDR-GPIO_SWPORT_DDR_L-GPIO_SWPORT_DDR_H 是什么
在Rockchip(RK)平台的芯片中,GPIO_SWPORT_DDR 是一个关键的寄存器,用于控制GPIO的输入/输出方向。为了更高效地管理,这个寄存器在硬件上被分成了 GPIO_SWPORT_DDR_L 和 GPIO_SWPORT_DDR_H 两部分。
下面这个表格能帮你快速了解它们的核心区别:
| 特性 | GPIO_SWPORT_DDR_L | GPIO_SWPORT_DDR_H |
|---|---|---|
| 控制范围 | GPIO Bank的低16位引脚 | GPIO Bank的高16位引脚 |
| 具体引脚 | 位0 ~ 位15 (例如 Pin 0 - Pin 15) | 位16 ~ 位31 (例如 Pin 16 - Pin 31) |
| 寄存器位宽 | 16位 | 16位 |
| 共同构成 | 32位方向寄存器的低半部分 | 32位方向寄存器的高半部分 |
详解RK的GPIO方向寄存器
要理解上面的划分,我们需要先了解一些RK平台GPIO的基本结构:
-
Bank分组:RK平台的GPIO通常被组织成多个Bank(例如RK3568有GPIO0~GPIO4)
-
32位宽度:每个Bank最多可以控制32个引脚。因此,用于设置引脚方向的寄存器(GPIO_SWPORT_DDR)在硬件上本身就是一个32位的寄存器
-
软件分拆访问:GPIO_SWPORT_DDR_L 和 GPIO_SWPORT_DDR_H 很可能是为了软件编程方便,将这个完整的32位方向寄存器在地址空间上分拆成的两个16位寄存器。
在驱动代码中,我们通常看到的是对 swport_ddr 这个整体寄存器进行操作,这背后可能就是通过操作 GPIO_SWPORT_DDR_L 和 GPIO_SWPORT_DDR_H 来实现的。
为何GPIO0_B7方向在GPIO_SWPORT_DDR_L
首先,我们需要了解RK平台GPIO的引脚编号规则。根据搜索结果,RK3568的GPIO引脚号计算方式如下:
引脚号 = 32 * bank_num + 8 * group + x
其中:
bank_num:0 ~ 4,对应GPIO0~GPIO4。
group:0 ~ 3,对应A~D(即GPIOA~GPIOD)。
x:0 ~ 7,对应组内的具体引脚。
对于 GPIO0_B7:
- 它属于 GPIO0 (bank_num = 0)。
- 它属于B组 (group = 1,因为A=0, B=1, C=2, D=3)。
- 它是B组内的第7个引脚 (x = 7)。
我们来计算一下:
引脚号 = 32 * 0 + 8 * 1 + 7 = 15
计算得出,GPIO0_B7在该Bank内部的引脚编号是15。根据之前的介绍,编号0~15的引脚正是由 GPIO_SWPORT_DDR_L 这个低16位寄存器控制的。因此,GPIO0_B7的方向确实由 GPIO_SWPORT_DDR_L 寄存器中相应的位来控制。
特别提醒: 判断是否在GPIO_SWPORT_DDR_L 还是 GPIO_SWPORT_DDR_H,实际并不是通过引脚号来判断的, 务必根据目标引脚在Bank中的具体编号(0-15还是16-31)来选择合适的寄存器。 也就是group+X 值来判断的。
GPIO_SWPORT_DDR_L 与 GPIO_SWPORT_DDR_H总结与注意事项
简单来说,GPIO_SWPORT_DDR_L管一个GPIO Bank里编号小的那一半引脚是输入还是输出,而GPIO_SWPORT_DDR_H则管编号大的那一半。
在实际编程中,需要注意:
-
务必根据目标引脚在Bank中的具体编号(0-15还是16-31)来选择合适的寄存器。
-
在配置寄存器时,请务必参考你所使用的具体RK芯片型号的官方技术参考手册,因为不同型号的地址偏移量可能会有差异
查找方向寄存器的值
根据上面基础知识和 上面复用查表基础,找到GPIO_SWPORT_DDR_L ,如下: 偏移地址:0x0008
找到GPIO_SWPORT_DDR_L 描述

找到GPIO_SWPORT_DDR_L 具体描述信息,位控制逻辑。

同样,如寄存器复用功能找基地址一样,在地址映射表中找到GPIO映射基地址,如下:GPIO基地址 0xFDD60000

所以:方向寄存器的地址=基地址+偏移地址=0xFDD60000+0x0008=0xFDD60008
使用 IO 命令查看该寄存器的值:如下,
[root@topeet:/]# io -r -4 0xFDD60008
fdd60008: 00000044
[root@topeet:/]#
用计算器看一下十六进制的 0x00000044 的32位值,验证15位值 代表的是输入还是输出,如下:

不是1 而是0 ,那么应该就是输入 in 了, 那么改变一下输入值,用以前的知识点:GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO 来查看一下 direction 方向到底是输入还是输出,如下:

果然是输入模式,那么就用命令改变成输出吧,再继续看看寄存器值:

现在验证到了,就是输出out 模式了。 现在已经把 GPIO口复用给GPIO使用,把方向设置为输出,剩下的就是通过设置寄存器值来实现高低电平来控制 LED灯亮灭了。
三、查找方向寄存器
在上面方向寄存器的时候,已经找到了GPIO 基地址:0xFDD60000
知识点 GPIO_SWPORT_DR、GPIO_SWPORT_DR_L和GPIO_SWPORT_DR_H 是什么
在Rockchip(RK)平台的芯片中,GPIO_SWPORT_DR、GPIO_SWPORT_DR_L和GPIO_SWPORT_DR_H是与GPIO数据值相关的寄存器,用于控制GPIO的输出电平或读取输入电平
如上面分析 方向 输入输出一样,的到了, GPIO0B7 用的高低电平控制就是 GPIO_SWPORT_DR_L 了
查找方向寄存器的值
根据上面基础知识和 上面复用查表基础,找到GPIO_SWPORT_DR_L,如下: 偏移地址:0x0000
找到GPIO_SWPORT_DR_L描述,如下:

首先看一下,数据寄存器地址值多少:
数据寄存器的地址为基地址+偏移地址=0xFDD60000
io 命令查看一次:
[root@topeet:/]# io -r -4 0xFDD60008
fdd60008: 00008044

设置使能打开,处于高电平情况下: 得到十六进制值 80008044


io 命令设置高低电平、使能开关实现LED亮灭
如上分析,这里直接使用命令控制即可:实现灯的亮灭
[root@topeet:/]# io -w -4 0xFDD60000 0x80008044
[root@topeet:/]# io -w -4 0xFDD60000 0x80000044
总结
- 很遗憾的是 这种偏底层io 控制寄存器值很少很少用了,基本上都是通过设备树来配置好GPIO 输入、输出模式,默认高低电平等。 但是对于理解寄存器相关知识点绝对有很大的好处和意义。
- 最大的收获是:学会了看参考手册,基础知识的扩充。熟悉了很多专业术语:
PMU_GRF、PMU_GRF_GPIO0B7_IOMUX_L 、PMU_GRF_GPIO0B7_IOMUX_H、GPIO_SWPORT_DDR-GPIO_SWPORT_DDR_L-GPIO_SWPORT_DDR_H、GPIO_SWPORT_DR、GPIO_SWPORT_DR_L、GPIO_SWPORT_DR_H
1273

被折叠的 条评论
为什么被折叠?



