Linux嵌入式驱动开发10——设备树开发详解

全系列传送门

Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)

Linux嵌入式驱动开发02——驱动编译到内核

Linux嵌入式驱动开发03——杂项设备驱动(附源码)

Linux嵌入式驱动开发04——应用层和内核层数据传输

Linux嵌入式驱动开发05——物理地址到虚拟地址映射

Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写

Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)

Linux嵌入式驱动开发08——字符设备(步步为营)

Linux嵌入式驱动开发09——平台总线详解及实战

Linux嵌入式驱动开发10——设备树开发详解

Linux嵌入式驱动开发11——平台总线模型修改为设备树实例

Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作

Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)

Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)

Linux嵌入式驱动开发15——等待队列和工作队列

Linux嵌入式驱动开发16——按键消抖实验(内核定时器)

Linux嵌入式驱动开发17——输入子系统

Linux嵌入式驱动开发18——I2C通信

常用名词解释

DT:Device Tree

设备树

FDT: Flattened Device Tree

展开设备树
开放固件,设备树起源于OF,所以我们在设备树中可以看到很多有of字母的函数

device tree source(dts)

设备树代码

device tree source, includeDTB(dtsi)

更通用的设备树代码,也就是相同芯片但不同平台都可以使用的代码

device tree blob(dtb)

编译之后得到的DTB文件

device tree compoler(dtc)

设备树编译器

在这里插入图片描述

设备树基本语法

设备树基本框架

  1. 设备树从根节点开始,每一个设备都是一个节点
  2. 节点和节点之间可以互相嵌套,形成父子关系
  3. 设备的属性用key-value对(键值对)来描述,每个属性用分号来结束

设备树语法

节点

在这里插入图片描述

所以要从根节点开始看,从左到右,从上往下

节点名称

在这里插入图片描述

节点别名

在这里插入图片描述

节点的引用

在这里插入图片描述
在这里插入图片描述

属性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设备树添加自定义节点

查看节点

/proc/device-tree

在这里插入图片描述

 cat model

在这里插入图片描述
这里的model打印信息就是我们的dtb文件中的定义的model信息,描述板子信息
在这里插入图片描述
通过这种方法,我们可以验证设备书中有没有成功添加进入我们的节点

例如,我们想要看我们添加的gpio的节点信息,进入到gpios文件夹

cd gpios

在这里插入图片描述
然后查看

cat compatible

在这里插入图片描述
和我们的添加的节点信息一致,就可以判断节点添加正确
在这里插入图片描述

除了通过上面的方法查看,还可以通过下面的方法

/sys/firmware/devicetree/base

在这里插入图片描述
在这里插入图片描述

编写测试dts文件

在我们的imx6q-c-sabresd.dts文件中,添加我们的测试代码

	test1:test{							// test1就是test的别名
		#address-cells = <1>;
		#size-cells = <1>;

		compatible = "test";			// 相当于总线模型中用于匹配的name

		reg = <0x20ac000 0x0000004>;
		status = "okay";
	};

在这里插入图片描述
然后这里只需要重新编译设备树就可以了

make dtbs

在这里插入图片描述
这样是编译所有的dts文件,也可以指定文件编译

make imx6q-c-sabresd.dtb

在这里插入图片描述
通过tftp下载过去dtb文件,然后启动

tftp方法见文章

uboot使用tftp网络启动加载zImage、dtb到内存,文件系统本地启动(通用!!!)

进入到开发板,可以看到我们的test文件夹,这就是我们刚才新建的一个子节点
在这里插入图片描述
打开test文件夹,可以看到我们刚才写入的信息都在里面
在这里插入图片描述
这样我们的设备节点就准备好了,剩下的就是等待驱动来跟我们的设备节点匹配,匹配就是通过我们的 compatible名字来进行匹配

现在我们需要修改compatible属性的名称,那么需要怎么修改呢?

一般不要直接修改,而是通过引用来进行修改

代码示例如下:

&gpio_user {
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_user>;
        fsl,user;
        status = "okay";

	test1:test{							// test1就是test的别名
		#address-cells = <1>;
		#size-cells = <1>;

		compatible = "test";			// 相当于总线模型中用于匹配的name

		reg = <0x20ac000 0x0000004>;
	};
};

&test1{									// 直接引用别名就可以了
	compatible = "test12345";			// 这样就可以覆盖掉之前的内容
	status = "okay";					// 或者添加之前没有的内容
};

在这里插入图片描述
然后查看修改后的结果
在这里插入图片描述

设备树中常用的of操作函数

现在我们使用设备树来描述硬件信息,Linux给我们提供了一系列的函数来设置节点信息,这个of.h文件在这个目录下

/include/linux/of.h

在这里插入图片描述
都是以of开头的函数

device_node结构体描述节点

