NanoPi NEO Air使用一:介绍
NanoPi NEO Air使用二:固件烧录
NanoPi NEO Air使用三:OverlayFS、CPU温度和频率、wifi、蓝牙、npi-config
NanoPi NEO Air使用四:操作GPIO
NanoPi NEO Air使用五:安装Xfce和xrdp,实现远程访问
NanoPi NEO Air使用六:使用摄像头
NanoPi NEO Air使用七:获取并编译U-boot和Linux的源码
NanoPi NEO Air使用八:编写个简单的驱动和应用程序
NanoPi NEO Air使用九:使用Linux内核自带的LED驱动
NanoPi NEO Air使用十:自己编写驱动来控制LED
NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V

LED灯的驱动文件

  一般Linux 内核会自带了LED 灯驱动,要使用 Linux 内核自带的 LED 灯驱动首先得先配置 Linux 内核,使能自带的 LED 灯驱动,输入如下命令打开 Linux 配置菜单:make menuconfig
  按照如下路径打开 LED 驱动配置项:

-> Device Drivers-> LED Support (NEW_LEDS [=y])->LED Support for GPIO connected LEDs

  按照上述路径,选择“LED Support for GPIO connected LEDs”,将其编译进 Linux 内核,也即是在此选项上按下“Y”键,使此选项前面变为“<*>”,如图:

  在“LED Support for GPIO connected LEDs”上按下‘?’ 可以打开此选项的帮助信息,如图:

  可以看到Symbol值为:LEDS_GPIO。我们知道make menuconfig进行的修改会保存到.config文件中,我们把“LED Support for GPIO connected LEDs”使能后,可以在.config文件中搜索LEDS_GPIO找到:

  CONFIG_LEDS_GPIO赋值为y。
  make menuconfig后会进行make,此时会调用/drivers/leds/Makefile 这个文件,打开它搜索CONFIG_LEDS_GPIO可以看见:

  可以看见/drivers/leds/leds-gpio.c这个文件会被编译进内核,他就是LED的驱动文件。
  打开leds-gpio.c文件:

236 static const struct of_device_id of_gpio_leds_match[] = {237     { .compatible = "gpio-leds", },
238     {},
239 };
......
290 static struct platform_driver gpio_led_driver = {291     .probe = gpio_led_probe,
292     .remove = gpio_led_remove,
293     .driver = {294         .name = "leds-gpio",
295         .of_match_table = of_gpio_leds_match,
296     },
297 };
298
299 module_platform_driver(gpio_led_driver);

  第 236-239 行, LED 驱动的匹配表,此表只有一个匹配项, compatible 内容为“gpio-leds”,因此设备树中的 LED 灯设备节点的 compatible 属性值也要为“gpio-leds”,否则设备和驱动匹配不成功,驱动就没法工作。
  第 290-296 行, platform_driver 驱动结构体变量,可以看出, Linux 内核自带的 LED 驱动采用了 platform 框架。第 291 行可以看出 probe 函数为 gpio_led_probe,因此当驱动和设备匹配成功以后 gpio_led_probe 函数就会执行。从 294 行可以看出,驱动名字为“leds-gpio”,因此会在/sys/bus/platform/drivers 目录下存在一个名为“leds-gpio”的文件,如图所示:

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

  module_platform_driver 是宏定义,展开后发现是驱动入口和出口的合集,如下:

static int __init gpio_led_driver_init(void)
{return platform_driver_register (&(gpio_led_driver));
}
module_init(gpio_led_driver_init);
static void __exit gpio_led_driver_exit(void)
{platform_driver_unregister (&(gpio_led_driver) );
}
module_exit(gpio_led_driver_exit);

  当驱动和设备匹配以后 gpio_led_probe 函数就会执行,此函数主要是从设备树中获取 LED灯的 GPIO 信息,缩减后的函数内容如下:

243 static int gpio_led_probe(struct platform_device *pdev)
244 {245     struct gpio_led_platform_data *pdata =dev_get_platdata(&pdev->dev);
246     struct gpio_leds_priv *priv;
247     int i, ret = 0;
248
249     if (pdata && pdata->num_leds) { /* 非设备树方式 *//* 获取 platform_device 信息 */......
268     } else { /* 采用设备树 */
269         priv = gpio_leds_create(pdev);
270         if (IS_ERR(priv))
271             return PTR_ERR(priv);
272     }
273
274     platform_set_drvdata(pdev, priv);
275
276     return 0;
277 }

  第 269-271 行,如果使用设备树的话,使用 gpio_leds_create 函数从设备树中提取设备信息,获取到的 LED 灯 GPIO 信息保存在返回值中, gpio_leds_create 函数内容如下:

