当前位置: 首页 > news >正文

Linux学习笔记--GPIO控制器驱动

1. 核心数据结构

gpio_chip 结构体

这是GPIO子系统的核心结构,代表一个GPIO控制器:

struct gpio_chip {const char *label;                    // 控制器名称int (*direction_input)(...);          // 设置为输入模式int (*direction_output)(...);         // 设置为输出模式int (*get)(...);                      // 读取GPIO值void (*set)(...);                     // 设置GPIO值struct device *parent;                // 父设备struct module *owner;                 // 模块所有者int base;                            // 起始GPIO编号u16 ngpio;                           // GPIO数量// ... 其他成员
};

2. 全局变量

static struct gpio_chip *g_virt_gpio;  // GPIO控制器实例
static int g_gpio_val = 0;            // 虚拟的GPIO状态寄存器

3. GPIO操作函数实现

方向设置函数

static int virt_gpio_direction_output(struct gpio_chip *gc,unsigned offset, int val)
{printk("set pin %d as output %s\n", offset, val ? "high" : "low");return 0;
}static int virt_gpio_direction_input(struct gpio_chip *chip,unsigned offset)
{printk("set pin %d as input\n", offset);return 0;
}

电平读写函数

static int virt_gpio_get_value(struct gpio_chip *gc, unsigned offset)
{int val;val = (g_gpio_val & (1<<offset)) ? 1 : 0;  // 从虚拟寄存器读取printk("get pin %d, it's val = %d\n", offset, val);return val;
}static void virt_gpio_set_value(struct gpio_chip *gc,unsigned offset, int val)
{printk("set pin %d as %d\n", offset, val);if (val)g_gpio_val |= (1 << offset);    // 设置对应位为1elseg_gpio_val &= ~(1 << offset);   // 设置对应位为0
}

4. 平台驱动探测函数

static int virtual_gpio_probe(struct platform_device *pdev)
{int ret;unsigned int val;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1. 分配gpio_chip */g_virt_gpio = devm_kzalloc(&pdev->dev, sizeof(*g_virt_gpio), GFP_KERNEL);/* 2. 设置gpio_chip *//* 2.1 设置函数 */g_virt_gpio->label = pdev->name;g_virt_gpio->direction_output = virt_gpio_direction_output;g_virt_gpio->direction_input  = virt_gpio_direction_input;g_virt_gpio->get = virt_gpio_get_value;g_virt_gpio->set = virt_gpio_set_value;g_virt_gpio->parent = &pdev->dev;g_virt_gpio->owner = THIS_MODULE;/* 2.2 设置base、ngpio值 */g_virt_gpio->base = -1;  // 自动分配起始编号ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val);g_virt_gpio->ngpio = val;  // 从设备树读取GPIO数量/* 3. 注册gpio_chip */ret = devm_gpiochip_add_data(&pdev->dev, g_virt_gpio, NULL);return 0;
}
  • pdev:平台设备指针,包含设备信息和设备树节点

  • ret:用于存储函数返回值,检查错误

  • val:临时变量,用于从设备树读取数值

分配 gpio_chip 内存

g_virt_gpio = devm_kzalloc(&pdev->dev, sizeof(*g_virt_gpio), GFP_KERNEL);

详细解释:

  • devm_kzalloc:设备资源管理的内存分配函数

    • 自动管理内存生命周期

    • 设备卸载时自动释放内存

    • 避免内存泄漏

  • &pdev->dev:父设备指针,用于资源管理

  • sizeof(*g_virt_gpio):分配 gpio_chip 结构体大小的内存

  • GFP_KERNEL:内存分配标志,表示在内核空间分配可睡眠的内存

等效的传统代码:

// 如果没有使用devm_系列函数
g_virt_gpio = kzalloc(sizeof(*g_virt_gpio), GFP_KERNEL);
// 需要在remove函数中手动释放:kfree(g_virt_gpio);

设置 gpio_chip 操作函数

/* 2.1 设置函数 */
g_virt_gpio->label = pdev->name;
g_virt_gpio->direction_output = virt_gpio_direction_output;
g_virt_gpio->direction_input  = virt_gpio_direction_input;
g_virt_gpio->get = virt_gpio_get_value;
g_virt_gpio->set = virt_gpio_set_value;

每个字段的作用:

字段作用对应函数
labelGPIO控制器名称使用平台设备名称
direction_output设置GPIO为输出模式virt_gpio_direction_output
direction_input设置GPIO为输入模式virt_gpio_direction_input
get读取GPIO电平值virt_gpio_get_value
set设置GPIO电平值virt_gpio_set_value

设置设备关系信息

g_virt_gpio->parent = &pdev->dev;
g_virt_gpio->owner = THIS_MODULE;
  • parent:设置父设备,建立设备层次关系

    • 在sysfs中体现为:/sys/devices/.../virtual_gpio/gpio/

  • owner:模块所有者,用于模块引用计数

    • 防止模块在使用中被卸载

 配置GPIO数量和基址

/* 2.2 设置base、ngpio值 */
g_virt_gpio->base = -1;  // 自动分配起始编号
ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val);
g_virt_gpio->ngpio = val;  // 从设备树读取GPIO数量

详细解释:

base = -1

  • 作用:让系统自动分配GPIO起始编号

  • 示例:如果系统已有GPIO 0-479,则自动分配480为起始

  • 手动指定:也可以设置具体值,如 base = 480

of_property_read_u32

  • 函数:从设备树节点读取32位无符号整数属性

  • 参数

    • pdev->dev.of_node:设备树节点

    • "ngpios":要读取的属性名

    • &val:存储读取值的变量指针

