驱动框架入门——以LED为例
2025-01-14 来源:cnblogs
一、什么是驱动框架?
数据结构,这些是驱动框架的直接表现。
Linux/init.h中。
这个宏的功能是:将其声明的函数放到一个特定的段:.initcall4.init。
(2)分析module_init宏,可以看出它将函数放到了.initcall6.init段中。
module_init
__initcall
device_initcall
__define_initcall('6',fn,6)
(3)内核在启动过程中,需要按照顺序执行很多事情。内核如何实现按照先后顺序去做很多初始化操作?
内核的解决方案就是将内核启动时要调用的所有函数归类,然后每个类按照一定的次序去调用执行。
这些分类名就叫.initcalln.init,n的值从1到8。
内核开发者在编写内核代码时只要将函数设置合适的级别,链接的时候,这些函数就会被放入特定的段,内核启动时再按照(内核链接脚本中指定的)段顺序去依次执行各个段即可。内核链接脚本(编译之后才有)在arch/arm/kernel/vmlinux.lds中。

(4)经过分析可以看出,subsys_initcall和module_init的作用是一样的,只不过前者所声明的函数要比后者在内核启动时的执行顺序更早。
架构:涉及哪些目录的哪些文件测试通过;
(2)扩展支持led2和led3、led4,可以分开注册,也可以使用gpio_request_array去一次注册;
(3)学习linux中查看gpio使用情况的方法



