Linux驱动-单总线-DS18b20-驱动设备树配置-GPIO复用
2026/5/7 19:19:35 网站建设 项目流程

提示:DS18B20驱动配置设备树

文章目录

  • 前言
  • 一、参考资料
  • 二、 原理图分析-配置设备树
    • 1、字符设备驱动框架-再分析-需求描述
    • 2、地板原理图简要分析
      • DS18b20 引脚
      • 底板原理图简要分析
        • 实物图
        • 20pin的原理图
        • pinctrl 引脚复用-可复用的引脚配置图
    • 3、配置设备树
      • 配置gpio
        • 1. ds18b20_gpio: gpio0_b0
        • 2. compatible = "ds18b20";
        • 3. ds18b20-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;
        • 4. pinctrl-names = "default";
        • 5. pinctrl-0 = <&ds18b20_gpio_ctrl>;
      • 配置pinctrl
        • 1. &pinctrl
        • 2. ds18b20_gpio
        • 3. ds18b20_gpio_ctrl: ds18b20-gpio-ctrl
        • 4. rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
      • 两段配置的关系(核心逻辑)
  • 三、源码-原理分析-验证
    • 1、源码说明-加载驱动
    • 2、验证设备树配置的gpio节点是否生成` cd /proc/device-tree/`
    • 3、检查设备树是否加载进入内核-`ls -l /proc/device-tree/gpio0_b0`
    • 4、验证申请的设备号-`cat /proc/devices`
    • 5、验证申请的字符设备类-`ls /sys/class/`-`ls /sys/class/sensors`
    • 6、验证申请的字符设备 -`ls -l /dev/`-`ls -l /dev/ds18b20`
  • 总结

前言

这里还是结合前面的知识点,前面文章已经讲解、总结、归纳了驱动框架、字符驱动框架,里面还及到平台总线知识点。接下来就是设备树知识点。

  • 很早以前我们学习每个知识点都是 加载驱动、按照我们的demo 流程来进行知识点验证
  • 前面的温度传感器-DS18B20驱动框架编写 、温度传感器-DS18B20字符设备驱动框架 就是写了框架,其实驱动还没有加载成功的,比如平台总线驱动还没有识别到,没有调用probe函数。

一、参考资料

这里我们把以前的相关知识点,拿到这里规整一下

  • 总线设备知识点:
    platform总线注册流程分析
    在总线下注册设备及设备注册流程分析
    Linux驱动之platform 总线设备注册流程分析

  • 总线驱动相关知识点:
    驱动-在自定义总线上创建驱动-分析驱动注册流程

  • 总线、字符设备、相关知识点
    讯为:第十四期 | 单总线
    RK3568驱动指南|第十四篇 单总线-第158章DS18B20编写字符设备驱动框架
    涉及到核心知识点:alloc_chrdev_region // 动态申请字符设备号
    驱动-申请字符设备号
    涉及到核心知识点:cdev_init //字符设备初始化 cdev_initcdev_add //字符设备的注册 - cdev_add
    驱动-注册字符设备
    涉及到核心知识点:class_create() - 创建设备类device_create() - 创建设备节点
    驱动-创建设备节点

  • gpio-pinctrl知识点
    GPIO 控制和操作-使用命令通过sysfs文件系统控制GPIO
    使用C程序通过sysfs文件系统控制gpio
    操作寄存器来控制GPIO-点亮LED灯
    将原理图中的一个引脚复用为gpio功能
    驱动GPIO-获取单个gpio描述符
    Linux驱动-GPIO基本函数api
    Linux驱动-GPIO子系统与pinctrl子系统相结合

温度传感器-DS18B20驱动框架编写
温度传感器-DS18B20字符设备驱动框架

其实,我们这里用到的DS18B20外设,然后整个链路把知识点串联起来而已,之前相关知识点都学过的。 如果忘记 强烈建议自己再去补一补,基础的东西忘记了不行的。

二、 原理图分析-配置设备树

1、字符设备驱动框架-再分析-需求描述

在字符驱动设备框架温度传感器-DS18B20字符设备驱动框架中 我们对驱动源码进行了详细的分析的。

但是我们没做什么?

我们只是写了框架,平台总线设备并没有加载成功,也就是说:probe函数这个驱动的灵魂:当设备树中compatible = "ds18b20"的设备与驱动匹配成功时,内核才会自动调用probe,完成字符设备的完整注册流程。

