设备树与LED驱动

目录

 

一、工程环境

修改makefile 

​编辑 detled.c文件

 修改设备树文件

 编译复制查看

二、驱动框架和编译

1、 注册字符设备、添加字符设备

2、添加自动创建设备节点、设置私有数据

编译验证 

 3、获取设备树属性内容

 ①获取status属性数据 

编译验证

 ②、获取compatible属性数据

③、获取reg属性数据

编译验证

 三、添加led灯驱动

编译验证 

of_iomap函数


一、工程环境

修改makefile 

 detled.c文件

 修改设备树文件

将使用到的外设添加进设备树,这里继续用led为例

设备节点添加到比较明显的地方,也就是一级节点

 编译复制查看

  把dtb文件复制到tftpboot目录下,启动开发板,检查设备属性文件

 添加的属性都存在,下面开始编写驱动

二、驱动框架和编译

1、 注册字符设备、添加字符设备

....

#define DTSLED_CNT  1   /*设备号个数*/
#define DTSLED_NAME "dtsled"    /*设备号名字*/

/*dtsled设备结构体*/
struct dtsled_dev{
    dev_t devid;    /*设备号*/
    struct cdev cdev; /*字符设备*/
    int major;      /*主设备号*/
    int minor;      /*次设备号*/
}dtsled; /*设备*/
/*字符设备操作集*/
static const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE, 
    
};

/*入口函数*/
static int __init dtsled_init(void){
    int ret =0;
    /*注册字符设备*/
    /*1、申请设备号*/
    dtsled.major = 0;   /*设备号由内涵分配*/
    if(dtsled.major){   /*定义了设备号*/
        dtsled.devid = MKDEV(dtsled.major,0);/*构建设备号*/
        /*分配指定的设备编号*/
        ret = register_chrdev_region(dtsled.devid,DTSLED_CNT,DTSLED_NAME);
    }else{  /*没有给定设备号*/
        /*申请设备号*/
        ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_CNT,DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);/*提取主设备号*/
        dtsled.minor = MINOR(dtsled.devid);/*提取次设备号*/
    }
    if(ret < 0){
        goto fail_devid;
    }
    /*2、添加字符设备*/
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev,&dtsled_fops);/*初始化的 cdev 结构体变量*/
    ret = cdev_add(&dtsled.cdev,dtsled.devid,DTSLED_CNT);/*添加字符设备*/
    if(ret < 0){
        goto fail_cdev;
    }
    

    printk("dtsled_init");
    return 0;
fail_cdev:/*假如注册设备号失败就注销释放*/
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
fail_devid:
    return ret;
}
/*出口函数*/
static void __exit dtsled_exit(void){

    /*删除字符设备*/
    cdev_del(&dtsled.cdev);
    /*注销释放设备号*/
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
}
....

2、添加自动创建设备节点、设置私有数据

/*dtsled设备结构体*/
struct dtsled_dev{
    dev_t devid;    /*设备号*/
    struct cdev cdev; /*字符设备*/
    struct class *class; /*类*/
    struct device *device; /*设备*/
    int major;      /*主设备号*/
    int minor;      /*次设备号*/
}dtsled; /*设备*/
static int dtsled_open(struct inode *inode, struct file *filp){
    filp->private_data = &dtsled;/*设置私有数据*/
    return 0;
}
static ssize_t dtsled_write(struct file *filp, const char __user *buf, 
                        size_t cnt, loff_t *offt){
    /*取出私有数据*/
    struct dtsled_dev *dev =(struct dtsled_dev *)filp->private_data;
    return 0;
}
static int dtsled_release(struct inode *inode, struct file *filp){
    /*取出私有数据*/
    struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
    return 0;
}
/*字符设备操作集*/
static const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE, 
    .write = dtsled_write,
    .open = dtsled_open,
    .release = dtsled_release,
};

/*入口函数*/
static int __init dtsled_init(void){
    int ret =0;
    /*注册字符设备*/
    /*1、申请设备号*/
    dtsled.major = 0;   /*设备号由内涵分配*/
    if(dtsled.major){   /*定义了设备号*/
        dtsled.devid = MKDEV(dtsled.major,0);/*构建设备号*/
        /*分配指定的设备编号*/
        ret = register_chrdev_region(dtsled.devid,DTSLED_CNT,DTSLED_NAME);
    }else{  /*没有给定设备号*/
        /*申请设备号*/
        ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_CNT,DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);/*提取主设备号*/
        dtsled.minor = MINOR(dtsled.devid);/*提取次设备号*/
    }
    if(ret < 0){
        goto fail_devid;
    }
    /*2、添加字符设备*/
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev,&dtsled_fops);/*初始化的 cdev 结构体变量*/
    ret = cdev_add(&dtsled.cdev,dtsled.devid,DTSLED_CNT);/*添加字符设备*/
    if(ret < 0){
        goto fail_cdev;
    }
    /*3、自动创建设备节点*/
    dtsled.class = class_create(THIS_MODULE,DTSLED_NAME);
    if(IS_ERR(dtsled.class)){
        ret = PTR_ERR(dtsled.class);
        goto fail_class;
    }
    dtsled.device = device_create(dtsled.class,NULL,dtsled.devid,NULL,DTSLED_NAME);
    if(IS_ERR(dtsled.device)){
        ret = PTR_ERR(dtsled.device);
        goto fail_device;
    }

    printk("dtsled_init");
    return 0;