static struct gpio_leds_priv *gpio_leds_create(struct platform_device *pdev)
{struct device *dev = &pdev->dev;struct fwnode_handle *child;struct gpio_leds_priv *priv;int count, ret;count = device_get_child_node_count(dev);if (!count)return ERR_PTR(-ENODEV);priv = devm_kzalloc(dev, sizeof_gpio_leds_priv(count), GFP_KERNEL);if (!priv)return ERR_PTR(-ENOMEM);device_for_each_child_node(dev, child) {struct gpio_led_data *led_dat = &priv->leds[priv->num_leds];struct gpio_led led = {};const char *state = NULL;struct device_node *np = to_of_node(child);ret = fwnode_property_read_string(child, "label", &led.name);if (ret && IS_ENABLED(CONFIG_OF) && np)led.name = np->name;if (!led.name) {fwnode_handle_put(child);return ERR_PTR(-EINVAL);}led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,GPIOD_ASIS,led.name);if (IS_ERR(led.gpiod)) {fwnode_handle_put(child);return ERR_CAST(led.gpiod);}fwnode_property_read_string(child, "linux,default-trigger",&led.default_trigger);if (!fwnode_property_read_string(child, "default-state",&state)) {if (!strcmp(state, "keep"))led.default_state = LEDS_GPIO_DEFSTATE_KEEP;else if (!strcmp(state, "on"))led.default_state = LEDS_GPIO_DEFSTATE_ON;elseled.default_state = LEDS_GPIO_DEFSTATE_OFF;}if (fwnode_property_present(child, "retain-state-suspended"))led.retain_state_suspended = 1;if (fwnode_property_present(child, "retain-state-shutdown"))led.retain_state_shutdown = 1;if (fwnode_property_present(child, "panic-indicator"))led.panic_indicator = 1;ret = create_gpio_led(&led, led_dat, dev, np, NULL);if (ret < 0) {fwnode_handle_put(child);return ERR_PTR(ret);}led_dat->cdev.dev->of_node = np;priv->num_leds++;}return priv;
}

  第 8行,调用 device_get_child_node_count 函数统计子节点数量,一般在在设备树中创建一个节点表示 LED 灯,然后在这个节点下面为每个 LED 灯创建一个子节点。因此子节点数量也是 LED 灯的数量。
  第 8行,为子节点分配内存。
  第 16行,遍历每个子节点,获取每个子节点的信息。device_for_each_child_node是宏定义,其实是个for循环。
  第 22 行,读取子节点 label 属性值,因为使用 label 属性作为 LED 的名字。
  第 30行,获取 LED 灯所使用的 GPIO 信息。
  第 38 行,获取“linux,default-trigger”属性值,可以通过此属性设置某个 LED 灯在Linux 系统中的默认功能,比如作为系统心跳指示灯等等。
  第 41-49行,获取“default-state”属性值,也就是 LED 灯的默认状态属性。
  第 58行,调用 create_gpio_led 函数创建 LED 相关的 io,其实就是设置 LED 所使用的 io为输出之类的。 create_gpio_led 函数主要是初始化 led_dat 这个 gpio_led_data 结构体类型变量,led_dat 保存了 LED 的操作函数等内容。

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

  进入系统后,可以通过ls /sys/bus/platform/drivers命令查看linux内核编译进了那些驱动文件:

修改设备树

  从U-boot的加载信息可以知道,当前使用的设备树文件为sun8i-h3-nanopi-neo-air.dtb

  dtb是由/home/ql/linux/H3/linux/arch/arm/boot/dts/sun8i-h3-nanopi-neo-air.dts编译得到的。

  platform驱动文件与设备是靠compatible属性匹配的,platform驱动文件的compatible属性为gpio-leds,因此打开设备树文件sun8i-h3-nanopi-neo-air.dts,再打开该文件的头文件,一层一层的搜索,最终在arch\arm\boot\dts\sun8i-h3-nanopi.dtsi文件中找到:

 leds {compatible = "gpio-leds";pinctrl-names = "default";pinctrl-0 = <&leds_npi>, <&leds_r_npi>;status {label = "status_led";gpios = <&pio 0 10 GPIO_ACTIVE_HIGH>;linux,default-trigger = "heartbeat";};pwr {label = "LED2";gpios = <&r_pio 0 10 GPIO_ACTIVE_HIGH>;default-state = "on";};};

  这是在根节点下一个名为leds的设备节点,然后该节点又有两个名为statuspwr的子节点。两个子节点有label 属性,相当于别称,用来表示节点的具体功能,比如status_led、red、green等。
  可以通过ls /sys/bus/platform/devices/查看目前根节点上的设备:

  进入该目录继续查看它的子节点:

  这两个子目录的名字就是我们在设备树中设置的 label 属性值。

  我们发现“status_led”灯的“linux,default-trigger”属性值为 “heartbeat”,说明这个灯的默认功能为心跳指示灯。我们现在要手动控制这个灯的亮灭,因此把“linux,default-trigger”注释掉,变为:

     status {label = "status_led";gpios = <&pio 0 10 GPIO_ACTIVE_HIGH>;/*linux,default-trigger = "heartbeat";*/};

  重新编译设备树并更新到TF卡或emmc中,然后重启开发板。

