您的位置:首页 > 运维架构

iTop4412之DS18B20驱动开发

2017-01-15 14:24 405 查看
执行序列

通过单线总线端口访问 DS18B20 的协议如下:

步骤1. 初始化

步骤2. ROM 操作指令

步骤3. DS18B20 功能指令

步骤1. 初始化



主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答,若无低电平出现一直都是高电平说明总线上无器件应答

作为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备,若没有检测到就一直在检测等待。

2.写时序



写周期最少为60微秒,最长不超过120微秒,写周期一开始作为主机先把总线拉低1微秒表示写周期开始,随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平;若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。

而作为从机的DS18B20则在检测到总线被拉低后等待15微秒然后从15μs到45μs开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。

3.读时序



对于读数据操作时序也分为读0时序和读1时序两个过程,读时序是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束;若要送出1则释放总线为高电平。

主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0,采样期内总线为高电平则确认为1,完成一个读时序过程,至少需要60μs才能完成。

操作方法

DS18B20单线通信功能是分时完成的,有严格的时序概念,如果出现序列混乱,1-WIRE器件将不影响主机,因此读写时序很重要。系统对DS18B20的各种操作必须按协议进行,根据DS18B20的协议规定,微控制器控制DS18B20完成温度的转换必须经过以下4个步骤:

1)每次读写前对DS18B20进行复位初始化。复位要求主CPU将数据线下拉500μs,然后释放,DS18B20收到信号后等待16μs-60μs左右,然后发出60μs-240μs的存在低脉冲,主CPU收到此信号后表示复位成功。

2)发送一条ROM指令

3)发送存储器指令

1、让DS18B20进行一次温度转换的具体操作如下:

a – 主机先做个复位操作;

b – 主机再写跳过ROM的操作(CCH)命令;

c – 然后主机接着写转换温度的操作指令,后面释放总线至少1秒,让DS18B20完成转换操作。需要注意的是每个命令字节在写的时候都是低字节先写,例如CCH的二进制为11001100,在写到总线上时要从低位开始写,写的顺序是“0、0、1、1、0、0、1、1”,整个操作的总线状态如图所。

2、读取RAM的温度数据,同样,这个操作也要按照三个步骤:

a – 主机发出复位操作并接受DS18B20的应答(存在)脉冲;

b – 主机发出跳过对ROM操作的命令(CCH);

c – 主机发出读取RAM的命令(BEH),随后主机依次读取DS18B20发出的从第0-第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可,同样读取数据也是低位在前,整个操作的总线状态如图所示。

#include<linux/init.h>
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/stat.h>
#include<linux/kdev_t.h>
#include<linux/string.h>
#include<linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-exynos4.h>
#include <linux/mm.h>
/*dev_t等的定义*/
#include <linux/types.h>
/*包含copy_to_user和copy_from_user的头文件*/
#include <asm/uaccess.h>
/*包含寄存器操作函数的头文件*/
#include <asm/io.h>
/*延时定义*/
#include <linux/delay.h>
/*包含函数device_create 结构体class等头文件*/
#include <linux/device.h>
/*错误诊断和输出需要的头文件*/
#include <linux/errno.h>

#define GPA0CON 0x1140_0000
//GPA0CON寄存器的物理地址
#define GPA0DAT 0x1140_0004
//GPA0DAT寄存器的物理地址
#define ds18b20_io (EXYNOS4_GPA0(7))
static int *gpa0con;
static int *gpa0dat;
struct cdev cdev;
dev_t devno;
void ds18b20_reset(void)
{
int ret;

s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*配置成输出*/
gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,并保持高电平状态约100微秒*/
udelay(100);
gpio_set_value(ds18b20_io,0);/*向18B20发送一个下降沿,并保持低电平状态约600微秒*/
udelay(600);
gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,此时可释放DS18B20总线*/
udelay(100);
/*以上操作是给DS18B20一个复位脉冲*/

s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));/*配置为输入,可以检测到DS18B20是否复位成功*/
/*如果低电平出现说明总线上有器件已做出应答*/
ret = gpio_get_value(ds18b20_io);/*读取io上的电平.若总线在释放后总线状态为高电平,则复位失败*/
/*if(!ret){
printk(KERN_EMERG "ds18b20 init is success!\n");
}
else{
printk(KERN_EMERG "ds18b20 init is failed!\n");
}*/
}

/*写一个字节*/
/*写“1”时隙:
保持总线在低电平1微秒到15微秒之间
然后再保持总线在高电平15微秒到60微秒之间
理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平

写“0”时隙:
保持总线在低电平15微秒到60微秒之间
然后再保持总线在高电平1微秒到15微秒之间
理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
*/
void write_data (unsigned char dat)
{
unsigned char i;

s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*端口设置为输出*/
for(i=0;i<8;i++){
/*主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。*/
gpio_set_value(ds18b20_io,0);
udelay(1);

/*此处为写"1"*/
/*若byte变量的D0位是1,则需向总线上写“1”
根据写“1”时隙规则,电平在此处翻转为高*/
if(((dat)&(0x01))==1)
gpio_set_value(ds18b20_io,1);
udelay(80);
gpio_set_value(ds18b20_io,1);
udelay(15);
dat = dat>>1;
}
gpio_set_value(ds18b20_io,1);
}

