Linux驱动开发基础(DS18B20温度模块)

news2024/11/15 6:47:24

所学来自百问网

目录

1.DS18B20 简介

2.硬件设计

3.软件设计

3.1 存储器介绍

3.2 通信时序

3.2.1 初始化时序

3.2.2 写时序

3.2.3 读时序

3.3 常用命令

4. 示例代码

4.1 驱动代码

4.2 应用代码

4.3 Makefile

4.4 实验效果


1.DS18B20 简介

DS18B20 温度传感器具有线路简单、体积小的特点,用来测量温度非常简单, 在一根通信线上可以挂载多个 DS18B20 温度传感器。用户可以通过编程实现 9~12 位的温度读数,每个DS18B20有唯一的64位序列号,保存在rom中,因 此一条总线上可以挂载多个DS18B20。

2.硬件设计

DS18B20 也使用的是“1-Wire单总线”,只通过一条数据线传输数据,既要控制器发送数据给芯片,又要通过芯片发送数据给控制器,是双向传输数据。

原理图如下:

3.软件设计

3.1 存储器介绍

DS18B20内部有个64位只读存储器(ROM)和 64位配置存储器(SCRATCHP)。 64 位只读存储器(ROM)包含序列号等,具体格式如下图:

低八位用于CRC校验,中间48位是DS18B20唯一序列号,高八位是该系列产品系列号(固定为28h)。因此,根据每个DS18B20 唯一的序列号,可以实现一条总线上可以挂载多个DS18B20时,获取指定DS18B20的温度信息。

64 位配置存储器(SCRATCHP)由9个Byte组成,包含温度数据、配置信息等,具体格式如下图:

Byte[0:1]:温度值。也就是当我们发出一个测量温度的命令之后,还需要发送一个读内存的命令才能把温度值读取出来。

Byte[2:3]:TL是低温阈值设置,TH是高温阈值设置。当温度低于/超过阈值,就会报警。 TL、TH存储在EEPROM中,数据在掉电时不会丢失;

Byte4:配置寄存器。用于配置温度精度为9、10、11或12位。配置寄存器也存储在EEPROM中,数据在掉电时不会丢失;

Byte[5:7]:厂商预留;

Byte[8]:CRC校验码。

3.2 通信时序

3.2.1 初始化时序

主机要跟DS18B20通信,首先需要发出一个开始信号。 深黑色线表示由主机驱动信号,浅灰色线表示由DS18B20驱动信号。 最开始时引脚是高电平,想要开始传输信号必须要拉低至少480us,这是复位信号; 然后拉高释放总线,等待15~60us之后,如果GPIO上连有DS18B20芯片,它会拉低60~240us。如果主机在最后检查到60~240us的低脉冲,则表示DS18B20初始化成功。

3.2.2 写时序

写0:拉低至少60us(写周期为60-120us)即可;

写1:先拉低至少1us,然后拉高,整个写周期至少为60us即可。

3.2.3 读时序

主机先拉低至少1us,随后读取电平,如果为0,即读到的数据是0,如果为1,即可读到的数据是1。

注意:整个过程必须在15us内完成,15us后引脚都会被拉高。

3.3 常用命令

DS18B20中有两类命令:ROM命令、功能命令

列表如下:

4. 示例代码

4.1 驱动代码

#include <linux/module.h>
#include <linux/poll.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/version.h>


static int major;
static struct class *ds18b20_class;
static struct gpio_desc *ds18b20_gpio_pin;

// 延时函数
void ds18b20_delay_us(int us)
{
	// 定义变量记录时间
	u64 pre, last;
// 根据版本不同调用不同的函数
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
	pre = ktime_get_boot_ns(); 
	while(1)
	{
		last = ktime_get_boot_ns();
		if(last - pre >= us * 1000)
			break;
	}
#else
	pre = ktime_get_boottime_ns();
	while(1)
	{
		last = ktime_get_boottime_ns();
		if(last - pre >= us * 1000)
			break;
	}
#endif
}

