【Linux驱动开发】点亮一个LED的三种字符设备驱动框架

目录

硬件原理图分析 

GPIO3_IO03的引脚配置及原理分析

1、 寄存器映射版本

 led.c

2、设备树版本

led.c

imx6ull-alientek-emmc.dts

3、pinctrl、gpio子系统版本

led.c

imx6ull-alientek-emmc.dts

三个字符驱动版本都通用的LED点灯应用程序App.c

Makefile工程管理器源码

实验现象

总结


三种驱动框架分别为:

①寄存器映射版本

②设备树版本

③pinctrl、gpio子系统版本

应用程序可共用同一个

CPU:NXP的I.MX6ULL,基于ARM Cortex-A7架构;

开发板:正点原子I.MX6U-ALPHA;

linux内核:linux-4.1.15‘;

硬件原理图分析 

 

        板子的LED0接到了GPIO3_IO03引脚上,当GPIO3_IO03输出为低电平(0)时二极管导通LED0就会被点亮;当GPIO3_IO03输出为高电平(1)时二极管截止LED0就会熄灭;

 (一个小小的建议:初学者一定要养成多动手翻看参考手册和原理图的习惯,这样今后的学习过程更容易举一反三)

GPIO3_IO03的引脚配置及原理分析

<1>使能 GPIO1时钟

将寄存器CCM_CCGR1(物理地址:0x020C406C)的 bit27 和 bit26 这两个位,置成11;

 

<2>设置 GPIO1_IO03的复用功能为GPIO模式

往寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03中写入0x5即可,对应下图中的ALT5

<3>配置 GPIO1_IO03 的电气属性

往寄存器“IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03"写入0x10B0(前十六位为: 0001 0000 1011 0000);

设置 IO的上下拉、速度等等。

0x10B0(前十六位 0001 0000 1011 0000)的含义​​​​​​

<4>设置GPIO1_IO03作为输出功能

把寄存器 GPIO1_GDIR 的 bit3 置 1 ,表示GPIO作输出

<5>控制GPIO1_IO03输出电平

向 GPIO1_DR寄存器的 bit3 写入0即可控制 GPIO1_IO03输出低电平(开灯),写入1即可控制 GPIO1_IO03输出高电平(熄灯)

1、 寄存器映射版本

 led.c

/*
 * @Author: imysy_22
 * @Description: led灯字符型驱动程序寄存器映射版本
 * @Date: 2022-10-17 12:46:27
 * @LastEditTime: 2022-10-17 00:00:09
 * @FilePath: /undefined/home/imysy22/IMX6U/rootfs/home/root/MyDrivers/2_led_chrdev/common_version/led.c
 */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/io.h>

#define DevName "Mychr_led"

static int DevMajor = 11;
static int DevMinor = 0;

/* 要点亮一个led灯所要操作的寄存器地址 */
#define CCM_CCGR1_BASE 0x020C406C
#define SW_MUX_GPIO1_IO03_BASE 0x020E0068
#define SW_PAD_GPIO1_IO03_BASE 0x020E02F4
#define GPIO1_DR_BASE 0x0209C000
#define GPIO1_GDIR_BASE 0x0209C004

/* 寄存器物理地址映射的虚拟地址 */
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

/**
 * @description: 初始化、配置好寄存器
 */
static void led_ioremap(void)
{
    unsigned long RegVal = 0;

    /* 1、把寄存器的物理地址映射到虚拟地址 */
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);   //4代表4个字节,4x8=32位寄存器
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    /* 2、开启GPIO1的时钟 */
    RegVal = readl(IMX6U_CCM_CCGR1);
    RegVal &= ~(0x3 << 26);
    RegVal |= (0x3 << 26);
    writel(RegVal, IMX6U_CCM_CCGR1);

    /* 3、把GPIO1_IO3复用成gpio */
    writel(0x5, SW_MUX_GPIO1_IO03);

    /* 4、设置io属性 */
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    /* 5、配置成输出功能 */
    RegVal = readl(GPIO1_GDIR);
    RegVal &= ~(0x1 << 3);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_GDIR);

    /* 6、默认输出高电平,关闭led */
    RegVal = readl(GPIO1_DR);
    RegVal &= ~(0x1 << 3);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_DR);
}   

