RK3568驱动指南|第十四篇 单总线-第162章DS18B20驱动读时序编写

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第十四篇 单总线_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第162章DS18B20驱动读时序编写

在上个章节中完善了DS18B20驱动的写时序,在本小节中将完善DS18B20的读时序。

162.1 读时序分析

DS18B20数据手册中关于读取的时序图如下所示:

关于DS18B20有读0和读1两种时序,他们的时序是不同的,接下来首先对前半部分读0进行分析。

步骤1:主机拉低总线,从高电平变成低电平,且有时间限制,要确保拉低的时间最少为1微秒

步骤2:主机释放总线,从机拉低总线,15us以内主机完成采样工作,如果这时候从机仍旧处于拉低总线的状态,则采集到的就是0.

步骤3:拉高总线,恢复总线的高电平状态,且要求读操作必须大于60毫秒。

总结出的读取0操作代码如下所示:

unsigned char ds18b20_readbit(void) {unsigned char bit;        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        udelay(2);// 延时 2 微秒        gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   udelay(10);// 延时 10 微秒       bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       udelay(60);// 延时 60 微秒return bit;
}

然后来对读1的步骤进行分析:

步骤1:主机拉低总线,从高电平变成低电平,且有时间限制,要确保拉低的时间最少为1微秒

步骤2:通过电阻进行上拉,15us以内主机完成采样工作,如果这时候处于电阻上拉的状态,则采集到的就是1

总结出的读1操作代码如下所示:

unsigned char ds18b20_readbit(void) {unsigned char bit;        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        udelay(2);// 延时 2 微秒        gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       return bit;
}

综合上面读1和读0操作的代码以及时序图,可以将两个代码进行整合在一起,整合之后的代码如下所示,大家可以神奇的发现该函数和读0中的函数是相同的。

unsigned char ds18b20_readbit(void) {unsigned char bit;        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        udelay(2);// 延时 2 微秒        gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   udelay(10);// 延时 10 微秒       bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       udelay(60);// 延时 60 微秒return bit;
}

但这样修改之后的代码仅仅只能读取一个字符,如果要读取8位字符就需要连续使用8次该函数,而为了更方便,可以重新添加一个函数,从而直接读取一个字节的数据,具体内容如下所示:

