Linux的gpio子系统

GPIO其实也是某个pin的功能之一。

上一小节讲解了 pinctrl 子系统,pinctrl 子系统重点是设置 PIN(有的 SOC 叫做 PAD)的复用和电气属性,如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的 API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,也就是说,不用我们再去初始化GPIO了,极大的方便了驱动开发者使用 GPIO。 类似于库函数。

I.MX6ULL 的 gpio 子系统驱动

设备树中的 gpio 信息

I.MX6ULL-ALPHA 开发板上的 UART1_RTS_B 做为 SD 卡的检测引脚,UART1_RTS_B复用为 GPIO1_IO19,通过读取这个 GPIO 的高低电平就可以知道 SD 卡有没有插入。首先肯定是将 UART1_RTS_B 这个 PIN 复用为GPIO1_IO19,并且设置电气属性,也就是上一小节讲的pinctrl 节点。打开 imx6ull-alientek-emmc.dts,UART1_RTS_B 这个 PIN 的 pincrtl 设置如下:

可见,pinctrl使用起来还是蛮简单的,虽然配置引脚本身就比较简单,可是现在更加简单了。

pinctrl 配置好以后就是设置 gpio 了,SD 卡驱动程序通过读取 GPIO1_IO19 的值来判断 SD卡有没有插入,但是 SD 卡驱动程序怎么知道 CD 引脚连接的 GPIO1_IO19 呢?也就是说,怎么把这个IO关联到SD卡中去?肯定是需要通过设备树告诉SD的驱动啊!也就是,需要在SD卡对应的设备树信息中添加这个IO信息。在设备树中 SD 卡节点下添加一个属性来描述 SD 卡的 CD 引脚就行了,SD卡驱动直接读取这个属性值就知道 SD 卡的 CD 引脚使用的是哪个 GPIO 了。SD 卡连接在 I.MX6ULL 的 usdhc1 接口上,在 imx6ull-alientek-emmc.dts 中找到名为“usdhc1”的节点,这个节点就是 SD 卡设备节点,如下所示:

这里的有些属性是自定义名称的,没有特别的要求,后续引用时使用相同名称即可。

第 765 行,此行本来没有,是作者添加的,usdhc1 节点作为 SD 卡设备总节点,usdhc1 节点需要描述 SD 卡所有的信息,因为驱动要使用。本行就是描述 SD 卡的 CD 引脚 pinctrl 信息所在的子节点,因为 SD 卡驱动需要根据 pincrtl 节点信息来设置 CD 引脚的复用功能等。762~764行的 pinctrl-0~2 都是 SD 卡其他 PIN 的 pincrtl 节点信息。但是大家会发现,其实在 usdhc1 节点中“pinctrl-3 = <&pinctrl_hog_1>”这一行是被注释掉的,也就是说并没有指定 CD 引脚的 pinctrl 信息,那么 SD 卡驱动就没法设置 CD 引脚的复用功能啊?这个不用担心,因为在“iomuxc”节点下引用了 pinctrl_hog_1 这个节点,所以 Linux 内核中的 iomuxc 驱动就会自动初始化 pinctrl_hog_1节点下的所有 PIN。

注意,到这里,只是通过pinctrl子系统设置了引脚的复用、上下拉和驱动功能,还没有关联CD和该引脚。

继续看第 766 行,属性“cd-gpios”描述了 SD 卡的 CD 引脚使用的哪个 IO。属性值一共有三个, 我们来看一下这三个属性值的含义,“&gpio1”表示 CD 引脚所使用的 IO 属于 GPIO1 组,“19” 表示 GPIO1 组的第 19 号 IO,通过这两个值 SD 卡驱动程序就知道 CD 引脚使用了 GPIO1_IO19这 GPIO。“GPIO_ACTIVE_LOW”表示低电平有效,如果改为“GPIO_ACTIVE_HIGH”就表示高电平有效。 这里的低电平高电平有效没明白,待解决。

根据上面这些信息,SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了,打开 imx6ull.dtsi,在里面找到如下所示内容:

gpio1 节点信息描述了 GPIO1 控制器的所有信息,重点就是 GPIO1 外设寄存器基地址以及兼 容 属 性 。 关 于 I.MX 系 列 SOC 的 GPIO 控 制 器 绑 定 信 息 请 查 看 文 档Documentation/devicetree/bindings/gpio/ fsl-imx-gpio.txt。

第 505 行,设置 gpio1 节点的 compatible 属性有两个,分别为“fsl,imx6ul-gpio”和“fsl,imx35-gpio”,在 Linux 内核中搜索这两个字符串就可以找到 I.MX6UL 的 GPIO 驱动程序。

第 506 行,reg 属性设置了 GPIO1 控制器的寄存器基地址为 0X0209C000,大家可以打开《I.MX6ULL 参考手册》找到“Chapter 28:General Purpose Input/Output(GPIO)”章节第 28.5 小节,有如图 45.2.2.1 所示的寄存器地址表:

