树莓派的Linux 内核已经集成了LED 灯驱动。 Linux 内核的 LED 灯驱动采用 platform 框架。编译树莓派linux内核时,会输入配置命令构建配置linux内核,如:

cd linux
KERNEL=kernel7l
make bcm2711_defconfig

  linux内核的makefile会从arch/arm/configs 目录中寻找默认配置文件: bcm2711_defconfig,配置完成后会生成.config 文件。打开.config 文件,有“CONFIG_LEDS_GPIO=y”,说明配置了LED灯驱动。

#
# LED drivers
#
# CONFIG_LEDS_AN30259A is not set
# CONFIG_LEDS_BCM6328 is not set
# CONFIG_LEDS_BCM6358 is not set
# CONFIG_LEDS_CR0014114 is not set
# CONFIG_LEDS_LM3530 is not set
# CONFIG_LEDS_LM3532 is not set
# CONFIG_LEDS_LM3642 is not set
# CONFIG_LEDS_LM3692X is not set
CONFIG_LEDS_PCA9532=m
# CONFIG_LEDS_PCA9532_GPIO is not set
CONFIG_LEDS_GPIO=y

一、Linux 内核自带 LED 驱动

  打开/drivers/leds/Makefile 这个文件,有:

obj-$(CONFIG_LEDS_GPIO_REGISTER) += leds-gpio-register.o
obj-$(CONFIG_LEDS_GPIO)         += leds-gpio.o
obj-$(CONFIG_LEDS_LP3944)       += leds-lp3944.o

  说明如果定义了 CONFIG_LEDS_GPIO 的话就会编译 leds-gpio.c 这个文件,而在.config 文件中有“CONFIG_LEDS_GPIO=y”这一行,因此 leds-gpio.c 驱动文件会被编译进linux内核。
  打开LED 灯驱动文件/drivers/leds/leds-gpio.c,有如下所示内容:

static const struct of_device_id of_gpio_leds_match[] = {{ .compatible = "gpio-leds", },{},
};

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

static struct platform_driver gpio_led_driver = {.probe     = gpio_led_probe,.shutdown = gpio_led_shutdown,.driver        = {.name   = "leds-gpio",.of_match_table = of_gpio_leds_match,},
};module_platform_driver(gpio_led_driver);

  platform_driver 驱动结构体变量,可以看出, Linux 内核自带的 LED 驱动采用了 platform 框架。 probe 函数为 gpio_led_probe,当驱动和设备匹配成功以后 gpio_led_probe 函数就会执行。驱动名字为“leds-gpio”,因此会在/sys/bus/platform/drivers 目录下存在一个名为“leds-gpio”的文件。
  当驱动和设备匹配以后 gpio_led_probe 函数就会执行,此函数主要是从设备树中获取 LED灯的 GPIO 信息。gpio_led_probe内容为:

static int gpio_led_probe(struct platform_device *pdev)
{struct gpio_led_platform_data *pdata = dev_get_platdata(&pdev->dev);struct gpio_leds_priv *priv;int i, ret = 0;if (pdata && pdata->num_leds) {/* 非设备树方式 */priv = devm_kzalloc(&pdev->dev,sizeof_gpio_leds_priv(pdata->num_leds),GFP_KERNEL);if (!priv)return -ENOMEM;priv->num_leds = pdata->num_leds;for (i = 0; i < priv->num_leds; i++) {const struct gpio_led *template = &pdata->leds[i];struct gpio_led_data *led_dat = &priv->leds[i];if (template->gpiod)led_dat->gpiod = template->gpiod;elseled_dat->gpiod =gpio_led_get_gpiod(&pdev->dev,i, template);if (IS_ERR(led_dat->gpiod)) {dev_info(&pdev->dev, "Skipping unavailable LED gpio %d (%s)\n",template->gpio, template->name);continue;}ret = create_gpio_led(template, led_dat,&pdev->dev, NULL,pdata->gpio_blink_set);if (ret < 0)return ret;}} else {/* 采用设备树 */priv = gpio_leds_create(pdev);if (IS_ERR(priv))return PTR_ERR(priv);}platform_set_drvdata(pdev, priv);return 0;
}

  如果使用设备树的话,会使用 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;/** Acquire gpiod from DT with uninitialized label, which* will be updated after LED class device is registered,* Only then the final LED name is known.*/led.gpiod = devm_fwnode_get_gpiod_from_child(dev, NULL, child,GPIOD_ASIS,NULL);if (IS_ERR(led.gpiod)) {fwnode_handle_put(child);return ERR_CAST(led.gpiod);}led_dat->gpiod = 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, child, NULL);if (ret < 0) {fwnode_handle_put(child);return ERR_PTR(ret);}/* Set gpiod label to match the corresponding LED name. */gpiod_set_consumer_name(led_dat->gpiod,led_dat->cdev.dev->kobj.name);priv->num_leds++;}return priv;
}

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

