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

驱动开发硬核特训 · Day 19:字符设备驱动实战(控制 LED)

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

一、背景引入:字符设备 + 实际硬件控制

在 Linux 驱动开发中,字符设备(Character Device)是一类通过 read / write 接口与用户空间进行数据交互的基本设备类型。今天我们将结合 i.MX8MP EVK 开发板的 gpio-leds 节点,构造一个简单、直观的字符设备驱动,实现对物理 LED 的开关控制。

这一过程不仅涉及字符设备驱动的基础流程,还能帮助我们理解设备树与实际硬件的结合方式。
在这里插入图片描述


二、设备树分析:找到目标 GPIO

开发板设备树 arch/arm64/boot/dts/freescale/imx8mp-evk.dts 中,预定义了一个 GPIO 控制的 LED 节点:

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";};
};

这段节点中定义了一个黄色 LED,使用 gpio3_16 进行控制。GPIO 的编号计算方式为:

GPIO 编号 = bank × 32 + offset = 3 × 32 + 16 = 112

我们将在驱动中直接控制该 GPIO(编号 112)进行点亮和熄灭。


三、字符设备驱动实现

我们不再依赖 gpio-leds 子系统,而是直接注册一个字符设备,通过 echo 1 > /dev/ledchar 控制物理 LED 点亮,echo 0 熄灭。

3.1 驱动代码

// SPDX-License-Identifier: GPL-2.0
#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_LED_NUM 112  // gpio3_16 -> 3*32 + 16static 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_LED_NUM, kbuf[0] == '1' ? 1 : 0); // 设置 GPIO 电平return len;
}static struct file_operations led_fops = {.owner = THIS_MODULE,.write = led_write,  // 用户空间 write 映射此函数
};static int __init led_dev_init(void)
{gpio_request(GPIO_LED_NUM, "led_gpio");             // 请求 GPIO 控制权gpio_direction_output(GPIO_LED_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");  // 创建 sysfs 类device_create(led_class, NULL, devt, NULL, "ledchar"); // 创建设备节点 /dev/ledcharpr_info("ledchar device init done\n");return 0;
}static void __exit led_dev_exit(void)
{gpio_set_value(GPIO_LED_NUM, 0);                     // 卸载前熄灭 LEDgpio_free(GPIO_LED_NUM);                             // 释放 GPIOdevice_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");
MODULE_AUTHOR("嵌入式Jerry");

3.2 编译说明

使用 Makefile 编译为模块:

obj-m += ledchar.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

加载模块:

insmod ledchar.ko

控制 LED:

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

四、字符设备和设备树的关联解读

❗ 重点:我们没有直接解析设备树节点,而是“硬编码”了 GPIO 号 112

这是一种“直接控制硬件资源”的方式,适用于教学或特定场景,但不具备移植性和灵活性。在生产环境中,我们推荐:

  • 使用 of_find_node_by_name 等接口动态获取设备树中的 gpio
  • 或者用 platform_driver 的形式自动注册字符设备,配合 of_device_id 匹配设备树。

五、字符设备与设备模型的区别与融合

项目字符设备设备模型
本质文件操作接口系统框架结构
接口表现file_operations (read/write 等)device、driver、bus 等结构体
功能侧重面向用户空间的数据操作面向系统资源管理与驱动匹配
是否依赖可独立存在统一纳入 driver model 框架更佳

字符设备是数据访问的“通道”,而设备模型是驱动统一管理的“系统总线”。两者并不冲突,在现代驱动开发中,常常将字符设备纳入设备模型管理之中。


六、实战拓展建议

  • 下一步建议学习内容

    • 如何通过 platform_driver 注册字符设备;
    • 如何用 device_create_file() 添加 sysfs 接口;
    • 将 GPIO 控制“封装成子系统”接口(如 LED 子系统);
  • 推荐实战目标

    • 为一个温度传感器或输入按键编写字符驱动;
    • 理解 udev 如何自动创建设备节点;
    • 进一步结合设备树进行设备节点注册。

七、总结

本篇训练以字符设备驱动为核心,结合 i.MX8MP EVK 实际 GPIO,完成从驱动编写、设备注册、到用户空间控制的完整流程。关键点如下:

  • 使用 alloc_chrdev_region / cdev_add 完成字符设备注册;
  • 通过 gpio_request / gpio_set_value 操作 GPIO;
  • 构建了 /dev/ledchar 控制节点,实现最基础的内核设备控制通道。

接下来我们将深入 platform_driver 与 sysfs 接口,逐步构建更完整的驱动结构。


📢 视频教程请关注 B 站:“嵌入式Jerry”
📚 本系列每日更新,持续训练,打造 Linux 驱动开发硬核能力!

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

相关文章:

  • MP自动填充之MetaObjectHandler核心方法解析
  • MATLAB技巧——平滑滤波,给出一定的例程和输出参考
  • openGauss手工配置主备
  • Java大师成长计划之第3天:Java中的异常处理机制
  • 神经网络基础[损失函数,bp算法,梯度下降算法 ]
  • Linux 内核网络协议栈中 inet_stream_ops 与 tcp_prot 的深度解析
  • 搜索一个冷门搜索不到可以搜索专栏
  • 回归测试:保障软件质量的重要防线
  • A Comprehensive Survey of Spoken Language Models
  • 《AI大模型趣味实战》构建基于Flask和Ollama的AI助手聊天网站:分布式架构与ngrok内网穿透实现
  • 什么是测试驱动开发(TDD)?
  • C# 综合示例 库存管理系统7 主界面(FormMain)
  • 38 python random
  • 唯创安全:从传统到智能,工厂智能叉车AI防撞系统解决方案
  • 什么是非关系型数据库
  • 如何避免IDEA每次打开新项目都重复配置Maven?
  • 【交互式数据仪表板】Plotly Dash完全指南:从零搭建到部署全流程 | Python数据可视化必备
  • CSS Position 属性完全指南
  • 02.05、链表求和
  • 10前端项目----商品详情页/滚轮行为
  • 第七章.干货干货!!!Langchain4j开发智能体-文生图文生视频
  • QT窗口相关控件及其属性
  • 大模型——快速部署和使用 Deep Research Web UI
  • linux安装单节点Elasticsearch(es),安装可视化工具kibana
  • 如何创建极狐GitLab 私密议题?
  • 【MySQL】(8) 联合查询
  • 常见网络安全攻击类型深度剖析(二):SQL注入攻击——原理、漏洞利用演示与代码加固方法
  • MySQL 存储过程:解锁数据库编程的高效密码
  • 抓包工具Wireshark的应用解析
  • 期货有哪些种类?什么是股指、利率和外汇期货?