static void led_on(void)
{
    unsigned long RegVal = 0;

    RegVal = readl(GPIO1_DR);
    RegVal &= ~(0x1 << 3);
    writel(RegVal, GPIO1_DR);

}

static void led_off(void)
{
    unsigned long RegVal = 0;

    RegVal = readl(GPIO1_DR);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_DR);

}

struct led_dev {
    struct cdev mdev;

    struct class *pcls;
    struct device *pdev;
};

struct led_dev gmydev;

/**
 * @description: 
 * @param {inode} *pnode
 * @param {file} *pfile     设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
 * @return {*}
 */
static int chr_led_open(struct inode *pnode,struct file *pfile)
{
    pfile->private_data = (void *) (container_of(pnode->i_cdev, struct led_dev, mdev)); //设置私有数据,后续可以直接找到gmydev的地址

    return 0;
}

static int chr_led_release(struct inode *pnode,struct file *pfile)
{

    return 0;
}

static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
{
    int ret = 0;
    char buf[1] = {0};
    unsigned char led_state;

    if((ret = copy_from_user(buf, puser, size)))
    {
        printk("copy_from_user failed...\n");
        return -1;
    }

    led_state = buf[0];

    if(led_state == 0)
    {
        led_on();   //低电平开灯
    }

    if(led_state == 1)
    {
        led_off();  //高电平关灯
    }

    return 1;
}


struct file_operations myops = {
    .owner = THIS_MODULE,
    .open = chr_led_open,
    .release = chr_led_release,
    .write = chr_led_write,
};

static int __init chr_led_init(void)
{
    int ret = 0;
    dev_t devno = MKDEV(DevMajor, DevMinor);

    led_ioremap();  //配置好寄存器

    ret = register_chrdev_region(devno, 1, DevName);
    if(ret)
    {
        ret = alloc_chrdev_region(&devno, DevMinor, 1, DevName);
        if(ret)
        {
            printk("register failed...\n");
            return -1;
        }

        DevMajor = MAJOR(devno);
    }

    cdev_init(&gmydev.mdev, &myops);

    gmydev.mdev.owner = THIS_MODULE;
    cdev_add(&gmydev.mdev, devno, 1);

    gmydev.pcls = class_create(THIS_MODULE, DevName);
    if(IS_ERR(gmydev.pcls))
    {
        printk("class_create failed...\n");
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(devno, 1);
        
        return -1;
    }

    gmydev.pdev = device_create(gmydev.pcls, NULL, devno, NULL, DevName);
    if(gmydev.pdev == NULL)
    {
        printk("device_create failed...\n");
        class_destroy(gmydev.pcls);
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(devno, 1);
        
        return -1;
    }

    printk("New character device(%d:%d) added successfully.\n", DevMajor, DevMinor);    

    return 0;
}

static void __exit chr_led_exit(void)
{
    dev_t devno = MKDEV(DevMajor, DevMinor);
    
    /* 取消映射 */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    device_destroy(gmydev.pcls, devno);
    class_destroy(gmydev.pcls);

    cdev_del(&gmydev.mdev); 

    unregister_chrdev_region(devno, 1);
}