二、树莓派4b的设备树节点

  在linux内核的.config 配置文件文件中有

#
# Other Architectures
#
CONFIG_ARCH_BCM2835=y
# CONFIG_ARCH_BCM_53573 is not set
# CONFIG_ARCH_BCM_63XX is not set
# CONFIG_ARCH_BRCMSTB is not set

  说明会编译“CONFIG_ARCH_BCM2835”配置下的设备树文件。
  在 arch/arm/boot/dts/Makefile文件中有如下内容:

dtb-$(CONFIG_ARCH_BCM2835) += \bcm2708-rpi-b.dtb \bcm2708-rpi-b-rev1.dtb \bcm2708-rpi-b-plus.dtb \bcm2708-rpi-cm.dtb \bcm2708-rpi-zero.dtb \bcm2708-rpi-zero-w.dtb \bcm2709-rpi-2-b.dtb \bcm2710-rpi-2-b.dtb \bcm2710-rpi-3-b.dtb \bcm2710-rpi-3-b-plus.dtb \bcm2711-rpi-4-b.dtb \bcm2710-rpi-cm3.dtb \bcm2711-rpi-cm4.dtb..........dtb-$(CONFIG_ARCH_BCM2835) += \bcm2835-rpi-b.dtb \bcm2835-rpi-a.dtb \bcm2835-rpi-b-rev2.dtb \bcm2835-rpi-b-plus.dtb \bcm2835-rpi-a-plus.dtb \bcm2835-rpi-cm1-io1.dtb \bcm2836-rpi-2-b.dtb \bcm2837-rpi-3-a-plus.dtb \bcm2837-rpi-3-b.dtb \bcm2837-rpi-3-b-plus.dtb \bcm2837-rpi-cm3-io3.dtb \bcm2711-rpi-4-b.dtb \bcm2835-rpi-zero.dtb \bcm2835-rpi-zero-w.dtb........# Enable fixups to support overlays on BCM2835 platforms
ifeq ($(CONFIG_ARCH_BCM2835),y)DTC_FLAGS ?= -@
endif

  编译设备树的时候会将这些.dts文件 编译为二进制的.dtb文件。如果使用uboot 启动linux内核,则uboot 中会使用 bootz 或 bootm命令向 Linux 内核传递二进制设备树文件(.dtb))。树莓派的bootloader会根据板子选择加载具体的设备树文件。
  在arch/arm/boot/dts/bcm2711-rpi-4-b.dts文件中有:

#include "bcm2711.dtsi"
#include "bcm2835-rpi.dtsi"
......leds {act {gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;};pwr {label = "PWR";gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;default-state = "keep";linux,default-trigger = "default-on";};};
......
&leds {act_led: act {label = "led0";linux,default-trigger = "mmc0";gpios = <&gpio 42 GPIO_ACTIVE_HIGH>;};pwr_led: pwr {label = "led1";linux,default-trigger = "default-on";gpios = <&expgpio 2 GPIO_ACTIVE_LOW>;};
};

  在arch/arm/boot/dts/bcm2835-rpi.dtsi文件中有:

 leds {compatible = "gpio-leds";act {label = "ACT";default-state = "keep";linux,default-trigger = "heartbeat";};};

  说明:
  ①、创建了一个节点leds表示 LED 灯设备。
  ②、 leds节点的 compatible 属性值为“gpio-leds”。
  ③、 leds节点的两个子节点,都有一个 label 属性“led0”和“led0”, label 属性一般表示LED 灯的名字。
  ④、这两个子节点都有 gpios 属性值,表示此 LED 所使用的 GPIO 引脚。
  ⑤、这两个子节点都设置“linux,default-trigger”属性值,也就是设置 LED 灯的默认功能。

三、运行测试

  启动树莓派以后查看/sys/bus/platform/devices/leds目录。证明在platform总线上加载了节点leds。在/sys/bus/platform/devices/leds目录下进入子目录leds。在 leds 目录下有两个个名为“led0”和“led1”的子目录,这两个子目录的名字就是设备树中两个子节点的 label 属性值。
  同时在目录sys/devices/platform/leds中也有相同的内容。
  测试时通过/sys/class/leds/led[LED_ID]/trigger文件进行配置。其中[LED_ID]需要替换为 0(代表 ACT LED)或 1(代表 PWR LED)。

