《嵌入式操作系统》_GPIOLIB前置知识_20260328

1.基本的使用流程

申请、使用、释放

2.初始化过程

内核初始化过程中,与静态映射同时期初始化

3. struct s3c_gpio_chip

这是三星 S3C/S5P 系列 ARM 芯片(如 S3C2440、S3C6410、Exynos 等)Linux 内核中 GPIO 控制器的核心结构体,是三星平台 GPIO 驱动的基础,用于描述一个 GPIO 端口(如 GPA、GPB 组)。

封装了 Linux 通用 GPIO 框架 + 三星芯片私有 GPIO 特性

cpp 复制代码
struct s3c_gpio_chip {
	// 1. Linux 内核通用 GPIO 芯片基类(核心框架层)
	struct gpio_chip	chip;
	// 2. 三星私有 GPIO 配置(模式、上拉/下拉等)
	struct s3c_gpio_cfg	*config;
	// 3. 电源管理相关配置
	struct s3c_gpio_pm	*pm;
	// 4. GPIO 寄存器物理地址映射后的虚拟基地址
	void __iomem		*base;
	// 5. 外部中断(EINT)偏移量
	int			eint_offset;
	// 6. 自旋锁(并发访问保护)
	spinlock_t		 lock;
	// 7. 休眠/唤醒时的寄存器保存数组(仅 CONFIG_PM 开启时有效)
#ifdef CONFIG_PM
	u32			pm_save[7];
#endif
};

3.1 gpio_chip

cpp 复制代码
struct gpio_chip {
	/* 1. 基础描述信息 */
	const char		    *label;      // GPIO 芯片名称(如 "gpiochip0")
	struct device		*dev;        // 关联的设备结构体(设备模型用)
	struct module		*owner;      // 驱动所属模块(THIS_MODULE)

	/* 2. GPIO 申请/释放回调(资源管理) */
	int			(*request)(struct gpio_chip *chip, unsigned offset);
	void		(*free)(struct gpio_chip *chip, unsigned offset);