从图 45.2.2.1 可以看出,GPIO1 控制器的基地址就是 0X0209C000。

第 509 行,“gpio-controller”表示 gpio1 节点是个 GPIO 控制器。

第 510 行,“#gpio-cells”属性和“#address-cells”类似,#gpio-cells 应该为 2,表示一共有两个 cell,第一个 cell 为 GPIO 编号,比如“&gpio1 3”就表示 GPIO1_IO03。第二个 cell 表示GPIO 极 性 , 如 果 为 0(GPIO_ACTIVE_HIGH) 的 话 表 示 高 电 平 有 效 , 如 果 为1(GPIO_ACTIVE_LOW)的话表示低电平有效。

GPIO 驱动程序简介

gpio1 节点的 compatible 属性描述了兼容性,在 Linux 内核中搜索“fsl,imx6ul-gpio”和 “fsl,imx35-gpio”这两个字符串,查找 GPIO 驱动文件。drivers/gpio/gpio-mxc.c 就是 I.MX6ULL的 GPIO 驱动文件,在此文件中有如下所示 of_device_id 匹配表:

第 156 行的 compatible 值为“fsl,imx35-gpio”,和 gpio1 的 compatible 属性匹配,因此 gpio-mxc.c 就是 I.MX6ULL 的 GPIO 控制器驱动文件。gpio-mxc.c 所在的目录为 drivers/gpio,打开这个目录可以看到很多芯片的 gpio 驱动文件, “gpiolib”开始的文件是 gpio 驱动的核心文件,如图 45.2.2.2 所示:

我们重点来看一下 gpio-mxc.c 这个文件,在 gpio-mxc.c 文件中有如下所示内容:

可以看出 GPIO 驱动也是个平台设备驱动,因此当设备树中的设备节点与驱动的 of_device_id 匹配以后 probe 函数就会执行,在这里就是 mxc_gpio_probe 函数,这个函数就是I.MX6ULL 的 GPIO 驱动入口函数。我们简单来分析一下 mxc_gpio_probe 这个函数,函数内容如下:

第 405 行,设备树节点指针。

第 406 行,定义一个结构体指针 port,结构体类型为 mxc_gpio_port。gpio-mxc.c 的重点工

作就是维护 mxc_gpio_port,mxc_gpio_port 就是对 I.MX6ULL GPIO 的抽象。mxc_gpio_port 结构体定义如下:

mxc_gpio_port 的 bgc 成员变量很重要,因为稍后的重点就是初始化 bgc。

继续回到 mxc_gpio_probe 函数函数,第 411 行调用 mxc_gpio_get_hw 函数获取 gpio 的硬

件相关数据,其实就是 gpio 的寄存器组,函数 mxc_gpio_get_hw 里面有如下代码:

注意第 385 行,mxc_gpio_hwdata 是个全局变量,如果硬件类型是 IMX35_GPIO 的话设置

mxc_gpio_hwdat 为 imx35_gpio_hwdata。对于 I.MX6ULL 而言,硬件类型就是IMX35_GPIO,imx35_gpio_hwdata 是个结构体变量,描述了 GPIO 寄存器组,内容如下:

大家将 imx35_gpio_hwdata 中的各个成员变量和图 45.2.2.1 中的 GPIO 寄存器表对比就会发现,imx35_gpio_hwdata 结构体就是 GPIO 寄存器组结构。这样我们后面就可以通过

mxc_gpio_hwdata 这个全局变量来访问 GPIO 的相应寄存器了。

继续回到示例代码 45.2.2.5 的 mxc_gpio_probe 函数中,第 417 行,调用函数 platform_get_resource 获取设备树中内存资源信息,也就是 reg 属性值。前面说了 reg 属性指定了 GPIO1 控制器的寄存器基地址为 0X0209C000,在配合前面已经得到的mxc_gpio_hwdata,这样 Linux 内核就可以访问 gpio1 的所有寄存器了。

第 418 行,调用 devm_ioremap_resource 函数进行内存映射,得到 0x0209C000 在 Linux 内核中的虚拟地址。

第 422、423 行,通过 platform_get_irq 函数获取中断号,第 422 行获取高 16 位 GPIO 的中

断号,第 423 行获取底 16 位 GPIO 中断号。

第 428、429 行,操作 GPIO1 的 IMR 和 ISR 这两个寄存器,关闭 GPIO1 所有 IO 中断,并

且清除状态寄存器。

第 438~448 行,设置对应 GPIO 的中断服务函数,不管是高 16 位还是低 16 位,中断服务

函数都是 mx3_gpio_irq_handler。

第 450~453 行,bgpio_init 函数第一个参数为 bgc,是 bgpio_chip 结构体指针。bgpio_chip

