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

2024-09-01 17:20

本文主要是介绍Linux驱动开发基础(DS18B20温度模块),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

所学来自百问网

目录

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;}
#elsepre = 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;elsereturn 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{// 输出0gpiod_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); // 恢复中断//转换需要时间,延时1sset_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.88all:make -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o ds18b20_test ds18b20_test.c
clean:make -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.order  ds18b20_test# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.oobj-m += ds18b20_drv.o

4.4 实验效果

这篇关于Linux驱动开发基础(DS18B20温度模块)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1127575

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注