所以 ,我们要做的是什么:

  • 配置gpio: 驱动层配置一个gpioDS18b20外设来用
  • 配置pinCtrl: 但是系统里面,默认的pin点可能是配置了其它的功能、或者 默认情况下不是gpio功能。 那么就需要用pin-ctrl进行引脚复用到GPIO功能,这样外设才能正常使用,匹配到gpio,实现

2、地板原理图简要分析

DS18b20 引脚

DS18B20引脚如下


这里DC 就是信号引脚,需要一个GPIO来连接的,那么就在主板上面找一个GPIO不就行了嘛。

底板原理图简要分析

参考资料:topeet_rk3568_main_v1_7.pdf底板原理图

实物图

先看实物图: 打算用这个20pin脚里面的GPIO0_B0的拐角作为GPIO控制,来做DS18B20的信号连接。

20pin的原理图

topeet_rk3568_main_v1_7.pdf底板原理图 里面去找
那么就去找J39 的原理图,如下:我们看到了拐角17 对应的就是GPIO0_B0的拐角,全称:DVP_PWREN0_H_GPIO0_B0

pinctrl 引脚复用-可复用的引脚配置图

那么这个引脚DVP_PWREN0_H_GPIO0_B0我想用来做GPIO来用,那么就要去配置pinctrl引脚复用,继续看 引脚复用,继续在topeet_rk3568_main_v1_7.pdf底板原理图 里面去找:如下,说明引脚DVP_PWREN0_H_GPIO0_B0可以复用的功能有:CLK32K_IN 、CLK32K_OUTO、PCIE30X2_BUTTONRSTn、GPIO0_B0

3、配置设备树

文件路径:kernel/arch/arm64/boot/dts/rockchip/rk3568/rk3568-evb1-ddr4-v10.dtsi
参考文档:

  • 将原理图中的一个引脚复用为gpio功能
    -Linux驱动-GPIO子系统与pinctrl子系统相结合

要求:就是当有GPIO复用功能需求,不应该无从下手,应该很熟练的去配置。

配置gpio

在根节点中配置

/{.......................ds18b20_gpio:gpio0_b0{compatible="ds18b20";ds18b20-gpios=<&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;pinctrl-names="default";pinctrl-0=<&ds18b20_gpio_ctrl>;status="okay";};}
1. ds18b20_gpio: gpio0_b0
  • ds18b20_gpio:节点标签,方便其他地方引用
  • gpio0_b0:节点名字,随便写,建议和引脚对应
2. compatible = “ds18b20”;
  • 驱动匹配字符串
  • 告诉内核:这个设备要去找名字叫 ds18b20 的驱动来匹配、接管
  • 内核就是靠这个字段找到对应驱动
3. ds18b20-gpios = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;

这是最重要的一行,指定 DS18B20 接在哪个 GPIO,但是就是一个gpio描述符

  • &gpio0:使用 GPIO0 控制器
  • RK_PB0:GPIO0_B0 引脚
  • GPIO_ACTIVE_HIGH:高电平有效(单总线必须这样)
4. pinctrl-names = “default”;
  • 定义引脚状态名称
  • default 表示默认工作状态
5. pinctrl-0 = <&ds18b20_gpio_ctrl>;
  • 关联引脚配置
  • 意思是:去引用 &pinctrl 里定义的 ds18b20_gpio_ctrl 引脚配置
  • 这是两段配置之间的 “桥梁”

配置pinctrl

在&pinctrl 节点中配置pinctrl 相关信息

&pinctrl{.............ds18b20_gpio{ds18b20_gpio_ctrl:ds18b20-gpio-ctrl{rockchip,pins=<0 RK_PB0 RK_FUNC_GPIO&pcfg_pull_none>;};};}
1. &pinctrl
  • 引用 RK3568 的引脚控制器
  • 所有引脚复用、上下拉、驱动能力都在这里配置
2. ds18b20_gpio
  • 给这组引脚配置起个名字,随便写
3. ds18b20_gpio_ctrl: ds18b20-gpio-ctrl
  • 这是真正被引用的配置节点
  • 第一段 pinctrl-0 就是引用它
4. rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
参数功能
参数 1:0GPIO 组号:GPIO0
参数 2:RK_PB0引脚编号:GPIO0_B0
参数 3:RK_FUNC_GPIO功能模式:普通 GPIO 输入输出模式;不是 I2C、UART、PWM 等复用功能
参数 4:&pcfg_pull_none上下拉配置:pull_none = 无上下拉;DS18B20 必须外部接 4.7k 上拉电阻,内部不能开上下拉