结构体有个 gc 成员变量,gc 是个 gpio_chip 结构体类型的变量。gpio_chip 结构体是抽象出来的GPIO 控制器,gpio_chip 结构体如下所示(有缩减):

可以看出,gpio_chip 大量的成员都是函数,这些函数就是 GPIO 操作函数。bgpio_init 函数

主 要 任 务 就 是 初 始 化 bgc->gc 。 bgpio_init 里 面 有 三 个 setup 函 数 : bgpio_setup_io 、bgpio_setup_accessors 和 bgpio_setup_direction。这三个函数就是初始化 bgc->gc 中的各种有关GPIO 的操作,比如输出,输入等等。第 451~453 行的GPIO_PSR、GPIO_DR 和 GPIO_GDIR 都是 I.MX6ULL 的 GPIO 寄存器。这些寄存器地址会赋值给 bgc 参数的 reg_dat、reg_set、reg_clr和 reg_dir 这些成员变量。至此,bgc 既有了对 GPIO 的操作函数,又有了 I.MX6ULL 有关 GPIO的寄存器,那么只要得到 bgc 就可以对 I.MX6ULL 的 GPIO 进行操作。继续回到mxc_gpio_probe 函数,第461行调用函数gpiochip_add向Linux内核注gpio_chip,也就是 port->bgc.gc。注册完成以后我们就可以在驱动中使用 gpiolib 提供的各个 API 函数。

gpio 子系统 API 函数

对于驱动开发人员,设置好设备树以后就可以使用 gpio 子系统提供的 API 函数来操作指定

的 GPIO,gpio 子系统向驱动开发人员屏蔽了具体的读写寄存器过程。这就是驱动分层与分离的好处,大家各司其职,做好自己的本职工作即可。gpio 子系统提供的常用的 API 函数有下面几个:

1gpio_request 函数

gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用gpio_request

进行申请,函数原型如下:

int gpio_request(unsigned gpio, const char *label)

函数参数和返回值含义如下:

gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信

息,此函数会返回这个 GPIO 的标号。

label:给 gpio 设置个名字。

返回值:0,申请成功;其他值,申请失败。

2gpio_free 函数

如果不使用某个 GPIO 了,那么就可以调用 gpio_free 函数进行释放。函数原型如下:

void gpio_free(unsigned gpio)

函数参数和返回值含义如下:

gpio:要释放的 gpio 标号。

返回值:无。

3gpio_direction_input 函数

此函数用于设置某个 GPIO 为输入,函数原型如下所示:

int gpio_direction_input(unsigned gpio)

函数参数和返回值含义如下:

gpio:要设置为输入的 GPIO 标号。

返回值:0,设置成功;负值,设置失败。

4gpio_direction_output 函数

此函数用于设置某个 GPIO 为输出,并且设置默认输出值,函数原型如下:

int gpio_direction_output(unsigned gpio, int value)

函数参数和返回值含义如下:

gpio:要设置为输出的 GPIO 标号。

valueGPIO 默认输出值。

返回值:0,设置成功;负值,设置失败。

5gpio_get_value 函数

此函数用于获取某个 GPIO 的值(0 或 1),此函数是个宏,定义所示:

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

函数参数和返回值含义如下:

gpio:要获取的 GPIO 标号。

返回值:非负值,得到的 GPIO 值;负值,获取失败。

6gpio_set_value 函数

此函数用于设置某个 GPIO 的值,此函数是个宏,定义如下

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

函数参数和返回值含义如下:

gpio:要设置的 GPIO 标号。

value要设置的值。

返回值:

关于 gpio 子系统常用的 API 函数就讲这些,这些是我们用的最多的。

设备树中添加 gpio 节点模板 

继续完成 45.1.3 中的 test 设备,在 45.1.3 中我们已经讲解了如何创建 test 设备的 pinctrl 节

点。本节我们来学习一下如何创建 test 设备的 GPIO 节点。

1、创建 test 设备节点

在根节点“/”下创建 test 设备子节点,如下所示:

注意,这里是设备,一会儿设备要指定使用哪个引脚,这个引脚需要先在pinctrl子系统里设置好。

2、添加 pinctrl 信息

在 45.1.3 中我们创建了 pinctrl_test 节点,此节点描述了 test 设备所使用的 GPIO1_IO00 这

个 PIN 的信息,我们要将这节点添加到 test 设备节点中,如下所示:

第 2 行,添加 pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。

第 3 行,添加 pinctrl-0 节点,此节点引用 45.1.3 中创建的 pinctrl_test 节点,表示 tset 设备

的所使用的 PIN 信息保存在 pinctrl_test 节点中,所以,最好还是写上,不知道上面为啥要注释掉。

3、添加 GPIO 属性信息

我们最后需要在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚,添

加完成以后如下所示:

