简单、规范的Linux下的ds18b20驱动(基于mini2440开发板,2.6.29内核)

Linux内核中已经有现成的1-wire驱动,写这个驱动纯粹是为了练手。

参考了:

http://blog.csdn.net/sg131971/article/details/7069118

http://blog.csdn.net/xgg0602/article/details/7058071

ds18b20的时序图如下:

复位时序:

简单、规范的Linux下的ds18b20驱动(基于mini2440开发板,2.6.29内核)_第1张图片

读写时序:


驱动代码如下:

#include 
#include 
#include 
#include 
#include 	/* size_t.. */
#include 		/* everything.. */
#include 
#include 		/* kmalloc() */
#include 
#include 
#include 
#include 
#include 
#include 
#include 	/* copy_*_user() */

#define DS1820_CMD_SKIP_ROM	(0xCC)
#define DS1820_CMD_CONVERT	(0x44)
#define DS1820_CMD_READSCR	(0xBE)

#define DS1820_DQ	S3C2410_GPF3
#define make_dq_out() 	s3c2410_gpio_cfgpin(DS1820_DQ, S3C2410_GPIO_OUTPUT)
#define make_dq_in() 	s3c2410_gpio_cfgpin(DS1820_DQ, S3C2410_GPIO_INPUT)
#define set_dq(_val)	s3c2410_gpio_setpin(DS1820_DQ, _val)
#define get_dq()	s3c2410_gpio_getpin(DS1820_DQ)
#define dq_pullup(_val) s3c2410_gpio_pullup(DS1820_DQ, _val)

#define DS1820_DEV_NAME "ds18b20"

struct ds1820_device
{
	struct mutex res_mutex;
	struct cdev cdev;
};
static int ds1820_major = 0;
static struct ds1820_device *ds1820_devp;
static struct class *my_class;

#define ds1820_existed() ds1820_reset()
static unsigned int ds1820_reset(void)
{
	unsigned int ret;

	make_dq_out();
	dq_pullup(0);	/* we use a external 4.7K pullup resistor */
	set_dq(0);	/* set dq low to info slave */
	udelay(600);	/* hold for 480~960us */
	set_dq(1);	/* release dq */
	make_dq_in();
	udelay(60);	/* wait for 60us, ds1820 will hold dq low for 240us */
	ret = ~get_dq();/* ds1820 pulldown dq */
	udelay(240);
	return ret;
}

static void ds1820_write_byte(unsigned char byte)
{
	unsigned int i;

	make_dq_out();
	for (i = 8; i != 0; --i) {
		set_dq(0);
		udelay(10); 	/* delay 10us, smaller than 15us */
		if (byte & 0x01)
			set_dq(1);

		udelay(50);	/* delay (10 + 50 >= 60us) */
		set_dq(1);	/* set dq high for next write slot */
		udelay(3);	/* delay a while */
		byte >>= 1;
	}
}

static unsigned char ds1820_read_byte(void)
{
	unsigned int i;
	unsigned char ret = 0x00;

	for (i = 8; i != 0; --i) {
		ret >>= 1;
		make_dq_out();
		set_dq(0);
		udelay(2);	/* falling edge of dq > 1us */
		make_dq_in();
		udelay(4);	/* sample window < 15us */
		if (get_dq())
			ret |= 0x80;

		udelay(60);	/* delay (2 + 4 + 60 > 60us) */
	}

	return ret;
}

static int ds1820_open(struct inode *inode, struct file *filp)
{
	struct ds1820_device *dev;
	int retval;

	dev = container_of(inode->i_cdev, struct ds1820_device, cdev);
	filp->private_data = dev;
	mutex_lock_interruptible(&dev->res_mutex);
	retval = ds1820_existed() ? 0: -ENODEV;
	mutex_unlock(&dev->res_mutex);
	return retval;
}