// 响应信号 
// 确认ds18b20设备的存在 先拉高后拉低 表示可以接收信号
int ds18b20_wait_for_ack(void)
{
	
	int timeout_count = 500;
	while(gpiod_get_value(ds18b20_gpio_pin) && timeout_count)
	{
		udelay(1);
		timeout_count--;
	}
	if(!timeout_count)
	{
		return -1;
	}
	
	timeout_count = 500;
	while(!gpiod_get_value(ds18b20_gpio_pin) && timeout_count)
	{
		udelay(1);
		timeout_count--;
	}
	if(!timeout_count)
	{
		return -1;
	}
	return 0;
	
}

// 复位信号
static int ds18b20_reset(void)
{

	int ret;
	// 拉低480us,这是复位信号
	gpiod_direction_output(ds18b20_gpio_pin, 0);
	ds18b20_delay_us(480);
	// 设置引脚为输入模式
	ret = gpiod_direction_input(ds18b20_gpio_pin);
	if(ds18b20_wait_for_ack())
		return -1;
	else
		return 0;
}

// 写字节
static void ds18b20_write_byte(unsigned char data)
{
	int i;
	for(i = 0; i < 8; i++)
	{
		if(data & (1 << i))
		{
			// 输出1 
			gpiod_direction_output(ds18b20_gpio_pin, 0);
			ds18b20_delay_us(2);

			gpiod_direction_input(ds18b20_gpio_pin);
			ds18b20_delay_us(60);
		}else{
			// 输出0
			gpiod_direction_output(ds18b20_gpio_pin, 0);
			ds18b20_delay_us(60);

			gpiod_direction_input(ds18b20_gpio_pin);
			ds18b20_delay_us(2);
		}

	}

}
// 读字节
unsigned char ds18b20_read_byte(void)
{
	unsigned char data = 0;
	int i;

	for(i = 0; i < 8; i++)
	{
		gpiod_direction_output(ds18b20_gpio_pin, 0);
		ds18b20_delay_us(2);
		/* 设置为输入 */
		gpiod_direction_input(ds18b20_gpio_pin);
		/* 7us之后读引脚 */
		ds18b20_delay_us(7);
		if (gpiod_get_value(ds18b20_gpio_pin))
			data |= (1<<i);

		/* 读到数据后, 等待足够60us */
		ds18b20_delay_us(60);				
	}
	return data;
}

static ssize_t ds18b20_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	unsigned long flags;
	unsigned char tempL=0,tempH=0;
	unsigned int integer;
	unsigned char decimal1,decimal2,decimal;
	int err;
	if (size != 5)
		return -EINVAL;

	local_irq_save(flags);	  // 关中断

	if (ds18b20_reset())
	{
		gpiod_direction_output(ds18b20_gpio_pin, 1);
		
		local_irq_restore(flags); // 恢复中断
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -ENODEV;
	}

	ds18b20_write_byte(0xcc); //忽略rom指令,直接使用功能指令
	ds18b20_write_byte(0x44); //温度转换指令

	/* 不能省略! */
	gpiod_direction_output(ds18b20_gpio_pin, 1);

	local_irq_restore(flags); // 恢复中断

	//转换需要时间,延时1s
	set_current_state(TASK_INTERRUPTIBLE);
	schedule_timeout(HZ); 

	local_irq_save(flags);	  // 关中断
	if (ds18b20_reset())
	{		
		gpiod_direction_output(ds18b20_gpio_pin, 1);
		local_irq_restore(flags); // 恢复中断
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		return -ENODEV;
	}
	
	ds18b20_write_byte(0xcc); //忽略rom指令,直接使用功能指令
	ds18b20_write_byte(0xbe); //读暂存器指令

	tempL = ds18b20_read_byte(); //读温度低8位
	tempH = ds18b20_read_byte(); //读温度高8位
	if (tempH > 0x7f)      							//最高位为1时温度是负
	{
		tempL    = ~tempL;         				    //补码转换,取反加一
		tempH    = ~tempH+1;      
		integer  = tempL/16+tempH*16;      			//整数部分
		decimal1 = (tempL&0x0f)*10/16; 			//小数第一位
		decimal2 = (tempL&0x0f)*100/16%10;			//小数第二位
		decimal  = decimal1*10+decimal2; 			//小数两位
	}
	else
	{
		integer  = tempL/16+tempH*16;      				//整数部分
		decimal1 = (tempL&0x0f)*10/16; 					//小数第一位
		decimal2 = (tempL&0x0f)*100/16%10;				//小数第二位
		decimal  = decimal1*10+decimal2; 				//小数两位
	}
		
	local_irq_restore(flags); // 恢复中断
	
	gpiod_direction_output(ds18b20_gpio_pin, 1);

	err = copy_to_user(buf, &integer, 4);
	err = copy_to_user(buf+4, &decimal, 1);

	return 5;
	
	

}
static unsigned int ds18b20_poll (struct file *file, struct poll_table_struct *wait)
{

	return 0;
}

