imx6ull设备树

本文介绍了设备树的概念,DTS文件结构,以及如何在设备树下开发驱动程序,包括添加驱动设备节点、处理兼容性和状态属性、使用reg属性和地址单元,以及内核模块的编译和应用实例。
摘要由CSDN通过智能技术生成

概念

  • 什么是设备树

描述设备树的文件叫DTS,实际上就是在这个DTS文件里面,用树状的结构存储设备之间的关系。在以前这棵树就是设备树。

  • 什么是DTS、DTB、DTC

DTS就是我们上面的设备树源码文件、DTB是它的二进制文件、DTC是我们编译DTS的工具,类似于我们的gcc

基本结构

在我们的设备树文件下,引用了一个dtsi文件,在下面

可以发现,这个文件里也有一个根  / ,其实这是同一个根,这个文件里还有一些&符号带着的,这是追加

比如

我们在根外面写了这么一个节点,就是向 / 下面的 aliases节点里的i2c0节点追加了设备节点,我们在/pro/device-tree里可以看到设备i2c0

我们追加的信息就在i2c0这个设备里。这是一个通用接口,我们只需要在这个设备上写驱动就行了。

设备树下的驱动开发

添加驱动设备节点

打开我们在内核移植时复制的dts文件

我们添加的alpha_led设备,是在根设备节点下面创建的一个节点,具有以下属性。

属性

compatible(兼容性):

一个字符串列表,比如上面gpio_spi设备中,有“fairchild, 74hc595”,fairchild表示厂商,74hc595一般是模块对应驱动名字。

也可以为字符串列表,如 :compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";

status(设备状态):

#address-cells 和#size-cells 属性--------reg属性

#address-cells 和#size-cells 表明了子节点应该如何编写 reg 属性值,reg的属性值与地址相关,
格式为reg = <address1 length1 address2 length2 address3 length3……>,其中 address 是起始地址, length 是地址长度, #address-cells 表明 address 这个数据所占用的字长, #size-cells 表明 length 这个数据所占用的字长

添加了设备节点后我们到顶层目录编译我们的设备树文件

make  dtbs                                 

生成新的设备树文件

复制.dts文件到tftpboot里

工程文件的创建

我们在

创建一个我们以后写驱动的目录,在VScode打开,ctrl + shift + p,添加如下

前面改为自己的目录

编写程序

几个常用函数

程序如下(都在my_driver里)

dtsled.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define DTSLED_CNT      1        //device number
#define DTSLED_NAME     "dtsled" //device name

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

//dteled设备结构体
struct dtsled_dev{
    dev_t devid;                //设备号
    struct cdev cdev;           //CDEV
    struct class *class;        //类
    struct device *device;      //设备
    int major;                  //主设备号
    int minor;                  //次设备号
    struct device_node *nd;     //设备节点
};

struct dtsled_dev dtsled;       //设备

void led_set(u8 set_value)      //1打开led, 0关闭
{
    u32 val = 0;
    if(set_value == 1)
    {
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);s
    }
    else if(set_value == 0)
    {
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        wtitel(val, GPIO1_DR);
    }
}

static int led_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &dtsled;   //设置私有数据
    return 0;
};

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t offt)
{
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    int ret;
    unsigned char databuf[1];
    unsigned char ledstat;

    ret = copy_from_user(databuf, buf, cnt);
    if(ret < 0)
    {
        printk("kernel copy_from_user failed: %d\n", ret);
        return -EFAULT;
    }

    ledstat = databuf[0];
    if(1 == ledstat)            //开
    {
        led_set(1);
    }
    else if(0 == databuf[0])    //关
    {
        led_set(0);
    }

    return 0;
}

static int led_close(struct inode *inode, struct file *file)
{
    return 0;
}

static struct file_operations dtsled_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_close,
};