static int ds1820_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static ssize_t ds1820_read(struct file *filp, char *buf, size_t count,
		loff_t *pos)
{
	struct ds1820_device *dev;
	unsigned char tmp[2];
	unsigned long err;

	dev = filp->private_data;
	mutex_lock_interruptible(&dev->res_mutex);
	/* send temperature convert command */
	ds1820_reset();
	ds1820_write_byte(DS1820_CMD_SKIP_ROM);
	ds1820_write_byte(DS1820_CMD_CONVERT);
	udelay(3);/* wait a while */

	/* send read scratchpad command */
	ds1820_reset();
	ds1820_write_byte(DS1820_CMD_SKIP_ROM);
	ds1820_write_byte(DS1820_CMD_READSCR);

	/* read temperature data */
	tmp[0] = ds1820_read_byte();
	tmp[1] = ds1820_read_byte();
	ds1820_reset();	/* terminate the data transfer, we only need 2 bytes */

	//printk(KERN_NOTICE "byte0:%X, byte1:%X\n", tmp[0], tmp[1]);
	err = copy_to_user(buf, tmp, sizeof(tmp));
	mutex_unlock(&dev->res_mutex);
	return err ? -EFAULT: min(sizeof(tmp), count);
}

static struct file_operations ds1820_fops =
{
	.owner 	= THIS_MODULE,
	.open	= ds1820_open,
	.release= ds1820_release,
	.read	= ds1820_read
};

static int __init ds1820_init(void)
{
	int result;
	dev_t devno = 0;

	if (ds1820_major) {
		devno = MKDEV(ds1820_major, 0);
		result = register_chrdev_region(devno, 1, DS1820_DEV_NAME);
	} else {
		result = alloc_chrdev_region(&devno, 0, 1, DS1820_DEV_NAME);
		ds1820_major = MAJOR(devno);
	}

	if (result < 0) {
		printk(KERN_ERR "ds1820: init error!\n");
		return result;
	}

	ds1820_devp = kzalloc(sizeof(struct ds1820_device), GFP_KERNEL);
	if (!ds1820_devp) {
		result = -ENOMEM;
		goto fail;
	}

	mutex_init(&ds1820_devp->res_mutex);
	/* setup cdev */
	cdev_init(&ds1820_devp->cdev, &ds1820_fops);
	ds1820_devp->cdev.owner = THIS_MODULE;
	ds1820_devp->cdev.ops = &ds1820_fops;
	if (cdev_add(&ds1820_devp->cdev, devno, 1))
		printk(KERN_NOTICE "Error adding ds1820 cdev!\n");

	/* setup device node */
	my_class = class_create(THIS_MODULE, "ds1820_class");
	device_create(my_class, NULL, MKDEV(ds1820_major, 0), \
				NULL, DS1820_DEV_NAME);
	printk(KERN_NOTICE "hello ds1820, everything is ok!\n");
	return 0;
fail:
	unregister_chrdev_region(devno, 1);
	return result;
}

static void __exit ds1820_exit(void)
{
	dev_t devno = MKDEV(ds1820_major, 0);

	if (ds1820_devp)
		cdev_del(&ds1820_devp->cdev);

	kfree(ds1820_devp);
	device_destroy(my_class, devno);
	class_destroy(my_class);

	unregister_chrdev_region(devno, 1);
	printk(KERN_NOTICE "bye ds1820!\n");
}

module_init(ds1820_init);
module_exit(ds1820_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("dkpure");



测试程序代码如下:

#include 
#include 
#include 
#include 

int main()
{
	int fd;
	unsigned char result[2];    // 从ds18b20读出的结果,result[0]存放低八位
	unsigned char integer_value = 0;
	float decimal_value = 0;    // 温度数值,decimal_value为小数部分的值
	float temperature = 0;
  
	fd = open("/dev/ds18b20", 0);
	if (fd < 0) {
		perror("open device failed\n");
		exit(1);
	}

	while (1) {
		read(fd, &result, sizeof(result));
		integer_value = ((result[0] & 0xf0) >> 4) | ((result[1] & 0x07) << 4);
		// 精确到0.25度
		decimal_value = 0.5 * ((result[0] & 0x0f) >> 3) + 0.25 * ((result[0] & 0x07) >> 2);
		temperature = (float)integer_value + decimal_value;
		printf("Current Temperature:%6.2f\n", temperature);
		sleep(2);
	}
	return 0;
}

所有源代码已经上传至:

http://download.csdn.net/detail/dreamgirl55555/6003321


欢迎交流!


你可能感兴趣的:(Linux内核驱动)