使用pinctrl 和 gpio 子系统的字符设备驱动

一、 修改设备树文件

打开 imx6ull-alientekemmc.dts,在根节点“/”下创建 LED 灯节点,节点名为“gpioled”。

/*yqh2021/5/12*/
	gpioled{
		compatible = "alientek,gpioled";
		pinctrl-name = "default";
		pinctrl-0 = <&pinctrl_gpioled>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

打开 imx6ull-alientekemmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点。

/*yqh2021/5/12*/
		pinctrl_gpioled: ledgrp{
			fsl,pihs = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10b0
			>;
		};

在 imx6ull-alientek-emmc.dts 中找到如下内容,屏蔽gpio1_3

pinctrl_tsc: tscgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO01__GPIO1_IO01	0xb0
				MX6UL_PAD_GPIO1_IO02__GPIO1_IO02	0xb0
				/*YQH 2021/5/12*/
				/*MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0xb0*/
				MX6UL_PAD_GPIO1_IO04__GPIO1_IO04	0xb0
			>;
		};
&tsc {
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_tsc>;
	/*YQH 2021/5/12*/
	/*xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;*/
	measure-delay-time = <0xffff>;
	pre-charge-time = <0xfff>;
	status = "okay";
};

设备树编写完成以后使用“make dtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-alientek-emmc.dtb 文件启动 Linux 系统。
使用新生成的设备树 进入查看节点是否存在。

sudo cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /home/yqh/linux/tftpboot/ -f
cd /proc/device-tree/
ls

二、 LED 灯驱动程序编写

/*参照linux内核去写驱动*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>                        //printk需要包含的头文件
#include <linux/init.h>
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

/*注册字符设备个数*/
#define GPIOLED_CNT     1        
/*注册字符设备名字*/
#define GPIOLED_NAME    "gpioled"
#define LEDOFF 				0			/* 关灯 */
#define LEDON 				1			/* 开灯 */

/*创建gpioled设备结构体*/
struct gpioled_dev{
    dev_t devid;             //设备号
    int major;
    int minor;
    struct cdev cdev;        //字符设备号
    struct class *class;     //类
    struct device *device;   //类下创建设备
    struct device_node *nd;  //设备节点
    int led_gpio;            //gpio编号
};
/*定义结构体变量*/
struct gpioled_dev gpioled;

static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data=&gpioled;        
    //在 open 函数里面设置好私有数据以后,在 write、read、close 等函数中直接读取 private_data即可得到设备结构体
    return 0;
}
static ssize_t led_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
    int retvalue;
	unsigned char databuf[1];
	unsigned char ledstat;
	struct gpioled_dev *dev = filp->private_data;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	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;
}
static int led_release(struct inode *inode, struct file *filp)
{
    /*
    struct dtsled_dev *dev=(struct dtsled_dev*)filp->private_data;  //结构体强制转换
    dev->device                                                           //这样就可以通过dev进行访问私有数据里的成员变量
    */
    return 0;
}
/*定义字符驱动操作集*/
static struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    
    .open = led_open,
    //.read = led_read,
    .write = led_write,
    .release = led_release,
    
};
/*驱动入口函数*/
static int __init led_init(void)
{
    int ret=0;            //用来接收错误
    /*注册字符设备驱动*/
    gpioled.major = 0;    //由系统分配
    if(gpioled.major)     //给定主设备号的情况
    {
        gpioled.devid=MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devid,GPIOLED_CNT,GPIOLED_NAME);
    }else                 //没有给定设备号
    {
        ret=alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);   //申请设备号
        gpioled.major=MAJOR(gpioled.devid);    //获取分配的主设备号
        gpioled.minor=MINOR(gpioled.devid);    //获取分配的次设备号
    }
    if(ret<0)
    {
        printk("dtsled chrdev_region error!\r\n");
        goto failed_devid;
    }
    printk("gpioled major=%d,minor=%d\r\n",gpioled.major,gpioled.minor);
    /*初始化cdev*/
    gpioled.cdev.owner = THIS_MODULE;
    /*初始化字符设备需要定义:定义字符设备,定义操作函数*/
    cdev_init(&gpioled.cdev,&gpioled_fops);
    /*添加cdev 字符设备添加进内核*/
    ret=cdev_add(&gpioled.cdev,gpioled.devid,GPIOLED_CNT);
    if(ret<0)                                                    //添加失败在goto语句里释放设备号
    {
        goto failed_cdev;
    }
    /*创建类*/
    /*自动创建设备节点通过class和device*/
    gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);
    if (IS_ERR(gpioled.class)) {
        return PTR_ERR(gpioled.class);
        goto failed_class;
    }
    /*创建设备*/
    gpioled.device = device_create(gpioled.class, NULL,gpioled.devid, NULL, GPIOLED_NAME);
    //1.类2.父设备为NULL3.设备号4.设备使用数据一般为NULL5.设备名
    if (IS_ERR(gpioled.device)) {
        return PTR_ERR(gpioled.device);
        goto failed_device;
    }
    /*获取设备节点从设备树*/
    gpioled.nd=of_find_node_by_path("/gpioled");
    if(gpioled.nd==NULL) {
        ret=-EINVAL;
        goto fail_findnode;
    }
    /*获取设备树中的 gpio 属性,得到LED所使用的LED编号*/
    gpioled.led_gpio=of_get_named_gpio(gpioled.nd, "led-gpio", 0);
    if(gpioled.led_gpio < 0) {
        printk("can't get led-gpio");
        ret=-EINVAL;
        goto fail_findnode;
    }
    printk("led-gpio num = %d\r\n", gpioled.led_gpio);
    /*申请IO 如果申请失败说明IO被占用*/
    if(ret){
        printk("Failed to request the led gpio\r\n");
        ret=-EINVAL;
        goto fail_findnode;
    }
    /*使用IO 设置为输出 并且输出高电平,默认关闭LED灯*/
    ret = gpio_direction_output(gpioled.led_gpio, 1);
    if(ret < 0) {
        printk("can't set gpio!\r\n");
        goto fail_setoutput;
    }
    /*输出低电平点灯*/
    //gpio_set_value(gpioled.led_gpio,0);

    return 0;