第 4 行,test 设备所使用的 gpio。

关于 pinctrl 子系统和 gpio 子系统就讲解到这里,接下来就使用 pinctrl 和 gpio 子系统来驱

动 I.MX6ULL-ALPHA 开发板上的 LED 灯。

gpio 相关的 OF 函数

注意,以下这些of函数并不是gpio子系统提供的函数,而是设备树机制提供的获取设备树中gpio信息相关的接口。

在示例代码 45.2.4.3 中,我们定义了一个名为“gpio”的属性,gpio 属性描述了 test 这个设

备所使用的 GPIO。在驱动程序中需要读取 gpio 属性内容,Linux 内核提供了几个与 GPIO 有关的 OF 函数,常用的几个 OF 函数如下所示:

1of_gpio_named_count 函数

of_gpio_named_count 函数用于获取设备树某个属性里面定义了几个 GPIO 信息,要注意的

是空的 GPIO 信息也会被统计到,比如:

上述代码的“gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。

通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个,此函数原型如下:

int of_gpio_named_count(struct device_node *np, const char *propname)

函数参数和返回值含义如下:

np:设备节点。

propname:要统计的 GPIO 属性。

返回值:正值,统计到的 GPIO 数量;负值,失败。

2of_gpio_count 函数

和 of_gpio_named_count 函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属

性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的 GPIO 信息,函数原型如下所示:

int of_gpio_count(struct device_node *np)

函数参数和返回值含义如下:

np:设备节点。

返回值:正值,统计到的 GPIO 数量;负值,失败。

3of_get_named_gpio 函数

此函数获取 GPIO 编号,因为 Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,

此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编

号,此函数在驱动中使用很频繁!函数原型如下:

int of_get_named_gpio(struct device_node *np, const char *propname, int index)

函数参数和返回值含义如下:

np:设备节点。

propname:包含要获取 GPIO 信息的属性名。

indexGPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO

的编号,如果只有一个 GPIO 信息的话此参数为 0。

返回值:正值,获取到的 GPIO 编号;负值,失败。

实验程序编写

本实验对应的例程路径为:开发板光盘-> 2、Linux 驱动例程-> 5_gpioled。

本章实验我们继续研究 LED 灯,在第四十四章实验中我们通过设备树向 dtsled.c 文件传递

相应的寄存器物理地址,然后在驱动文件中配置寄存器。本章实验我们使用 pinctrl 和 gpio 子系统来完成 LED 灯驱动。

修改设备树文件 

1、添加 pinctrl 节点

I.MX6U-ALPHA 开发板上的 LED 灯使用了 GPIO1_IO03 这个 PIN,打开 imx6ull-alientek

emmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点,节点

内容如下所示:

第 3 行,将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03,电气属性值为 0X10B0。

2、添加 LED 设备节点

在根节点“/”下创建 LED 灯节点,节点名为“gpioled”,节点内容如下:

第 6 行,pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点。

第 7 行,led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平

有效。稍后编写驱动程序的时候会获取 led-gpio 属性的内容来得到 GPIO 编号,因为 gpio 子系统的 API 操作函数需要 GPIO 编号。

使用gpio子系统的前提是pin已经(通过pinctrl子系统或者其他原始方式)被复用为了gpio功能,然后,因为gpio也是被某个具体外设所使用,所以通常还需要在具体外设的节点中关联上对应的gpio。

关联分为两个部分:

  • 通过pinctrl-names和pinctrl-x的形式来指定使用的是哪个pinctrl信息;
  • 关联上具体的gpio控制器;

3、检查 PIN 是否被其他外设使用

这一点非常重要!!!

很多初次接触设备树的驱动开发人员很容易因为这个小问题栽了大跟头!因为我们所使用的设备树基本都是在半导体厂商提供的设备树文件基础上修改而来的,而半导体厂商提供的设备树是根据自己官方开发板编写的,很多 PIN 的配置和我们所使用的开发板不一样。比如 A 这个引脚在官方开发板接的是 I2C 的 SDA,而我们所使用的硬件可能将 A 这个引脚接到了其他的外设,比如 LED 灯上,接不同的外设,A 这个引脚的配置就不同。一个引脚一次只能实现一个功能,如果 A 引脚在设备树中配置为了 I2C 的 SDA 信号,那么 A 引脚就不能再配置为 GPIO,否则的话驱动程序在申请 GPIO 的时候就会失败。检查 PIN 有没有被其他外设使用包括两个方面:

①、检查 pinctrl 设置。

②、如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。

在本章实验中 LED 灯使用的 PIN 为 GPIO1_IO03,因此先检查 GPIO_IO03 这个 PIN 有没

有被其他的 pinctrl 节点使用,在 imx6ull-alientek-emmc.dts 中找到如下内容:

