DS18B20驱动编写--杂项设备框架注册

DS18B20驱动编写–杂项设备框架注册

  设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面,让存储设备能记录文件和数据。
  驱动程序是应用层和硬件层的连接桥梁,应用层只管完成应用逻辑开发和界面设计,驱动层则处理硬件配置,实现应用层相关接口函数。
  杂项设备:字符设备类的一种,杂项设备主设备号为10。

1.DS18B20简介

  DS18B20是Dallas半导体公司生产的数字温度传感器,是世界上第一片支持"一线总线"接口的温度传感器。测量温度范围为-55℃ ~ +125℃,精度为±0.5℃。分辨率为9 ~ 12位。支持3V ~ 5.5V输入电压。抗干扰能力强。
  每一个DSl820包括一个唯一的64位长的序号,该序号值存放在 DSl820 内部的 ROM(只读存贮器)中。开始8位是产品类型编码(DSl820 编码均为 10H) ,接着的 48位是每个器件唯一的序号,最后 8 位是前面 56 位的CRC(循环冗余校验)码。
在这里插入图片描述

引脚 说明
GND
DQ 数字信号脚
VDD 电源脚3V~5.5V

  DS18B20以9位数字量形式反应器件的温度值。
  DS18B20采用单总线通讯,与CPU之间只需要DQ脚相连,再和CPU之间共地即可。每一个DS18B20都有唯一的64位光刻ROM,因此可以在一根数据线上接多个DS18B20模块。

  • 单总线协议特性
  • 总线协议:一个数据线可挂载多个设备(DS18B20通过64位光刻ROM区分设备);
  • 半双工通讯:数据线上同一时间只能发送或者接收数据;

2.DS18B20驱动时序

2.1 发送复位脉冲和检测存在信号

在这里插入图片描述
在这里插入图片描述
  DS18B20初始化过程首先需要发送复位脉冲:至少480us的低电平信号。接下来释放总线,DS18B20开始返回存在信号:60~240us的低电平。最后释放总线,模块初始化完成。

/*发送复位信号,检测存在脉冲*/
static u8 ds18b20_CheckRst(void)
{
    
    
	u8 time=0;
	DS18B20_OUT_MODE();/*输出模式*/
	DS18B20_OUT(0);/*总线拉低*/
	udelay(600);/*至少480us低电平*/
	DS18B20_OUT(1);/*释放总线,恢复为空闲电平*/
	udelay(15);
	DS18B20_INPUT_MODE();/*输入模式*/
	while(DS18B20_IN)
	{
    
    
		time++;
		udelay(1);
		if(time>=100)return 1;/*等待存在脉冲失败*/
	}
	time=0;
	while(!DS18B20_IN)
	{
    
    
		time++;
		udelay(1);
		if(time>=250)return 2;//模块出错
	}
	return 0;	
}

2.2 写一位数据时序

在这里插入图片描述
  要实现写一字节数据,则首先要实现的是写一位数据时序。分为写1和写0。首先是总线拉低,产生写间隙(至少1us)。接着往数据线DQ上写入0或者1,周期时间为60us,最后释放总线(总线拉高,至少1us),至此,写数据完成。

DS18B20_OUT_MODE();/*输出模式*/
DS18B20_OUT(0);//总线拉低,产生写间隙时间
udelay(2);
if(dat&0x01)DS18B20_OUT(1);
else DS18B20_OUT(0);
udelay(60);//写周期时间
DS18B20_OUT(1);//释放总线
udelay(2);
dat>>=1;//继续发送下一位数据

2.3 读一位数据时序

在这里插入图片描述
  读数据首先需要主机产生读间隙:总线拉低,至少1us的低电平信号。接着释放总线,在15us内进行数据读取,读数据周期时间为60us,最后释放总线:总线拉高,至少1us时间。 至此,读一位数据完成。

