提示: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_init;cdev_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: 驱动层配置一个gpio给DS18b20外设来用 - 配置
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:0 | GPIO 组号: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:/]# dmesg2、验证设备树配置的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配置复用、字符设备、平台总线 这些基本知识点之前都学过,这里通过实际案例再次实操,务必掌握
- 务必在实际开发中,搞清楚开发流程,如果遇到问题知道怎么定位问题,然后分析问题