700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 树莓派linux led字符设备驱动( linux自带)

树莓派linux led字符设备驱动( linux自带)

时间:2020-02-15 23:43:17

相关推荐

树莓派linux led字符设备驱动( linux自带)

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

cd linuxKERNEL=kernel7lmake 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 setCONFIG_LEDS_PCA9532=m# CONFIG_LEDS_PCA9532_GPIO is not setCONFIG_LEDS_GPIO=y

一、Linux 内核自带 LED 驱动

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

obj-$(CONFIG_LEDS_GPIO_REGISTER)+= leds-gpio-register.oobj-$(CONFIG_LEDS_GPIO)+= leds-gpio.oobj-$(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 platformsifeq ($(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)。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。