Linux驱动-GPIO基本函数api

结合设备树,了解gpio 部分控制方法


一、参考文档:

基础相关:驱动相关知识、gpio 这一块,基本知识,常识需要了解的,可以参考之前 体系文章。
驱动GPIO-获取单个gpio描述符
使用C程序通过sysfs文件系统控制gpio

相关扩展知识内容:
命令行控制 - sysfs控制方法
在Linux驱动中使用gpio子系统
GPIO使用总结

二、学习目标-了解哪些控制方法

1.获取GPIO

在之前篇章已经了解过 驱动GPIO-获取单个gpio描述符,新版本gpio 体系中,最基本你需要先获取得到gpio 描述符吧。

gpiod_get;
gpiod_get_index;
gpiod_get_array;
devm_gpiod_get;
devm_gpiod_get_index;
devm_gpiod_get_array;

2.设置方向

gpiod_direction_input;
gpiod_direction_output;

3.读值、写值

gpiod_get_value;
gpiod_set_value;

4. 设为中断(如果必要)

request_irq(gpiod_to_irq(gpio_desc)...); //将gpio转为对应的irq,然后注册该irq的中断handler

5.释放GPIO

gpiod_put;
gpiod_put_array;
devm_gpiod_put;
devm_gpiod_put_array;

备注:这里先从学习目标上面列出需要掌握的内容。 说得高大上,其实就是理解几个api,然后会用。

三、必备基础 - sysfs 基础知识再熟悉

 在不了解gpio 前提下,很多基础知识必须掌握的,一步一步方便理解。这里再次列出sysfs 里面的基础知识点,方便下文后续继续理解。

强烈建议参考之前文章: GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO

这里最核心的就是需要了解:基础文件active_low、direction、edge、value 描述 ,如下图:
在这里插入图片描述

这里再简要说明,方便理解,个人觉得,理解了这些,你才知道为什么需要我们gpio 相关的api

  • active_low:该文件用于设置GPIO引脚的电平触发方式。写入"1"意味着将该引脚的电平触发逻辑翻转(即,硬件上的高电平被视作逻辑上的低电平,硬件上的低电平被视作逻辑上的高电平)。写入"0"则表示使用正常的电平触发逻辑。
  • direction:通过这个文件可以设置GPIO引脚是作为输入还是输出。写入"out"将GPIO配置为输出模式,写入"in"则将其配置为输入模式。
  • edge:这个文件用于配置中断触发边缘,只对设置为输入模式的GPIO有效。可写入的值通常有:“none”(无触发),“rising”(上升沿触发),“falling”(下降沿触发),或"both"(上升沿和下降沿都触发)。
  • uevent:这个文件允许用户向用户空间发送事件,通知关于GPIO引脚状态的变化。
  • value:这个文件表示GPIO引脚的当前值。对于输出模式的GPIO,写入"1"或"0"可以改变引脚的电平状态。对于输入模式的GPIO,读取这个文件可以获取当前引脚的电平状态("1"表示高电平,"0"表示低电平)。

四、实验测试

定义设备树

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

  • 在根节点下定义gpio 节点-my_gpio
  • 在pin-ctrl 设备树节点中定义my_gpio

抛出问题-为什么定义一个自己的节点,需要在设备树根节点里面创建一个并在pin-ctrl 里面在创建一个节点呢?

简单来说,这两个步骤分别完成了两个不同的、但都必不可少的任务:

  • 在根节点定义:声明一个GPIO资源。意思是“系统中存在这么一组GPIO引脚,它的控制权在这里定义”。
  • 在pinctrl节点中定义:配置这些GPIO引脚的电气特性和复用状态。意思是“当我的设备要使用这些引脚时,请把它们设置为以下具体状态(如上下拉、驱动强度、复用为GPIO功能等)”。

在这里插入图片描述
在这里插入图片描述

为什么必须分两步?—— 总结

职责分离 (Separation of Concerns):
  • GPIO控制器节点:描述硬件资源本身。内核需要知道有哪些GPIO端口、它们的地址在哪里,才能进行底层的读写操作。
  • Pinctrl配置:描述资源的使用方式。一个引脚在被用作GPIO之前,必须通过SoC的复用寄存器将其功能切换到GPIO模式,并设置好电气特性。这是 pinctrl 子系统的核心工作。