对应的设备树配置:
virtual_gpio {compatible = "100ask,virtual_gpio";ngpios = <16>;  // 这里读取的就是这个值status = "okay";
};

注册GPIO控制器

/* 3. 注册gpio_chip */
ret = devm_gpiochip_add_data(&pdev->dev, g_virt_gpio, NULL);

详细解释:

devm_gpiochip_add_data

  • 作用:向GPIO子系统注册GPIO控制器

  • 参数

    • &pdev->dev:父设备

    • g_virt_gpio:要注册的gpio_chip结构体

    • NULL:私有数据指针(这里没有使用)

注册后的效果:

  1. 创建sysfs接口/sys/class/gpio/gpiochipXXX/

  2. 分配GPIO编号:如 GPIO 480-495(如果ngpios=16)

  3. 可用性:其他驱动可以通过标准GPIO API使用这些GPIO

 注册后的系统状态

/sys/class/gpio/
├── gpiochip480/           # 新注册的GPIO控制器
│   ├── base               # 起始编号:480
│   ├── label              # 控制器名称
│   ├── ngpio              # GPIO数量:16
│   └── subsystem -> ../../../../class/gpio
└── export                 # 可以导出GPIO 480-495

5. 设备树匹配

static const struct of_device_id virtual_gpio_of_match[] = {{ .compatible = "100ask,virtual_gpio", },  // 匹配设备树节点{ },
};

6. 平台驱动定义

static struct platform_driver virtual_gpio_driver = {.probe      = virtual_gpio_probe,.remove     = virtual_gpio_remove,.driver     = {.name   = "100ask_virtual_gpio",.of_match_table = of_match_ptr(virtual_gpio_of_match),}
};

7. 模块初始化和退出

static int __init virtual_gpio_init(void)
{   printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return platform_driver_register(&virtual_gpio_driver);
}static void __exit virtual_gpio_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);platform_driver_unregister(&virtual_gpio_driver);
}

8. 设备树配置示例

virtual_gpio: virtual_gpio {compatible = "100ask,virtual_gpio";ngpios = <32>;          // 32个虚拟GPIOstatus = "okay";
};

9. 用户空间使用

驱动加载后,可以通过标准GPIO接口访问:

# 查看GPIO控制器
cat /sys/kernel/debug/gpio# 使用GPIO
echo 480 > /sys/class/gpio/export        # 导出GPIO(假设base=480)
echo out > /sys/class/gpio/gpio480/direction
echo 1 > /sys/class/gpio/gpio480/value   # 设置高电平
echo 0 > /sys/class/gpio/gpio480/value   # 设置低电平

10. 在驱动中使用虚拟GPIO

其他内核驱动可以这样使用:

struct gpio_desc *virt_gpio;virt_gpio = gpio_to_desc(480);  // 获取GPIO描述符
gpiod_direction_output(virt_gpio, 1);  // 设置为输出高电平
gpiod_set_value(virt_gpio, 0);         // 设置为低电平

11. 工作流程总结

  1. 模块加载 → 注册平台驱动

  2. 设备匹配 → 设备树中找到 compatible="100ask,virtual_gpio" 的节点

  3. 驱动探测

    • 分配并初始化 gpio_chip

    • 设置操作函数

    • 从设备树读取 GPIO 数量

    • 注册到 GPIO 子系统

  4. 用户访问

    • 通过 /sys/class/gpio 接口

    • 或其他驱动通过 GPIO 子系统 API

  5. GPIO操作 → 调用对应的虚拟函数,操作内存变量

http://www.dtcms.com/a/495460.html

相关文章:

  • 重庆制作手机网站如何看一个站点是不是有wordpress
  • 网站如何在推广设计公司logo软件
  • 价值1w的数据分析课知识点汇总-excel使用(第一篇)
  • android取消每次u盘插入创建无用(媒体)文件夹
  • 个人如何办网站1m的带宽做网站可以吗
  • 多机部署,负载均衡
  • python通过win32com库调用UDE工具来做开发调试实现自动化源码,以及UDE的知识点介绍
  • 关于Unity中ComputeShader 线程id的理解
  • 幽冥大陆(十六)纸币器BV20识别纸币——东方仙盟筑基期
  • 设计网站的方法做彩票网站需要什么条件
  • Windows 平台 HOOK DWM 桌面管理程序,实现输出变形的桌面图像到显示器
  • Java 大视界 -- Java 大数据在智能电网电力市场交易数据分析与策略制定中的关键作用
  • 安徽和县住房城乡建设局网站wordpress移动端顶部导航栏
  • oracle:判断字段不以开头
  • 学习笔记3-深度学习之logistic回归向量化
  • 哈尔滨网站建设网站优秀个人网站设计欣赏
  • 高辐射环境下AS32S601ZIT2型MCU的抗辐照性能与应用潜力分析
  • 基于STM32F407与FT245R芯片实现USB转并口通信时序控制
  • Retrofit 与 OkHttp 全面解析与实战使用(含封装示例)
  • qiankun知识点
  • 面向接口编程与自上而下的系统设计艺术
  • 数据结构基石:单链表的全面实现、操作详解与顺序表对比
  • 网站 无限下拉做一个小程序需要多少钱
  • 【Kubernetes】常见面试题汇总(二十六)
  • 微网站设计制作wordpress在线文档
  • “自来水”用英语怎么说?
  • 小杰深度学习(fifteen)——视觉-经典神经网络——MobileNetV1
  • 网站建设方任务 职责如何建设网站兴田德润可信赖
  • EWM - TM 集成(ASR)
  • 数据库数据插入与查询