static struct file_operations ds18b20_opes = {
	.owner = THIS_MODULE,
	.read = ds18b20_read,
	.poll = ds18b20_poll,
};

int ds18b20_probe(struct platform_device *pdev)
{
	ds18b20_gpio_pin = gpiod_get(&pdev->dev, NULL, GPIOD_OUT_HIGH);

	device_create(ds18b20_class,  NULL, MKDEV(major, 0), NULL, "myds18b20");
	return 0;
}
int ds18b20_remove(struct platform_device *pdev)
{
	device_destroy(ds18b20_class, MKDEV(major, 0));
	gpiod_put(ds18b20_gpio_pin);
	return 0;
}

static const struct of_device_id ask100_ds18b20[] = {
	{ .compatible = "100ask,ds18b20" },
	{},

};

static struct platform_driver ds18b20_dri = {
	.probe = ds18b20_probe,
	.remove = ds18b20_remove,
	.driver = {
		.name = "100ask_ds18b20",
		.of_match_table = ask100_ds18b20,
	},

};

static int __init ds18b20_init(void)
{
	int err;
	major =	register_chrdev(0, "ds18b20", &ds18b20_opes);
	ds18b20_class = class_create(THIS_MODULE, "ds18b20_class");

	err = platform_driver_register(&ds18b20_dri);
	return err;
}

static void __exit ds18b20_exit(void)
{
	unregister_chrdev(major,"ds18b20");
	class_destroy(ds18b20_class);

	platform_driver_unregister(&ds18b20_dri);
	
}


module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");

4.2 应用代码


#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>


/*
 * ./ds18b20_test /dev/myds18b20
 *
 */
int main(int argc, char **argv)
{
	int fd;
	unsigned char data[5];
	unsigned int integer;
	unsigned char decimal;

	int i;
	
	/* 1. 判断参数 */
	if (argc != 2) 
	{
		printf("Usage: %s <dev>\n", argv[0]);
		return -1;
	}


	/* 2. 打开文件 */
//	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	fd = open(argv[1], O_RDWR);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}


	while (1)
	{
		if (read(fd, data, 5) == 5)
		{
			integer = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
			decimal = data[4];
			
			printf("get temprature: %d.%d\n", integer, decimal);
		}
		else 
		{
			printf("get temprature: -1\n");
		}
		sleep(1);
	}
	
	close(fd);
	
	return 0;
}

4.3 Makefile


# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
#       请参考各开发板的高级用户使用手册

#KERN_DIR =  /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4
KERN_DIR =  /home/book/100ask_imx6ull-sdk/Linux-4.9.88

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	$(CROSS_COMPILE)gcc -o ds18b20_test ds18b20_test.c
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order  ds18b20_test

# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o



obj-m += ds18b20_drv.o

4.4 实验效果

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

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

相关文章

[线程]阻塞队列