static int __init led_init(void)
{
    u32 val = 0;
    int ret;
    u32 regdata[14];
    const char *str;
    struct property *proper;

    dtsled.nd = of_find_node_by_path("/alphaled");  //通过路径查找指定节点,返回节点
    if(dtsled.nd == NULL)
    {
        printk("find_node_by_path failed\n");
        return -EINVAL;
    }
    printk("alphaled node has been found\n");

    proper = of_find_property(dtsled.nd, "compatible", NULL);   //查找指定属性(节点, 属性名, 字节数)返回属性结构体
    if(proper == NULL)
    {
        printk("find_perpor_err failed\n");       
    }
    else 
    {
        printk("compatoble = %s\n", (char *)proper->value);
    }

    ret = of_property_read_string(dtsled.nd, "status", &str);   //读取属性中字符串值(设备节点,要读取的属性名,读取到的字符串值)
    if(ret < 0)
    {
        printk("property_read_string_err failed\n");
    }
    else 
    {
        printk("status = %s\n", str);
    }

    ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);    //获取reg属性内容存到regdata数组,在下面调用
    if(ret < 0)
    {
        printk("reg property_read_u32_array_err failed\n");
    }
    else 
    {
        u8 = i;
        printk("reg data:\n");
        for(i = 0; i < 10; ++i)
        {
            printk("%#x ", regdata[i]);
        }
        printk("\n");
    }


#if 0
    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]);
#else
    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);
#endif                                          //直接内存映射

    val = readl(IMX6U_CCM_CCGR1);               //使能GPIO1时钟
    val &= ~(3 << 26);
    val |= (3 << 26);
    writel(val, IMX6U_CCM_CCGR1);

    writel(5, SW_MUX_GPIO1_IO03);

    writel(0x10B0, SW_PAD_GPIO1_IO03);

    val = readl(GPIO1_GDIR1);                   // 设置GPIO IO03为输出
    val &= ~(1 << 3);
    val |= (1 <<3);
    writel(val, GPIO1_GDIR);

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

    if(dtsled.major)                            //如果定义了设备号用它
    {
        dtsled.devid = MKDEV(dtsled.major, 0);
        register_chrdev_region(dtsled.devid, DTSLED_CNT, DTSLED_NAME);
    }
    else                                        //没定义设备号,自动创建
    {
        alloc_chrdev_region(&dtsled.devid, 0, DTSLED.CNT, DTSLED.NAME);
        dtsled.major = MAJOR(dtsled.devid);
        dtsled.minor = MINOR(dtsled.devid);
    }
    printk("dtsled major = %d minor = %d\n", dtsled.major, dtsled.minor);  //打印主次设备号

    dtsled.cdev.owner = THIS_MODULE;                //初始化cdev
    cdev_init(&dtsled.cdev, &dtsled.fops);

    cdev_add(&dtsled.cdev, &dtsled.devid, DTSLED_CNT);  //添加cdev

    dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);  //创建类
    if(IS_ERR(dtsled.class))
    {
        return PTR_ERR(dtsled.class);
    }

    dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL, DTSLED_NAME);//创建设备
    if(IS_ERR(dtsled.device))
    {
        return PTR_ERR(dtsled.device);
    }
    return 0;
}

static void __exit led_exit(void)
{
    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);
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");

ledapp.c

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"

int main()
{
    int fd = open("/dev/dtsled", O_RDWR);
    char a[1] = {1};
    char b[1] = {0};
    if(fd < 0)
    {
        printf("cant open file: /dev/dstled\n");
        return -1;
    }
    
    while(1)
    {
        write(fd, a, 1);
        sleep(1);
        write(fd, b, 1);
        sleep(1);
    }

    close(fd);
    return 0;
}

编写Makefile

KERNELDIR := /home/violet/imx6ul/uboot/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := dtsled.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

执行make -j3将dtsled.ko放到根目录文件下/lib/modules/4.1.15

编译ledapp.c同样放到这个目录下

在板子上

然后执行ledapp

灯闪烁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值