module_init(chr_led_init);
module_exit(chr_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("imysy_22-2022.10.17");

2、设备树版本

led.c

/*
 * @Author: imysy_22
 * @Description: led灯字符型驱动程序设备树版本
 * @Date: 2022-10-16 15:37:01
 * @LastEditTime: 2022-10-18 17:31:31
 * @FilePath: /undefined/home/imysy22/IMX6U/rootfs/home/root/MyDrivers/2_led_chrdev/device_tree_version/led.c
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/mm.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <asm/io.h>

#define DevName "Mychr_led"

//static int gmydev.major = 13;
//static int gmydev.minor = 0;

static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

struct led_dev {
    int major;
    int minor;
    dev_t devno;

    struct cdev mdev;

    struct class *pcls;
    struct device *pdev;

    struct device_node *pnd;
    struct property *proper;
};

struct led_dev gmydev;

/**
 * @description: 初始化、配置好寄存器
 */
static void led_ioremap(struct led_dev gmydev, u32 *RegData);
static void led_on(void);
static void led_off(void);


/**
 * @description: 
 * @param {inode} *pnode
 * @param {file} *pfile     设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
 * @return {*}
 */
static int chr_led_open(struct inode *pnode,struct file *pfile)
{
    pfile->private_data = (void *) (container_of(pnode->i_cdev, struct led_dev, mdev));

    return 0;
}

static int chr_led_release(struct inode *pnode,struct file *pfile)
{

    return 0;
}

static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
{
    int ret = 0;
    char buf[1] = {0};
    unsigned char led_state;

    if((ret = copy_from_user(buf, puser, size)))
    {
        printk("copy_from_user failed...\n");
        return -1;
    }

    led_state = buf[0];

    if(led_state == 0)
    {
        led_on();   //低电平开灯
    }

    if(led_state == 1)
    {
        led_off();  //高电平关灯
    }

    return 1;
}


struct file_operations myops = {
    .owner = THIS_MODULE,
    .open = chr_led_open,
    .release = chr_led_release,
    .write = chr_led_write,
};

static int __init chr_led_init(void)
{
    int ret = 0;
    const char *str;
    u32 RegData[14] = {0};
    unsigned char i = 0;

    /* 获取设备树中的属性数据 */
    
    /* 1、获取设备节点:alpha-led */
    if((gmydev.pnd = of_find_node_by_path("/alpha-led")) == NULL)
    {
        printk("of_find_node_by_path failed...\n");
        return -EINVAL;
    }

    /* 2、获取compatible属性内容 */
    if((gmydev.proper = of_find_property(gmydev.pnd, "atkalpha-led", NULL)) == NULL)
    {
        printk("of_find_property failed...\n");
        //return -1;
    }

    /* 3、获取status属性内容 */
    if((ret = of_property_read_string(gmydev.pnd, "status", &str)) < 0)
    {
        printk("of_property_read_string failed...\n");
        //return -1;
    }

    printk("status = %s\n", str);

    /* 4、获取reg属性内容 */

    if((ret = of_property_read_u32_array(gmydev.pnd, "reg", RegData, 10)) < 0)
    {
        printk("of_property_read_u32_array failed...\n");
        //return -1;
    }

    
    printk("reg data :\n");

    for(i = 0;i < 10; i++)
        printk("%#x ", RegData[i]);

    printk("\n");

    /* static void led_ioremap(void)函数 */
    led_ioremap(gmydev, RegData);
    
    gmydev.major = 13;
    gmydev.minor = 0;
    gmydev.devno = MKDEV(gmydev.major, gmydev.minor);

    ret = register_chrdev_region(gmydev.devno, 1, DevName);
    if(ret)
    {
        ret = alloc_chrdev_region(&gmydev.devno, gmydev.minor, 1, DevName);
        if(ret)
        {
            printk("register failed...\n");
            return -1;
        }

        gmydev.major = MAJOR(gmydev.devno);
        gmydev.minor = MINOR(gmydev.devno);
    }

    cdev_init(&gmydev.mdev, &myops);

    gmydev.mdev.owner = THIS_MODULE;
    cdev_add(&gmydev.mdev, gmydev.devno, 1);

    gmydev.pcls = class_create(THIS_MODULE, DevName);
    if(IS_ERR(gmydev.pcls))
    {
        printk("class_create failed...\n");
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(gmydev.devno, 1);
        
        return -1;
    }

    gmydev.pdev = device_create(gmydev.pcls, NULL, gmydev.devno, NULL, DevName);
    if(gmydev.pdev == NULL)
    {
        printk("device_create failed...\n");
        class_destroy(gmydev.pcls);
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(gmydev.devno, 1);
        
        return -1;
    }

    printk("New character device(%d:%d) added successfully.\n", gmydev.major, gmydev.minor);    

    return 0;
}

static void __exit chr_led_exit(void)
{
    /* 取消映射 */
    iounmap(IMX6U_CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    device_destroy(gmydev.pcls, gmydev.devno);
    class_destroy(gmydev.pcls);

    cdev_del(&gmydev.mdev); 

    unregister_chrdev_region(gmydev.devno, 1);
}

static void led_ioremap(struct led_dev gmydev, u32 *RegData)
{
    unsigned long RegVal = 0;

    /* 1、把寄存器的物理地址映射到虚拟地址 */
#if 1
    IMX6U_CCM_CCGR1 = ioremap(RegData[0], RegData[1]);   //4代表4个字节,4x8=32位寄存器
    SW_MUX_GPIO1_IO03 = ioremap(RegData[2], RegData[3]);
    SW_PAD_GPIO1_IO03 = ioremap(RegData[4], RegData[5]);
    GPIO1_DR = ioremap(RegData[6], RegData[7]);
    GPIO1_GDIR = ioremap(RegData[8], RegData[9]);
#else
    IMX6U_CCM_CCGR1 = of_ioremap(RegData[0], RegData[1]);   //4代表4个字节,4x8=32位寄存器
    SW_MUX_GPIO1_IO03 = of_ioremap(RegData[2], RegData[3]);
    SW_PAD_GPIO1_IO03 = of_ioremap(RegData[4], RegData[5]);
    GPIO1_DR = of_ioremap(RegData[6], RegData[7]);
    GPIO1_GDIR = of_ioremap(RegData[8], RegData[9]);
#endif
    /* 2、开启GPIO1的时钟 */
    RegVal = readl(IMX6U_CCM_CCGR1);
    RegVal &= ~(0x3 << 26);
    RegVal |= (0x3 << 26);
    writel(RegVal, IMX6U_CCM_CCGR1);

    /* 3、把GPIO1_IO3复用成gpio */
    writel(0x5, SW_MUX_GPIO1_IO03);

    /* 4、设置io属性 */
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    /* 5、配置成输出功能 */
    RegVal = readl(GPIO1_GDIR);
    RegVal &= ~(0x1 << 3);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_GDIR);

    /* 6、默认输出高电平,关闭led */
    RegVal = readl(GPIO1_DR);
    RegVal &= ~(0x1 << 3);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_DR);
}

