Linux驱动-GPIO子系统与pinctrl子系统相结合

Linxu 驱动 中 GPIO 与 pinctrl 子系统


前言

如果对于一个初学者来说,GPIO 子系统已经了解了部分,这里目标就是GPIO子系统结合pinctrl 子系统的知识理解。

必须具备知识点:

  • gpio 子系统理解,包括基本GPIO的API
  • pinctrl 子系统的理解,包括pinctrl 子系统的基本API

一、参考资料

GPIO子系统GPIO的调试方法
GPIO的调试方法
Pinctrl子系统和GPIO子系统——LED实验
gpio子系统与pinctrl子系统一:关系
Linux驱动-GPIO基本函数api
Linux 驱动-GPIO 三级节点获取和控制相关API
GPIO子系统-第135章 GPIO子系统与pinctrl子系统相结合实验
Linux驱动设备树-pinctrl篇

深入思考

    首先,对于初学者来说,gpio结合pinctrl 这个需求 或者 说这个意思到底是什么? 上面提供的参考资料如果说深入研究、自己结合自己之前的知识点 理解后。 回过头来看 就像一个圈圈一样 知识点实现了闭环。

    在 Linux驱动-GPIO基本函数api 中,我就提到过一个问题,为什么 抛出问题-为什么定义一个自己的节点,需要在设备树根节点里面创建一个并在pin-ctrl 里面在创建一个节点呢?为什么必须分两步?Linux 驱动-GPIO 三级节点获取和控制相关API 学习的内容中也 列出了实现所需要修改的设备树 ,反问道为什么设备树需要这么修改。 在 Linux驱动设备树-pinctrl篇 中,我们实验过 gpio+pinctrl 实现默认复用gpio 脚具体功能。
    回过头来看,其实我们已经学习过gpio+pinctrl 的使用方法和最基本的理论知识并实践过。 但是 知识点都是零散的,突然对知识点总结起来,发现一脸懵逼,再思考一下,其实自己还是理解的,这样知识就形成了闭环。

理解指引-目标

上面思考,其实理解了gpio、pinctrl 子系统的知识点,使用方案,这里再次加深理解,并搞清楚我们这里需要加深理解的知识点是什么?

问题:pinctrl-names = “myled1”; 指定名称和默认default 有什么区别

在设备树(DTS)配置中,pinctrl-names 配置 default 和指定名称的主要区别如下:

default 名称 - 特点
pinctrl-names = "default";
pinctrl-0 = <&mygpio_ctrl>;

特点

  • 系统在设备初始化时会自动加载对应的 pinctrl 状态
  • 不需要在驱动代码中显式调用状态切换
  • 适用于设备只需要一种固定的引脚配置状态

我们之前学过的知识点 Linux驱动设备树-pinctrl篇 已经配置过自己定义的led 灯相关的 pinctrl 实例编写-将 led 的控制引脚复用为 GPIO 模式 的知识点。

指定名称-特点
pinctrl-names = "myled1", "sleep";
pinctrl-0 = <&mygpio_ctrl>;
pinctrl-1 = <&mygpio_sleep>;

特点

  • 需要在驱动代码中手动切换状态
  • 支持多种工作模式(如:正常模式、睡眠模式等)
  • 提供更灵活的引脚状态管理

目标

所以,这里我们就是在指定pinctrl-names 情况下,在驱动中去指定pin-ctrl 的功能到硬件。

二、pin-ctrl 相关函数

在Linux驱动开发中,pinctrl 子系统用于管理引脚的复用和电气属性。下面详细解释 pinctrl_get、pinctrl_put、pinctrl_lookup_state 和 pinctrl_select_state 这几个核心函数。

pinctrl_get

pinctrl_get 函数用于获取与指定设备关联的 pinctrl 控制句柄。

struct pinctrl *pinctrl_get(struct device *dev);
dev:指向要获取引脚控制器的设备对象指针

功能:根据设备对象 dev,获取该设备关联的 pinctrl(引脚控制器)结构体指针。

返回值:

成功时返回一个指向 struct pinctrl 的指针。

失败(如设备不支持)返回 NULL

使用场景
在设备驱动探测(probe)函数中,通常首先调用此函数来获取设备的引脚控制句柄,为后续的引脚状态配置做准备。

pinctrl_put

pinctrl_put 函数用于释放通过 pinctrl_get 获取的 pinctrl 资源。

函数原型与参数
void pinctrl_put(struct pinctrl *p);
p:要释放的 pinctrl 结构体指针

功能:释放通过 pinctrl_get() 获取的 pinctrl 资源,避免内存泄漏。
返回值:无返回值