两段配置的关系(核心逻辑)

第一段(设备节点) pinctrl-0=<&ds18b20_gpio_ctrl>↓ 引用 第二段(pinctrl 引脚配置) ds18b20_gpio_ctrl{...}

第一段告诉内核 “这是什么设备、用哪个引脚”,第二段告诉内核 “这个引脚要怎么配置(GPIO 模式、无上下拉)”。

核心点小结:

属性名称作用
compatible匹配驱动
ds18b20-gpios指定引脚
pinctrl-0关联引脚配置
rockchip,pins设置引脚为 GPIO 模式 + 无上下拉

三、源码-原理分析-验证

1、源码说明-加载驱动

这里不演示源码,用的是上一节中一摸一样的源码 温度传感器-DS18B20字符设备驱动框架 ,源码如下:

#include<linux/init.h>#include<linux/module.h>#include<linux/platform_device.h>#include<linux/of.h>#include<linux/fs.h>#include<linux/cdev.h>#include<linux/kdev_t.h>#include<linux/slab.h>#include<linux/gpio.h>#include<linux/gpio/consumer.h>#include<linux/delay.h>struct ds18b20_data{dev_t dev_num;// 什么一个字符设备号,就是设备号struct cdev ds18b20_cdev;//声明一个字符设备structclass*ds18b20_class;//声明一个设备类struct device*ds18b20_device;//声明一个设备//device};intds18b20_open(struct inode*inode,struct file*file){return0;}ssize_tds18b20_read(struct file*file,char__user*buf,size_t size,loff_t*offs){return0;}intds18b20_release(struct inode*inode,struct file*file){return0;}struct file_operations ds18b20_fops={.open=ds18b20_open,.read=ds18b20_read,.release=ds18b20_release,.owner=THIS_MODULE,};struct ds18b20_data*ds18b20;intds18b20_probe(struct platform_device*dev){intret;printk("This is probe \n");ds18b20=kzalloc(sizeof(*ds18b20),GFP_KERNEL);if(ds18b20==NULL){printk("====kzalloc error \n");gotoerror_0;}// 第一步骤:申请设备号ret=alloc_chrdev_region(&ds18b20->dev_num,0,1,"myds18b20");//通过动态方式进行设备号注册if(ret<0){printk("alloc_chrdev_region error \n");ret=-EAGAIN;gotoerror_1;}// 第二步:字符设备初始化 cdev_initcdev_init(&ds18b20->ds18b20_cdev,&ds18b20_fops);// 使用 cdev_init()函数初始化 cdev_test 结构体, 并链接到cdev_test_ops 结构体ds18b20->ds18b20_cdev.owner=THIS_MODULE;// 将 owner 字段指向本模块, 可以避免在模块的操作正在被使用时卸载该模块// 第三步:字符设备的注册 - cdev_addret=cdev_add(&ds18b20->ds18b20_cdev,ds18b20->dev_num,1);// 使用 cdev_add()函数进行字符设备的添加// 第四步,class_create() - 创建设备类ds18b20->ds18b20_class=class_create(THIS_MODULE,"sensors");//使用class_create进行类的创建,类名称为sensorsif(IS_ERR(ds18b20->ds18b20_class)){printk("========class_create error\n");ret=PTR_ERR(ds18b20->ds18b20_class);gotoerror_2;}// 第五步,创建device device_create() - 创建设备节点ds18b20->ds18b20_device=device_create(ds18b20->ds18b20_class,NULL,ds18b20->dev_num,NULL,"ds18b20");//使用device_create进行设备的创建,设备名称为device_testif(IS_ERR(ds18b20->ds18b20_device)){printk("========device_create error\n");ret=PTR_ERR(ds18b20->ds18b20_device);gotoerror_3;}return0;//error_4:// device_destroy(ds18b20->ds18b20_class, ds18b20->dev_num);error_3:class_destroy(ds18b20->ds18b20_class);error_2:cdev_del(&ds18b20->ds18b20_cdev);unregister_chrdev_region(ds18b20->dev_num,1);error_1:kfree(ds18b20);error_0:returnret;}conststruct of_device_id ds18b20_match_table[]={{.compatible="ds18b20"},{},};struct platform_driver ds18b20_driver={.driver={.owner=THIS_MODULE,.name="ds18b20",.of_match_table=ds18b20_match_table,},.probe=ds18b20_probe,};staticint__initds18b20_init(void){intret;ret=platform_driver_register(&ds18b20_driver);if(ret<0){printk("platform_driver_register error\n");return-1;}return0;}staticvoid__exitds18b20_exit(void){device_destroy(ds18b20->ds18b20_class,ds18b20->dev_num);class_destroy(ds18b20->ds18b20_class);cdev_del(&ds18b20->ds18b20_cdev);unregister_chrdev_region(ds18b20->dev_num,1);kfree(ds18b20);platform_driver_unregister(&ds18b20_driver);}module_init(ds18b20_init);module_exit(ds18b20_exit);MODULE_LICENSE("GPL");

