嵌入式Linux中的LED驱动控制(设备树方式)

Linux3.1之后的内核版本,引入了设备树的概念。同时,设备树还需要Bootloader的支持,如果使用Uboot,在1.1.3版本之后就可以支持设备树了。

设备树概念的提出其实有两方面的原因。其一当然是代码冗余,导致Linux内核臃肿不堪。在Linux内核源码中,只要是通过了基金会的认可,就可以把某个厂商的板级支持代码纳入到Linux内核源码当中。比如国内曾经风靡一时的友善之臂Mini2440开发板,从Linux-2.6.31版本开始就被Linux官方内核所支持了,至今还可以在Linux内核源码中看到它的相关文件(如:arch/arm/mach-s3c24xx/mach-mini2440.c以及arch/arm/configs/mini2440_defconfig等)。然而在Linux内核源码中,同样还包含有很多其他厂商的板级支持文件。其实这些开发板只要所选用的芯片相同,则很大一部分代码是相同的。这就造成了内核的冗余和臃肿,并有愈演愈烈的趋势(难怪Linus要发火了)。第二个原因,其实也是顺理成章的。从Linux2.6版本之后内核就引入了platform总线平台的概念,把驱动分成了设备(platform_device)和驱动(platform_driver)两个单独的文件,现在只要把设备文件从内核中提出来,单独形成一个第三方文件,不就不影响内核了吗。这样做还有一个好处,即这个提出来的设备文件,只要在驱动中统一相关接口和命名规则,该文件的大部分工作还可以由厂商来完成(或由厂商提供的工具来完成),大大提高了开发的效率和可靠性。这个被单独提出来的第三方文件,后来就演化成了现在的设备树配置文件。

下面就通过设备树方式来实现对LED的驱动。先给出设备树的配置内容,在内核源码(本例在/opt/ebf_linux_kernel/目录下)的arch/arm/boot/dts目录下找到一个名为“imx6ull-mmc-npi.dts”的文件,该文件就是开发板(EMMC版本)配套提供的设备树源文件。打开它,并在根节点的最后加入本例LED设备的配置内容,如下。

/ {
    model = "Embedfire i.MX6ULL Board";
    compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";

    aliases {
        pwm0 = &pwm1;
        pwm1 = &pwm2;
        pwm2 = &pwm3;
        pwm3 = &pwm4;
    };
    chosen {
        stdout-path = &uart1;
    };

    memory {
        reg = <0x80000000 0x20000000>;
    };

    reserved-memory {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        linux,cma {
            compatible = "shared-dma-pool";
            reusable;
            size = <0x14000000>;
            linux,cma-default;
        };
    };

    regulators {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <0>;
        reg_sd1_vmmc: regulator@1 {
            compatible = "regulator-fixed";
            regulator-name = "VSD_3V3";
            regulator-min-microvolt = <3300000>;
            regulator-max-microvolt = <3300000>;
            gpio = <&gpio1 9 GPIO_ACTIVE_HIGH>;
            off-on-delay = <20000>;
            enable-active-high;
        };
    };

    leds {
        compatible = "gpio-leds";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_led>;

        led0: cpu {
            label = "cpu";
            gpios = <&gpio5 3 GPIO_ACTIVE_LOW>;
            default-state = "on";
            linux,default-trigger = "heartbeat";
        };
    };

    sound: sound {
                status = "disabled";
    };

    spi4: 74hc595 {
        compatible = "spi-gpio";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_spi4>;
        pinctrl-assert-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
        status = "disabled";
        gpio-sck = <&gpio5 11 0>;
        gpio-mosi = <&gpio5 10 0>;
        cs-gpios = <&gpio5 7 0>;
        num-chipselects = <1>;
        #address-cells = <1>;
        #size-cells = <0>;
        gpio_spi: gpio_spi@0 {
            compatible = "fairchild,74hc595";
            gpio-controller;
            #gpio-cells = <2>;
            reg = <0>;
            registers-number = <1>;
            registers-default = /bits/ 8 <0x57>;
            spi-max-frequency = <100000>;
        };
    };

    //以下为本次LED的追加内容
    rgb_led{
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "fire,rgb_led";
        //红色LED节点
        ranges;
        rgb_led_red@0x020C406C{
            compatible = "fire,led_red";
            reg = <0x020C406C 0x00000004
                   0x020E006C 0x00000004
                   0x020E02F8 0x00000004
                   0x0209C000 0x00000004
                   0x0209C004 0x00000004>;
            status = "okay";
        };
        //绿色LED节点
        rgb_led_green@0x020C4074{
            compatible = "fire,led_green";
            reg = <0x020C4074 0x00000004
                   0x020E01E0 0x00000004
                   0x020E046C 0x00000004
                   0x020A8000 0x00000004
                   0x020A8004 0x00000004>;
            status = "okay";
        };
        //蓝色LED节点
        rgb_led_blue@0x020C4074{
            compatible = "fire,led_blue";
            reg = <0x020C4074 0x00000004
                   0x020E01DC 0x00000004
                   0x020E0468 0x00000004
                   0x020A8000 0x00000004
                   0x020A8004 0x00000004>;
            status = "okay";
        };
    };    
};