资源复用 (Resource Sharing):
  • 一个GPIO控制器(如 gpio1)提供了32个引脚资源。你的LED可能只用其中一个,其他人的按键可能用另一个。大家共享同一个资源库,但各自通过不同的 pinctrl 配置项来申请和使用自己需要的那部分。
动态配置 (Dynamic Configuration):
  • 设备可以在不同状态下使用不同的引脚配置(通过 pinctrl-names 和 pinctrl-0, pinctrl-1 等实现)。但无论怎么配置,它操作的底层硬件控制器始终是 gpio1。

如下, 我把两处 根节点、pin-ctrl 定义放到一起,方便查看:

     my_gpio:gpio1_a0{
        compatible =  "mygpio";
        my-gpios =  <&gpio1  RK_PA0  GPIO_ACTIVE_HIGH>;
	    pinctrl-names  =  "default";
	    pinctrl-0  =  <&my_gpio_ctrl>;
	  };
	  
	  
		mygpio{
		my_gpio_ctrl:my-gpio-ctrl {
			rockchip,pins = <1 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>;
		};
	};  

实验代码

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

struct gpio_desc *mygpio1; // GPIO 描述符指针
int dir, value, irq;

// 平台设备初始化函数
//  平台设备初始化函数
static int my_platform_probe(struct platform_device *dev)
{
    printk("This is my_platform_probe\n");

    // 获取可选的GPIO描述符
    mygpio1 = gpiod_get_optional(&dev->dev, "my", 0);
    if (mygpio1 == NULL)
    {
        printk("gpiod_get_optional error\n");
        return -1;
    }

    gpiod_direction_output(mygpio1, 0);

    gpiod_set_value(mygpio1, 1);

    dir = gpiod_get_direction(mygpio1);

    if (dir == 0)
    {
        printk("dir is GPIO     OUT\n");
    }
    else if (dir == 1)
    {
        printk("dir is    GPIO      IN \n");
    }

    value = gpiod_get_value(mygpio1);

    printk("gpio value  %d  \n", value);

    irq = gpiod_to_irq(mygpio1);

    printk("gpio   irq   %d  \n", irq);

    // 释放GPIO描述符
    gpiod_put(mygpio1);

    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)
{
    // 注销平台驱动
    gpiod_put(mygpio1);
    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");

编译后,验证使用方法结果

在这里插入图片描述

总结

  • gpio 篇章,了解、使用 gpio 相关的api 使用方法。 其实还是围绕 高低电平设置、方向、读值、中断号获取等 基本API
  • 实验简单,实际工作中会经常用到
【四轴飞行器】非线性三自由度四轴飞行器模拟器研究(Matlab代码实现)内容概要:本文围绕非线性三自由度四轴飞行器模拟器的研究展开,重点介绍基于Matlab代码实现的四轴飞行器动力学建模与仿真方法。研究构建了考虑非线性特性的飞行器数学模型,涵盖姿态动力学与运动学方程,实现了三自由度(滚转、俯仰、偏航)的精确模拟。文中详细阐述了系统建模过程、控制算法设计思路及仿真结果分析,帮助读者深入理解四轴飞行器的飞行动力学特性与控制机制;同时,该模拟器可用于算法验证、控制器设计与教学实验。; 适合人群:具备一定自动控制理论基础和Matlab编程能力的高校学生、科研人员及无人机相关领域的工程技术人员,尤其适合从事飞行器建模、控制算法开发的研究生和初级研究人员。; 使用场景及目标:①用于四轴飞行器非线性动力学特性的学习与仿真验证;②作为控制器(如PID、LQR、MPC等)设计与测试的仿真平台;③支持无人机控制系统教学与科研项目开发,提升对姿态控制与系统仿真的理解。; 阅读建议:建议读者结合Matlab代码逐模块分析,重点关注动力学方程的推导与实现方式,动手运行并调试仿真程序,以加深对飞行器姿态控制过程的理解。同时可扩展为六自由度模型或加入外部干扰以增强仿真真实性。
基于分布式模型预测控制DMPC的多智能体点对点过渡轨迹生成研究(Matlab代码实现)内容概要:本文围绕“基于分布式模型预测控制(DMPC)的多智能体点对点过渡轨迹生成研究”展开,重点介绍如何利用DMPC方法实现多智能体系统在复杂环境下的协同轨迹规划与控制。文中结合Matlab代码实现,详细阐述了DMPC的基本原理、数学建模过程以及在多智能体系统中的具体应用,涵盖点对点转移、避障处理、状态约束与通信拓扑等关键技术环节。研究强调算法的分布式特性,提升系统的可扩展性与鲁棒性,适用于多无人机、无人车编队等场景。同时,文档列举了大量相关科研方向与代码资源,展示了DMPC在路径规划、协同控制、电力系统、信号处理等多领域的广泛应用。; 适合人群:具备一定自动化、控制理论或机器人学基础的研究生、科研人员及从事智能系统开发的工程技术人员;熟悉Matlab/Simulink仿真环境,对多智能体协同控制、优化算法有一定兴趣或研究需求的人员。; 使用场景及目标:①用于多智能体系统的轨迹生成与协同控制研究,如无人机集群、无人驾驶车队等;②作为DMPC算法学习与仿真实践的参考资料,帮助理解分布式优化与模型预测控制的结合机制;③支撑科研论文复现、毕业设计或项目开发中的算法验证与性能对比。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,重点关注DMPC的优化建模、约束处理与信息交互机制;按文档结构逐步学习,同时参考文中提及的路径规划、协同控制等相关案例,加深对分布式控制系统的整体理解。
### 关于Linux SPI GPIO 驱动开发 #### 开发环境准备 为了在Zybo板上进行Linux下的SPI-GPIO驱动开发,建议先熟悉嵌入式Linux的基础知识并完成初步设置。这包括但不限于了解如何配置和编译适合目标硬件平台的内核版本以及构建根文件系统[^1]。 #### 获取必要的工具链和支持材料 访问Digilent网站上的Embedded Linux页面获取官方提供的《Embedded Linux Development Guide》,这份文档对于理解整个流程至关重要,并能提供每一步所需的知识点介绍。 #### 编写SPI设备树节点定义 针对特定型号的处理器编写对应的Device Tree Source(DTS) 文件来描述SPI控制器及其连接到系统的GPIO引脚。下面是一个简单的DTS片段用于说明: ```dts &spi0 { pinctrl-names = "default"; pinctrl-0 = <&spi0_pins_a>; status = "okay"; spidev@0 { compatible = "spidev"; reg = <0>; /* chip select 0 */ spi-max-frequency = <500000>; gpio-leds { compatible = "gpio-leds"; led0: led@0 { label = "status_led"; gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>; }; }; }; }; ``` 此段代码展示了如何通过device tree为SPI外设分配资源,并关联了一个LED作为例子展示GPIO控制方法。 #### 实现字符设备接口 创建一个新的模块源码文件`spi_gpio.c`,实现基本的操作函数集(open/read/write/close),以便应用程序可以通过标准POSIX I/O API与底层硬件交互。这里给出部分框架性的指导而不是完整的实现细节: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/spi/spi.h> #include <linux/gpio/consumer.h> static struct spi_device *spi; // ...其他必要声明... static int __init spi_gpio_init(void){ // 初始化逻辑... } static void __exit spi_gpio_exit(void){ // 清理工作... } module_init(spi_gpio_init); module_exit(spi_gpio_exit); MODULE_LICENSE("GPL"); ``` 上述模板中包含了加载/卸载时调用的关键入口点;实际项目里还需要补充具体的数据传输处理机制等内容。 #### 测试验证 一旦完成了驱动程序的设计编码之后,在宿主机环境中交叉编译生成适用于ARM架构的目标二进制文件。接着将其部署至Zybo开发板并通过命令行测试新加入的功能特性是否正常运作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

野火少年

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

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

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

打赏作者

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

抵扣说明:

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

余额充值