static void led_on(void)
{
    unsigned long RegVal = 0;

    RegVal = readl(GPIO1_DR);
    RegVal &= ~(0x1 << 3);
    writel(RegVal, GPIO1_DR);

}

static void led_off(void)
{
    unsigned long RegVal = 0;

    RegVal = readl(GPIO1_DR);
    RegVal |= (0x1 << 3);
    writel(RegVal, GPIO1_DR);

}

module_init(chr_led_init);
module_exit(chr_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("imysy_22-2022.10.16");

imx6ull-alientek-emmc.dts

需要在 “ / ” 根节点下增加一个“alpha-led”子节点

alpha-led {		/* LED第一代:《设备树版》 */
	#address-cells = <1>;	/* 用于描述子节点的地址信息,即reg属性中,地址信息占几个uint32 */
	#size-cells = <1>;		/* 地址长度占几个u32,单位为u32(4字节) */
	compatible = "atkalpha-led";
	status = "okey";
	reg = <	0X020C406C 0X04 /* CCM_CCGR1_BASE */
			0X020E0068 0X04	/* SW_MUX_GPIO1_IO03_BASE */
			0X020E02F4 0X04	/* SW_PAD_GPIO1_IO03_BASE */
			0X0209C000 0X04	/* GPIO1_DR_BASE */
			0X0209C004 0X04	/* GPIO1_GDIR_BASE */
		>;
};

3、pinctrl、gpio子系统版本

led.c

/*
 * @Author: imysy_22
 * @Description: led灯字符型驱动程序pinctrl_gpio子系统版本
 * @Date: 2022-10-18 23:12:03
 * @LastEditTime: 2022-10-18 23:48:27
 * @FilePath: /undefined/home/imysy22/IMX6U/Linux_Drivers/2_led_chrdev/pinctrl_gpio_version/led.c
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/mm.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <asm/io.h>

#define DevName "gpioled"

struct gpioled_dev {
    int major;
    int minor;
    int led_gpio;   /* led所使用的gpio编号 */
    dev_t devno;

    struct cdev mdev;

    struct class *pcls;
    struct device *pdev;

    struct device_node *pnd;
    struct property *proper;

};

struct gpioled_dev gmydev;

static void led_on(struct file *pfile);
static void led_off(struct file *pfile);

/**
 * @description: 
 * @param {inode} *pnode
 * @param {file} *pfile     设备文件,file结构体有个叫做private_data的成员变量,一般在open的时候将private_data指向设备结构体。
 * @return {*}
 */
static int chr_led_open(struct inode *pnode,struct file *pfile)
{
    pfile->private_data = (void *) (container_of(pnode->i_cdev, struct gpioled_dev, mdev));

    return 0;
}

static int chr_led_release(struct inode *pnode,struct file *pfile)
{

    return 0;
}