文章目录 阻塞队列生产者消费者模型通过BlockingQueue理解阻塞队列自己实现阻塞队列 阻塞队列 我们之前学的队列, 其实是最基础的队列, 实际开发中, 针对队列还有很多种变种 普通队列优先级队列阻塞队列 先进先出, 线程安全, 并且带有阻塞功能 阻塞功能指: 如果队列为空, 尝试…

23种设计模式之模板模式

一.什么是模板模式 ‌‌模板模式是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;而将一些步骤留给子类实现。‌这种模式允许子类在不改变算法结构的基础上&#xff0c;重新定义算法的某些步骤。模板模式属于行为型设计模式&#xff0c;主要用于处理那些需…

excel透视图、看板案例(超详细)

一、简介 Excel透视图&#xff08;Pivot Table&#xff09; 功能&#xff1a;透视图是一种强大的数据分析工具&#xff0c;用于汇总、分析和展示数据。它允许用户对数据进行重新排列和分类&#xff0c;从而更容易发现数据中的模式和趋势。用途&#xff1a;可以用来生成动态报表…

python07-单元测试框架unittest1-3

当测试用例数量增加&#xff0c;一个一个执行效率低下&#xff0c;需要将工程下的&#xff0c;case收集并按顺序执行将对应的代码放入run_tests.py run_tests.py&#xff1a;运行程序目的 收集所有的测试用例执行生成测试报告 运用测试用例的收集器或测试用例的加载器 7 Tes…

2.4梯度下降与量化策略优化

1. 梯度下降法的基本原理 欢迎来到“梯度下降”的世界&#xff01;听上去有点像在爬山对吧&#xff1f;其实&#xff0c;这个算法的灵感确实来自爬山。想象你在一个山谷中迷路了&#xff0c;周围雾蒙蒙的&#xff0c;看不清楚路&#xff0c;只能摸着石头一步一步往下走。每走一…

短效ip—互联网利器

《瞬息万变&#xff1a;短效IP在网络世界的奇幻之旅》 在浩瀚无垠的数字宇宙中&#xff0c;互联网如同一条奔腾不息的河流&#xff0c;携带着无数创新与技术的浪花。在这片日新月异的疆域里&#xff0c;短效IP以其独有的魅力&#xff0c;悄然成为网络探险家们手中的魔法钥匙。它…

编译原理概述

编译原理概述 编译原理是计算机科学的重要领域&#xff0c;主要研究编译器如何将高级编程语言转换为机器可执行代码。编译器的工作流程可以分为多个阶段&#xff0c;每个阶段都有特定的功能和目标。理解编译原理对于编写高效的代码、优化程序性能以及开发新语言或编译器非常重…

Java 线程实现暂停、中止

需求&#xff1a;用户可以开启任务&#xff0c;暂停任务和中止任务。 用户开启任务后&#xff0c;可以随时暂停或者中止。暂停后又可以回到原进度继续运行。 这里写目录标题 demo版-使用废弃的stop、suspend、resume实现为什么废弃了&#xff1f;不用stop&#xff0c;如何销毁线…

MySQL5.7.36之主从复制部署安装-centos7

主库是192.168.31.209:3306 从库是192.168.31.210:3308、192.168.31.209:3307、192.168.31.210:3309、192.168.31.211:3310、192.168.31.211:3311 切记&#xff1a;不管是主库还是从库&#xff0c;server_id一定不能重复 1、主库创建复制账号及授权 create user repl% iden…

Linux驱动开发基础(IRDA 红外遥控模块)

所学来自百问网 目录 1.红外遥控简介 2.硬件设计 3.软件设计 4. 示例代码 4.1 驱动代码 4.2 Makefile 4.3 实验效果 1.红外遥控简介 红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中&#xff0c;像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统…

分类预测|基于灰狼GWO优化BP神经网络的数据分类预测Matlab程序GWO-BP 含基础BP对比模型