树莓派linux led字符设备驱动( linux自带)相关推荐

  1. 树莓派linux led字符设备驱动(信号量)

    一.信号量   Linux 内核提供了信号量机制,信号量常常用于控制对共享资源的访问.相比于自旋锁,信号量可以使线程进入休眠状态,使用信号量会提高处理器的使用效率.但是,信号量的开销要比自旋锁大,因为 ...

  2. 树莓派linux led字符设备驱动(原子操作)

    一.原子操作   linux驱动中为了处理对共享资源的并发访问,引入了原子操作.原子操作就是指不能再进一步分割的操作,一般原子操作用于变量或者位操作. Linux 内核提供了两组原子操作 API 函数 ...

  3. linux内核字符驱动设备,Linux学习笔记——linux内核字符设备驱动-Go语言中文社区...

    尝试在树莓派安装的raspbian系统上进行linux字符设备驱动 1.更新安装kernel header源码 sudo apt-get update sudo apt-get install ras ...

  4. Linux之字符设备驱动框架

    目录 一.驱动介绍 1.内核模块 2.日志级别 3.模块符号的导出 4.内核模块参数 二.字符设备驱动(一) 1.模块加载 2.注册字符设备驱动 3.内存映射 三.字符设备驱动(二) 1.模块加载 2 ...

  5. linux3.0字符设备驱动,linux字符设备驱动的 ioctl 幻数

    在Linux字符设备驱动入门(一)中,我们实现了字符设备的简单读写字符功能,接下来我们要在这个基础上加入ioctl功能.首先,我们先来看看3.0内核下../include/linux/fs.h中fil ...

  6. linux 编译字符设备驱动错误,linux字符设备驱动框架及编写流程

    流程: init { } exit { } 申请设备号 (动态注册/静态注册) 创建一个字符设备 cdev_alloc 初始化字符设备 cdev_init 设备号和字符设备关联 cdev_add 销毁 ...

  7. Linux SPI 字符设备 驱动例子

    其实,在Linux中,SPI和IIC的注册流程很相似.在这里我还是用iTOP4412做演示. 从原理图可以得知我们要用到的引脚是这几个.用的是SPI_2.记住这个数字,下面设备注册要用到. 首先我们要 ...

  8. 【正点原子MP157连载】第二十章 字符设备驱动开发-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  9. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

  10. linux用户空间flash驱动,全面掌握Linux驱动框架——字符设备驱动、I2C驱动、总线设备驱动、NAND FLASH驱动...

    原标题:全面掌握Linux驱动框架--字符设备驱动.I2C驱动.总线设备驱动.NAND FLASH驱动 字符设备驱动 哈~ 这几天都在发图,通过这种方式,我们希望能帮大家梳理学过的知识,全局的掌握Li ...

最新文章

  1. ARM处理器的运行模式和ARM寄存器
  2. php mysql简单链接_php mysql 数据库链接简单例子
  3. [原创]Retrofit使用教程(二)
  4. 天猫首页迷思之-jquery实现左侧广告牌图片轮播
  5. 尽力去帮助一个陌生人
  6. java ee程序设计师_软件设计师:Java EE开发四大常用框架[1]
  7. Java还有发展前景吗?现在该怎么去学习?
  8. (Origin)设置图例位置
  9. [转载]仿射变换(Affine Transformation)
  10. 设计一个递归算法由二叉树BT复制产生另一棵二叉树BT1(假设二叉树采用二叉链存储结构)
  11. 开发者 发展 9 心智模式
  12. 电子计算机主机房国标,中华人民共和国国家标准电子计算机机房设计规范GB50174-93...
  13. 邮箱登陆时显示服务器连接失败,邮箱显示无法连接服务器
  14. 银行笔试计算机基础知识点归纳,银行笔试:六大行笔试考情及重点梳理(内含免费模考)...
  15. 微分方程求通解推导-----专升本
  16. .Net/C#: 一个将在线简体中文网页转为繁体中文页简单方法
  17. 软件设计的基本原理和流程
  18. 视觉伺服控制工具Visual Servoing Platform---VISP(6)----基于4个平面点的姿态估计
  19. python调用libvirt_python实战系列之通过libvirt操作KVM(六)
  20. [SQL]将子查询作为查询条件

热门文章

  1. BC1.2 PD协议
  2. 大麻和烟草对表观基因组产生双重影响
  3. 施柏阁保时捷设计酒店全球陆续开设15家酒店;汉庭新品旗舰店在武汉街道口商圈亮相;开元酒店用国潮赋能中高端品牌 | 全球旅报...
  4. spline本地运行的方法
  5. java rnn生成古诗_基于循环神经网络(RNN)的古诗生成器
  6. android sd 权限修改,真正免root的sd卡权限修改软件详细使用教程
  7. 华盛顿邮报专访苹果CEO库克:带领苹果是个孤独的工作
  8. Flutter各种虚线实战和虚线边框原理
  9. SQL查询语句之查询数据
  10. SL4A_API翻译贴镜像