提示Linux驱动-单总线-DS18b20-复位时序编写文章目录前言一、参考资料二、DS18B20 驱动获取 GPIOgpio 获取-控制资料-知识点回顾相关gpio 代码说明新增封装gpio 信息在probe 函数中获取gpio 信息-设置方向三、复位和应答信号时序分析复位信号时序分析应答信号时序分析将复位时序转换成代码流程源码实现流程分析源码准备把 GPIO 设为输出高释放总线发送复位拉低 GPIO保持 ≥480us释放总线拉高 GPIO → 转为输入高阻态等待 DS18B20 拉低检测应答信号等待 DS18B20 释放总线应答结束最后等待 480us确保复位彻底完成代码 ↔ 文字 协助理解四、DS18b20 驱动复位时序编写总结前言这里还是结合前面的知识点前面文章已经讲解、总结、归纳了驱动框架、字符驱动框架里面还及到平台总线知识点。接下来就是单总线协议通过对gpio 的控制结合单总线协议来实现基本功能。 这里讲解的是复位时序问题。前面的温度传感器-DS18B20驱动框架编写 、温度传感器-DS18B20字符设备驱动框架 就是写了框架其实驱动加载成功的 在Linux驱动-单总线-DS18b20-驱动设备树配置-GPIO复用 中有所讲解。接下来就是协议对接了对接外设协议。一、参考资料这里我们把以前的相关知识点拿到这里规整一下总线设备知识点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基本函数apiLinux驱动-GPIO子系统与pinctrl子系统相结合温度传感器-DS18B20驱动框架编写温度传感器-DS18B20字符设备驱动框架Linux驱动-单总线-DS18b20-驱动设备树配置-GPIO复用DS18B20 电路及驱动笔记其实 之前是对基础知识的补充这里其实核心是对协议的对接这里只是对复位操作的时序控制。二、DS18B20 驱动获取 GPIOgpio 获取-控制资料-知识点回顾驱动GPIO-获取单个gpio描述符Linux驱动-GPIO基本函数apiLinux驱动-单总线-DS18b20-驱动设备树配置-GPIO复用相关gpio 代码说明新增封装gpio 信息struct ds18b20_data{dev_t dev_num;// 什么一个字符设备号就是设备号struct cdev ds18b20_cdev;//声明一个字符设备structclass*ds18b20_class;//声明一个设备类struct device*ds18b20_device;//声明一个设备/devicestruct gpio_desc*ds18b20_gpio;// 5. GPIO 描述符内核标准 GPIO 操作对象控制 DS18B20 数据线};在probe 函数中获取gpio 信息-设置方向.............// 从设备树中获取 DS18B20 对应的 GPIO 引脚可选获取不强制ds18b20-ds18b20_gpiogpiod_get_optional(dev-dev,ds18b20,0);// 判断 GPIO 是否获取成功失败NULLif(ds18b20-ds18b20_gpioNULL){ret-EBUSY;// 返回 设备忙/资源占用 错误码gotoerror_4;// 跳转到错误处理释放前面申请的资源}// GPIO 获取成功 → 设置为输出模式默认输出高电平gpiod_direction_output(ds18b20-ds18b20_gpio,1);// 所有初始化成功返回 0return0;// 错误处理标签GPIO 获取失败销毁之前创建的设备节点error_4:device_destroy(ds18b20-ds18b20_class,ds18b20-dev_num);.............三、复位和应答信号时序分析这个复位时序图 是DS18b20 提供的是一个复位时序然后驱动工程师或者应用工程师拿到这个复位时序图 进行 复位程序编写。复位信号时序分析在复位的过程中 首先将 GPIO 引脚拉低 保持一段时间通常为 480 微秒 这是为了确保发送复位信号的低电平持续足够的时间触发 DS18b20 的复位操作。接下来 释放 GPIO 引脚 使其回到高电平状态 此时 DS18b20 将检测到引脚状态的变化并开始响应复位信号。在释放引脚之后需要等待一段时间 以确保 ds18b20 完成复位操作并准备好接收后续的命令应答信号时序分析在发送命令或数据之前 需要先向 DS18B20 发送一个启动信号 然后等待 DS18B20 的应答信号。 启动信号是通过将 GPIO 引脚拉低一小段时间通常为 1 微秒 然后立即拉高来发送的。接下来 驱动程序需要检查引脚的状态 以确定 DS18B20 是否发送了应答信号。DS18B20 的应答信号是在启动信号后的一小段时间内由 DS18B20 将引脚拉低发送的。 驱动程序需要检测引脚状态是否被成功拉低 以确认应答信号的接收。将复位时序转换成代码流程源码实现流程voidds18b20_reset(void){gpiod_direction_output(ds18b20-ds18b20_gpio,1);gpiod_set_value(ds18b20-ds18b20_gpio,0);udelay(700);gpiod_set_value(ds18b20-ds18b20_gpio,1);gpiod_direction_input(ds18b20-ds18b20_gpio);while(gpiod_get_value(ds18b20-ds18b20_gpio));while(!gpiod_get_value(ds18b20-ds18b20_gpio));udelay(480);}分析源码准备把 GPIO 设为输出高释放总线gpiod_direction_output(ds18b20-ds18b20_gpio,1);//复位开始前让总线保持高电平空闲状态发送复位拉低 GPIO保持 ≥480usgpiod_set_value(...,0);// 拉低 首先将 GPIO 引脚拉低保持一段时间通常为 480 微秒udelay(700);// 保持低电平 700us 这里代码用 700us 比标准 480us 更长更稳定、更兼容。释放总线拉高 GPIO → 转为输入高阻态gpiod_set_value(...,1);// 先输出高 接下来释放 GPIO 引脚使其回到高电平状态gpiod_direction_input(...,);// 转为输入真正释放总线 1-Wire 标准做法释放 转输入让上拉电阻控制电平。等待 DS18B20 拉低检测应答信号while(gpiod_get_value(...));// 等待引脚变低DS18b20 将检测到引脚状态的变化并开始响应复位信号。DS18B20 的应答信号是由 DS18B20 将引脚拉低发送的。这句话的意思主机释放总线 → 总线变高DS18B20 会主动拉低总线 作为应答 presence 信号代码在这里循环等待引脚变低就是在等待应答等待 DS18B20 释放总线应答结束while(!gpiod_get_value(...));// 等待引脚变回高释放引脚之后需要等待一段时间以确保 ds18b20 完成复位操作并准备好接收后续的命令DS18B20 拉低一段时间后会自动释放总线总线变回高电平代表复位 应答完成。最后等待 480us确保复位彻底完成udelay(480);等待一段时间确保 ds18b20 完成复位操作并准备好接收后续命令代码 ↔ 文字 协助理解代码步骤你的文字描述功能gpiod_direction_output(…,1)复位前准备总线初始高电平set 0 udelay(700)拉低 GPIO 保持 480us发送复位信号set 1 input 释放 GPIO回到高电平主机释放总线while(高电平等待)等待 DS18B20 拉低检测应答信号while(低电平等待)等待 DS18B20 释放应答结束udelay(480)等待复位完成确保芯片准备就绪四、DS18b20 驱动复位时序编写综上分析完整源码如下即可实现 复位业务。#includelinux/init.h#includelinux/module.h#includelinux/platform_device.h#includelinux/of.h#includelinux/fs.h#includelinux/cdev.h#includelinux/kdev_t.h#includelinux/slab.h#includelinux/gpio.h#includelinux/gpio/consumer.h#includelinux/delay.hstruct ds18b20_data{dev_t dev_num;// 什么一个字符设备号就是设备号struct cdev ds18b20_cdev;//声明一个字符设备structclass*ds18b20_class;//声明一个设备类struct device*ds18b20_device;//声明一个设备struct gpio_desc*ds18b20_gpio;};struct ds18b20_data*ds18b20;voidds18b20_reset(void){gpiod_direction_output(ds18b20-ds18b20_gpio,1);gpiod_set_value(ds18b20-ds18b20_gpio,0);udelay(700);gpiod_set_value(ds18b20-ds18b20_gpio,1);gpiod_direction_input(ds18b20-ds18b20_gpio);while(gpiod_get_value(ds18b20-ds18b20_gpio));while(!gpiod_get_value(ds18b20-ds18b20_gpio));udelay(480);}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{.opends18b20_open,.readds18b20_read,.releaseds18b20_release,.ownerTHIS_MODULE,};struct ds18b20_data*ds18b20;intds18b20_probe(struct platform_device*dev){intret;printk(This is probe \n);ds18b20kzalloc(sizeof(*ds18b20),GFP_KERNEL);if(ds18b20NULL){printk(kzalloc error \n);gotoerror_0;}// 第一步骤:申请设备号retalloc_chrdev_region(ds18b20-dev_num,0,1,myds18b20);//通过动态方式进行设备号注册if(ret0){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.ownerTHIS_MODULE;// 将 owner 字段指向本模块 可以避免在模块的操作正在被使用时卸载该模块// 第三步字符设备的注册 - cdev_addretcdev_add(ds18b20-ds18b20_cdev,ds18b20-dev_num,1);// 使用 cdev_add()函数进行字符设备的添加// 第四步class_create() - 创建设备类ds18b20-ds18b20_classclass_create(THIS_MODULE,sensors);//使用class_create进行类的创建类名称为sensorsif(IS_ERR(ds18b20-ds18b20_class)){printk(class_create error\n);retPTR_ERR(ds18b20-ds18b20_class);gotoerror_2;}// 第五步创建device device_create() - 创建设备节点ds18b20-ds18b20_devicedevice_create(ds18b20-ds18b20_class,NULL,ds18b20-dev_num,NULL,ds18b20);//使用device_create进行设备的创建设备名称为device_testif(IS_ERR(ds18b20-ds18b20_device)){printk(device_create error\n);retPTR_ERR(ds18b20-ds18b20_device);gotoerror_3;}// 从设备树中获取 DS18B20 对应的 GPIO 引脚可选获取不强制ds18b20-ds18b20_gpiogpiod_get_optional(dev-dev,ds18b20,0);// 判断 GPIO 是否获取成功失败NULLif(ds18b20-ds18b20_gpioNULL){ret-EBUSY;// 返回 设备忙/资源占用 错误码gotoerror_4;// 跳转到错误处理释放前面申请的资源}// GPIO 获取成功 → 设置为输出模式默认输出高电平gpiod_direction_output(ds18b20-ds18b20_gpio,1);// 所有初始化成功返回 0return0;// 错误处理标签GPIO 获取失败销毁之前创建的设备节点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[]{{.compatibleds18b20},{},};struct platform_driver ds18b20_driver{.driver{.ownerTHIS_MODULE,.nameds18b20,.of_match_tableds18b20_match_table,},.probeds18b20_probe,};staticint__initds18b20_init(void){intret;retplatform_driver_register(ds18b20_driver);if(ret0){printk(platform_driver_register error\n);return-1;}ds18b20_reset();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);总结gpio 相关操作熟悉、温习ds18b20 复位时序的理解对业务如何代码实现的理解。初学者就是很难理解要自己理解基础上写出来。