内核中提供了虚拟文件系统debugfs,里面有一个gpio文件,提供了gpio的使用信息(诸如谁被使用了,谁没有被使用)。
使用方法:mount -t debugfs debugfs /tmp,然后cat /tmp/gpio即可得到gpio的所有信息,使用完后umount /tmp卸载掉debugfs
(4)代码(驱动申请LED1资源而已)
#include #include #include #include #include #include #include #include #include #define GPIO_LED1 S5PV210_GPJ0(3) #define GPIO_LED2 S5PV210_GPJ0(4) #define GPIO_LED3 S5PV210_GPJ0(5) #define X210_LED_OFF 1 // X210中LED是正极接电源,负极节GPIO #define X210_LED_ON 0 // 所以1是灭,0是亮 static struct led_classdev mydev1; // 定义结构体变量 static struct led_classdev mydev2; // 定义结构体变量 static struct led_classdev mydev3; // 定义结构体变量 // 这个函数就是要去完成具体的硬件读写任务的 static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value) { printk(KERN_INFO 's5pv210_led1_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT); gpio_set_value(GPIO_LED1, X210_LED_OFF); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT); gpio_set_value(GPIO_LED1, X210_LED_ON); } } static void s5pv210_led2_set(struct led_classdev *led_cdev, enum led_brightness value) { printk(KERN_INFO 's5pv2102_led_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT); } } static void s5pv210_led3_set(struct led_classdev *led_cdev, enum led_brightness value) { printk(KERN_INFO 's5pv210_led3_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT); } } static int __init s5pv210_led_init(void) { // 用户insmod安装驱动模块时会调用该函数 // 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备 int ret = -1; // 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源 if (gpio_request(GPIO_LED1, 'led1_gpj0.3')) //这里是申请失败 { printk(KERN_ERR 'gpio_request failedn'); } else //申请成功后 { // 设置为输出模式,并且默认输出1让LED灯灭 gpio_direction_output(GPIO_LED1, 1); } // led1 mydev1.name = 'led1'; mydev1.brightness = 0; mydev1.brightness_set = s5pv210_led1_set; ret = led_classdev_register(NULL, &mydev1); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } // led2 mydev2.name = 'led2'; mydev2.brightness = 0; mydev2.brightness_set = s5pv210_led2_set; ret = led_classdev_register(NULL, &mydev2); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } // led3 mydev3.name = 'led3'; mydev3.brightness = 0; mydev3.brightness_set = s5pv210_led3_set; ret = led_classdev_register(NULL, &mydev3); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } return 0; } static void __exit s5pv210_led_exit(void) { led_classdev_unregister(&mydev1); led_classdev_unregister(&mydev2); led_classdev_unregister(&mydev3); gpio_free(GPIO_LED1); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE('GPL'); // 描述模块的许可证 MODULE_AUTHOR('aston <1264671872@qq.com>'); // 描述模块的作者 MODULE_DESCRIPTION('s5pv210 led driver'); // 描述模块的介绍信息 MODULE_ALIAS('s5pv210_led'); // 描述模块的别名信息 (4)代码(驱动申请LED1资源而已) #include #include #include #include #include #include #include #include #include #define GPIO_LED1 S5PV210_GPJ0(3) #define GPIO_LED2 S5PV210_GPJ0(4) #define GPIO_LED3 S5PV210_GPJ0(5) #define X210_LED_OFF 1 // X210中LED是正极接电源,负极节GPIO #define X210_LED_ON 0 // 所以1是灭,0是亮 static struct led_classdev mydev1; // 定义结构体变量 static struct led_classdev mydev2; // 定义结构体变量 static struct led_classdev mydev3; // 定义结构体变量 // 这个函数就是要去完成具体的硬件读写任务的 static void s5pv210_led1_set(struct led_classdev *led_cdev,enum led_brightness value) { printk(KERN_INFO 's5pv210_led1_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<3)), GPJ0DAT); gpio_set_value(GPIO_LED1, X210_LED_OFF); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<3)), GPJ0DAT); gpio_set_value(GPIO_LED1, X210_LED_ON); } } static void s5pv210_led2_set(struct led_classdev *led_cdev, enum led_brightness value) { printk(KERN_INFO 's5pv2102_led_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<4)), GPJ0DAT); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<4)), GPJ0DAT); } } static void s5pv210_led3_set(struct led_classdev *led_cdev, enum led_brightness value) { printk(KERN_INFO 's5pv210_led3_setn'); //writel(0x11111111, GPJ0CON); // 在这里根据用户设置的值来操作硬件 // 用户设置的值就是value if (value == LED_OFF) { // 用户给了个0,希望LED灭 //writel(0x11111111, GPJ0CON); // 读改写三部曲 //writel((readl(GPJ0DAT) | (1<<5)), GPJ0DAT); } else { // 用户给的是非0,希望LED亮 //writel(0x11111111, GPJ0CON); //writel((readl(GPJ0DAT) & ~(1<<5)), GPJ0DAT); } } static int __init s5pv210_led_init(void) { // 用户insmod安装驱动模块时会调用该函数 // 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备 int ret = -1; // 在这里去申请驱动用到的各种资源,当前驱动中就是GPIO资源 if (gpio_request(GPIO_LED1, 'led1_gpj0.3')) //这里是申请失败 { printk(KERN_ERR 'gpio_request failedn'); } else //申请成功后 { // 设置为输出模式,并且默认输出1让LED灯灭 gpio_direction_output(GPIO_LED1, 1); } // led1 mydev1.name = 'led1'; mydev1.brightness = 0; mydev1.brightness_set = s5pv210_led1_set; ret = led_classdev_register(NULL, &mydev1); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } // led2 mydev2.name = 'led2'; mydev2.brightness = 0; mydev2.brightness_set = s5pv210_led2_set; ret = led_classdev_register(NULL, &mydev2); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } // led3 mydev3.name = 'led3'; mydev3.brightness = 0; mydev3.brightness_set = s5pv210_led3_set; ret = led_classdev_register(NULL, &mydev3); if (ret < 0) { printk(KERN_ERR 'led_classdev_register failedn'); return ret; } return 0; } static void __exit s5pv210_led_exit(void) { led_classdev_unregister(&mydev1); led_classdev_unregister(&mydev2); led_classdev_unregister(&mydev3); gpio_free(GPIO_LED1); } module_init(s5pv210_led_init); module_exit(s5pv210_led_exit); // MODULE_xxx这种宏作用是用来添加模块描述信息 MODULE_LICENSE('GPL'); // 描述模块的许可证 MODULE_AUTHOR('aston <1264671872@qq.com>'); // 描述模块的作者 MODULE_DESCRIPTION('s5pv210 led driver'); // 描述模块的介绍信息 MODULE_ALIAS('s5pv210_led'); // 描述模块的别名信息
- 艾迈斯欧司朗与美志光电就Spider Farmer灯具所用LED专利纠纷达成和解
- 亮度超50000nits!天马成功点亮12英寸Micro-LED高亮车载显示屏
- e络盟与 Fulham 宣布建立全球分销合作伙伴关系
- 聚积科技前进2026慕尼黑车灯展 以「闪耀你的光芒」定义次世代车用照明新美学
- LED产业百花怒放ISE 2026,聚积科技驱动LED应用不设限
- 丰田合成开发可在车内投射动态图案的LED灯组照明
- 艾迈斯欧司朗推出专为智能眼镜优化的紧凑型RGGB LED解决方案
- 艾迈斯欧司朗推出以人眼安全为核心设计准则的LED驱动芯片方案
- 艾迈斯欧司朗与合作伙伴联合推出可大幅降低二氧化碳排放的纸质卷盘LED运输解决方案
- 共创生态高效汽车照明制造:艾迈斯欧司朗携手DP Patterning助力智能LED控制
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 蓝牙信道探测技术原理与开发套件实践
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析