pinctrl_tsc 节点是 TSC(电阻触摸屏接口)的 pinctrl 节点,从第 484 行可以看出,默认情况下

GPIO1_IO03 作为了 TSC 外设的 PIN。所以我们需要将第 484 行屏蔽掉!和 C 语言一样,在要屏蔽的内容前后加上“/*”和“*/”符号即可。其实在 I.MX6U-ALPHA 开发板上并没有用到 TSC接口,所以第 482~485 行的内容可以全部屏蔽掉。

因为本章实验我们将 GPIO1_IO03 这个 PIN 配置为了 GPIO,所以还需要查找一下有没有

其他的外设使用了 GPIO1_IO03,在 imx6ull-alientek-emmc.dts 中搜索“gpio1 3”,找到如下内容:

tsc 是 TSC 的外设节点,从 726 行可以看出,tsc 外设也使用了 GPIO1_IO03,同样我们需

要将这一行屏蔽掉。然后在继续搜索“gpio1 3”,看看除了本章的 LED 灯以外还有没有其他的

地方也使用了 GPIO1_IO03,找到一个屏蔽一个。

设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-alientek-emmc.dtb 文件启动 Linux 系统。启动成功以后进入“/proc/device-tree”目录中查看“gpioled”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证),结果如图 45.4.1.1 所示:

linux中gpio子系统的使用流程

在Linux中,GPIO子系统的使用流程主要涉及到设备树配置、驱动程序编写与注册、以及用户空间的应用开发。以下是详细的步骤:

设备树配置

添加pinctrl节点:在设备树文件(通常是.dts.dtsi文件)中,添加pinctrl节点来描述GPIO控制器的引脚复用和配置信息。这些节点定义了哪些引脚可以用作GPIO,以及它们的默认功能和电气属性。

添加设备节点:除了pinctrl节点外,还需要在设备树中添加具体的设备节点,这些节点描述了使用GPIO的设备(如LED、按钮等)的相关信息,包括它们连接到哪个GPIO引脚、是否使用中断等。

编写驱动程序

初始化GPIO子系统:在驱动程序的初始化函数中,首先需要调用相关函数来初始化GPIO子系统,确保系统能够识别和管理GPIO设备。

获取GPIO资源:使用Linux内核提供的API函数(如gpio_requestgpio_free等)来请求和释放GPIO资源。这些函数允许驱动程序指定要操作的GPIO引脚,并设置其初始状态(如输入/输出方向、初始电平等)。

实现设备操作函数:根据设备的具体需求,实现相应的设备操作函数。例如,对于LED设备,可能需要实现打开和关闭LED的函数;对于按钮设备,则可能需要实现读取按钮状态的函数。

注册设备驱动:将编写好的驱动程序注册到Linux内核中,使系统能够识别并加载该驱动。这通常涉及到填充特定的数据结构(如platform_driver),并调用platform_driver_register等函数进行注册。

用户空间的应用开发

访问GPIO设备:在用户空间应用程序中,可以通过读取和写入/sys/class/gpio目录下的文件来控制GPIO设备。例如,向/sys/class/gpio/gpio<number>/value文件写入“1”或“0”可以分别打开和关闭连接到该GPIO引脚的LED灯。

处理中断事件:如果设备使用了中断功能(如按钮按下时触发中断),则需要在用户空间应用程序中处理这些中断事件。这通常涉及到安装中断处理程序,并在中断发生时执行相应的操作。

总的来说,通过遵循以上步骤,可以在Linux系统中有效地使用GPIO子系统来控制硬件设备。这不仅简化了硬件控制的复杂性,还提高了系统的可扩展性和可维护性。

LED 灯驱动程序编写 

设备树准备好以后就可以编写驱动程序了,本章实验在第四十四章实验驱动文件 dtsled.c 的

基础上修改而来。新建名为“5_gpioled”文件夹,然后在 5_gpioled 文件夹里面创建 vscode 工

程,工作区命名为“gpioled”。工程创建好以后新建 gpioled.c 文件,在 gpioled.c 里面输入如下内容:

第 41 行,在设备结构体 gpioled_dev 中加入 led_gpio 这个成员变量,此成员变量保存 LED

等所使用的 GPIO 编号。

第 55 行,将设备结构体变量 gpioled 设置为 filp 的私有数据 private_data。

第 85 行,通过读取 filp 的 private_data 成员变量来得到设备结构体变量,也就是 gpioled。

这种将设备结构体设置为 filp 私有数据的方法在 Linux 内核驱动里面非常常见。

第 96、97 行,直接调用 gpio_set_value 函数来向 GPIO 写入数据,实现开/关 LED 的效果。不需要我们直接操作相应的寄存器。

第 133 行,获取节点“/gpioled”。

第 142 行,通过函数 of_get_named_gpio 函数获取 LED 所使用的 LED 编号。相当于将

