• Linux学习第30天:Linux 自带的 LED 灯驱动实验:驱动开发思维方式的转变势在必行


    Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


             学习嵌入式Linux驱动开发整整30天了。今天简单做一个小结。因为之前的主要工作是做ARM的裸机开发,所以接触Linux以后感觉很多东西都变了。不仅仅包括相关知识点的归纳,更重要的是思维方式的转变。做裸机开发的时候,在很多时候是要弄清楚最底层的逻辑才能进行代码的开发。而Linux则更像是站在一个更高的维度去思考,很细枝末叶的东西是不需要考虑的。相对裸机开发的维度来说,裸机开发后做Linux驱动开发更有点降维打击的感觉。但并不是说Linux驱动开发简单,而是说裸机开发对底层的要求可能会更高一些。以上仅仅是个人的一点自我感受。

            本节笔记主要学习Linux自带的LED驱动试验。主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。

    一、Linux内核自带的LED灯驱动使能

            需要配置Linux内核,使能自带的LED驱动。使用make menuconfig打开Linux配置菜单。

            按照如下路径打开LED驱动配置项:

            Device Drivers

                    LED Support(NEW_LEDS=[y])

                            LED Support for GPIO connected LEDs. 在此选项上按下“ Y”键,使此选项前面变为“ <*>”,

            重新编译 Linux 内核,然后使用新编译出来的 zImage 镜像启动开发板。

    二、Linux内核自带LED驱动简介

    1、LED灯驱动框架分析

    obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o

    1. 236 static const struct of_device_id of_gpio_leds_match[] = {
    2. 237 { .compatible = "gpio-leds", },
    3. 238 {},
    4. 239 };

            LED 驱动的匹配表,此表只有一个匹配项, compatible 内容为“ gpio-leds”,因此设备树中的 LED 灯设备节点的 compatible 属性值也要为“ gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。

    1. static struct platform_driver gpio_led_driver = {
    2. 291 .probe = gpio_led_probe,
    3. 292 .remove = gpio_led_remove,
    4. 293 .driver = {
    5. 294 .name = "leds-gpio",
    6. 295 .of_match_table = of_gpio_leds_match,
    7. 296 },

            platform_driver 驱动结构体变量,可以看出, Linux 内核自带的 LED 驱动采用了 platform 框架。

    module_platform_driver(gpio_led_driver);

            通过 module_platform_driver 函数向 Linux 内核注册 gpio_led_driver 这个 platform
    驱动。

    2、module_platform_driver函数简介

            module_platform_driver 定义在 include/linux/platform_device.h 文件中为一个,

            module_platform_driver 依赖 module_driver, module_driver 也是一个宏。

            module_platform_driver 函数的功能就是完成 platform 驱动的注册和删除

    3、gpio_led_probe函数简析

    1. 269 priv = gpio_leds_create(pdev);
    2. 270 if (IS_ERR(priv))
    3. 271 return PTR_ERR(priv);

            如果使用设备树的话,使用 gpio_leds_create 函数从设备树中提取设备信息,获取到的 LED 灯 GPIO 信息保存在返回值中。

            在gpio_leds_create 函数中,调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。
     

    device_for_each_child_node(dev, child) {

            遍历每个子节点,获取每个子节点的信息。

    led.gpiod = devm_get_gpiod_from_child(dev, NULL, child);

            获取 LED 灯所使用的 GPIO 信息。

    1. 196 if (fwnode_property_present(child, "label")) {
    2. 197 fwnode_property_read_string(child, "label", &led.name);

            读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字。

    1. 204 fwnode_property_read_string(child, "linux,default-trigger",
    2. 205 &led.default_trigger);

            获取“ linux,default-trigger”属性值,可以通过此属性设置某个 LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等。

    1. 207 if (!fwnode_property_read_string(child, "default-state",
    2. 208 &state)) {
    3. 209 if (!strcmp(state, "keep"))
    4. 210 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
    5. 211 else if (!strcmp(state, "on"))
    6. 212 led.default_state = LEDS_GPIO_DEFSTATE_ON;
    7. 213 else
    8. 214 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
    9. 215 }

            获取“ default-state”属性值,也就是 LED 灯的默认状态属性。

    1. 220 ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
    2. 221 dev, NULL);

            调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat 这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。

            gpio_led_probe 函数主要功能就是获取 LED 灯的设备信息,然后根据这些信息来初始化对应的 IO,设置为输出等。     

    三、设备树节点编写

            在编写设备节点的时候要注意以下几点:
    ①、创建一个节点表示 LED 灯设备,比如 dtsleds,如果板子上有多个 LED 灯的话每个 LED
    灯都作为 dtsleds 的子节点。
    ②、 dtsleds 节点的 compatible 属性值一定要为“ gpio-leds”。
    ③、设置 label 属性,此属性为可选,每个子节点都有一个 label 属性, label 属性一般表示
    LED 灯的名字,比如以颜色区分的话就是 red、 green 等等。
    ④、每个子节点必须要设置 gpios 属性值,表示此 LED 所使用的 GPIO 引脚!
    ⑤、可以设置“ linux,default-trigger”属性值,也就是设置 LED 灯的默认功能,比如:
    backlight: LED 灯作为背光。
    default-on: LED 灯打开
    heartbeat: LED 灯作为心跳指示灯,可以作为系统运行提示灯。
    ide-disk: LED 灯作为硬盘活动指示灯。
    timer: LED 灯周期性闪烁,由定时器驱动,闪烁频率可以修改
    ⑥、可以设置“ default-state”属性值,可以设置为 on、 off 或 keep,为 on 的时候 LED 灯默
    认打开,为 off 的话 LED 灯默认关闭,为 keep 的话 LED 灯保持当前模式。

    1. 1 dtsleds {
    2. 2 compatible = "gpio-leds";
    3. 3 4
    4. led0 {
    5. 5 label = "red";
    6. 6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
    7. 7 default-state = "off";
    8. 8 };
    9. 9 };

    四、运行测试

            启 动 开 发 板 , 启 动 以 后 查 看/sys/bus/platform/devices/dtsleds 这个目录是否存在。

    进入到 leds 目录中,此目录中的内容如图所示:

            在 leds 目录下有一个名为“ red”子目录,这个子目录的名字就是我们在设备树中第 5 行设置的 label 属性值。

            输入如下命令打开 RED 这个 LED 灯:
    echo 1 > /sys/class/leds/red/brightness //打开 LED0
            关闭 RED 这个 LED 灯的命令如下:
    echo 0 > /sys/class/leds/red/brightness //关闭 LED0

            系统运行指示灯:

    1. 1 dtsleds {
    2. 2 compatible = "gpio-leds";
    3. 3 4
    4. led0 {
    5. 5 label = "red";
    6. 6 gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
    7. 7 linux,default-trigger = "heartbeat";
    8. 8 default-state = "on";
    9. 9 };
    10. 10 };

            设置 LED0 作为系统指示灯,在 dtsleds 这个设备节点中加入“ linux,default-trigger”属性信息即可,属性值为“ heartbeat”。

    五、总结

            本节笔记主要内容包括驱动使能、简介、设备树节点编写及运行测试。其中驱动简介又包括LED灯驱动框架分析、module_platform_driver函数简介和gpio_led_probe函数简析。


    本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

  • 相关阅读:
    ffmpeg知识点整理
    netty 底层的工作原理
    【Java数据结构】详解LinkedList与链表(二)
    乳企齐冲上市,是百家争鸣还是内卷加剧?
    3.10 haas506 2.0开发教程-example-TFT
    详解链表oJ<反转链表,链表的中间节点及链表的回文>
    WinForm中DataGridView分页的实现【DataTable.rows.add()报错:该行已经属于另一个表】
    记一次完整的PHP代码审计——yccms v3.4审计
    力扣 -- 377. 组合总和 Ⅳ
    JS 数组的操作
  • 原文地址:https://blog.csdn.net/jiage987450/article/details/134159172