基于迅为Itop4412开发板上控制ds18b20测量温度

2016/10/22 Linux

1、说明: 供电为3.3V(3—5V),DQ引脚为gpio的13脚; 如果要直接编译进入内核,需要配置内核驱动字符目录下的Makefile、Kconfig、make menuconfig三处文件,如果以平台设备的方式,还需配置mach-itop4412.c这个文件。原在4412上13脚原为485,需要先去掉。

2、驱动代码

/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
#include <linux/init.h>
/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
#include <linux/module.h>
/*三个字符设备函数*/
#include <linux/fs.h>
/*定义字符设备的结构体*/
#include <linux/cdev.h>
/*分配内存空间函数头文件*/
#include <linux/slab.h>
/*定义module_param module_param_array中perm的头文件*/
#include <linux/stat.h>
/*MKDEV转换设备号数据类型的宏定义*/
#include <linux/kdev_t.h>
/*字符定义*/
#include <linux/string.h>
/*linux系统提供的申请端口函数和设置端口状态的函数*/
#include <linux/gpio.h>
/*设置GPIO状态,上下拉,输入输出就,复用等等相关函数*/
#include <plat/gpio-cfg.h>		
/*包含GPIO端口的宏定义*/
#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 ds18b20_io (EXYNOS4_GPA0(7))/*4412开发板的gpio部分,gpio第13引脚,控制总线*/
#define DEVICE_NAME "ds18b20"	/*定义的设备节点,于/dev目录下,通过其对具体设备进行访问*/

#define ds18b20_MAJOR 245	/*主设备号*/
#define ds18b20_MINOR 0		/*次设备号*/
#define DEVICE_MINOR_NUM 1	/*次设备个数*/

/*主次设备号赋值*/
int ds18b20_major = ds18b20_MAJOR;
int ds18b20_minor = ds18b20_MINOR;

/*创建了一个总线类型,会在/sys/class下生成myclass目录*/
static struct class *ds18b20_class;

/*字符驱动结构体*/
struct ds18b20_dev{
	struct cdev cdev;
};
struct ds18b20_dev *ds18b20_device;

/*ds18b20的初始化*/
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_fops = {
	.owner	=	THIS_MODULE,
	.open	=	ds18b20_open,
	.read	=	ds18b20_read,
	.release	=	ds18b20_release,
};

/*注册设备到系统*/
static void ds18b20_setup_cdev(struct ds18b20_dev *dev,int index)
{
	int err;
	/*获取设备号*/
	int devno = MKDEV(ds18b20_major,index);

	/*cdev结构体初始化*/
	cdev_init(&dev->cdev,&ds18b20_fops);
	dev->cdev.owner = THIS_MODULE;/*给cdev结构体赋值*/
	dev->cdev.ops = &ds18b20_fops;
	/*注册进系统*/
	err = cdev_add(&dev->cdev,devno,1);
	if(err){
		printk(KERN_EMERG "cdev_add %d is failed! %d\n",index,err);
	}
	else{
		printk(KERN_EMERG "cdev_add %d is success!\n",ds18b20_major);
	}
}

static int ds18b20_init(void)
{
	int ret = 0;
	/*dev_t在cdev字符驱动结构体里定义,必须通过dev_t来描述设备号*/
	dev_t ds18b20_dev;

	/*打印输出主次设备号*/
	printk(KERN_EMERG "ds18b20_major is %d!\n",ds18b20_major);
	printk(KERN_EMERG "ds18b20_minor is %d!\n",ds18b20_minor);

	/*申请主次设备号*/
	if(ds18b20_major){
		/*MKDEV为设备号处理宏命令*/
		ds18b20_dev = MKDEV(ds18b20_major,ds18b20_minor);
		/*主设备号不为0则静态注册设备*/
		ret = register_chrdev_region(ds18b20_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
	}
	else{
		/*动态注册设备*/
		ret = alloc_chrdev_region(&ds18b20_dev,ds18b20_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
		ds18b20_major = MAJOR(ds18b20_dev);/*得到主设备号*/
		printk(KERN_EMERG "alloc_chrdev_region is %d\n",ds18b20_major);
	}
	if(ret<0){
		printk(KERN_EMERG "register_chrdev_region is %d failed!\n",ds18b20_major);
	}
	/*创建一个ds18b20_class的总线*/
	ds18b20_class = class_create(THIS_MODULE,DEVICE_NAME);
	/*申请内存空间*/
	ds18b20_device = kmalloc(DEVICE_MINOR_NUM * sizeof(struct ds18b20_dev),GFP_KERNEL);
	if(!ds18b20_device){
		ret = -ENOMEM;
		goto fail;
	}
	/*清空内存空间的数据*/
	memset(ds18b20_device,0,DEVICE_MINOR_NUM*sizeof(struct ds18b20_dev));
	/*注册设备到系统*/
	ds18b20_setup_cdev(ds18b20_device,0);
	/*创建设备节点*/
	device_create(ds18b20_class,NULL,MKDEV(ds18b20_major,ds18b20_minor),NULL,DEVICE_NAME);
	printk(KERN_EMERG "ds18b20 is initation!\n");
	return 0;

	/*注册失败*/
fail:
	unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
	printk(KERN_EMERG "ds18b20 exit!\n");
	return ret;
}

/*设备注销*/
static void __exit ds18b20_exit(void)
{
	/*注销设备号*/
	unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
	/*注销设备*/
	cdev_del(&(ds18b20_device->cdev));
	/*摧毁设备节点*/
	device_destroy(ds18b20_class,MKDEV(ds18b20_major,ds18b20_minor));
	/*释放总线*/
	class_destroy(ds18b20_class);
	/*释放内存*/
	kfree(ds18b20_device);
	/*释放gpiio*/
	gpio_free(ds18b20_io);
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);

MODULE_AUTHOR("star sky");/*作者*/
MODULE_DESCRIPTION("ITOP4412 ds18b20 driver");/*模块功能描述*/
MODULE_LICENSE("Dual BSD/GPL");/*开源声明*/
MODULE_VERSION("ds18b20 V1.0");/*代码修订版本*/
```

3、测试程序
```C
#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;
}
```

4、Makefile

```bash
#!/bin/bash
#通知编译器我们要编译模块的哪些源码
#这里是编译DS18B20.c这个文件编译成中间文件DS18B20.o
obj-m += DS18B20.o 

#源码目录变量,这里用户需要根据实际情况选择路径
#作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0

#当前目录变量
PWD ?= $(shell pwd)

#make命名默认寻找第一个目标
#make -C就是指调用执行的路径
#$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0
#$(PWD)当前目录变量
#modules要执行的操作
all:
	make -C $(KDIR) M=$(PWD) modules

#make clean执行的操作是删除后缀为o的文件
clean:
	rm -rf *.mod.c *.o *.order *.mod.o *.symvers 

5、实验现象 实时测量温度:

这里写图片描述

注册的设备类:

这里写图片描述

设备节点:

这里写图片描述

6、参考链接 http://blog.csdn.net/tandesir/article/details/7247558 http://blog.csdn.net/benjamin_xc/article/details/5110483

想留言却没看到评论框?点这里。

Search

    Post Directory