Linux内核中已经有现成的1-wire驱动,写这个驱动纯粹是为了练手。
参考了:
http://blog.csdn.net/sg131971/article/details/7069118
http://blog.csdn.net/xgg0602/article/details/7058071
ds18b20的时序图如下:
复位时序:
读写时序:
驱动代码如下:
#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
欢迎交流!