static ssize_t chr_led_write(struct file *pfile,const char __user *puser,size_t size,loff_t *p_pos)
{
    int ret = 0;
    char buf[1] = {0};
    unsigned char led_state;

    if((ret = copy_from_user(buf, puser, size)))
    {
        printk("copy_from_user failed...\n");
        return -1;
    }

    led_state = buf[0];

    if(led_state == 0)
    {
        led_on(pfile);   //低电平开灯
    }

    if(led_state == 1)
    {
        led_off(pfile);  //高电平关灯
    }

    return 1;
}


struct file_operations myops = {
    .owner = THIS_MODULE,
    .open = chr_led_open,
    .release = chr_led_release,
    .write = chr_led_write,
};

static int __init chr_led_init(void)
{
    int ret = 0;

    /* 
        获取设备树中的属性数据 
            前面操作设备树节点这几步操作和设备树版本的不一样
    */
    
    /* 1、获取设备节点:gpioled */
    if((gmydev.pnd = of_find_node_by_path("/gpioled")) == NULL)
    {
        printk("of_find_node_by_path failed...\n");
        return -EINVAL;
    }

    /* 2、获取gpio子系统中设备树中的gpio的电气属性,并得到Led所使用的GPIO编号 */
    if((gmydev.led_gpio = of_get_named_gpio(gmydev.pnd, "led-gpio", 0)) < 0)
    {
        printk("of_get_named_gpio failed...\n");
        return -EINVAL;
    }

    printk("led-gpio num = %d\n", gmydev.led_gpio);

    /* 3、设置GPIO1_IO03为输出,并输出高电平,默认熄灭led灯 */
    if((ret = gpio_direction_output(gmydev.led_gpio, 1)) < 0)
    {
        printk("gpio_direction_output failed...\n");
        return -EINVAL;
    }

    printk("**LED0 initialzation successed***\n");
    
    /* 注册设备流程和设备树版本的LED一样 */

    gmydev.major = 14;
    gmydev.minor = 0;
    gmydev.devno = MKDEV(gmydev.major, gmydev.minor);

    ret = register_chrdev_region(gmydev.devno, 1, DevName);
    if(ret)
    {
        ret = alloc_chrdev_region(&gmydev.devno, gmydev.minor, 1, DevName);
        if(ret)
        {
            printk("register failed...\n");
            return -1;
        }

        gmydev.major = MAJOR(gmydev.devno);
        gmydev.minor = MINOR(gmydev.devno);
    }

    cdev_init(&gmydev.mdev, &myops);

    gmydev.mdev.owner = THIS_MODULE;
    cdev_add(&gmydev.mdev, gmydev.devno, 1);

    gmydev.pcls = class_create(THIS_MODULE, DevName);
    if(IS_ERR(gmydev.pcls))
    {
        printk("class_create failed...\n");
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(gmydev.devno, 1);
        
        return -1;
    }

    gmydev.pdev = device_create(gmydev.pcls, NULL, gmydev.devno, NULL, DevName);
    if(gmydev.pdev == NULL)
    {
        printk("device_create failed...\n");
        class_destroy(gmydev.pcls);
        cdev_del(&gmydev.mdev);
        unregister_chrdev_region(gmydev.devno, 1);
        
        return -1;
    }

    printk("New character device(%d:%d) added successfully.\n", gmydev.major, gmydev.minor);    

    return 0;
}

static void __exit chr_led_exit(void)
{
    device_destroy(gmydev.pcls, gmydev.devno);
    class_destroy(gmydev.pcls);

    cdev_del(&gmydev.mdev); 

    unregister_chrdev_region(gmydev.devno, 1);
}

static void led_on(struct file *pfile)
{
    struct gpioled_dev *pmydev = pfile->private_data;

    gpio_set_value(pmydev->led_gpio, 0);

}

static void led_off(struct file *pfile)
{
    struct gpioled_dev *pmydev = pfile->private_data;

    gpio_set_value(pmydev->led_gpio, 1);
}