fail_device:
    class_destroy(dtsled.class);
fail_class:
    cdev_del(&dtsled.cdev);
fail_cdev:
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
fail_devid:
    return ret;
}
/*出口函数*/
static void __exit dtsled_exit(void){

    /*删除字符设备*/
    cdev_del(&dtsled.cdev);
    /*注销释放设备号*/
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
    /*摧毁设备*/
    device_destroy(dtsled.class,dtsled.devid);
    /*摧毁类*/
    class_destroy(dtsled.class);
}

到这就完成基本的驱动框架

编译验证 

 cat /proc/devices

 3、获取设备树属性内容

 ①获取status属性数据 

在入口函数在后面添加 

 /* 获取设备树属性内容*/
    dtsled.nd = of_find_node_by_path("/myled");/*从路径找节点*/
    if(dtsled.nd == NULL){
        ret = -EINVAL;
        goto fail_findnd;
    } 
     /*获取属性*/
    ret = of_property_read_string(dtsled.nd,"status",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("status=%s\r\n",str);
    }
printk("dtsled_init\r\n");
    return 0;
fail_rs:
fail_findnd:
    device_destroy(dtsled.class,dtsled.devid);

编译验证

 ②、获取compatible属性数据

   ret = of_property_read_string(dtsled.nd,"compatible",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("compatible=%s\r\n",str);
    }
    ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,10);
    if(ret < 0){
        goto fail_rs;

③、获取reg属性数据

    ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,10);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("reg data:\r\n");
        for(i=0; i<10;i++){
            printk("%#X ",regdata[i]);
        }
        printk("\r\n");
    }
 printk("dtsled_init\r\n");
    return 0;
fail_rs:
fail_findnd:
    device_destroy(dtsled.class,dtsled.devid);

编译验证

 三、添加led灯驱动

#define DTSLED_CNT  1   /*设备号个数*/
#define DTSLED_NAME "dtsled"    /*设备号名字*/

#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 */

/* 映射后的寄存器虚拟地址指针 */
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;

/*dtsled设备结构体*/
struct dtsled_dev{
    dev_t devid;    /*设备号*/
    struct cdev cdev; /*字符设备*/
    struct class *class; /*类*/
    struct device *device; /*设备*/
    int major;      /*主设备号*/
    int minor;      /*次设备号*/
    struct device_node *nd; /*设备节点*/
}dtsled; /*设备*/
/*LED打开/g关闭*/
void led_switch(u8 sta)
{
	u32 val = 0;
	if(sta == LEDON) {
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);	
		writel(val, GPIO1_DR);
	}else if(sta == LEDOFF) {
		val = readl(GPIO1_DR);
		val|= (1 << 3);	
		writel(val, GPIO1_DR);
	}	
}
static int dtsled_open(struct inode *inode, struct file *filp){
    filp->private_data = &dtsled;/*设置私有数据*/
    return 0;
}
static ssize_t dtsled_write(struct file *filp, const char __user *buf, 
                        size_t cnt, loff_t *offt){
    /*取出私有数据*/
    struct dtsled_dev *dev =(struct dtsled_dev *)filp->private_data;
    int retvalue;
	unsigned char databuf[1];
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		return -EFAULT;
	}
    led_switch(databuf[0]);
    return 0;
}
static int dtsled_release(struct inode *inode, struct file *filp){
    /*取出私有数据*/
    struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
    return 0;
}
/*字符设备操作集*/
static const struct file_operations dtsled_fops = {
    .owner = THIS_MODULE, 
    .write = dtsled_write,
    .open = dtsled_open,
    .release = dtsled_release,
};

/*入口函数*/
static int __init dtsled_init(void){
    int ret =0;
    const char *str;
    u8 i=0;
    u32 regdata[10];
    unsigned int val=0;
    /*注册字符设备*/
    /*1、申请设备号*/
    dtsled.major = 0;   /*设备号由内核分配*/
    if(dtsled.major){   /*定义了设备号*/
        dtsled.devid = MKDEV(dtsled.major,0);/*构建设备号*/
        /*分配指定的设备编号*/
        ret = register_chrdev_region(dtsled.devid,DTSLED_CNT,DTSLED_NAME);
    }else{  /*没有给定设备号*/
        /*申请设备号*/
        ret = alloc_chrdev_region(&dtsled.devid,0,DTSLED_CNT,DTSLED_NAME);
        dtsled.major = MAJOR(dtsled.devid);/*提取主设备号*/
        dtsled.minor = MINOR(dtsled.devid);/*提取次设备号*/
    }
    if(ret < 0){
        goto fail_devid;
    }
    /*2、添加字符设备*/
    dtsled.cdev.owner = THIS_MODULE;
    cdev_init(&dtsled.cdev,&dtsled_fops);/*初始化的 cdev 结构体变量*/
    ret = cdev_add(&dtsled.cdev,dtsled.devid,DTSLED_CNT);/*添加字符设备*/
    if(ret < 0){
        goto fail_cdev;
    }
    /*3、自动创建设备节点*/
    dtsled.class = class_create(THIS_MODULE,DTSLED_NAME);
    if(IS_ERR(dtsled.class)){
        ret = PTR_ERR(dtsled.class);
        goto fail_class;
    }
    dtsled.device = device_create(dtsled.class,NULL,dtsled.devid,NULL,DTSLED_NAME);
    if(IS_ERR(dtsled.device)){
        ret = PTR_ERR(dtsled.device);
        goto fail_device;
    }

    /* 获取设备树属性内容*/
    dtsled.nd = of_find_node_by_path("/myled");/*从路径找节点*/
    if(dtsled.nd == NULL){
        ret = -EINVAL;
        goto fail_findnd;
    } 
     /*获取属性*/
    ret = of_property_read_string(dtsled.nd,"status",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("status=%s\r\n",str);
    }
    ret = of_property_read_string(dtsled.nd,"compatible",&str);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("compatible=%s\r\n",str);
    }