gpioled 节点中的“led-gpio”属性值转换为对应的 LED 编号。

第 150 行,调用函数 gpio_direction_output 设置 GPIO1_IO03 这个 GPIO 为输出,并且默认高电平,这样默认就会关闭 LED 灯。

可以看出 gpioled.c 文件中的内容和第四十四章的 dtsled.c 差不多,只是取消掉了配置寄存

器的过程,改为使用 Linux 内核提供的 API 函数。在 GPIO 操作上更加的规范化,符合Linux

代码框架,而且也简化了 GPIO 驱动开发的难度,以后我们所有例程用到 GPIO 的地方都采用此方法。

接下来的测试内容和之前一样,就不赘述了。 

貌似并没有使用gpio_request呀?

gpio_request函数在以下几种情况下使用:

  • 驱动初始化阶段

    • 硬件资源预留:当驱动程序加载并开始初始化时,需要确定要控制的GPIO引脚。此时,调用gpio_request函数向内核请求特定编号的GPIO引脚的使用权,以便后续对其进行操作和控制。这确保了该引脚在整个驱动程序运行期间被正确分配和使用,避免了与其他设备或驱动程序产生冲突。

    • 设置引脚功能:通过gpio_request函数请求GPIO引脚后,可以进一步设置引脚的功能,如将引脚配置为输入模式用于读取外部信号,或者配置为输出模式用于控制外部设备等。

  • 设备操作之前

    • 确保独占访问权:在对GPIO引脚进行读写操作之前,必须先成功调用gpio_request函数获取该引脚的控制权。这样可以保证当前进程或驱动程序对引脚的独占访问,防止其他进程或设备同时访问该引脚导致数据冲突或错误。

    • 避免冲突:如果多个设备或驱动程序试图同时访问同一个GPIO引脚,可能会导致系统不稳定或出现错误的结果。gpio_request函数的使用可以避免这种冲突,确保每个设备或驱动程序都能正确地获取到自己所需的GPIO引脚资源。

  • 实现特定功能时

    • 根据需求动态申请:在一些复杂的系统中,设备的运行状态可能会根据不同的条件发生变化,需要动态地控制不同的GPIO引脚。例如,当某个事件发生时,驱动程序可能需要临时申请一个特定的GPIO引脚来实现相应的功能,如点亮一个指示灯、发送一个信号等。在这种情况下,gpio_request函数可以在需要的时候被调用来请求所需的GPIO引脚。

    • 与其他硬件协同工作:当设备需要与其他外部硬件设备进行通信或协同工作时,可能需要通过GPIO引脚来传输信号或数据。在建立这种连接之前,需要先使用gpio_request函数请求相应的GPIO引脚,以便与外部设备进行正确的交互。

综上所述,gpio_request函数在Linux内核中扮演着至关重要的角色,它确保了对GPIO引脚的安全、有效管理,是实现硬件设备控制与交互不可或缺的一环。

暂时搞不太明白,后面再补充吧。

我看这几个gpio的操作函数的参数都是gpio编号,可以通过of_get_named_gpio 获得,也不需要gpio_request的什么返回值,所以,gpio_request有啥意义?

使用了of_get_named_gpio 函数还需要使用gpio_request函数吗?

在使用 of_get_named_gpio 函数获取到 GPIO 编号后,是否需要使用 gpio_request 函数取决于具体的应用场景和需求。以下是对此问题的详细分析:

  • 需要使用 gpio_request 的情况

    • 独占访问需求:如果需要在驱动程序中独占地使用某个 GPIO 引脚,确保其他进程或设备无法同时访问该引脚,那么就需要使用 gpio_request 函数来请求该 GPIO 引脚的控制权。这样可以保证对该引脚的操作是安全且唯一的,避免与其他使用者产生冲突。

    • 配置引脚方向等操作:若要对获取到的 GPIO 引脚进行进一步的配置,如设置引脚方向为输入或输出、初始化引脚状态等,通常需要先通过 gpio_request 函数成功请求该引脚后,才能使用相关的配置函数(如 gpio_direction_inputgpio_direction_output 等)对其进行操作。

    • 规范的设备驱动开发:在遵循 Linux 内核的设备驱动开发规范和最佳实践时,即使在某些简单场景下可能看似可以直接使用获取到的 GPIO 编号进行操作,但为了保证代码的可维护性、可扩展性和兼容性,也应该先使用 gpio_request 函数来正确地请求和管理 GPIO 资源。

  • 可能不需要使用 gpio_request 的情况

    • 只读取信息:如果仅仅是从设备树中获取 GPIO 引脚的编号或其他相关信息,而不打算对该引脚进行实际的控制或配置操作,那么可以不使用 gpio_request 函数。例如,在一些只涉及硬件信息查询的场景中,只需要知道某个 GPIO 引脚的存在和其编号即可,无需进一步的交互。

    • 特殊的硬件抽象层或中间层处理:在某些特定的硬件抽象层(HAL)或中间层实现中,可能已经对 GPIO 资源的管理和访问进行了统一的处理和封装,使得在上层应用或驱动程序中无需直接调用 gpio_request 函数来请求 GPIO 引脚。这种情况下,开发者只需按照既定的接口和规范来使用 GPIO 资源即可,无需关心底层的请求细节。

