Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践

Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践


文章目录

  • [Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践](#Linux驱动开发实战(十一):GPIO子系统深度解析与RGB LED驱动实践)
  • 前言
  • 一、GPIO控制器设备树剖析
    • [1.1 核心节点定义解析](#1.1 核心节点定义解析)
    • [1.2 GPIO编号计算(当拓展看看即可)](#1.2 GPIO编号计算(当拓展看看即可))
  • [二、RGB LED设备节点实现](#二、RGB LED设备节点实现)
    • [2.1 设备树配置实例](#2.1 设备树配置实例)
    • [2.2 关键解析点:](#2.2 关键解析点:)
  • 三、驱动开发
    • [3.1 关键API深度解析](#3.1 关键API深度解析)
    • [3.2 代码解析](#3.2 代码解析)
    • [3.3 关键代码解析:](#3.3 关键代码解析:)
  • 四、实验
  • 总结

前言

本文讲:

  1. 如何通过设备树(Device Tree)优雅地描述硬件资源
  2. 平台设备驱动(Platform Driver)的注册与匹配机制
  3. GPIO资源的申请、配置与原子化操作

一、GPIO控制器设备树剖析

1.1 核心节点定义解析

以gpio1节点为例:

dts:arch/arm/boot/dts/imx6ull-mmc-npi.dtsi

c 复制代码
gpio1: gpio@209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    reg = <0x209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
             <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clks IMX6UL_CLK_GPIO1>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    gpio-ranges = <&iomuxc 0 23 10>, 
              <&iomuxc 10 17 6>,
              <&iomuxc 16 33 16>;
};

关键属性解析:

  • reg:0x209c000为GPIO1的物理基地址,0x4000表示地址范围
  • gpio-ranges :实现GPIO编号到物理引脚的映射,格式为:
    <&pinctrl 起始GPIO 起始PIN 数量>
    第一个映射:<&iomuxc 0 23 10>表示:
  • GPIO1_IO0 对应物理引脚IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO00(基地址+0x14)

1.2 GPIO编号计算(当拓展看看即可)

系统GPIO编号计算公式:

bash 复制代码
全局编号 = bank编号 * 32 + pin号

i.MX6ULL各GPIO控制器对应bank:

  • GPIO1:0*32 = 0
  • GPIO2:1*32 = 32
  • GPIO5:4*32 = 128(注意中间有保留bank)
    示例:GPIO4_IO19的全局编号:
bash 复制代码
(4-1)*32 + 19 = 3*32 +19 = 115

二、RGB LED设备节点实现

2.1 设备树配置实例

c 复制代码
rgb_led {
    #address-cells = <1>;  // 子节点地址用1个u32表示
    #size-cells = <1>;     // 子节点大小用1个u32表示
    compatible = "fire,rgb_led"; // 驱动匹配标识符
    pinctrl-names = "default";   // 引脚控制状态名称
    pinctrl-0 = <&pinctrl_rgb_led>; // 关联的引脚配置组

    // 红色LED使用两个GPIO(阵列格式)
    rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW   // GPIO1_IO04
                   &gpio1 10 GPIO_ACTIVE_LOW>; // GPIO1_IO10
                   
    // 绿色LED配置(单GPIO)
    rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>; // GPIO4_IO20
    
    // 蓝色LED配置(单GPIO)
    rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;  // GPIO4_IO19
    
    status = "okay";  // 启用设备
};
};

2.2 关键解析点:

  1. 引脚控制组:
c 复制代码
pinctrl-0 = <&pinctrl_rgb_led>;

需要配套的引脚定义(通常在iomuxc节点中)前文有讲:

c 复制代码
pinctrl_rgb_led: rgbledgrp {
    fsl,pins = <
        MX6ULL_PAD_GPIO1_IO04__GPIO1_IO04 0x10B0
        MX6ULL_PAD_GPIO1_IO10__GPIO1_IO10 0x10B0
        MX6ULL_PAD_CSI_HSYNC__GPIO4_IO20  0x10B0
        MX6ULL_PAD_CSI_VSYNC__GPIO4_IO19  0x10B0
    >;
};
  1. GPIO编号计算:
  • &gpio1 4 = (1-1)*32 + 4 = 4
  • &gpio4 20 = (4-1)*32 +20 = 116
c 复制代码
// &gpioX Y → 全局编号 = (X-1)*32 + Y

rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW  // 全局编号 = (1-1)*32 +4 = 4
              &gpio1 10 GPIO_ACTIVE_LOW>; // 全局编号 = (1-1)*32 +10 = 10

rgb_led_green = <&gpio4 20 GPIO_ACTIVE_LOW>; // (4-1)*32 +20 = 116
rgb_led_blue = <&gpio4 19 GPIO_ACTIVE_LOW>;  // (4-1)*32 +19 = 115

将物理引脚(如GPIO1_IO04)转换为Linux GPIO子系统使用的统一编号

  1. 电平极性:
  • GPIO_ACTIVE_LOW 表示低电平点亮LED
  • 在驱动中需要取反电平设置:
c 复制代码
gpio_set_value(gpio_num, !status); 

三、驱动开发

3.1 关键API深度解析

  1. of_get_named_gpio()
  • 自动计算全局GPIO编号
  • 支持多索引值(如rgb_led_red的两个GPIO)
  1. gpio_direction_output()
c 复制代码
static inline int gpio_direction_output(unsigned gpio, int value)
{
    return gpio_direction_output_raw(gpio, value);
}
  • 内部会操作GPIO_GDIR寄存器(方向寄存器)
  • 对于i.MX6ULL,设置方向寄存器的对应bit为1表示输出

3.2 代码解析

c 复制代码
// ... 头文件包含部分 ...
/*------------------GPIO获取关键部分----------------------*/
int rgb_led_red;
int rgb_led_green;
int rgb_led_blue;

static int led_probe(struct platform_device *pdv)
{
    // 1. 查找设备树节点(核心操作)
    rgb_led_device_node = of_find_node_by_path("/rgb_led");
    if (!rgb_led_device_node) {
        printk(KERN_ERR "Failed to find rgb_led node\n");
        return -ENODEV;
    }

    // 2. 获取GPIO编号(设备树解析核心)
    rgb_led_red = of_get_named_gpio(rgb_led_device_node, "rgb_led_red", 0);
    if (gpio_is_valid(rgb_led_red)) {
        printk(KERN_INFO "Red GPIO: %d\n", rgb_led_red);
    } else {
        printk(KERN_ERR "Invalid red GPIO\n");
        return -EINVAL;
    }

    // 3. GPIO请求与配置(硬件操作)
    ret = gpio_request(rgb_led_red, "led-red");
    if (ret) {
        printk(KERN_ERR "Red GPIO request failed: %d\n", ret);
        return ret;
    }
    gpio_direction_output(rgb_led_red, 1); // 初始化为高电平

    // ... 绿色和蓝色GPIO的相同处理流程 ...
}

3.3 关键代码解析:

  1. 设备树节点查找
c 复制代码
of_find_node_by_path("/rgb_led")

通过绝对路径查找设备树节点

对应设备树中的:

c 复制代码
rgb_led {
    compatible = "fire,rgb_led";
    // ...
};
  1. GPIO编号获取:
c 复制代码
of_get_named_gpio(np, "rgb_led_red", 0)
  • 解析设备树属性rgb_led_red的第一个GPIO
  • 对应设备树中的:
c 复制代码
rgb_led_red = <&gpio1 4 GPIO_ACTIVE_LOW>;
  • 返回值是全局GPIO编号,计算方式:
c 复制代码
global_gpio = (gpio_bank-1)*32 + pin
例如:gpio1_4 → (1-1)*32 +4 =4
  1. GPIO硬件操作:
c 复制代码
gpio_request(rgb_led_red, "led-red");
gpio_direction_output(rgb_led_red, 1);
  • gpio_request:标记GPIO占用,防止冲突
  • gpio_direction_output:配置为输出模式,初始电平为高
  • 绿色:驱动注册入口
  • 蓝色:核心探测函数
  • 黄色:设备树操作
  • 紫色:GPIO硬件操作

四、实验

更改根节点的信息


编译设备树

替换原来的设备树

加载驱动

点灯成功!!!


总结

  1. 设备树配置
  • 使用gpio-leds节点声明LED硬件参数
  • 通过of_find_node_by_path()精准定位设备节点
  • 利用of_get_named_gpio()解析GPIO编号
  1. 平台驱动架构
  • 实现platform_driver结构体注册
  • 构建led_probe()初始化流程
  • 完善led_remove()资源释放机制
相关推荐
崇山峻岭之间6 分钟前
单片机USB虚拟串口实验
单片机·嵌入式硬件
Shadow(⊙o⊙)10 分钟前
进程间通信0.0-pipe()匿名管道,详细分析进程池调度队列执行逻辑,进程池模拟实现。
linux·运维·服务器·开发语言·c++
wait a minutes14 分钟前
【Ubuntu】Ubuntu 20.04 升级到 24.04 完整指南
ubuntu
崇山峻岭之间18 分钟前
单片机USB U盘实验
单片机·嵌入式硬件
CQU_JIAKE18 分钟前
6.6aaaaaa
linux·运维·服务器
Apibro23 分钟前
【Linux】Qt Creator 中文输入法
linux·qt
smallswan31 分钟前
第十四 算数运算
linux·服务器·前端
点灯小铭33 分钟前
基于单片机的锅炉压力与温度监测报警系统设计
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
丑过三八线34 分钟前
Umi 配置文件 .umirc.ts 详解
linux·运维·ubuntu·react.js
环境倒逼我学习34 分钟前
无人机地面站之第13章 Mission Planner 入门与界面总览
单片机·嵌入式硬件·无人机