	/* 3. GPIO 方向与电平操作(核心功能) */
	int			(*direction_input)(struct gpio_chip *chip, unsigned offset);
	int			(*get)(struct gpio_chip *chip, unsigned offset); // 读电平
	int			(*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
	void		(*set)(struct gpio_chip *chip, unsigned offset, int value); // 写电平

	/* 4. 扩展功能 */
	int			(*set_debounce)(...); // 消抖配置(按键用)
	int			(*to_irq)(...);      // GPIO 转中断号
	void		(*dbg_show)(...);    // 调试信息展示

	/* 5. 硬件配置参数 */
	int			base;    // 此 GPIO 控制器的**起始编号**
	u16			ngpio;  // 此控制器拥有多少个 GPIO 引脚
	const char		*const *names; // 引脚名称数组(可选)

	/* 6. 标志位 */
	unsigned		can_sleep:1;  // 操作是否可能休眠(I2C 扩展 GPIO 会休眠)
	unsigned		exported:1;   // 是否通过 sysfs 导出给用户态
};
bash 复制代码
cd /sys/class/gpio/
[root@LXY210 gpio]# ls
export       gpiochip164  gpiochip235  gpiochip316  gpiochip396  gpiochip56
gpiochip0    gpiochip172  gpiochip244  gpiochip325  gpiochip40   gpiochip62
gpiochip104  gpiochip181  gpiochip253  gpiochip334  gpiochip405  gpiochip71
gpiochip112  gpiochip188  gpiochip262  gpiochip343  gpiochip414  gpiochip80
gpiochip120  gpiochip197  gpiochip271  gpiochip35   gpiochip423  gpiochip89
gpiochip128  gpiochip206  gpiochip280  gpiochip351  gpiochip431  gpiochip9
gpiochip137  gpiochip212  gpiochip289  gpiochip360  gpiochip438  gpiochip96
gpiochip14   gpiochip221  gpiochip29   gpiochip369  gpiochip447  unexport
gpiochip146  gpiochip226  gpiochip298  gpiochip378  gpiochip456
gpiochip155  gpiochip23   gpiochip307  gpiochip387  gpiochip47
[root@LXY210 gpiochip0]# ls
base       label      ngpio      power      subsystem  uevent
[root@LXY210 gpiochip0]# cat label
GPA0
[root@LXY210 gpiochip0]# cat base
0

gpiochipxxx中有base label ngpio power subsystem uevent

base:数值等于base

label:给人看的编组信息

ngpio:该组gpio的数量

为什么gpiochip0的base是0,ngpio是8,下一个却是gpiochip9?因为gpio8无效,但是保留了下来

base数值计算的具体实现:

3.2函数绑定与驱动注册samsung_gpiolib_add_4bit_chips

3.2.1 输入输出函数绑定samsung_gpiolib_add_4bit

输入输出函数的具体执行:

cpp 复制代码
// 函数定义:静态函数,返回int,是GPIO子系统的输入配置回调函数
// chip:GPIO芯片抽象结构体;offset:要配置的引脚编号(相对于当前芯片)
static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
				      unsigned int offset)
{
	// 1. 类型转换:从通用gpio_chip转为三星专用的s3c_gpio_chip结构体
	// to_s3c_gpio是内核宏,用于获取三星GPIO私有数据
	struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
	
	// 2. 获取GPIO控制器的**寄存器物理地址映射后的虚拟地址**
	// __iomem:内核标记,代表这是IO内存地址(硬件寄存器地址)
	void __iomem *base = ourchip->base;
	
	// 3. 定义变量,用于存储GPIO配置寄存器的值
	unsigned long con;

	// 4. 读取GPIO配置寄存器(GPIOCON_OFF是配置寄存器的偏移地址)
	// __raw_readl:内核底层函数,直接从硬件寄存器读32位数据(无同步、无缓存)
	con = __raw_readl(base + GPIOCON_OFF);
	
	// 5. 关键操作:清零当前引脚对应的4个配置位
	// con_4bit_shift(offset):计算当前引脚在CON寄存器中的4bit起始偏移位
	// ~(0xf << ...):生成掩码,把对应4bit置0
	// 三星规定:4bit全为0 → 引脚配置为**输入模式**
	con &= ~(0xf << con_4bit_shift(offset));
	
	// 6. 将修改后的值写回GPIO配置寄存器,完成硬件配置
	__raw_writel(con, base + GPIOCON_OFF);

	// 7. 调试打印:输出函数名、寄存器基地址、配置后的值
	gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);

	// 8. 函数执行成功,返回0
	return 0;
}

那pm函数是什么?负责电源管理(低功耗)的函数

3.2.2电平设置与读取&驱动注册s3c_gpiolib_add

电平的读取与设置:s3c_gpiolib_set

驱动注册:ret = gpiochip_add(gc);

4. 内核驱动注册函数gpiochip_add

该函数针对所有的平台,任何平台都需要使用该函数完成驱动的创建。但该函数的输入参数由平台决定。在注册之前要完成结构体的填充。

cpp 复制代码
int gpiochip_add(struct gpio_chip *chip)

所以是干了什么给注册了?

其实就是将chip结构体的首地址填到了内核维护的gpio_decs这个数组里面。

5. 内核提供的GPIO函数

这些函数就是我们移植驱动的时候使用的。

5.1 gpiochip_add

gpiochip_add:给厂家编写驱动用,用于向内核注册gpiolib

5.2 gpio_request

cpp 复制代码
// 头文件:#include <linux/gpio.h>
int gpio_request(unsigned int gpio, const char *label);
  • 申请 GPIO 资源:向内核注册,声明要使用该 GPIO 引脚
  • 独占保护:防止多个驱动同时操作同一个 GPIO(避免硬件冲突)
  • 合法性检查:校验 GPIO 编号是否有效、是否已被占用
  • 标记归属 :用label标记该 GPIO 的使用者(方便调试)

