• i.MX 6ULL 驱动开发 五:LED 驱动


    一、原理分析

    i.MX6ULL裸机开发 一:LED_lqonlylove的博客-CSDN博客

    二、pinctrl 子系统和gpio子系统基本概念

    Linux 驱动开发 二十一:pinctrl子系统和gpio子系统基本概念_lqonlylove的博客-CSDN博客_设备树gpio

    三、linux 源码对于 pinctrl 子系统的说明

    Linux 驱动开发 十九:《pinctrl-bindings.txt》翻译_lqonlylove的博客-CSDN博客

    Linux 驱动开发 五十五:《fsl,imx-pinctrl.txt》翻译_lqonlylove的博客-CSDN博客

    四、linux 源码对于 gpio 子系统的说明

    Linux 驱动开发 五十六:《gpio.txt》翻译_lqonlylove的博客-CSDN博客

    Linux 驱动开发 三十八:《fsl-imx-gpio.txt》翻译_lqonlylove的博客-CSDN博客

    五、修改设备树

    1、确认使用引脚

    通过原理图分析 LED 灯使用 GPIO1_IO03 引脚进行控制。

    2、添加 GPIO1_IO03 引脚 pinctrl 子系统配置

    1、确认 GPIO1_IO03 是否被使用。

    2、根据 linux 内核中描述以及NXP提供设备配置添加 GPIO1_IO03 引脚配置,配置内容如下:

    pinctrl_led: ledgrp {
        fsl,pins = <
        	MX6UL_PAD_GPIO1_IO03__GPIO1_IO03        0x10B0 /* LED0 */
        >;
    };
    

    MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 宏定义在 linux-imx-4.1.15\arch\arm\boot\dts\imx6ul-pinfunc.h 文件中。imx6ul-pinfunc.h 文件被设备源码文件引用。

    3、添加 GPIO1_IO03 引脚 gpio 子系统配置

    1、确认 GPIO1_IO03 是否被使用。

    2、根据 linux 内核中描述以及NXP提供设备配置添加 GPIO1_IO03 引脚配置,配置内容如下:

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

    led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW> 表示 GPIO1_IO03 引脚被 gpio 子系统设置为低电平。

    4、测试

    1、编译设备树

    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ make dtbs
      CHK     include/config/kernel.release
      CHK     include/generated/uapi/linux/version.h
      CHK     include/generated/utsrelease.h
    make[1]: 'include/generated/mach-types.h' is up to date.
      CHK     include/generated/bounds.h
      CHK     include/generated/asm-offsets.h
      CALL    scripts/checksyscalls.sh
      DTC     arch/arm/boot/dts/imx6ull-alientek-emmc.dtb
      DTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$
    

    2、拷贝编译成功的设备树文件

    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
    total 11544
    -rwxrwxr-x 1 onlylove onlylove 5916896 Sep 10 21:40 zImage
    -rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /home/onlylove/my/tftp
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
    total 11584
    -rw-rw-r-- 1 onlylove onlylove   39272 Sep 17 01:47 imx6ull-alientek-emmc.dtb
    -rwxrwxr-x 1 onlylove onlylove 5916896 Sep 10 21:40 zImage
    -rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
    onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$
    

    3、启动 linux 查看设备树解析是否成功

    /sys/firmware/devicetree/base # pwd
    /proc/device-tree
    /sys/firmware/devicetree/base # ls
    #address-cells                 interrupt-controller@00a01000
    #size-cells                    key
    aliases                        memory
    alphaled                       model
    backlight                      name
    beep                           pxp_v4l2
    chosen                         regulators
    clocks                         reserved-memory
    compatible                     sii902x-reset
    cpus                           soc
    gpio-keys                      sound
    gpioled                        spi4
    /sys/firmware/devicetree/base # cd gpioled/
    /sys/firmware/devicetree/base/gpioled # ls -l
    total 0
    -r--r--r--    1 root     0                4 Jan  1 00:17 #address-cells
    -r--r--r--    1 root     0                4 Jan  1 00:17 #size-cells
    -r--r--r--    1 root     0               11 Jan  1 00:17 compatible
    -r--r--r--    1 root     0               12 Jan  1 00:17 led-gpio
    -r--r--r--    1 root     0                8 Jan  1 00:17 name
    -r--r--r--    1 root     0                4 Jan  1 00:17 pinctrl-0
    -r--r--r--    1 root     0                8 Jan  1 00:17 pinctrl-names
    -r--r--r--    1 root     0                5 Jan  1 00:17 status
    /sys/firmware/devicetree/base/gpioled # cat compatible
    lq-gpioled/sys/firmware/devicetree/base/gpioled #
    /sys/firmware/devicetree/base/gpioled # cat name
    gpioled/sys/firmware/devicetree/base/gpioled #
    /sys/firmware/devicetree/base/gpioled #
    

    六、驱动编写

    1、makefile

    KERNELDIR := /home/onlylove/my/linux/linux-imx-4.1.15
    CURRENT_PATH := $(shell pwd)
    obj-m := led.o
    
    build: kernel_modules
    
    kernel_modules:
    	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
    clean:
    	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
    

    2、led.c

    #include "linux/init.h"
    #include "linux/module.h"
    #include "linux/kdev_t.h"
    #include "linux/fs.h"
    #include "linux/cdev.h"
    #include "linux/device.h"
    #include 
    #include 
    #include 
    #include 
    
    #define NEWCHRDEV_MAJOR 0   		/* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
    #define NEWCHRDEV_MINOR 0   		/* 次设备号 */
    #define NEWCHRDEV_COUNT 1   		/* 设备号个数 */
    #define NEWCHRDEV_NAME  "lq-led" /* 名子 */
    
    #define LEDOFF 				0			/* 关灯 */
    #define LEDON 				1			/* 开灯 */
    
    typedef struct{
        struct cdev dev;        /* cdev 结构体 */
        int major;              /* 主设备号 */
        int minor;              /* 次设备号 */
        dev_t devid;            /* 设备号 */
        struct class *class;    /* 类 */
        struct device *device;  /* 设备 */
        struct device_node	*nd; /* 设备节点 */
    	int led_gpio;			/* led所使用的GPIO编号		*/
    }newchrdev_t;
    
    newchrdev_t newchrdev;
    
    int led_init(void)
    {
        int ret = 0;
        /***** 处理设备树 *****/
        /* 1、获取设备节点:gpioled */
    	newchrdev.nd = of_find_node_by_path("/gpioled");
    	if(newchrdev.nd == NULL) {
    		printk("gpioled node not find!\r\n");
    		return 1;
    	} else {
    		printk("gpioled node find!\r\n");
    	}
        /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */
    	newchrdev.led_gpio = of_get_named_gpio(newchrdev.nd, "led-gpio", 0);
    	if(newchrdev.led_gpio < 0) {
    		printk("can't get led-gpio");
    		return 2;
    	}
    	printk("led-gpio num = %d\r\n", newchrdev.led_gpio);
        /***** 使用gpio子系统设置引脚 *****/
        /* 1、向 gpio 子系统申请 GPIO 管脚 */
        ret = gpio_request(newchrdev.led_gpio,"led-gpio");
        if(ret){
            printk("can't request gpio!\r\n");
        }
        /* 2、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */
    	ret = gpio_direction_output(newchrdev.led_gpio, 1);
    	if(ret < 0) {
    		printk("can't set gpio!\r\n");
            goto led_init_error;
    	}
        /* 3、设置led默认状态(默认点灯) */
        gpio_set_value(newchrdev.led_gpio,0);
    
        return 0;
    led_init_error:
        gpio_free(newchrdev.led_gpio);
        return -1;
    }
    
    int led_exit(void)
    {
        /* 1、设置led退出状态(关灯) */
        gpio_set_value(newchrdev.led_gpio,1);
        /* 2、释放从gpio子系统申请的GPIO管脚 */
        gpio_free(newchrdev.led_gpio);
        return 0;
    }
    
    /*
     * @description		: 打开设备
     * @param - inode 	: 传递给驱动的inode
     * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
     * 					  一般在open的时候将private_data指向设备结构体。
     * @return 			: 0 成功;其他 失败
     */
    static int led_open(struct inode *inode, struct file *filp)
    {
        printk("led_open!\r\n");
        filp->private_data = &newchrdev; /* 设置私有数据 */
        return 0;
    }
    
    /*
     * @description		: 从设备读取数据 
     * @param - filp 	: 要打开的设备文件(文件描述符)
     * @param - buf 	: 返回给用户空间的数据缓冲区
     * @param - cnt 	: 要读取的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 读取的字节数,如果为负值,表示读取失败
     */
    static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
    {
        printk("led_read!\r\n");
    	return 0;
    }
    
    /*
     * @description		: 向设备写数据 
     * @param - filp 	: 设备文件,表示打开的文件描述符
     * @param - buf 	: 要写给设备写入的数据
     * @param - cnt 	: 要写入的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 写入的字节数,如果为负值,表示写入失败
     */
    static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
    {
        long retvalue = 0;
    	unsigned char databuf[1];
    	unsigned char ledstat;
        newchrdev_t *dev = filp->private_data;
    
        printk("led_write!\r\n");
        retvalue = copy_from_user(databuf, buf, cnt);
    	if(retvalue < 0) {
    		printk("kernel write failed!\r\n");
    		return -EFAULT;
    	}
        printk("databuf = %d\r\n",databuf[0]);
    	ledstat = databuf[0];		/* 获取状态值 */
        if(ledstat == LEDON) {	
    		gpio_set_value(dev->led_gpio, 0);	/* 打开LED灯 */
    	} else if(ledstat == LEDOFF) {
    		gpio_set_value(dev->led_gpio, 1);	/* 关闭LED灯 */
    	}
    
        return 0;
    }
    
    /*
     * @description		: 关闭/释放设备
     * @param - filp 	: 要关闭的设备文件(文件描述符)
     * @return 			: 0 成功;其他 失败
     */
    static int led_release(struct inode *inode, struct file *filp)
    {
        printk("led_release!\r\n");
    	return 0;
    }
    
    static const struct file_operations ledops = {
        .owner   = THIS_MODULE,
        .open = led_open,
    	.read = led_read,
    	.write = led_write,
    	.release = led_release,
    };
    
    /* 驱动入口函数 */
    static int __init newchrdev_init(void)
    {
        /* 驱动入口函数具体内容 */
        /* 1、字符设备号分配 */
        int ret;
        newchrdev.major = NEWCHRDEV_MAJOR;
        if(newchrdev.major){
            newchrdev.minor = NEWCHRDEV_MINOR;
            newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
            ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
        }else{
            ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
            newchrdev.major = MAJOR(newchrdev.devid);
            newchrdev.minor = MINOR(newchrdev.devid);
        }
        if(ret < 0){
            printk("newchrdev xxx_chrdev_region failed!\r\n");
            goto newchrdev_chrdev_region_failed;
        }
        printk("newchrdev major=%d,minor=%d\r\n",newchrdev.major,newchrdev.minor);
    
        /* 2、注册字符设备 */
        newchrdev.dev.owner = THIS_MODULE;
        cdev_init(&newchrdev.dev,&ledops);
        ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
        if(ret < 0){
            printk("newchrdev cdev_add failed!\r\n");
            goto newchrdev_cdev_add_failed;
        }
    
        /* 3、创建类 */
        newchrdev.class = class_create(THIS_MODULE, NEWCHRDEV_NAME);
        if(IS_ERR(newchrdev.class)) {
            printk("newchrdev class_create failed!\r\n");
            goto newchrdev_class_create_failed;
        }
    
        /* 4、创建设备 */
        newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
        if(IS_ERR(newchrdev.device)){
            printk("newchrdev device_create failed!\r\n");
            goto neschrdev_device_creat_failed;
        }
    
        /* led 初始化 */
        led_init();
    
        return 0;
    neschrdev_device_creat_failed:
        class_destroy(newchrdev.class);
    newchrdev_class_create_failed:
        cdev_del(&newchrdev.dev);
    newchrdev_cdev_add_failed:
        unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
    
    newchrdev_chrdev_region_failed:   /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
        return ret;
    }
    
    /* 驱动卸载函数 */
    static void __exit newchrdev_exit(void)
    {
        /* led 操作 */
        led_exit();
        /* 驱动卸载函数具体内容 */
        /* 4、删除设备 */
        device_destroy(newchrdev.class,newchrdev.devid);
        /* 3、删除类 */
        class_destroy(newchrdev.class);
        /* 2、注销字符设备 */
        cdev_del(&newchrdev.dev);
        /* 1、释放设备号 */
        unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
    }
    
    module_init(newchrdev_init);
    module_exit(newchrdev_exit);
    
    MODULE_LICENSE("GPL");
    

    七、应用程序编写

    1、makefile

    led_app:
    	arm-linux-gnueabihf-gcc led_app.c -o led_app
    
    clean:
    	rm -rf led_app
    

    2、led_app.c

    #include 
    #include 
    #include 
    #include "stdio.h"
    #include "stdlib.h"
    
    int main(int argc, char *argv[])
    {
        int fd = 0, retvalue = 0;
        char writebuf[1] = {0};
        unsigned int connect = 0;
        if(argc != 3){
    		printf("Error Usage!\r\n");
    		return -1;
    	}
        writebuf[0] = atoi(argv[2]);
        fd = open(argv[1],O_RDWR);
        if(fd < 0){
            printf("Can't open file %s\r\n", argv[1]);
            return -1;
        }
        retvalue = write(fd, writebuf, sizeof(writebuf));
        close(fd);
        return 0;
    }
    

    八、测试

    / # ls
    bin            include        minicom.log    proc           tmp
    dev            led.ko         mnt            root           usr
    drivers        led_app        music          sbin           var
    etc            lib            newchrdev.ko   share          video
    home           linuxrc        newchrdev_app  sys
    / # insmod led.ko
    newchrdev major=248,minor=0
    gpioled node find!
    led-gpio num = 3
    / # ls -l /dev/lq-led
    crw-rw----    1 root     0         248,   0 Jan  1 02:50 /dev/lq-led
    / # rmmod led.ko
    / # ls -l /dev/lq-led
    ls: /dev/lq-led: No such file or directory
    / # insmod led.ko
    newchrdev major=248,minor=0
    gpioled node find!
    led-gpio num = 3
    / # ls -l /dev/lq-led
    crw-rw----    1 root     0         248,   0 Jan  1 02:51 /dev/lq-led
    / # rmmod led.ko
    / # ls -l /dev/lq-led
    ls: /dev/lq-led: No such file or directory
    / #
    / # insmod led.ko
    newchrdev major=248,minor=0
    gpioled node find!
    led-gpio num = 3
    / # ./led_app /dev/lq-led 0
    led_open!
    led_write!
    led_release!
    / # ./led_app /dev/lq-led 1
    led_open!
    led_write!
    led_release!
    / # ./led_app /dev/lq-led 0
    led_open!
    led_write!
    led_release!
    / # ./led_app /dev/lq-led 1
    led_open!
    led_write!
    led_release!
    / # rmmod led.ko
    / # ls -l /dev/lq-led
    ls: /dev/lq-led: No such file or directory
    / #
    

    通过测试,led 灯可以正常被点亮和关闭。

  • 相关阅读:
    python毕业设计作品基于django框架个人博客系统毕设成品(7)中期检查报告
    Ribbon负载均衡
    【黑马-SpringCloud技术栈】【05】Nacos配置中心_搭建Nacos集群
    【Hack The Box】linux练习-- Meta
    抄写Linux源码(Day19:读取硬盘前的准备工作有哪些?)
    软件测试常见术语和名词解释
    el-tabs切换按钮定位不准确,部分内容被遮挡(前端vue实战踩坑记录)
    Xilinx FPGA 编程技巧之常用时序约束
    SS命令使用介绍
    C#-懒汉单例创建
  • 原文地址:https://blog.csdn.net/OnlyLove_/article/details/126941388