在这里插入图片描述

struct device_node {
        const char *name;
        const char *type;
        phandle phandle;
        const char *full_name;
        struct fwnode_handle fwnode;

        struct  property *properties;
        struct  property *deadprops;    /* removed properties */
        struct  device_node *parent;
        struct  device_node *child;
        struct  device_node *sibling;
        struct  kobject kobj;
        unsigned long _flags;
        void    *data;
#if defined(CONFIG_SPARC)
        const char *path_component_name;
        unsigned int unique_id;
        struct of_irq_controller *irq_trans;
#endif
};

property结构体描述属性

在这里插入图片描述


struct property {
        char    *name;
        int     length;
        void    *value;
        struct property *next;
        unsigned long _flags;
        unsigned int unique_id;
        struct bin_attribute attr;
};

设备树文件节点里面资源的步骤

步骤一:查找我们要找的节点

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

步骤二:获取我们需要的属性值

在这里插入图片描述

inline struct device_node *of_find_node_by_path(const char *path)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

of_iomap函数映射虚拟地址

在这里插入图片描述

查找节点

of_find_node_by_path

代码中使用函数of_find_node_by_path来找打节点的路径,并提取节点的信息
在这里插入图片描述
从dts文件中,可以看到我们的test节点是在gpios节点下
在这里插入图片描述
经过板子验证,我们的设备树的节点test确实在gpio节点下,并且test节点的name就是我们的test
在这里插入图片描述
我们在driver,c文件中的代码也是跟我们的设备树节点设置一致,路径是“/gpios/test”,我们只需要验证结果中能否正常输出test名字就可以了
在这里插入图片描述

结果验证

发送到开发板安装验证
在这里插入图片描述

获取节点compatible属性

of_find_property

    /********获取节点的属性***********/
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if(test_node_property == NULL) {
        printk("of_find_node_by_path error!!!\n");
        return -1;
    }else {
        printk("test_node_property name is %s\n", test_node_property->name);
        printk("test_node_property value is %s\n", (char *)test_node_property->value);
    }

结果验证

在这里插入图片描述

获取节点reg属性

of_property_read_u32_array

因为reg有两个u32类型的数,所以我们使用读u32类型数组的函数of_property_read_u32_array
在这里插入图片描述

    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if(ret < 0) {
        printk("of_property_read_u32_array error!!!\n");
        return -1;
    }else{
        printk("out_values[0] is 0x%08x\n", out_values[0]);
        printk("out_values[1] is 0x%08x\n", out_values[1]);
    }

结果验证

在这里插入图片描述

获取属性中字符串值

of_property_read_string

在这里插入图片描述
okay是一个字符串信息,所以使用函数of_property_read_string获得

    /********获取节点status的属性***********/
    ret = of_property_read_string(test_device_node, "status", &str);
    if(ret < 0) {
        printk("of_property_read_string error!!!\n");
        return -1;
    }else{
        printk("of_property_read_string status is 0%s\n", str);
    }
    return 0;

结果验证

在这里插入图片描述

driver测试代码

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

int size;
u32 out_values[2] = {0};
const char *str;

struct device_node  *test_device_node;
struct property *test_node_property;

static int driver_init(void)
{
    int ret = 0;

    printk("driver_init ok!!!\n");          // 在内核中无法使用c语言库,所以不用printf
    
    /********查找指定路径的节点***********/
    test_device_node = of_find_node_by_path("/gpios/test");

    if(test_device_node == NULL) {
        printk("of_find_node_by_path error!!!\n");
        return -1;
    }else {
        printk("of_find_node_by ok!!!\n");
        printk("test_device_node name is %s\n", test_device_node->name);
    }

    /********获取节点compatible的属性***********/
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if(test_node_property == NULL) {
        printk("of_find_node_by_path error!!!\n");
        return -1;
    }else {
        printk("test_node_property name is %s\n", test_node_property->name);
        printk("test_node_property value is %s\n", (char *)test_node_property->value);
    }

    /********获取节点reg的属性***********/
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if(ret < 0) {
        printk("of_property_read_u32_array error!!!\n");
        return -1;
    }else{
        printk("out_values[0] is 0x%08x\n", out_values[0]);
        printk("out_values[1] is 0x%08x\n", out_values[1]);
    }

    /********获取节点status的属性***********/
    ret = of_property_read_string(test_device_node, "status", &str);
    if(ret < 0) {
        printk("of_property_read_string error!!!\n");
        return -1;
    }else{
        printk("of_property_read_string status is 0%s\n", str);
    }
    return 0;
}

static void driver_exit(void)
{
    printk("driver_exit bye!!!\n");
}

module_init(driver_init);
module_exit(driver_exit);


MODULE_LICENSE("GPL");              //声明模块拥有开源许可

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值