你怎么知道gpio的具体编号?在这个头文件里

5.3 gpio_free

gpio_free:释放gpio

5.4 gpio_request变种

gpio_request_one:添加输入输出功能。

gpio_request_array:申请一个数组大小的gpio

gpio_free_array:释放一个数组大小的gpio

5.5 gpiochip_is_requested

gpiochip_is_requested:当前这个gpio端口是否已经被申请了

5.6 gpio_direction_input

gpio_direction_input:设置输入模式。但是没干活,还是指到了samsung_gpiolib_4bit_input这个函数。

5.7 gpio_direction_output

gpio_direction_output:设置输出模式。但是没干活

5.8 gpio_set_debounce

gpio_set_debounce:开启【按键消抖】的标准函数。还是没干活。调用的结构体函数

5.9 __gpio_get_value

被宏定义为gpio_get_value

5.10 __gpio_set_value

类似

6. 第二类内核函数

这些函数是给内核gpiolib自己使用的,可以认为是gpiolib的attribute部分。比如你去cat 一个gpio端口的端口号需要调用的函数。

该宏定义决定了gpio设备在sys目录下是否可用。该宏定义在config文件中,可以通过修改menuconfig来取消支持。

你怎么知道?

我们在这个宏定义之间发现了这些函数,函数将chip的base写到了buf缓冲区。

6.1 base、label、ngpio的初始化

确实我们在宏定义中都找到了base、label、ngpio的调用函数。但是谁调用的?怎么绑定的?

我们还找到了value的调用函数,应该是读取gpio的引脚电平函数,为什么开发板系统中没有?

该函数执行了gpiolib的初始化,同样是调用的系统初始化宏定义

cpp 复制代码
#define postcore_initcall(fn)		__define_initcall("2",fn,2)

我们看看这个初始化函数写的什么

函数在for循环中进行导出,我们看下导出的是啥

该函数进行了创建设备和绑定属性函数,我们看下绑定的什么

OK!证实了被初始化的属性只有base、label、ngpio。

6.2 value、active的初始化

那这些函数在哪初始化的?

ok先是找到了这两个函数的连接

然后被gpio_export调用

函数被export_store调用

然后被放到了这里

发现这些属性竟然在class类下

果然,接下来查看export设备的说明,输入一个数字就能导出相应gpio???

还真是!

ok再试试消除

没毛病!ok gpiolib的理论学习到此为止,下节课开始实操

相关推荐
17(无规则自律)2 小时前
深度剖析Linux Input子系统(2):驱动开发流程与现代 Multi-touch 协议
linux·驱动开发·嵌入式硬件
BackCatK Chen3 小时前
STM32保姆级入门教程|第5章:GPIO内部结构 + 8种模式 + 功能详解
stm32·嵌入式硬件·gpio·推挽输出·开漏输出·gpio内部结构·上拉输入
Heartache boy4 小时前
野火STM32_HAL库版课程笔记-TIM触发ADC采集
笔记·stm32·单片机·嵌入式硬件
你疯了抱抱我4 小时前
【Altium Designer】网络线(颜色、旋转、粗细);网络标签 && 端口的差别(重点!)
嵌入式硬件·嵌入式·pcb·电路
蓝凌y4 小时前
51单片机之按键控制RGB灯
单片机·嵌入式硬件
156082072194 小时前
关于XILINX的fir compiler小数倍内插设计
单片机·嵌入式硬件
ivy159868377159 小时前
芯锦科技 HP9116 QC3+多协议USB快充接口芯片
网络·单片机·嵌入式硬件·5g·p2p
LXY_BUAA14 小时前
《嵌入式操作系统》_使用GPIOLIB编写驱动_添加驱动到内核中_20260328
驱动开发·嵌入式硬件
liwulin050618 小时前
【ESP32-S3】通过ROS2使用YDLIDAR X2进行SLAM、自主导航方案选择
单片机·嵌入式硬件