运行测试

  首先查看一下系统中有没有“/sys/class/leds/status_led/brightness”这个文件,如果有的话就输入如下命令打开这个 LED 灯:

echo 1 > /sys/class/leds/status_led/brightness

  关闭这个 LED 灯的命令如下:

echo 0 > /sys/class/leds/status_led/brightness

NanoPi NEO Air使用九:使用Linux内核自带的LED驱动相关推荐

  1. NanoPi NEO Air使用十二:使用自带的fbtft驱动点亮SPI接口TFT屏幕,ST7789V

    上节自己编写spi驱动来点亮spi接口的小屏幕,其实Linux内核里已经提供spi接口小屏的设备驱动,即内核中已经自带了此类驱动,名字为fbtft.本节就来使用它. 引脚 240x240分辨率,1.3 ...

  2. Linux 内核自带的 LED 灯驱动

    系列文章 I.MX6ULL 手册查找使用方法 实战点亮LED(寄存器版) I.MX6ULL 手册查找使用方法 实战点亮LED(固件库版本) linux 字符设备驱动实战 linux LED设备驱动文件 ...

  3. 【嵌入式Linux驱动开发】二十一、Linux内核自带的KEY驱动探索

      君子应知进退方,时机不到且隐藏.   妆未梳成未见客,势弱稍时敛锋芒.   腹隐良谋待机至,东山再起斗志昂.   遥想曹刘煮酒事,高明刘备扮愚郎. 文章目录 一. Linux 内核自带 KEY 驱 ...

  4. 如何使用linux系统自带的led驱动

    使用Linux 内核的platform 框架驱动led灯 1.使用menuconfig使能(可跳过,系统默认配置好) 2.设备树节点编写 3.运行测试 我们平时要使用设备驱动的时候,总是需要我们自己去 ...

  5. Linux内核自带SPI设备驱动测试程序分析:spidev_test.c

        在Linux系统中,SPI 的用户模式设备接口的驱动源码位于 drivers/spi/spidev.c,在应用层生成 /dev/spidev* 的节点,可以通过 read. write 达到与 ...

  6. STM32MP157A驱动开发 | 07 - 使用Linux内核自带的触摸屏驱动(GT911)

    一.使能驱动 二.设备树描述 gt911@14 {compatible = "goodix,gt911";reg = <0x14>;interrupt-parent = ...

  7. NanoPi NEO Air使用七:获取并编译U-boot和Linux的源码

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  8. NanoPi NEO Air使用十五:移植RTL8723BU驱动

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

  9. NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V

    NanoPi NEO Air使用一:介绍 NanoPi NEO Air使用二:固件烧录 NanoPi NEO Air使用三:OverlayFS.CPU温度和频率.wifi.蓝牙.npi-config ...

最新文章

  1. word2010多级列表编号变成黑块的解决方案
  2. UA MATH567 高维统计专题1 稀疏信号及其恢复7 LASSO的预测误差与变量选择一致性
  3. 头像裁剪_课堂裁剪|19级油画班油画头像写生作品
  4. 给 COLA 做减法:应用架构中的“弯弯绕设计”
  5. 越努力越幸运,三年了!!!
  6. 高并发网络架构解决方案分析
  7. WinINet function(2)Request
  8. logback利用mdc机制为日志增加traceId
  9. cannot mount database in EXCLUSIVE mode
  10. 东北三省计算机专业好的学校,东北地区哪个大学比较好 各自的王牌专业是什么...
  11. Linux centosVMware mysql用户管理、常用sql语句、mysql数据库备份恢复
  12. 判断素数的方法(java)
  13. 基于MATLAB的特殊函数与画图(附图像与代码)
  14. photoshop入门教程_Photoshop入门
  15. 常用实验设计方法有哪些?
  16. 台式计算机搜索不到无线信号,台式机无线搜不到怎么办
  17. 小e和siri_华为手机语音助手“小E”功能大全,二十三种玩法你都会吗?
  18. android qq聊天动态表情的实现
  19. go老鸟也会经常出现 并发channal问题引起的协程泄漏
  20. 水晶报表官方实例下载:报表和应用程序

热门文章

  1. linux nexus状态,linux 启动 nexus
  2. mysql删除没有索引页_InnoDB中没有主键是如何运转的
  3. mediumtext_mediumtext数据类型 | 学步园
  4. php getimagesize图片宽高反了_PHP实现简单验证码识别
  5. lstm代码_贼好理解,这个项目教你如何用百行代码搞定各类NLP模型
  6. ubuntu14.04连接网络 No valid active connections found!
  7. 小马智行最新RoboTaxi来了!彭军:对方向有着前所未有的笃定
  8. 20+顶尖高校同时开打《王者荣耀》!实际上是一场科研battle,你能信?
  9. 只要一句话、一段文字,想让奥巴马说啥他就说啥
  10. 51年被发现9次,陶哲轩证明的公式成了重复造轮子?事情并没有这么简单