DS18B20_OUT_MODE();/*输出模式*/
DS18B20_OUT(0);//总线拉低,产生读间隙时间
udelay(2);
DS18B20_INPUT_MODE();//配置为输入模式
udelay(12);//等待数据到来
data>>=1;
if(DS18B20_IN)data|=0x80;
udelay(50);//读数据时间
DS18B20_OUT(1);//恢复总线为空闲电平
udelay(2);

3.DS18B20相关命令

  • 跳转指令0xCC
      这条指令允许控制器不需要提供64位光刻ROM就使用存储器操作命令,在总线上仅有一个DS18B20时使用,若有多个则会产生冲突。
  • 启动一次温度转换0x44
      此命令完成一次温度转换。执行此命令后,DS18B20保持等待状态。若总线在这条命令发送后跟着读间隙,而DS18B20正处于数据转换,则会输出一个0,若温度转换完成,则会输出1。若使用寄生电源,总线必须在这条命令发完后拉高总线,保存500ms。
  • 读取一次数据0xBE
      此命令用于读取暂存器中的内容,可连续读取9个字节数据。若只想读取温度数据,则只需要读取前两个字节即可。
    在这里插入图片描述
  • 读ROM 0x33
      此命令可以读取DS18B20的64位光刻ROM数据,此命令仅能在总线上一个设备的时候使用。
  • 匹配ROM 0x55
      此命令可以实现和DS18B20的ROM进行匹配,只有和DS1820的64位光刻ROM完全匹配才能响应后面存储器命令。此命令用于当总线上不止一个设备时使用。
  • 搜索ROM 0xF0
      当系统第一次启动时,无法确认总线上有多少个设备或者该设备的光刻ROM,搜索光刻ROM可以让控制器通过排除法识别总线上的所有设备的64位光刻ROM。

4.DS18B20采集一次温度步骤

  采用外部电源供电,且总线上仅有一个DS18B20模块
在这里插入图片描述

5.编写DS18B20驱动,通过杂项设备注册

  • 开发平台

开发平台:Ubuntu18.04
编译器:arm-linux-gcc
硬件平台:tiny4412基于Cortex-A9 4核1.5GHZ
开发板内核:Linux3.5

#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/delay.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static unsigned int ds18b20_gpio=EXYNOS4_GPB(4);//GPB_4
#define DS18B20_OUT_MODE() s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_OUTPUT)/*输出模式*/
#define DS18B20_INPUT_MODE()  s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_INPUT)/*输入模式*/
#define DS18B20_OUT(x)   gpio_set_value(ds18b20_gpio,(x))
#define DS18B20_IN   		gpio_get_value(ds18b20_gpio)
/*发送复位信号,检测存在脉冲*/
static u8 ds18b20_CheckRst(void)
{
    
    
	u8 time=0;
	DS18B20_OUT_MODE();/*输出模式*/
	DS18B20_OUT(0);/*总线拉低*/
	udelay(600);/*至少480us低电平*/
	DS18B20_OUT(1);/*释放总线,恢复为空闲电平*/
	udelay(15);
	DS18B20_INPUT_MODE();/*输入模式*/
	while(DS18B20_IN)
	{
    
    
		time++;
		udelay(1);
		if(time>=100)return 1;/*等待存在脉冲失败*/
	}
	time=0;
	while(!DS18B20_IN)
	{
    
    
		time++;
		udelay(1);
		if(time>=250)return 2;//模块出错
	}
	return 0;	
}
/*写一个字节函数*/
static void ds18b20_writeDat(u8 dat)
{
    
    
	int i=0;
	DS18B20_OUT_MODE();/*输出模式*/
	for(i=0;i<8;i++)
	{
    
    
		DS18B20_OUT(0);//总线拉低,产生写间隙时间
		udelay(2);
		if(dat&0x01)DS18B20_OUT(1);
		else DS18B20_OUT(0);
		udelay(60);//写周期时间
		DS18B20_OUT(1);//释放总线
		udelay(2);
		dat>>=1;//继续发送下一位数据
	}
}
/*读取1字节数据*/
static u8 ds18b20_readDat(void)
{
    
    
	int i=0;
	u8 data=0;
	for(i=0;i<8;i++)
	{
    
    
		DS18B20_OUT_MODE();/*输出模式*/
		DS18B20_OUT(0);//总线拉低,产生读间隙时间
		udelay(2);
		DS18B20_INPUT_MODE();//配置为输入模式
		udelay(12);//等待数据到来
		data>>=1;
		if(DS18B20_IN)data|=0x80;
		udelay(50);//读数据时间
		DS18B20_OUT(1);//恢复总线为空闲电平
		udelay(2);
	}
	return data;
}