编译程序make,生成驱动程序ds18b20.ko,然后执行驱动

[root@RK356X:/mnt/sdcard]# chmod777ds18b20.ko[root@RK356X:/mnt/sdcard]# insmod ds18b20.ko[root@RK356X:/mnt/sdcard]#

查看内核日志:dmesg看到打印了:This is probe
这就说明设备树中gpio 配置成功了:设备树中的compatible = "ds18b20";和驱动程序中的.name = "ds18b20",定义驱动名称 name 对应上了。

struct platform_driver ds18b20_driver={.driver={.owner=THIS_MODULE,.name="ds18b20",.of_match_table=ds18b20_match_table,},.probe=ds18b20_probe,};
[root@RK356X:/]# dmesg

2、验证设备树配置的gpio节点是否生成cd /proc/device-tree/

gpio配置如下: 节点名称是gpio0_b0

ds18b20_gpio:gpio0_b0{compatible="ds18b20";ds18b20-gpios=<&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;pinctrl-names="default";pinctrl-0=<&ds18b20_gpio_ctrl>;status="okay";};

执行命令:cd /proc/device-tree/,如下存在名称为gpio0_b0的设备树节点,说明设备树配置成功了的

3、检查设备树是否加载进入内核-ls -l /proc/device-tree/gpio0_b0

如上,只是说明设备树已经配置好了,那么相关属性是否配置到内核呢?

ds18b20_gpio:gpio0_b0{compatible="ds18b20";ds18b20-gpios=<&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>;pinctrl-names="default";pinctrl-0=<&ds18b20_gpio_ctrl>;status="okay";};

执行:ls -l /proc/device-tree/gpio0_b0命令验证,如下:实际和自己在设备树中的配置对上了。

[root@RK356X:/sys/firmware/devicetree/base]# ls-l/proc/device-tree/gpio0_b0 total0-r--r--r--1root root8Apr2608:06compatible-r--r--r--1root root12Apr2608:06ds18b20-gpios-r--r--r--1root root9Apr2608:06name-r--r--r--1root root4Apr2608:06phandle-r--r--r--1root root4Apr2608:06pinctrl-0-r--r--r--1root root8Apr2608:06pinctrl-names-r--r--r--1root root5Apr2608:06status[root@RK356X:/sys/firmware/devicetree/base]#

4、验证申请的设备号-cat /proc/devices

在驱动源码中,申请设备号,参考资料:驱动-申请字符设备号

// 第一步骤:申请设备号ret=alloc_chrdev_region(&ds18b20->dev_num,0,1,"myds18b20");//通过动态方式进行设备号注册

查看设备号命令:cat /proc/devices,我们看到申请的字符设备号了。

5、验证申请的字符设备类-ls /sys/class/-ls /sys/class/sensors

如上源码,创建设备类:ds18b20->ds18b20_class = class_create(THIS_MODULE,"sensors");
那就看一下设备类是否生成:``ls /sys/class/`
参考资料:驱动-创建设备节点

继续看一下,字符设备类下面对应的软链接到哪一个设备,命令:ls -l /sys/class/sensors/

6、验证申请的字符设备 -ls -l /dev/-ls -l /dev/ds18b20

如上源码,创建设备类:

// 第五步,创建device device_create() - 创建设备节点ds18b20->ds18b20_device=device_create(ds18b20->ds18b20_class,NULL,ds18b20->dev_num,NULL,"ds18b20");//使用device_create进行设备的创建,设备名称为device_test

参考资料:驱动-创建设备节点

  • 命令:ls -l /dev/查看当前设备: 如下结果,说明字符设备是创建成功了。


总结

  • 配置设备树、gpio定义声明、pinctrl配置复用、字符设备、平台总线 这些基本知识点之前都学过,这里通过实际案例再次实操,务必掌握
  • 务必在实际开发中,搞清楚开发流程,如果遇到问题知道怎么定位问题,然后分析问题

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询