注意,以上内容只是设备树文件imx6ull-mmc-npi.dts中的一部分内容,并未全部给出。上面内容中最末尾的部分才是本次追加的内容,其他部分内容是原设备树就有的,不要改动(包括未给出的部分),完成后保存并编译它。编译要在源码根目录下进行(即/opt/ebf_linux_kernel/目录下),先执行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_v7_defconfig进行配置,然后执行make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs进行编译。编译完成后,会在设备树所在目录下(arch/arm/boot/dts)生成名为imx6ull-mmc-npi.dtb的设备树文件,把该文件通过NFS拷贝到开发板的/usr/lib/linux-image-4.19.35-imx6/目录下并替换原有设备树文件,然后执行reboot重启开发板(不能按reset键重启)。重启后,可查看目录/proc/device-tree,会看到新增的设备节点,如下图所示。

111

以下是平台驱动部分的代码,文件名为led.c。

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/platform_device.h>
//以下定义led资源结构体,保存获取得到的节点信息以及转换后的虚拟寄存器地址
struct led_resource
{
    struct device_node *device_node;
    void __iomem *CCM_CCGR;
    void __iomem *IOMUXC_SW_MUX_CTL_PAD;
    void __iomem *IOMUXC_SW_PAD_CTL_PAD;
    void __iomem *DR;
    void __iomem *GDIR; 
};
static struct class *led_class;    //定义类结构体
struct led_dev
{
    dev_t devid;                     //设备号
    struct cdev led_cdev;            //定义字符型结构体             
    struct device_node *rgb_led_device_node; //rgb_led的设备树节点结构体
    struct led_resource led_red;
    struct led_resource led_green;
    struct led_resource led_blue;
};
//实现open函数,为file_oprations结构体成员函数
static int led_open(struct inode *inode, struct file *filp)
{
    unsigned int tmp;
    struct led_dev *led;
    if (!inode->i_cdev) {
        pr_err("btn_open: i_cdev is NULL\n");
        return -ENODEV;
    }
    led = container_of(inode->i_cdev, struct led_dev, led_cdev);    //把led结构体保存在file结构体的私有变量中
    filp->private_data = led;

    tmp = ioread32(led->led_red.CCM_CCGR);
    tmp |= (0x03 << 26);
    iowrite32(tmp, led->led_red.CCM_CCGR);
    tmp = ioread32(led->led_green.CCM_CCGR);
    tmp |= (0x03 << 12);
    iowrite32(tmp, led->led_green.CCM_CCGR); //开启时钟
    return 0;
}
//实现write函数,为file_oprations结构体成员函数
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    unsigned char value;
    unsigned int tmp;
    unsigned long n;
    //以下把私有变量private_data中的值赋值给指针led(该值在上面open函数中存入)
    struct led_dev *led = filp->private_data;
    n = copy_from_user(&value, buf, cnt);    //从应用空间获取值
    switch (value) {
    case 0: /* 全部点亮 */
        tmp = ioread32(led->led_red.DR);
        tmp &= ~(0x01 << 4);
        iowrite32(tmp, led->led_red.DR);
        tmp = ioread32(led->led_green.DR);
        tmp &= ~(0x01 << 20);
        iowrite32(tmp, led->led_green.DR);
        tmp = ioread32(led->led_blue.DR);
        tmp &= ~(0x01 << 19);
        iowrite32(tmp, led->led_blue.DR);
        break;
    case 1: /* 点亮红灯 */
        tmp = ioread32(led->led_red.DR);
        tmp &= ~(0x01 << 4);
        iowrite32(tmp, led->led_red.DR);
        break;
    case 2: /* 点亮绿灯 */
        tmp = ioread32(led->led_green.DR);
        tmp &= ~(0x01 << 20);
        iowrite32(tmp, led->led_green.DR);
        break;
    case 3: /* 点亮蓝灯 */
        tmp = ioread32(led->led_blue.DR);
        tmp &= ~(0x01 << 19);
        iowrite32(tmp, led->led_blue.DR);
        break;
    case 4: /* 熄灭红灯 */
        tmp = ioread32(led->led_red.DR);
        tmp |= (0x01 << 4);
        iowrite32(tmp, led->led_red.DR);
        break;
    case 5: /* 熄灭绿灯 */
        tmp = ioread32(led->led_green.DR);
        tmp |= (0x01 << 20);
        iowrite32(tmp, led->led_green.DR);
        break;
    case 6: /* 熄灭蓝灯 */
        tmp = ioread32(led->led_blue.DR);
        tmp |= (0x01 << 19);
        iowrite32(tmp, led->led_blue.DR);
        break;
    case 7: /* 全部熄灭 */
        tmp = ioread32(led->led_red.DR);
        tmp |= (0x01 << 4);
        iowrite32(tmp, led->led_red.DR);
        tmp = ioread32(led->led_green.DR);
        tmp |= (0x01 << 20);
        iowrite32(tmp, led->led_green.DR);
        tmp = ioread32(led->led_blue.DR);
        tmp |= (0x01 << 19);
        iowrite32(tmp, led->led_blue.DR);
        break;
    default: /* 其他值全部熄灭 */
        tmp = ioread32(led->led_red.DR);
        tmp |= (0x01 << 4);
        iowrite32(tmp, led->led_red.DR);
        tmp = ioread32(led->led_green.DR);
        tmp |= (0x01 << 20);
        iowrite32(tmp, led->led_green.DR);
        tmp = ioread32(led->led_blue.DR);
        tmp |= (0x01 << 19);
        iowrite32(tmp, led->led_blue.DR);
        break;
    }
       return cnt;
}
//实现release函数,为file_oprations结构体函数
static int led_release(struct inode *inode, struct file *filp)
{
    unsigned int tmp;
    struct led_dev *led = filp->private_data;
    tmp = ioread32(led->led_red.CCM_CCGR);
    tmp &= ~(0x03 << 26);
    iowrite32(tmp, led->led_red.CCM_CCGR);
    tmp = ioread32(led->led_green.CCM_CCGR);
    tmp &= ~(0x03 << 12);
    iowrite32(tmp, led->led_green.CCM_CCGR); //关闭时钟
    return 0;
}
//填充一个file_oprations类型的结构体,名为led_dev_fops,包含上述声明的成员函数
static struct file_operations led_dev_fops =
{
        .owner = THIS_MODULE,
        .open = led_open,            //指定open函数成员
        .write = led_write,          //指定write函数成员
        .release = led_release,      //指定release函数成员
};
//probe函数中,驱动提取设备树中的资源,并完成字符设备的注册
static int led_pdrv_probe(struct platform_device *pdev)
{
    unsigned int tmp;
    struct led_dev *led;
    //以下动态申请led结构体大小的内存
    led = devm_kzalloc(&pdev->dev, sizeof(struct led_dev), GFP_KERNEL);
    if(!led)
        return -ENOMEM;
    //获取rgb_led的设备树节点
    led->rgb_led_device_node = of_find_node_by_path("/rgb_led");
    if (led->rgb_led_device_node == NULL)
    {
        printk(KERN_ERR "\t  get rgb_led failed!  \n");
        return -1;
    }
    //获取rgb_led节点的红灯子节点
    led->led_red.device_node = of_find_node_by_name(led->rgb_led_device_node,"rgb_led_red");
    if (led->led_red.device_node == NULL)
    {
        printk(KERN_ERR "\n get rgb_led_red_device_node failed ! \n");
        return -1;
    }
    //以下获取设备节点中红灯子节点的reg属性并转化为虚拟地址
    led->led_red.CCM_CCGR = of_iomap(led->led_red.device_node, 0);
    led->led_red.IOMUXC_SW_MUX_CTL_PAD = of_iomap(led->led_red.device_node, 1);
    led->led_red.IOMUXC_SW_PAD_CTL_PAD = of_iomap(led->led_red.device_node, 2);
    led->led_red.DR = of_iomap(led->led_red.device_node, 3);
    led->led_red.GDIR = of_iomap(led->led_red.device_node, 4);
//    of_node_put(led->led_red.device_node);   // 映射完毕,释放引用
//    led->led_red.device_node = NULL;
    //初始化红灯
    tmp = ioread32(led->led_red.CCM_CCGR);
    tmp |= (0x03 << 26);
    iowrite32(tmp, led->led_red.CCM_CCGR); //开启时钟
    tmp = ioread32(led->led_red.IOMUXC_SW_MUX_CTL_PAD);
    tmp &= ~(0xf << 0);
    tmp |= (0x05 << 0);
    iowrite32(tmp, led->led_red.IOMUXC_SW_MUX_CTL_PAD); //设置复用功能
    tmp = ioread32(led->led_red.IOMUXC_SW_PAD_CTL_PAD);
    tmp = 0x10B0;
    iowrite32(tmp, led->led_red.IOMUXC_SW_PAD_CTL_PAD); //设置PAD属性
    tmp = ioread32(led->led_red.GDIR);
    tmp |= (0x01 << 4);
    iowrite32(tmp, led->led_red.GDIR); //设置GPIO1_04为输出模式
    tmp = ioread32(led->led_red.DR);
    tmp |= (0x01 << 4);
    iowrite32(tmp, led->led_red.DR); //设置GPIO1_04默认输出高电平
    //获取rgb_led节点的绿灯子节点
    led->led_green.device_node = of_find_node_by_name(led->rgb_led_device_node,"rgb_led_green");
    if (led->led_green.device_node == NULL)
    {
        printk(KERN_ERR "\n get rgb_led->led_green_device_node failed ! \n");
        return -1;
    }
    //以下获取设备节点中绿灯子节点的reg属性并转化为虚拟地址
    led->led_green.CCM_CCGR = of_iomap(led->led_green.device_node, 0);
    led->led_green.IOMUXC_SW_MUX_CTL_PAD = of_iomap(led->led_green.device_node, 1);
    led->led_green.IOMUXC_SW_PAD_CTL_PAD = of_iomap(led->led_green.device_node, 2);
    led->led_green.DR = of_iomap(led->led_green.device_node, 3);
    led->led_green.GDIR = of_iomap(led->led_green.device_node, 4);
  //  of_node_put(led->led_green.device_node);
  //  led->led_green.device_node = NULL;
    //初始化绿灯
    tmp = ioread32(led->led_green.CCM_CCGR);
    tmp |= (0x03 << 12);
    iowrite32(tmp, led->led_green.CCM_CCGR); //开启时钟
    tmp = ioread32(led->led_green.IOMUXC_SW_MUX_CTL_PAD);
    tmp &= ~(0xf << 0);
    tmp |= (0x05 << 0);
    iowrite32(tmp, led->led_green.IOMUXC_SW_MUX_CTL_PAD); //设置复用功能
    tmp = ioread32(led->led_green.IOMUXC_SW_PAD_CTL_PAD);
    tmp = (0x10B0);
    iowrite32(tmp, led->led_green.IOMUXC_SW_PAD_CTL_PAD); //设置PAD属性
    tmp = ioread32(led->led_green.GDIR);
    tmp |= (0x01 << 20);
    iowrite32(tmp, led->led_green.GDIR); //设置GPIO4_IO20为输出模式
    tmp = ioread32(led->led_green.DR);
    tmp |= (0x01 << 20);
    iowrite32(tmp, led->led_green.DR); //设置GPIO4_IO20默认输出高电平
    //获取rgb_led节点的蓝灯子节点
    led->led_blue.device_node = of_find_node_by_name(led->rgb_led_device_node,"rgb_led_blue");
    if (led->led_blue.device_node == NULL)
    {
        printk(KERN_ERR "\n get rgb_led->led_blue_device_node failed ! \n");
        return -1;
    }
    //以下获取设备节点中蓝灯子节点的reg属性并转化为虚拟地址
    led->led_blue.CCM_CCGR = of_iomap(led->led_blue.device_node, 0);
    led->led_blue.IOMUXC_SW_MUX_CTL_PAD = of_iomap(led->led_blue.device_node, 1);
    led->led_blue.IOMUXC_SW_PAD_CTL_PAD = of_iomap(led->led_blue.device_node, 2);
    led->led_blue.DR = of_iomap(led->led_blue.device_node, 3);
    led->led_blue.GDIR = of_iomap(led->led_blue.device_node, 4);
 //   of_node_put(led->led_blue.device_node);
 //   led->led_blue.device_node = NULL;
    //初始化蓝灯
    tmp = ioread32(led->led_blue.CCM_CCGR);
    tmp |= (0x03 << 12);
    iowrite32(tmp, led->led_blue.CCM_CCGR); //开启时钟
    tmp = ioread32(led->led_blue.IOMUXC_SW_MUX_CTL_PAD);
    tmp &= ~(0xf << 0);
    tmp |= (0x05 << 0);
    iowrite32(tmp, led->led_blue.IOMUXC_SW_MUX_CTL_PAD); //设置复用功能
    tmp = ioread32(led->led_blue.IOMUXC_SW_PAD_CTL_PAD);
    tmp = (0x10B0);
    iowrite32(tmp, led->led_blue.IOMUXC_SW_PAD_CTL_PAD); //设置PAD属性
    tmp = ioread32(led->led_blue.GDIR);
    tmp |= (0x01 << 19);
    iowrite32(tmp, led->led_blue.GDIR); //设置GPIO4_IO19为输出模式
    tmp = ioread32(led->led_blue.DR);
    tmp |= (0x01 << 19);
    iowrite32(tmp, led->led_blue.DR); //设置GPIO4_IO19默认输出高电平
    tmp = ioread32(led->led_red.CCM_CCGR);
    tmp &= ~(0x03 << 26);
    iowrite32(tmp, led->led_red.CCM_CCGR);
    tmp = ioread32(led->led_green.CCM_CCGR);
    tmp &= ~(0x03 << 12);
    iowrite32(tmp, led->led_green.CCM_CCGR); //关闭时钟
    //申请主设备号
    if (alloc_chrdev_region(&led->devid, 0, 1, "led") < 0)
    {
        printk("fail to alloc devid\n");
        return -EFAULT;
    }
    led->led_cdev.owner = THIS_MODULE;
    //绑定前面声明的file_oprations类型的结构体到字符设备
    cdev_init(&led->led_cdev, &led_dev_fops);
    //填充上面申请到的主设备号到字符设备
    if ( cdev_add(&led->led_cdev, led->devid, 1) < 0)
    {
        printk("fail to add cdev\n");
        return -EFAULT;
    }
    //创建一个设备节点
    device_create(led_class, NULL, led->devid, NULL, "led");
    //以下把LED数据信息存入在平台驱动结构体中pdev->dev->driver_data中,后面移除时会用到
    platform_set_drvdata(pdev, led);
    /* 所有子节点处理完毕,释放根节点 */
//    of_node_put(led->rgb_led_device_node);
//    led->rgb_led_device_node = NULL;
    printk("platform driver probed!\n");
    return 0;
}
//remove函数中,删除设备并释放设备号
static int led_pdrv_remove(struct platform_device *pdev)
{
    //platform_get_drvdata,获取当前LED灯对应的结构体
    struct led_dev *led = platform_get_drvdata(pdev);
    //以下实现各个寄存器的解除映射
    iounmap(led->led_green.CCM_CCGR);
    iounmap(led->led_green.IOMUXC_SW_MUX_CTL_PAD);
    iounmap(led->led_green.IOMUXC_SW_PAD_CTL_PAD);
    iounmap(led->led_green.DR);
    iounmap(led->led_green.GDIR);
    iounmap(led->led_red.CCM_CCGR);
    iounmap(led->led_red.IOMUXC_SW_MUX_CTL_PAD);
    iounmap(led->led_red.IOMUXC_SW_PAD_CTL_PAD);
    iounmap(led->led_red.DR);
    iounmap(led->led_red.GDIR);
    iounmap(led->led_blue.CCM_CCGR);
    iounmap(led->led_blue.IOMUXC_SW_MUX_CTL_PAD);
    iounmap(led->led_blue.IOMUXC_SW_PAD_CTL_PAD);
    iounmap(led->led_blue.DR);
    iounmap(led->led_blue.GDIR);
    unregister_chrdev_region(led->devid, 1);        //释放主设备号
    cdev_del(&led->led_cdev);                       //删除字符设备
    device_destroy(led_class, led->devid);          //销毁设备节点
    printk("platform driver removed!\n");
    return 0;
}
//填充of_device_id结构体,名为rgb_led,用于指明匹配表
static const struct of_device_id rgb_led[] = {
    {.compatible = "fire,rgb_led"},        //匹配内容
    {/* sentinel */}
};
//以下填充一个platform_driver结构体
struct platform_driver led_platform_driver = {
    .probe = led_pdrv_probe,        //指定probe函数成员
    .remove = led_pdrv_remove,      //指定remove函数成员
    .driver = {
        .name = "rgb-leds-platform",      //指定平台总线名称
        .owner = THIS_MODULE,
        .of_match_table = rgb_led,        //指定匹配表名称
    }
};
//以下定义模块的入口函数
static int __init led_pdrv_init(void)
{
    led_class = class_create(THIS_MODULE, "my_leds");    //创建一个类
    platform_driver_register(&led_platform_driver); //注册一个platform驱动
    printk("led platform driver initted!\n");
    return 0;
}
//以下定义模块的出口函数
static void __exit led_pdrv_exit(void)
{
    platform_driver_unregister(&led_platform_driver); //释放一个platform驱动
    class_destroy(led_class);                  //销毁类
    printk("led platform driver exited!\n");
}
module_init(led_pdrv_init);
module_exit(led_pdrv_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(intree,"Y");

在上述驱动程序中,在填充的of_device_id结构体中,最后一个成员必须是一对空的花括号,它用来用来标记数组的结束(即哨兵),以避免越界。配套的Makefile文件内容如下。

KERNEL_DIR=/opt/ebf_linux_kernel/build_image/build
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
export  ARCH  CROSS_COMPILE
obj-m := led.o
all:
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) modules
modules clean:
        $(MAKE) -C $(KERNEL_DIR) M=$(CURDIR) clean