综上所述,一般情况下,为了确保对 GPIO 引脚的正确控制和避免潜在的资源冲突,建议在使用 of_get_named_gpio 函数获取到 GPIO 编号后,根据具体的需求和使用场景来决定是否使用 gpio_request 函数来请求和管理该 GPIO 引脚。

总结

总体来说,使用还是比较简单的。

  1. pinctrl就是在设备树里配置pin信息;
  2. gpio子系统就是在设备树gpio的节点属性中引用pinctrl信息,并指明要使用的是哪个gpio;
  3. 之后在驱动里正常获取设备节点,并从属性里获取对应的gpio的编号,这个编号应该是系统自动分配的;
  4. 最后,就可以通过编号用对应的函数来操作gpio了。

具体的初始化过程,厂家维护的linux版本里就已经实现了。

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

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

相关文章

VC++ CPU指令集检测工具实现原理

&#x1f4c8; VC CPU指令集检测工具实现原理 例图&#xff1a;&#x1f9e0; 1. 核心原理&#xff1a;CPUID指令 // 使用CPUID指令获取CPU信息 int cpuInfo[4] { -1 }; __cpuid(cpuInfo, 0); // 调用CPUID指令 int nIds cpuInfo[0]; // 获取最大标准功能号CPUID指令工作流程…

大模型微调理论、实战:LLaMA-Factory、Unsloth

概述 微调&#xff0c;Fine-Tuning&#xff0c;简称FT&#xff0c;可理解为对LLM的定制&#xff0c;目的是增强专业领域知识&#xff0c;并优化特定任务的性能。通过在特定数据集上微调一个预训练模型&#xff0c;可实现&#xff1a; 更新知识&#xff1a;引入新的领域专属信…

【LCA 树上倍增】P9245 [蓝桥杯 2023 省 B] 景区导游|普及+

本文涉及知识点 树上倍增 P9245 [蓝桥杯 2023 省 B] 景区导游 题目描述 某景区一共有 NNN 个景点&#xff0c;编号 111 到 NNN。景点之间共有 N−1N-1N−1 条双向的摆渡车线路相连&#xff0c;形成一棵树状结构。在景点之间往返只能通过这些摆渡车进行&#xff0c;需要花费…

基于Python+Streamlit的旅游数据分析与预测系统:从数据可视化到机器学习预测的完整实现

&#x1f3de;️ 基于PythonStreamlit的旅游数据分析与预测系统&#xff1a;从数据可视化到机器学习预测的完整实现 &#x1f4dd; 前言 在大数据时代&#xff0c;旅游行业的数据分析变得越来越重要。如何从海量的旅游数据中挖掘有价值的信息&#xff0c;并进行准确的销量预测&…

飞算JavaAI全链路实战:智能构建高可用电商系统核心架构

飞算JavaAI全链路实战&#xff1a;智能构建高可用电商系统核心架构 前言&#xff1a;AI编程新时代的电商系统开发范式变革 在当今数字经济时代&#xff0c;电商系统作为企业数字化转型的核心载体&#xff0c;其复杂度和技术要求与日俱增。一个完整的电商系统不仅需要处理商品、…

论文精读(五):面向链接预测的知识图谱表示学习方法综述

笔者链接&#xff1a;扑克中的黑桃A 专栏链接&#xff1a;论文精读 本文关键词&#xff1a;知识图谱; 表示学习; 链接预测; 多元关系; 超关系 引 诸位技术同仁&#xff1a; 本系列将系统精读的方式&#xff0c;深入剖析计算机科学顶级期刊/会议论文&#xff0c;聚焦前沿突破…

Roo Code之自定义指令(Custom Instructions),规则(Rules)

在Roo Code 中&#xff0c;Custom Instructions 可以通过Instructions 设定和Rules 规则文件实现。什么是Custom Instructions&#xff1f; 自定义指令(Custom Instructions)定义了超出Roo基本角色定义范围的具体行为、偏好和约束。示例包括编码风格、文档标准、测试要求和工作…

9/8我是ai大师

一、变量定义部分&#xff08;理解程序的 "记忆"&#xff09;c运行/* USER CODE BEGIN PV */ static uint8_t last_button_state 1; // 初始为高电平&#xff08;未按下&#xff09; static uint8_t device_mode 0; // 设备模式&#xff1a;0LD1, 1LD3, 2蜂鸣器, 3…