module_init(chr_led_init);
module_exit(chr_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("imysy_22-2022.10.16");

imx6ull-alientek-emmc.dts

在" / "根节点部的 iomuxc 节点的“imx6ul-evk”子节点下创建一个名为“pinctrl_led”子子节点(套娃)

pinctrl_led: ledgrp {		/* LED第二代:《pinctrl和gpio子系统版》 */
	fsl,pins = <
		MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10B0
	>;
};

其中,MX6UL_PAD_GPIO1_IO03__GPIO1_IO03这个宏的定义在文件arch/arm/boot/dts/imx6ul-pinfunc.h中。

再在" / "根节点部创建一个“gpioled”节点

gpioled {
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "atkalpha-gpioled";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_led>;
	led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
	status = "okey";
};

添加完后,一定不要忘记检查PIN是否正被其他外设使用!!!

在设备树中搜索“ GPIO1_IO03 ”,如果有别的设备节点在也在使用这个PIN的话,屏蔽它

pinctrl_tsc节点是TSC(电阻触摸屏接口)的pinctrl节点,从第480行可以看出,默认情况下GPIO1_IO03作为了TSC外设的PIN。所以我们需要将第480行屏蔽掉!和C语言一样,在要屏蔽的内容前后加上“/*”和“*/”符号即可。实际上我们目前并不会用到这个节点,所以我干脆把它的PIN全屏蔽了。

编译设备树

 进入Linux顶层源码目录,生成imx6ull-alientek-emmc.dts并重启开发板时加载这个新的设备树

正确的现象:/proc/device-tree目录下生成了我们的“ gpioled ”文件,进入文件后会自动生成我们之前在设备树里 “gpioled” 节点中的'#address-cells'  compatible  led-gpio  name  pinctrl-0  pinctrl-names  '#size-cells'  status属性

三个字符驱动版本都通用的LED点灯应用程序App.c

第32行的open函数需根据不同的驱动程序申请的字符设备名字不同进行修改

/*
 * @Author: imysy_22
 * @Description: 三个字符驱动版本都统用的LED点灯应用程序
 * @Date: 2022-10-17 12:46:27
 * @LastEditTime: 2022-10-17 14:15:19
 * @FilePath: /undefined/home/imysy22/IMX6U/Linux_Drivers/2_led_chrdev/common_version/App.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    int fd = -1;
    int ret;
    unsigned char led_state[1];
    
    /* 低电平点灯,高电平熄灭 */
    if(argc != 2)
    {
        printf("*********************************************\n");
        printf("* Please input : ./App 0-led_on / 1-led_off *\n");
        printf("*********************************************\n");

        return -1;
    }

    if((fd = open("/dev/Mychr_led", O_WRONLY)) < 0)
    {
        perror("open");
        return -1;
    }

    printf("***open success***\n\n\n");

    led_state[0] = atoi(argv[1]);

    /* 如果是点灯 */
    if(led_state[0] == 0)
    {
        if((ret = write(fd, led_state, 1)) < 0)
        {
            perror("write");
        }

        printf("Led is ON!\n");
    }
    else if(led_state[0] == 1)
    {
        if((ret = write(fd, led_state, 1)) < 0)
        {
            perror("write");
        }

        printf("Led is OFF!\n");
    }
    else
    {
        printf("You can only input 0 or 1!\n");
        close(fd);
        return -1;
    }
    
    close(fd);

    return 0;
}

Makefile工程管理器源码


CONFIG_MODULE_SIG=n		#加了这一行就不会报错:signature and/or required key missing - tainting kernel
ifeq ($(KERNELRELEASE),)

ifeq ($(ARCH),arm)
KERNELDIR ?= /home/imysy22/IMX6U/linux-imx-rel_imx_4.1.15_2.1.0_ga
CC = /home/imysy22/LicheePi/buildroot-2017.08.1/output/host/bin/arm-linux-gnueabihf-gcc
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
endif
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules INSTALL_MOD_PATH=$(ROOTFS) modules_install
clean:
	rm -rf *.o *.ko .*.cmd *.mod.* modules.order Module.symvers .tmp_versions

else

obj-m += led.o

endif

实验现象

三个模块的实验现象都差不多,只不过insmod led.ko后printk打印的内容不一样而已

# ./App 0    //低电平点亮

# ./App 1    //高电平熄灭

总结

     ☆ * .  ☆      
   . ∧_∧   ∩ * ☆      
* ☆ ( `∀‘)/ .      
 . ⊂   ノ* ☆          
☆ * (つ ノ .☆
   (ノ

哈哈用超级复杂的方法点亮led灯,虽然这个实验现象非常low,但是用三种不同的驱动框架来点灯还是着实学不到了不少啊!!! 

猜你喜欢

转载自blog.csdn.net/imysy_22_/article/details/127399319