/*读一个字节*/
/* 读“1”时隙:
若总线状态保持在低电平状态1微秒到15微秒之间
然后跳变到高电平状态且保持在15微秒到60微秒之间
就认为从DS18B20读到一个“1”信号
理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平

读“0”时隙:
若总线状态保持在低电平状态15微秒到30微秒之间
然后跳变到高电平状态且保持在15微秒到60微秒之间
就认为从DS18B20读到一个“0”信号
理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
*/
unsigned char read_data(void)
{
unsigned char i;
unsigned char val = 0;

for(i=0;i<8;i++){
s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));
gpio_set_value(ds18b20_io,0);
udelay(1);
val >>=1;
gpio_set_value(ds18b20_io,1);
s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));
udelay(1);

/*若总线在我们设它为低电平之后若1微秒之内变为高
则认为从DS18B20处收到一个“1”信号
因此把byte的D7为置“1”   */
if(gpio_get_value(ds18b20_io))
val = val | 0x80;
udelay(60);
}
return val;
}

/*打开文件函数*/
static int ds18b20_open(struct inode *inide,struct file *flip)
{
int ret;
/*申请gpio*/
ret = gpio_request(ds18b20_io,"DS18B20");
if(ret<0){
printk(KERN_EMERG "gpio_request is failed!\n");
return 1;
}
printk(KERN_EMERG "open DS18B20\n");

return 0;
}

/*读文件函数*/
static ssize_t ds18b20_read(struct file *flip,char __user *buff,size_t count,loff_t *f_ops)
{
unsigned char buf[2];/*DS18B20将产生的温度数据以两个字节的形式存储到高速暂存器的温度寄存器中*/

ds18b20_reset();
udelay(420);
write_data(0xcc);/*跳过序列号命令*/
write_data(0x44);/*发送转换命令44H,完成温度测量和AD转换*/
mdelay(800);
ds18b20_reset();
udelay(400);
write_data(0xcc);
write_data(0xbe);/*发送读取命令,从0位到第9位*/
buf[0] = read_data();/*读取低位温度*/
buf[1] = read_data();/*读取高位温度*/
copy_to_user(buff,buf,sizeof(buf));/*传输数据到用户空间*/

return 0;
}

static int ds18b20_release(struct inode *inode, struct file *filp)
{
printk(KERN_EMERG "ds18b20_release is success!\n");

return 0;
}

static struct file_operations ds18b20_ops={
.owner = THIS_MODULE,
.open = ds18b20_open,
.read = ds18b20_read,
};
static int __init first_init(void)
{
cdev_init(&cdev,&ds18b20_ops);/*初始化cdev结构*/

alloc_chrdev_region(&devno, 0 , 1 , "ds18b20");
cdev_add(&cdev, devno, 1);

return 0;
}
static void __exit first_exit()
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno,1);
}
module_init(first_init);
module_exit(first_exit);
MODULE_LICENSE("Dual BSD/GPL");


用户测试程序

#include <stdio.h>//标准输入输出
#include <sys/types.h>//open和creat函数需要的头文件
#include <sys/stat.h>//open和creat函数需要的头文件
#include <fcntl.h>//open和creat函数需要的头文件
#include <unistd.h>//close,read,write函数需要的头文件
#include <sys/ioctl.h>//ioctl函数需要的头文件

int main()
{
int fd;
unsigned int tem = 0;
float temperature;
char buf[10];
char *ds18b20_node = "/dev/ds18b20";

/*O_RDWR只读打开,O_NDELAY非阻塞方式*/
if((fd = open(ds18b20_node,O_RDWR|O_NDELAY))<0)
{
printf("ds18b20 open %s failed",ds18b20_node);
return -1;
}
else{
printf("ds18b20 open %s success",ds18b20_node);
while(1)
{
read(fd,buf,2);/*从buf中读出数据*/
tem = buf[1];
tem <<= 8;/*高位移到前面*/
tem = tem | buf[0]; /*得到读出的数据*/
/*18B20是定点数据表示方式,12.4的编码,
即前12位是温度整数部分,后4位为小数部分,
4位分辨率就是1/16;
转换为10进制结果要乘以1/16=0.0625
*/
temperature = tem*0.0625;/*要求出正数的十进制值,必须将读取到的LSB字节,MSB字节进行整合处理,然后乘以0.0625即可*/
printf("temperature is :%7.4f\n",temperature);/*打印出温度*/
tem = 0;
sleep(1);
}
}

close(fd);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息