/*获取一次温度数据*/
static u16 ds18b20_GetTemp(void)
{
    
    
	u8 L,H;
	u16 temp;
	if(ds18b20_CheckRst())return 0xffff;
	ds18b20_writeDat(0xcc);
	ds18b20_writeDat(0x44);
	while(ds18b20_readDat()!=0xff);/*等待温度转换完成*/
	if(ds18b20_CheckRst())return 0xffff;
	ds18b20_writeDat(0xcc);
	ds18b20_writeDat(0xbe);/*读取一次温度*/
	L=ds18b20_readDat();
	H=ds18b20_readDat();
	temp=H<<8|L;
	return temp;
}
static int ds18b20_open(struct inode *inode, struct file *file)
{
    
    
	printk("open函数调用成功\n");
	if(ds18b20_CheckRst())
	{
    
    
		printk("DS18B20初始化失败\n");
	}
	return 0;
}
static int ds18b20_release(struct inode *inode, struct file *file)
{
    
    
	printk("release函数调用成功");
	return 0;
}
long ds18b20_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    
    
	int dir=_IOC_DIR(cmd);/*数据读写方向,00无参数,10用户层读,01用户层写,11可读写*/
	int type=_IOC_TYPE(cmd);/*魔术,标志符*/
	int size=_IOC_SIZE(cmd);/*arg的字节数*/
	printk("dir=%d,type=%c,size=%d\n",dir,type,size);
	int ret;
	u16 temp=ds18b20_GetTemp();/*获取一次温度*/
	ret=copy_to_user((void *)arg, &temp,size);
	return 4-ret;
}
static  struct file_operations ds18b20_fops=
{
    
    
	.open			=ds18b20_open,
	.release		=ds18b20_release,
	.unlocked_ioctl	=ds18b20_ioctl
};

/*杂项设备结构体*/
static struct miscdevice ds18b20_drv=
{
    
    
	.minor	=MISC_DYNAMIC_MINOR,/*255,有内核自动分配*/
	.name	="ds18b20",//设备节点名字
	.fops	=&ds18b20_fops,//文件操作集合
};
static int __init wbyq_ds18b20_init(void)
{
    
    
	/*1.GPIO注销*/
	gpio_free(ds18b20_gpio);
	/*2.注册GPIO*/
	gpio_request(ds18b20_gpio,"DS18B20");
	/*配置GPIO模式*/
	s3c_gpio_cfgpin(ds18b20_gpio,S3C_GPIO_OUTPUT);
	gpio_set_value(ds18b20_gpio,1);/*上拉*/
	/*注册杂项设备*/
	misc_register(&ds18b20_drv);
	return 0;
	
}
/*驱动释放*/
static void __exit wbyq_ds18b20_cleanup(void)
{
    
    
    printk("驱动出口,驱动注销成功\n");
	/*注销杂项设备*/
	misc_deregister(&ds18b20_drv);
	/*注销GPIO*/
	gpio_free(ds18b20_gpio);
}
module_init(wbyq_ds18b20_init);//驱动入口函数
module_exit(wbyq_ds18b20_cleanup);//驱动出口函数

MODULE_LICENSE("GPL");//驱动注册协议
MODULE_AUTHOR("it_ashui");
MODULE_DESCRIPTION("Exynos4 ds18b20 Driver");

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44453694/article/details/126770763