前沿重器[74] | 淘宝RecGPT:大模型推荐框架,打破信息茧房

前沿重器栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

jenkins加docker 部署项目

jenkins加docker 部署springboot项目 1项目结构Dockerfile 内容 FROM openjdk:8-jdk-alpine ARG JAR_FILEtarget/*.jar COPY ${JAR_FILE} app.jar ENTRYPOINT ["java","-jar","/app.jar","--server.port9090"]在A服务器上启动jenkins …

提示词工程(Prompt Engineering)的崛起——为什么“会写Prompt”成了新技能?

&#x1f380;【开场 猫猫狐狐的对话】&#x1f43e;猫猫扒着屏幕&#xff1a;“喵&#xff1f;咱写的这句 Prompt 怎么又跑偏啦&#xff1f;明明只是想让它帮忙写一段 Python 代码&#xff0c;它偏要给咱写论文摘要……” &#x1f98a;狐狐眯着眼&#xff0c;声音带点冷意&a…

供应链管理系统入门知识:是什么,功能模块,怎么定制开发?

如果你是刚接触企业运营的新手&#xff0c;听到 “供应链管理系统” 可能会觉得有点复杂。其实&#xff0c;它就像一个 “智能管家”&#xff0c;帮企业把从买材料到卖产品的一系列流程管得明明白白。今天就用大白话给你讲清楚这个系统到底是什么&#xff0c;以及它能帮上什么忙…

kotlin - 平板分屏,左右拖动,2个Activity计算宽度,使用ActivityOptions、Rect(三)

kotlin - 平板分屏&#xff0c;左右拖动&#xff0c;2个Activity计算宽度&#xff0c;使用ActivityOptions、Rect使用平板&#xff0c;api33才支持&#xff0c;可以左右拖动&#xff0c;分屏第一个页面 &#xff0c; 思考&#xff1a;分屏后&#xff0c;对整个app的影响&#x…

v0.29.3 敏感词性能优化之繁简体转换 opencc4j 优化

敏感词性能调优系列 v0.29.0 敏感词性能优化提升 14 倍全过程 v0.29.1 敏感词性能优化之内部类迭代器内部类 v0.29.2 敏感词性能优化之基本类型拆箱、装箱的进一步优化的尝试 v0.29.3 敏感词性能优化之繁简体转换 opencc4j 优化 背景 opencc4j opencc4j 中&#xff0c;因…

Spark SQL解析查询parquet格式Hive表获取分区字段和查询条件

首先说一下&#xff0c;这里解决的问题应用场景&#xff1a; sparksql处理Hive表数据时&#xff0c;判断加载的是否是分区表&#xff0c;以及分区表的字段有哪些&#xff1f;再进一步限制查询分区表必须指定分区&#xff1f; 这里涉及到两种情况&#xff1a;select SQL查询和…

谷歌发布文本嵌入模型EmbeddingGemma(附部署方式)

EmbeddingGemma是谷歌于2025年9月开源的开放式文本嵌入模型&#xff0c;专为端侧设备设计&#xff0c;具备以下核心优势&#xff1a; 性能优势 在MTEB基准测试中&#xff0c;EmbeddingGemma在500M以下参数规模的多语言文本嵌入模型中表现最佳&#xff0c;性能接近参数翻倍的顶…

CPU调度——调度的目标

2.2.2 调度的目标 当系统中“想运行”的实体多于 CPU 的数量时&#xff0c;调度就不可避免地要在“效率”与“公平”之间做取舍。直观地说&#xff0c;一类目标希望把硬件压榨到更高的利用率&#xff0c;让单位时间内做更多的工作&#xff1b;另一类目标则关心个体体验&#x…

C++ 8

封装一个学生的类&#xff0c;定义一个学生这样类的vector容器, 里面存放学生对象&#xff08;至少3个&#xff09;再把该容器中的对象&#xff0c;保存到文件中。再把这些学生从文件中读取出来&#xff0c;放入另一个容器中并且遍历输出该容器里的学生。#include <iostream…

短视频矩阵系统源码开发搭建技术指南--支持OEM

短视频矩阵系统架构设计短视频矩阵系统通常采用分布式架构&#xff0c;包含内容管理、用户管理、推荐算法、存储分发等模块。主流技术栈包括微服务框架&#xff08;Spring Cloud/Dubbo&#xff09;、消息队列&#xff08;Kafka/RabbitMQ&#xff09;、数据库&#xff08;MySQL/…

不连续页分配器补充

vmalloc流程 1. 背景&#xff1a;vmalloc() 要解决的问题 kmalloc() 要求 虚拟地址连续&#xff0c;物理页也连续。大块内存分配可能失败。vmalloc() 只保证 虚拟地址连续&#xff0c;物理内存可以由很多不连续的页拼接。 实现的关键就是&#xff1a; 在 vmalloc 区域 找一块空…