以下是测试用的应用程序代码,文件名为app.c。

#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
   int fd;
   unsigned char val = 7;
   fd = open("/dev/led", O_RDWR);        //打开设备节点
   if( fd < 0 )
      printf("can`t open\n");
   if( argc != 3 )                        //命令参数不对时提示
    {
       printf("Usage :\n");
       printf("%s <all|red|green|blue> <on|off>\n", argv[0]);
       return 0;
    }
   if(strcmp(argv[1], "all") == 0)
    {
      if(strcmp(argv[2], "on") == 0)
         val = 0;                        //值为0时全部点亮
      else
         val = 7;                        //值为7时全部熄灭
    }
   else if(strcmp(argv[1], "red") == 0)
    {
      if(strcmp(argv[2], "on") == 0)
        val = 1;                        //值为1时红色点亮
      else
        val = 4;                        //值为4时红色熄灭
    }
   else if(strcmp(argv[1], "green") == 0)
    {
      if(strcmp(argv[2], "on") == 0)
        val = 2;                        //值为2时绿色点亮
      else
        val = 5;                        //值为5时绿色熄灭
    }
   else if(strcmp(argv[1], "blue") == 0)
    {
      if(strcmp(argv[2], "on") == 0)
        val = 3;                        //值为3时蓝色点亮
      else
        val = 6;                        //值为6时蓝色熄灭
    }
   write(fd, &val, 1);            //把值写入设备节点
   close(fd);                     //关闭设备节点
   return 0;
}

完成后,先执行make命令编译驱动程序,若成功会生成名为led.ko的驱动模块文件。然后对应用程序进行交叉编译,执行“arm-linux-gnueabihf-gcc app.c -o app”即可。实验结果与“嵌入式Linux中的LED驱动控制”一文中的完全一样,这里就不给出了。

关于设备树的介绍,可以参考STM32MP157部分的”嵌入式Linux中的LED驱动控制(设备树方式)(续)“一文。

posted @ 2026-06-16 19:28  fxzq  阅读(50)  评论(0)    收藏  举报