单片机
返回首页

驱动框架入门——以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      // module_init  module_exit  

#include            // __init   __exit  

#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      // module_init  module_exit  

#include            // __init   __exit  

#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');                    // 描述模块的别名信息  


进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章