/*获取reg属性*/
    ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,10);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("reg data:\r\n");
        for(i=0; i<10;i++){
            printk("%#X ",regdata[i]);
        }
        printk("\r\n");
    }
    /*LED灯初始化*/
    /* 1、寄存器地址映射 ,要对应reg属性值*/
  	IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
	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]);
    /* 2、使能GPIO1时钟 */
	val = readl(IMX6U_CCM_CCGR1);
	val &= ~(3 << 26);	/* 清楚以前的设置 */
	val |= (3 << 26);	/* 设置新值 */
	writel(val, IMX6U_CCM_CCGR1);

	/* 3、设置GPIO1_IO03的复用功能,将其复用为
	 *    GPIO1_IO03,最后设置IO属性。
	 */
	writel(5, SW_MUX_GPIO1_IO03);
	
	/*寄存器SW_PAD_GPIO1_IO03设置IO属性
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 00 默认下拉
     *bit [13]: 0 kepper功能
     *bit [12]: 1 pull/keeper使能
     *bit [11]: 0 关闭开路输出
     *bit [7:6]: 10 速度100Mhz
     *bit [5:3]: 110 R0/6驱动能力
     *bit [0]: 0 低转换率
	 */
	writel(0x10B0, SW_PAD_GPIO1_IO03);

	/* 4、设置GPIO1_IO03为输出功能 */
	val = readl(GPIO1_GDIR);
	val &= ~(1 << 3);	/* 清除以前的设置 */
	val |= (1 << 3);	/* 设置为输出 */
	writel(val, GPIO1_GDIR);

	/* 5、默认关闭LED */
	val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);

    printk("dtsled_init\r\n");
    return 0;
fail_rs:
fail_findnd:
    device_destroy(dtsled.class,dtsled.devid);
fail_device:
    class_destroy(dtsled.class);
fail_class:
    cdev_del(&dtsled.cdev);
fail_cdev:
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
fail_devid:
    return ret;
}
/*出口函数*/
static void __exit dtsled_exit(void){
    unsigned int val=0;
    val = readl(GPIO1_DR);
	val |= (1 << 3);	
	writel(val, GPIO1_DR);
    /* 取消映射 */
	iounmap(IMX6U_CCM_CCGR1);
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

    /*删除字符设备*/
    cdev_del(&dtsled.cdev);
    /*注销释放设备号*/
    unregister_chrdev_region(dtsled.devid,DTSLED_CNT);
    /*摧毁设备*/
    device_destroy(dtsled.class,dtsled.devid);
    /*摧毁类*/
    class_destroy(dtsled.class);
    printk("dtsled_exit\r\n");
}

/*注册和卸载驱动*/
module_init(dtsled_init);
module_exit(dtsled_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ba che kai qi lai");

编译验证 

 继续利用之前编写的ledAPP进行测试开光灯

 开

 

关 

 

of_iomap函数

在上面代码中, 在获取reg属性到获取寄存器地址可以用of_iomap函数替换,如下


#if 0
    ret = of_property_read_u32_array(dtsled.nd,"reg",regdata,10);
    if(ret < 0){
        goto fail_rs;
    }else{
        printk("reg data:\r\n");
        for(i=0; i<10;i++){
            printk("%#X ",regdata[i]);
        }
        printk("\r\n");
    }
    /*LED灯初始化*/
    /* 1、寄存器地址映射 ,要对应reg属性值*/
  	IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
	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]);
#endif
    IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd , 0);
    SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd , 1);
    SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd , 2);
    GPIO1_DR = of_iomap(dtsled.nd , 3);
    GPIO1_GDIR = of_iomap(dtsled.nd , 4);

使用该函数需要头文件 #include <linux/of_address.h>,原型如下

of_iomap(struct device_node *device, int index)

参数device为设备节点, index为reg 属性中要完成内存映射的段
就像本章reg从0到4共有5段,reg 属性的值一般是以(address, length)为一段,所以是五段

用of_iomap函数代换上面代码的效果是一样的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值