八、设备树开发

1、为什么使用设备树?

使用设备树开发可以提高我们的代码通用性,我们可以设备树中添加硬件的信息,当我们驱动硬件的时候,我们可以从设备树中获取硬件的信息进行开发,如果我们需要修改硬件信息的话,直接去修改设备树,而驱动硬件的代码不用修改,这样即使换个芯片,代码依旧可用。

2、设备树的概念

 设备树本质上是一个文本文件,包含了芯片的各种信息。

设备树里面所编写的设备的信息是节点,节点里面的内容才是设备树关键。这里面的内容我们称之为属性!
属性的内容一共就分为两大类:数字属性、字符串属性

节点的构成:

        节点名 {
        属性名 = 属性值;
        };
其中属性值是 数字需要用 <> 括起来
属性值是 字符串 需要 "" 引起来

3、设备树常见的节点和属性

节点:

  • 根节点:所有节点必须写入到根节点
  • modle节点:提示当前板子信息
  • alises节点:启动的顺序
  • chosenj节点:设备树给内核的传参

 属性:

  • status: 节点信息的使能 (disabled/okay)
  • compatible:内核的驱动来匹配设备树信息的关键点
  • reg:传递寄存器的信息

 4、常用函数

  • 通过名字获取设备树的节点结构体
    函数头文件: <linux/of.h>
    函数的原型:
    struct device_node *of_find_node_by_name(struct device_node *from,
    const char *name);
    函数的参数:
    from:从哪里寻找节点,一般这个地方填 NULL->代表从 /节点出发寻找!
    name:查找节点名字
    函数返回值:就是设备树节点 在内核抽象的结构体
  • 通过路径获取节点
    函数头文件: <linux/of.h>
    函数的原型:
    struct device_node *of_find_node_by_path(const char *path)
    函数的参数:
    path:你要寻找的节点的路径!从 /路径出发
    函数返回值:就是设备树节点 在内核抽象的结构体
  • 获取节点属性的字符串属性值
    函数头文件: <linux/of.h>
    函数的原型: int of_property_read_string(
    const struct device_node *np,
    const char *propname,
    const char **out_string
    );
    函数的参数:
    np:你要提供你想获取属性的 节点结构体
    propname:你提供你想获取属性名
    out_string:会把获取得到的属性值写入到此处!
    函数返回值:获取属性成功返回 0,获取属性失败返回 非 0
  • 读取一个属性值是整形 u32 类型的属性
    函数头文件:<linux/of.h>
    函数的原型:
    int of_property_read_u32(
    const struct device_node *np,
    const char *propname,
    u32 *out_value
    )
    函数的参数:
    np:设备节点结构体
    propname :你要获取属性的属性名
    out_value:把获取得到的属性返回于此
    函数返回值:获取属性成功返回 0,获取属性失败返回 非 0
  • 通过接口来获取设备树的 GPIO 的属性
    函数头文件: <linux/of_gpio.h>
    函数的原型:
    int of_get_named_gpio_flags(
    struct device_node *np,
    const char *list_name,
    int index,
    enum of_gpio_flags *flags
    );
    函数的参数:
    np:设备树的节点
    list_name:你要获取 GPIO 属性值的 属性名
    index:索引号 多个引脚的时候,索引号代表你去获取不同的引脚!
    flags:获取完毕引脚后 会把引脚有效电平填入其中
    函数返回值:就是我们的 GPIO 的编号

 5、使用设备树实现流水灯 

        设备树代码

led_all {
		status = "okay";
		led_gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_LOW>,
			   <&gpio0 RK_PC6 GPIO_ACTIVE_LOW>,
			   <&gpio0 RK_PD0 GPIO_ACTIVE_LOW>,
			   <&gpio1 RK_PD5 GPIO_ACTIVE_LOW>;
	};

        设备代码

#include "linux/kernel.h"
#include "linux/module.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/gpio.h"
#include "linux/cdev.h"
#include "linux/of.h"
#include "linux/of_gpio.h"

struct device_node *mynode;
int gpio_num[10] = {0};
int count = 0;
enum of_gpio_flags active_flag[10] = {0};
dev_t mydevnum;
struct cdev mycdev;
struct file_operations myoper;
struct class *my_class;


//功能函数
int led_open(struct inode *i, struct file *f)
{
    for (int i = 0; i < count; i++)
    {
        gpio_set_value(gpio_num[i],!active_flag[i]);
    }
    return 0;
}

int led_close(struct inode *i, struct file *f)
{
    for (int i = 0; i < count; i++)
    {
        gpio_set_value(gpio_num[i],active_flag[i]);
    }
    return 0;
}

//加載函數
static int __init mybeep_init(void)
{
    //获取设备数节点
    mynode = of_find_node_by_path("/led_all");
    if (mynode == NULL)
    {
        /* code */
        printk("没有此节点\r\n");
        return -EINVAL;
    }
    //判断此节点是否使能
    const char * retstr;
    of_property_read_string(mynode,"status",&retstr);
    if (strcmp(retstr,"okay") != 0)
    {
        /* code */
        printk("此节点不能使用\r\n");
        return -EINVAL;
    }
    //获取GPIO信息
    while (1)
    {
        gpio_num[count] = of_get_named_gpio_flags(mynode,"led_gpio",count,&active_flag[count]);
        if (gpio_num[count] < 0)
        {
            /* code */
            break;
        }
        count++;
        
    }
    //初始化GPIO
    for (int i = 0; i < count; i++)
    {
        gpio_request(gpio_num[i],"RK_led");
        gpio_direction_output(gpio_num[i],active_flag[i]);
    }
    //申请设备号
    alloc_chrdev_region(&mydevnum,10,1,"myleddevice");
    //初始化Linux2.6核心结构体
    myoper.owner = THIS_MODULE;
    myoper.open = led_open;
    myoper.release = led_close;
    cdev_init(&mycdev,&myoper);
    //添加设备
    cdev_add(&mycdev,mydevnum,1);
    //添加设备文件
    my_class = class_create(THIS_MODULE,"myledall");
    //生成设备文件
    device_create(my_class,NULL,mydevnum,NULL,"myledall");

    return 0;    
}

static void __exit mybeep_exit(void) 
{
    device_destroy(my_class,mydevnum);
    class_destroy(my_class);
    cdev_del(&mycdev);
    unregister_chrdev_region(mydevnum,1);
}

module_init(mybeep_init);
module_exit(mybeep_exit);
MODULE_LICENSE("GPL");

         主函数代码

#include "stdio.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/types.h"
#include <sys/stat.h>

int main()
{
    int fd = 0;
    while (1)
    {
        /* code */
        fd = open("/dev/myledall", O_RDWR);
        sleep(1);
        close(fd);
        sleep(1);
    }
    return 0;
    
}   

 6、出现的错误

内核编译出错。原因是因为节点{}后没加;需要注意的是,一个使用多个属性时要用,隔开最后再加;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值