使用场景

  • 通常在驱动移除(remove)函数或探测失败时调用,用于释放之前获取的 pinctrl 资源。必须与 pinctrl_get 配对使用。

  • 对于 devm_pinctrl_get 获取的句柄,应使用 devm_pinctrl_put 释放,但通常可以不显式调用,由设备资源管理机制自动处理。

pinctrl_lookup_state

pinctrl_lookup_state 函数用于在指定的 pinctrl 实例中查找对应的引脚配置状态。

函数原型与参数
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name);
p:要查找状态的 pinctrl 结构体指针。
name:要查找的状态名称。

功能与返回值
功能:根据名称查找对应的引脚配置状态(如引脚模式、电气属性)。

返回值:
成功时返回该状态的指针。
未找到或出错返回 NULL

常用状态名称

  • PINCTRL_STATE_DEFAULT 或 “default”:默认状态
  • PINCTRL_STATE_IDLE 或 “idle”:空闲状态
  • PINCTRL_STATE_SLEEP 或 “sleep”:睡眠状态

使用场景
在设备驱动中,需要查找特定工作模式对应的引脚状态时使用。例如,查找设备的默认状态、睡眠状态或空闲状态。

pinctrl_select_state

pinctrl_select_state 函数用于将指定的引脚配置状态应用到硬件。

函数原型与参数
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);

p:要操作的 pinctrl 结构体指针。
s:要应用的引脚配置状态指针。


功能与返回值
功能:将指定的引脚配置状态 s 应用到硬件,更新引脚的实际设置(例如切换引脚功能)。

返回值:
成功返回 0。
失败返回负数错误码(如 -EINVAL)。



使用场景

  • 当需要动态切换设备引脚状态时使用,例如:
  • 设备进入或退出低功耗模式时切换睡眠状态
  • 设备工作模式改变时需要切换引脚功能
  • 运行时根据操作需求切换引脚配置

API 总结与简易对比

函数作用调用时机
pinctrl_get获取引脚控制句柄驱动探测时
pinctrl_lookup_state查找引脚状态获取句柄后
pinctrl_select_state应用引脚状态需要切换状态时
pinctrl_put释放引脚控制句柄驱动卸载或出错时

三、GPIO+pinctrl 子系统结合-动态配置pin引脚功能

上面提到过 指定pinctrl 名称的特点:下面就通过实际实验来说明使用方案、步骤、方法。
指定名称-特点

pinctrl-names = "myled1", "sleep";
pinctrl-0 = <&mygpio_ctrl>;
pinctrl-1 = <&mygpio_sleep>;

特点

  • 需要在驱动代码中手动切换状态
  • 支持多种工作模式(如:正常模式、睡眠模式等)
  • 提供更灵活的引脚状态管理

修改设备树

位置:kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi,两点:


/ {
	model = "Rockchip RK3568 EVB1 DDR4 V10 Board";
	compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";

....................................
my_gpio:gpio1_a0 {
    compatible = "mygpio";
	my-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_HIGH>;
	pinctrl-names = "myled1";
	pinctrl-0 = <&my_gpio_ctrl>;		
};
};





&pinctrl {
 .......................
  	mygpio{
		my_gpio_ctrl:my-gpio-ctrl {
			rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};
..................................
};

在第四行中的pinctrl-names参数并不是default,这就需要用到我们前面pinctrl子系统中的知识来查找并设置相应的pinctrl状态了

驱动程序测试源码

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>

struct pinctrl *led_pinctrl; // pinctrl 实例指针
struct pinctrl_state *led_state;// pinctrl 状态指针
int ret;
//平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
    printk("This is mydriver_probe\n");
    
    led_pinctrl = pinctrl_get(&dev->dev);// 获取 pinctrl 实例
    if (IS_ERR(led_pinctrl)) {
        printk("pinctrl_get is error\n");
        return -1;
    }
    
    led_state = pinctrl_lookup_state(led_pinctrl, "myled1");// 查找状态
    if (IS_ERR(led_state)) {
        printk("pinctrl_lookup_state is error\n");
        return -2;
    }
    
    ret = pinctrl_select_state(led_pinctrl, led_state);// 设置状态到硬件
    if (ret < 0) {
        printk("pinctrl_select_state is error\n");
        return -3;
    }
    
    return 0;
}

// 平台设备的移除函数
static int my_platform_remove(struct platform_device *pdev)
{
    printk(KERN_INFO "my_platform_remove: Removing platform device\n");

    // 清理设备特定的操作
    // ...

    return 0;
}


const struct of_device_id of_match_table_id[]  = {
	{.compatible="mygpio"},
};

// 定义平台驱动结构体
static struct platform_driver my_platform_driver = {
    .probe = my_platform_probe,
    .remove = my_platform_remove,
    .driver = {
        .name = "my_platform_device",
        .owner = THIS_MODULE,
		.of_match_table =  of_match_table_id,
    },
};