分类预测|基于灰狼GWO优化BP神经网络的数据分类预测Matlab程序GWO-BP 含基础BP对比模型 文章目录 一、基本原理1. 灰狼优化算法&#xff08;GWO&#xff09;简介GWO的基本步骤 2. BP神经网络简介**BP网络的基本结构****训练过程** 3. GWO-BP分类预测的结合**结合流程** 4. GWO-…

苹果mac数据恢复概率大吗 mac数据恢复专业软件哪个好用

一般情况下&#xff0c;当我们把电脑中的数据删掉后&#xff0c;都会保存在回收站里面&#xff0c;但如果回收站被清空了或者数据在回收站中没有找到的话&#xff0c;那么&#xff0c;之前被删掉的数据还能恢复吗&#xff1f;恢复的概率有多大呢&#xff1f; 答案是可以的&…

Hive 案例分析(B站用户行为大数据分析)

Hive 案例分析&#xff08;B站用户行为大数据分析&#xff09; 一、案例需求二、设计数据表结构2.1 user 表结构2.2 video 表结构 三、创建数据表3.1 创建 video 数据库3.2 创建外表3.1.2 创建 external_user3.1.3 创建 external_video 3.2 创建内表3.2.1 创建 orc_user3.2.2 创…

Atlas阿特拉斯wordpress主题

Atlas阿特拉斯是一个专为WordPress平台设计的多功能主题&#xff0c;该主题由简站wordpress主题开发&#xff0c;旨在为用户提供一个强大而灵活的工具&#xff0c;以构建各种类型的网站。以下是对Atlas阿特拉斯WordPress主题的简介&#xff1a; Atlas阿特拉斯WordPress主题简介…

谷歌账号被停用了怎么申诉?申诉了好多天没有收到回复怎么办?申诉了很多次都被拒了怎么办?只有一个办法

这段时间似乎谷歌的风控措施又变严了&#xff0c;许多新账号被封&#xff08;尤其是买来的账号或者新注册的账号&#xff09;&#xff0c;甚至有一些使用了一年以上的老账号“莫名其妙地”也封了。 注&#xff1a;这里的封号是谷歌用户的一个通俗的说法&#xff0c;实际上指的…

【AIGC】MimicMotion:姿态引导的高质量人体运动视频生成技术

资源 论文&#xff1a;https://arxiv.org/pdf/2406.19680 github:https://github.com/Tencent/MimicMotion comfyui:https://github.com/kijai/ComfyUI-MimicMotionWrapper 核心要点 1. confidence-aware pose guidance可以确保高质量视频和时间维度上的帧与帧之间的平滑 …

MySQL的索引原理及使用

索引模型&#xff08;基础数据结构&#xff09; 索引模型&#xff1a;哈希表、有序数组、搜索树&#xff0c;这里的模型是指索引的底层实现的基本数据结构&#xff0c;Mysql中不同的引擎对于索引的实现结构说的即是索引模型。 有序数组 有序数组这个在数据结构中是最基础的结…

【开关电源】数字交错式升压功率因数校正解析(1)

文章目录 Digital Interleaved PFC 链接: TIDM-02010 Dual motor control with digital interleaved PFC for HVAC reference design Digital Interleaved PFC 电路的主干部分不做过多介绍&#xff0c;默认读者已清楚知道PFC电路的控制原理。 这里想要探讨一下D5和L6的作用。 …

Go开发运维:Go项目工程化进行初始配置

目录 一、实验 1.环境 2.初始化Go项目 3.Go项目工程化 4. 导入Gin包 5.下载viper与logrus依赖 二、问题 1.Windows如何进行go的版本升级 2. Go mod常用命令 3.下载Gin依赖失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统 软件版本备注Windows1…

JUC-指令有序性

指令重排 JVM 会在不影响正确性的前提下&#xff0c;可以调整语句的执行顺序&#xff0c;思考下面一段代码 static int i; static int j; // 在某个线程内执行如下赋值操作 i ...; j ...; 可以看到&#xff0c;至于是先执行 i 还是 先执行 j &#xff0c;对最终的结果不…