fail_setoutput:
    gpio_free(gpioled.led_gpio);
fail_findnode:
    device_destroy(gpioled.class,dtsled.devid);
failed_device:
    class_destroy(gpioled.class);
failed_class:
    cdev_del(&gpioled.cdev);
failed_cdev:
    unregister_chrdev_region(gpioled.devid, DTSLED_CNT);
failed_devid:
    return ret;//没有申请什么所以返回一个返回值不需要释放操作
}
/*驱动出口函数*/
static void __exit led_exit(void)
{
    /*注销字符设备驱动*/
    cdev_del(&gpioled.cdev);
    /*释放设备号*/
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
    /*注销设备*/
    device_destroy(gpioled.class, gpioled.devid);
    /*注销类*/
    class_destroy(gpioled.class);
    /*释放注册IO*/
    gpio_free(gpioled.led_gpio);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YQH");

三、makefile

KERNELDIR := /home/yqh/linux/IMX6ULL/linux_kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga  
CURRENT_PATH := $(shell pwd)
obj-m :=gpioled.o
build: kernel_modules
kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

make -j32
编译成功以后就会生成一个名为“gpioled.ko”的驱动模块文件

sudo cp gpioled.ko /home/yqh/linux/nfs/rootfs/lib/modules/4.1.15/ -f

四、应用层代码

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#define LEDOFF 0
#define LEDON 1
/*argc应用程序参数个数
*argv[]:具体的参数内容,字符串形式
*./ledAPP <filename> <0:1> 1表示关灯1表示开灯
*./ledAPP /dev/dtsled 0 关灯
*./ledAPP /dev/dtsled 1 开灯
*1执行读,在应用层打印出来读得数据 2执行写,通过调用chrdevbase_write离得打印语句而打印出来
*/
int main(int argc,char *argv[])
{
    int fd;
    int retvalue;
    char *filename;
    unsigned char databuf[1];
    if(argc !=3)
    {
        printf("Error Usage\r\n");
        return -1;
    }
    filename=argv[1];
    fd=open(filename,O_RDWR);
    if(fd<0)
    {
        printf("file %s open failed!\r\n",filename);
        return -1;
    }
    databuf[0]=atoi(argv[2]);                       //将字符转化为数字
    retvalue=write(fd,databuf,sizeof(databuf));
    if(retvalue<0)
    {
        printf("LED Control Failed\r\n");
        //colse(fd);
        return -1;
    }
    
    retvalue=close(fd);
    if(retvalue<0)
    {
        printf("file %s close failed!\r\n",filename);
        return -1;
    }
    
    return 0;
}

运行测试

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

编译成功以后就会生成 ledApp 这个应用程序。
将编译出来的 gpioled.ko 和 ledApp 这两个文件拷贝到 rootfs/lib/modules/4.1.15目录中,重启开发板,进入到目录 lib/modules/4.1.15 中,输入如下命令加载 gpioled.ko 驱动
模块:

cd lib/modules/4.1.15/
depmod
modprobe gpioled.ko
./ledApp /dev/gpioled 1 //打开 LED 灯
./ledApp /dev/gpioled 0 //关闭 LED 灯
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值