// 模块初始化函数
static int __init my_platform_driver_init(void)
{
    int ret;

    // 注册平台驱动
    ret = platform_driver_register(&my_platform_driver);
    if (ret) {
        printk(KERN_ERR "Failed to register platform driver\n");
        return ret;
    }

    printk(KERN_INFO "my_platform_driver: Platform driver initialized\n");

    return 0;
}

// 模块退出函数
static void __exit my_platform_driver_exit(void)
{
    // 注销平台驱动
    platform_driver_unregister(&my_platform_driver);

    printk(KERN_INFO "my_platform_driver: Platform driver exited\n");
}

module_init(my_platform_driver_init);
module_exit(my_platform_driver_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("fangchen");

测试结果

先搞清楚,pin 脚是哪个脚 GPIO1_A0 ,所以它的pin脚号 就是 1*32+(A-A)*8+0=32
调试GPIO 方法,详细可参考 GPIO的调试方法

GPIO 调试-编译固件之前查看指定pin脚GPIO信息

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins | grep 32

结果如下:
在这里插入图片描述

驱动运行-加载驱动

如下,说明驱动已经运行起来了。
在这里插入图片描述

再次进行 GPIO 调试-编译固件之前查看指定pin脚GPIO信息

cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins | grep 32

结果如下:
在这里插入图片描述

总结

  • 这里只是对gpio-pinctrl 两个子系统结合起来用的一个总结、规整知识点而已,实际上我们一直都是这么用的。
  • 多总结,知识点 多分析,多思考。
基于数据驱动的 Koopman 算子的递归神经网络模型线性化,用于纳米定位系统的预测控制研究(Matlab代码实现)内容概要:本文围绕“基于数据驱动的Koopman算子的递归神经网络模型线性化”展开,旨在研究纳米定位系统的预测控制方法。通过结合数据驱动技术Koopman算子理论,将非线性系统动态近似为高维线性系统,进而利用递归神经网络(RNN)建模并实现系统行为的精确预测。文中详细阐述了模型构建流程、线性化策略及在预测控制中的集成应用,并提供了完整的Matlab代码实现,便于科研人员复现实验、优化算法并拓展至其他精密控制系统。该方法有效提升了纳米级定位系统的控制精度动态响应性能。; 适合人群:具备自动控制、机器学习或信号处理背景,熟悉Matlab编程,从事精密仪器控制、智能制造或先进控制算法研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①实现非线性动态系统的数据驱动线性化建模;②提升纳米定位平台的轨迹跟踪预测控制性能;③为高精度控制系统提供可复现的Koopman-RNN融合解决方案; 阅读建议:建议结合Matlab代码逐段理解算法实现细节,重点关注Koopman观测矩阵构造、RNN训练流程模型预测控制器(MPC)的集成方式,鼓励在实际硬件平台上验证并调整参数以适应具体应用场景。
提供了一套完整的基于51单片机的DDS(直接数字频率合成)信号波形发生器设计方案,适合电子爱好者、学生以及嵌入式开发人员学习和实践。该方案详细展示了如何利用51单片机(以AT89C52为例)结合AD9833 DDS芯片来生成正弦波、锯齿波、三角波等多种波形,并且支持通过LCD12864显示屏直观展示波形参数或状态。 内容概述 源码:包含完整的C语言编程代码,适用于51系列单片机,实现了DDS信号的生成逻辑。 仿真:提供了Proteus仿真文件,允许用户在软件环境中测试整个系统,无需硬件即可预览波形生成效果。 原理图:详细的电路原理图,指导用户如何连接单片机、DDS芯片及其他外围电路。 PCB设计:为高级用户准备,包含了PCB布局设计文件,便于制作电路板。 设计报告:详尽的设计文档,解释了项目背景、设计方案、电路设计思路、软硬件协同工作原理及测试结果分析。 主要特点 用户交互:通过按键控制波形类型和参数,增加了项目的互动性和实用性。 显示界面:LCD12864显示屏用于显示当前生成的波形类型和相关参数,提升了项目的可视化度。 教育价值:本资源非常适合教学和自学,覆盖了DDS技术基础、单片机编程和硬件设计多个方面。 使用指南 阅读设计报告:首先了解设计的整体框架和技术细节。 环境搭建:确保拥有支持51单片机的编译环境,如Keil MDK。 加载仿真:在Proteus中打开仿真文件,观察并理解系统的工作流程。 编译烧录:将源码编译无误后,烧录至51单片机。 硬件组装:根据原理图和PCB设计制造或装配硬件。 请注意,本资源遵守CC 4.0 BY-SA版权协议,使用时请保留原作者信息及链接,尊重原创劳动成果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

野火少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值