/*** 从 DS18B20 读取单个位(bit)* @return 读取到的位(bit),0 或 1*/
unsigned char ds18b20_readbit(void) {unsigned char bit;        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        udelay(2);// 延时 2 微秒        gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   udelay(10);// 延时 10 微秒       bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       udelay(60);// 延时 60 微秒return bit;
}/*** 从 DS18B20 读取一个字节(byte)数据* @return 读取到的字节数据*/
int ds18b20_readbyte(void) {int data = 0;int i;for (i = 0; i < 8; i++) {// 读取单个位(bit)并根据位的位置进行左移操作data |= ds18b20_readbit() << i;}return data;
}

至此,关于DS18B20的读操作相关函数就编写完成了,会在下个小节编写填加写时序相关函数的驱动。

162.2 DS18b20驱动读时序编写

本实验对应的网盘路径为:iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\97_ds18b20_04\

编写完成的ds18b20.c代码如下所示:

#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;struct class *ds18b20_class;struct device *ds18b20_device;struct gpio_desc *ds18b20_gpio;
};struct ds18b20_data *ds18b20;void ds18b20_reset(void)
{// 设置 GPIO 方向为输出,输出低电平gpiod_direction_output(ds18b20->ds18b20_gpio, 1);gpiod_set_value(ds18b20->ds18b20_gpio, 0);udelay(700); // 延迟 700 微秒// 设置 GPIO 输出高电平,并将 GPIO 方向设置为输入gpiod_set_value(ds18b20->ds18b20_gpio, 1);gpiod_direction_input(ds18b20->ds18b20_gpio);// 等待直到 GPIO 输入为低电平while (gpiod_get_value(ds18b20->ds18b20_gpio));// 等待直到 GPIO 输入为高电平while (!gpiod_get_value(ds18b20->ds18b20_gpio));udelay(480); // 延迟 480 微秒
}/*** 向 DS18B20 写入单个位(bit)* @param bit 要写入的位(bit),0 或 1*/
void ds18b20_writebit(unsigned char bit) {// 将 GPIO 方向设置为输出gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 输出设置为指定的位(bit)gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 若 bit 为 1,则延时 10 微秒if (bit){udelay(10);// 将 GPIO 方向设置为输出gpiod_direction_output(ds18b20->ds18b20_gpio, 1);}// 延时 65 微秒udelay(65);// 将 GPIO 方向设置为输出gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 延时 2 微秒udelay(2);
}/*** 向 DS18B20 写入一个字节(byte)数据* @param data 要写入的字节数据*/
void ds18b20_writebyte(int data) {int i;for (i = 0; i < 8; i++) {// 逐位写入数据ds18b20_writebit(data & 0x01);data = data >> 1;}
}/*** 从 DS18B20 读取单个位(bit)* @return 读取到的位(bit),0 或 1*/
unsigned char ds18b20_readbit(void) {unsigned char bit;        gpiod_direction_output(ds18b20->ds18b20_gpio, 1);// 将 GPIO 方向设置为输出        gpiod_set_value(ds18b20->ds18b20_gpio, 0);// 将 GPIO 输出设置为低电平        udelay(2);// 延时 2 微秒        gpiod_direction_input(ds18b20->ds18b20_gpio);// 将 GPIO 方向设置为输入   udelay(10);// 延时 10 微秒       bit = gpiod_get_value(ds18b20->ds18b20_gpio);// 读取 GPIO 的值作为位(bit)       udelay(60);// 延时 60 微秒return bit;
}/*** 从 DS18B20 读取一个字节(byte)数据* @return 读取到的字节数据*/
int ds18b20_readbyte(void) {int data = 0;int i;for (i = 0; i < 8; i++) {// 读取单个位(bit)并根据位的位置进行左移操作data |= ds18b20_readbit() << i;}return data;
}int ds18b20_open(struct inode *inode, struct file *file)
{return 0;
}ssize_t ds18b20_read(struct file *file, char __user *buf, size_t size, loff_t *offs)
{return 0;
}int ds18b20_release(struct inode *inode, struct file *file)
{return 0;
}struct file_operations ds18b20_fops = {.open = ds18b20_open,.read = ds18b20_read,.release = ds18b20_release,.owner = THIS_MODULE,
};int ds18b20_probe(struct platform_device *dev)
{int ret;printk("This is probe \n");// 分配内存给ds18b20_data结构体ds18b20 = kzalloc(sizeof(*ds18b20), GFP_KERNEL);if (ds18b20 == NULL){printk("kzalloc error\n");ret = -ENOMEM;goto error_0;}// 分配字符设备号ret = alloc_chrdev_region(&ds18b20->dev_num, 0, 1, "myds18b20");if (ret < 0){printk("alloc_chrdev_region error\n");ret = -EAGAIN;goto error_1;}// 初始化字符设备cdev_init(&ds18b20->ds18b20_cdev, &ds18b20_fops);ds18b20->ds18b20_cdev.owner = THIS_MODULE;cdev_add(&ds18b20->ds18b20_cdev, ds18b20->dev_num, 1);// 创建设备类ds18b20->ds18b20_class = class_create(THIS_MODULE, "sensors");if (IS_ERR(ds18b20->ds18b20_class)){printk("class_create error\n");ret = PTR_ERR(ds18b20->ds18b20_class);goto error_2;}// 创建设备ds18b20->ds18b20_device = device_create(ds18b20->ds18b20_class, NULL, ds18b20->dev_num, NULL, "ds18b20");if (IS_ERR(ds18b20->ds18b20_device)){printk("device_create error\n");ret = PTR_ERR(ds18b20->ds18b20_device);goto error_3;}// 获取GPIO描述符ds18b20->ds18b20_gpio = gpiod_get_optional(&dev->dev, "ds18b20", 0);if (ds18b20->ds18b20_gpio == NULL){ret = -EBUSY;goto error_4;}// 设置GPIO方向为输出gpiod_direction_output(ds18b20->ds18b20_gpio, 1);return 0;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:return ret;
}const struct 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,
};static int __init ds18b20_init(void)
{int ret;// 注册平台驱动ret = platform_driver_register(&ds18b20_driver);if (ret < 0){printk("platform_driver_register error\n");return -1;}return 0;
}static void __exit ds18b20_exit(void)
{// 释放资源gpiod_put(ds18b20->ds18b20_gpio);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");

由于读时序的实验需要后续的知识作为支撑,所以会在下个小节的驱动中完善温度读取相关的函数之后,再进行测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/787353.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秒验资深玩家熬夜整理的15个常见问题,拿走不谢!

1、双卡双待手机如何登录&#xff1f; 针对双卡双待手机只取当前流量卡号 2、用其他手机号如何登录&#xff1f; 使用传统验证方式登录&#xff0c;例如验证码登录 3、一键登录只支持4G吗&#xff1f; 电信支持4G,5G网络取号&#xff0c;移动, 联通支持5G,4G, 3G, 2G网络取号…

数值金额计算js封装包含加减乘除四个方法,能确保浮点数运算不丢失精度

项目场景&#xff1a; 商城类项目中大多需要金额计算&#xff0c;我们知道计算机编程语言里浮点数计算会存在精度丢失问题&#xff08;或称舍入误差&#xff09;&#xff0c;其根本原因是二进制和实现位数限制有些数无法有限表示 以下是十进制小数对应的二进制表示&#xff1…

python笔记(8)Tuple(元组)

目录 创建元组 元组取数 不支持修改和删除元素 元组运算符 元组内置函数 创建元组 Tuple 元组的元素不能修改&#xff0c;元组使用小括号&#xff08;&#xff09;&#xff0c;创建元组在括号里添加元素用逗号隔开即可。 创建空元组 tup1() 创建一个元素的元组&#xff…

Golang 开发实战day06 - Boolean Conditional

Golang 教程06 - Boolean & Conditional 1. Boolean & Conditional 1.1 什么是布尔类型&#xff1f; 想象一下&#xff0c;你正在玩一个古老的游戏&#xff0c;只有两个选项&#xff1a;是或否。在 Golang 中&#xff0c;这就是布尔类型&#xff0c;用 bool 关键字表…

【Linux实验室】DNS域名解析服务——超详细实验操作!

DNS域名解析 DNS域名解析服务——超详细实验操作&#xff01;&#xff01;&#xff01;序言DNS 基本概述分布式、层次数据库DNS 层次结构DNS 查询步骤DNS 查询类型DNS服务器类型DNS 缓存反向 DNS 查询如何检查 DNS 记录是否生效 Bind解析服务Bind简介bind的服务类型 DNS域名解析…

深入解析实时数仓Doris:Rollup上卷表与查询

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、基本概念二、Aggregate 和 Unique 模型中的 ROLLUP三、Duplicate 模型中的 ROLLUP四、ROLLUP 调整前缀索引五、ROLLUP使…

【深耕 Python】Data Science with Python 数据科学(7)书352页练习题

写在前面 关于数据科学环境的建立&#xff0c;可以参考我的博客&#xff1a; 【深耕 Python】Data Science with Python 数据科学&#xff08;1&#xff09;环境搭建 往期数据科学博文&#xff1a; 【深耕 Python】Data Science with Python 数据科学&#xff08;2&#xf…

Android 使用LeakCanary检测内存泄漏,分析原因

内存泄漏是指无用对象&#xff08;不再使用的对象&#xff09;持续占有内存或无用对象的内存得不到及时释放&#xff0c;从而造成内存空间的浪费称为内存泄漏。 平时我们在使用app时&#xff0c;少量的内存泄漏我们是发现不了的&#xff0c;但是当内存泄漏达到一定数量时&…

每日面经分享(pytest测试案例,接口断言,多并发断言)

pytest对用户登录接口进行自动化脚本设计 a. 创建一个名为"test_login.py"的测试文件&#xff0c;编写以下测试脚本 import pytest import requests# 测试用例1&#xff1a;验证登录成功的情况 # 第一个测试用例验证登录成功的情况&#xff0c;发送有效的用户名和密…

统计数码出现的个数

题目描述 输入一个数n&#xff0c;求出 [1, n] 中每个数码出现的次数&#xff0c;即0 - 9每个数出现的次数。 解题思路 首先是无情的暴力法&#xff0c;可以用于判断我们后续的优化代码是否正确。 import java.io.*; import java.util.*;public class Main1 {static int n;p…

iOS系统文件备份与还原:保护和管理手机中的关键数据

​ 目录 引言 用户登录工具和连接设备 查看设备信息&#xff0c;电池信息 查看硬盘信息 硬件信息 查看 基带信息 销售信息 电脑可对手机应用程序批量操作 运行APP和查看APP日志 IPA包安装测试 注意事项 引言 苹果手机与安卓手机不同&#xff0c;无法直接访问系统文件…

Chatgpt掘金之旅—有爱AI商业实战篇|文案写作|(三)

演示站点&#xff1a; https://ai.uaai.cn 对话模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 一、前言 人工智能&#xff08;AI&#xff09;技术作为当今科技创新的前沿领域&#xff0c;为创业者提供了广阔的机会和挑战。随着AI技术的快速发展和应用领域的不断拓展&…

#设计模式#4.6 Flyweight(享元) 对象结构型模式

享元模式是一种结构型设计模式&#xff0c;其主要目标是通过共享大量细粒度的对象来节省内存。享元模式的关键在于区分内部状态&#xff08;Intrinsic State&#xff09;和外部状态&#xff08;Extrinsic State&#xff09;。 内部状态是对象可共享的部分&#xff0c;通常是对…

是否应该升级到ChatGPT 4.0?深度对比ChatGPT 3.5与4.0的差异

如果只是想简单地体验AI的魅力&#xff0c;感受大模型的独特之处&#xff0c;或是玩一玩文字游戏&#xff0c;那么升级至ChatGPT 4.0可能并非必需。然而&#xff0c;若你期望将AI作为提升工作学习效率的得力助手&#xff0c;那么我强烈建议你升级到ChatGPT 4.0。 如果你不知道…

Linux和Windows安装PHP依赖管理工具Composer

Composer 是 PHP 的一个依赖管理工具。它允许申明项目所依赖的代码库&#xff0c;会在项目中安装它们。 Composer 不是一个包管理器。是的&#xff0c;它涉及 "packages" 和 "libraries"&#xff0c;但它在每个项目的基础上进行管理&#xff0c;在你项目的…

【Springboot整合系列】SpringBoot整合WebService

目录 Web服务介绍Web服务的两种类型Web服务架构Web服务的主要特点Web服务使用场景Web服务标准和技术 WebService介绍WebService的作用适用场景不适用场景 WebService的原理三个角色相关概念 WebService开发框架代码实现服务端1.引入依赖2.实体类3.业务层接口接口实现类 4.配置类…

python对接百度云车牌识别

注册百度智能云&#xff0c;选择产品服务。 https://console.bce.baidu.com/ 每天赠送200次&#xff0c;做开发测试足够了。 在应用列表复制 AppID , API Key ,Secret Key 备用。 SDK下载地址 https://ai.baidu.com/sdk#ocr 下载SDK文件&#xff0c;解压&#xff0c;…

matlab中旋转矩阵函数

文章目录 matlab里的旋转矩阵、四元数、欧拉角四元数根据两向量计算向量之间的旋转矩阵和四元数欧拉角转旋转矩阵旋转矩阵转欧拉角旋转矩阵转四元数参考链接 matlab里的旋转矩阵、四元数、欧拉角 旋转矩阵dcmR四元数quatq[q0,q1,q2,q3]欧拉角angle[row,pitch,yaw] % 旋转矩阵…

前端跨页面通信方案介绍

在浏览器中&#xff0c;我们可以同时打开多个Tab页&#xff0c;每个Tab页可以粗略理解为一个“独立”的运行环境&#xff0c;即使是全局对象也不会在多个Tab间共享。然而有些时候&#xff0c;我们希望能在这些“独立”的Tab页面之间同步页面的数据、信息或状态。这就是本文说说…

算法学习——LeetCode力扣动态规划篇2(343. 整数拆分、96. 不同的二叉搜索树、416. 分割等和子集、1049. 最后一块石头的重量 II)

算法学习——LeetCode力扣动态规划篇2 343. 整数拆分 343. 整数拆分 - 力扣&#xff08;LeetCode&#xff09; 描述 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得…