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

驱动开发硬核特训 · Day 19:从字符设备出发,掌握 Linux 驱动的实战路径(含 gpio-leds 控制示例)

视频教程请关注 B 站:“嵌入式 Jerry”

一、背景说明:字符设备驱动的角色定位

在 Linux 内核驱动体系中,**字符设备驱动(Character Device Driver)**扮演着关键的桥梁作用,它直接向用户空间程序提供 read/write/ioctl 等接口,适用于键盘、LED、GPIO、串口等多类设备。而在日常的嵌入式开发中,我们往往会面对一个核心问题:

“设备树已经定义了硬件设备,子系统(比如 gpio-leds)也帮我点亮了 LED,为何还要写字符设备驱动?”

本篇博文将围绕这个问题,逐步展开,从设备模型 → 子系统 → 字符设备驱动之间的逻辑关系梳理,再深入实践,完成一个基于 i.MX8MP 平台的 LED 控制字符设备驱动,掌握字符设备的创建、注册与用户空间控制。


在这里插入图片描述

二、核心概念回顾:设备模型、子系统与字符设备的关系

2.1 设备模型(Device Model)

Linux 内核的设备模型为设备、驱动和总线之间的关系建立了统一的框架:

  • struct device:表示一个实际设备;
  • struct device_driver:对应驱动;
  • struct bus_type:设备与驱动的桥梁。

所有设备都要在 device model 下注册,但设备模型 本身不提供功能接口,它只建立了设备的逻辑与生命周期。


2.2 子系统(Subsystem)

子系统是在设备模型基础上的功能分类,如:

  • input 子系统:键盘、触摸屏;
  • block 子系统:磁盘;
  • tty 子系统:串口;
  • gpio-leds 子系统:LED 控制。

子系统的出现解放了开发者,某些简单设备无需写驱动,只要配置设备树即可,例如 gpio-leds 自动帮你注册 LED class device,并在 /sys/class/leds 下生成节点,支持用户空间直接操作。

但问题来了:

如果我们需要通过 write() 等方式直接从应用控制 LED 呢?

这就要用到:


2.3 字符设备驱动(Character Device Driver)

字符设备驱动负责建立 /dev/mydev 这样的接口,实现 file_operations 结构体,从而让用户空间通过 open/read/write 与内核驱动打交道。它是最基础、最灵活的驱动方式。


三、问题提出:gpio-leds 为何无法直接与字符设备结合?

以设备树片段为例(位于 imx8mp-evk.dts):

gpio-leds {compatible = "gpio-leds";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_led>;status {label = "yellow:status";gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>;default-state = "on";};
};

系统自动生成 /sys/class/leds/yellow:status/brightness 节点,我们可:

echo 1 > /sys/class/leds/yellow:status/brightness  # 点亮
echo 0 > /sys/class/leds/yellow:status/brightness  # 熄灭

但此类节点仅通过 sysfs 接口工作,并未与 /dev/ledchar 之类的字符设备产生映射关系


四、实战:为 gpio-leds 控制引入字符设备接口

我们以 gpio3_16(GPIO 编号为 32 * 2 + 16 = 80)为例,构造一个最小的字符设备驱动模块。

4.1 驱动代码

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>#define GPIO_NUM 80  // 对应 gpio3_16
static dev_t devt;
static struct cdev cdev;
static struct class *led_class;static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{char kbuf[4];if (copy_from_user(kbuf, buf, len))return -EFAULT;gpio_set_value(GPIO_NUM, kbuf[0] == '1' ? 1 : 0);return len;
}static struct file_operations led_fops = {.owner = THIS_MODULE,.write = led_write,
};static int __init led_dev_init(void)
{gpio_request(GPIO_NUM, "led_gpio");gpio_direction_output(GPIO_NUM, 1);alloc_chrdev_region(&devt, 0, 1, "led_char");cdev_init(&cdev, &led_fops);cdev_add(&cdev, devt, 1);led_class = class_create(THIS_MODULE, "led_class");device_create(led_class, NULL, devt, NULL, "ledchar");pr_info("ledchar device initialized\n");return 0;
}static void __exit led_dev_exit(void)
{gpio_set_value(GPIO_NUM, 0);gpio_free(GPIO_NUM);device_destroy(led_class, devt);class_destroy(led_class);cdev_del(&cdev);unregister_chrdev_region(devt, 1);
}module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");

4.2 编译与测试

编译为模块:

obj-m += ledchar.o

加载模块:

insmod ledchar.ko

用户空间控制:

echo 1 > /dev/ledchar   # 点亮
echo 0 > /dev/ledchar   # 熄灭

五、总结对比:sysfs 与字符设备的协作与区别

方式适用场景优势劣势
sysfs接口自动生成的 class 设备无需写驱动,通用接口单一,不支持复杂逻辑
字符设备精细控制 / 用户接口可定义多操作 / 定制协议需自行注册,开发成本略高
两者结合桥接通用与专用可在 sysfs 生成节点 + 自定义更强大但设计复杂

在一些复杂场景(如 SPI 设备、定制协议、UI 控制等)中,字符设备几乎不可替代。


六、延伸思考:如何从子系统向字符设备扩展?

未来开发中,建议按如下路径深入:

  1. 明确内核已支持的子系统是否能满足需求
  2. 通过 class_create + cdev_add 绑定字符设备接口
  3. 使用 udev 自定义 /dev 节点自动创建设备文件
  4. 编写自定义 ioctl 进行配置交互(可参考 camera/v4l2)

七、写在最后

本节通过一个实战示例,深入讲解了:

  • 设备树中的 gpio-leds 与字符设备的区别;
  • 如何构造字符设备驱动访问 GPIO;
  • sysfs 与 /dev 的本质差异。

我们将在下一节中进一步引入 ioctl 控制方法,让字符设备驱动更具交互能力。


每日一句:不要轻视字符设备,它是所有复杂驱动的起点。


视频教程请关注 B 站:“嵌入式 Jerry”

http://www.xdnf.cn/news/1670.html

相关文章:

  • 成人高考难吗-录取线仅需120分?
  • Mysql主从复制和读写分离
  • 运维打铁:Centos 7 安装 redis_exporter 1.3.5
  • 大语言模型之提示词技巧
  • 多线程环境下的资源共享与线程安全问题
  • SpringBootTest报错
  • 更新英雄位置
  • CS144 Lab 6 实战记录:构建 IP 路由器
  • LLM量化技术全景:GPTQ、QAT、AWQ、GGUF与GGML
  • 如何在 Odoo 18 中配置自动化动作
  • 如何在 Unity 中导入 gltf /glb 文件
  • 机器人灵巧手有刷与无刷空心杯电机解析
  • Bean的生命周期
  • 免费的 HTML 网页托管服务
  • 图像预处理-霍夫变换
  • React学习路线
  • 解决高德地图AMapUtilCoreApi、NetProxy类冲突
  • 哑光蓝色调风光人像Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • Java for循环中,如何在内循环跳出外循环?
  • AI | 最近比较火的几个生成式对话 AI
  • STM32 的 GPIO和中断
  • 为什么提示词能够提高大语言模型的理解能力
  • git Http改用户下载
  • 动手试一试 Spring Security入门
  • 加深对vector理解OJ题
  • WASM与Kotlin反编译难度对比分析
  • 【Yolo精读+实践+魔改系列】Yolov1论文超详细精讲(翻译+笔记)
  • 架构-软件工程
  • linux:启动后,ubuntu屏幕变成红色了
  • 第14章 授权:保护应用程序