此代码为本人原创。该代码仍有不完善之处,有可能还要再次修改!仅供参考!
若有错误、疑问和意见请留言,非常感谢!
该驱动程序基于TQ2440开发板,内核2.6.30。
驱动程序比较简单,使用字符设备来实现。要注意的是在模拟DS18B20的总线时序时,处理器不能抢占当前线程从而造成时序错乱,因此使用了自旋锁来禁止处理器抢占。
代码比较简单,所以代码注释也比较少。如果有不明白的请参考DS18B20的datasheet以及<<Linux设备驱动程序>>的第三章。
NOTE:请使用驱动代码二,代码一不支持多进程访问。
//using cdev to create device #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/device.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <asm/uaccess.h> #define DEVICE_NAME "ds18b20" #define SKIP_ROM 0xcc #define CONVERT_T 0x44 #define READ_SCRATCHPAD 0xbe #define DQ S3C2410_GPB5 MODULE_LICENSE("GPL"); static struct ds18b20_info { struct mutex ds18b20_mutex; spinlock_t ds18b20_spinlock; }; static int ds18b20_init_work(struct ds18b20_info *dsin) { char n; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); s3c2410_gpio_setpin(DQ, 1); udelay(70); s3c2410_gpio_setpin(DQ, 0); udelay(500); s3c2410_gpio_setpin(DQ, 1); udelay(70); n = s3c2410_gpio_getpin(DQ); if(n == 1) n = -1; udelay(80); spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); return n; } static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){ unsigned char i; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); for (i = 0; i < 8; i++){ s3c2410_gpio_setpin(DQ, 0); udelay(10); s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0); udelay(40); s3c2410_gpio_setpin(DQ, 1); data >>= 1; udelay(1); } spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); } static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){ unsigned char i, value= 0; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); for(i = 0; i < 8; i++){ value >>= 1; s3c2410_gpio_setpin(DQ, 0); //pull low s3c2410_gpio_setpin(DQ, 1); //pull high if (s3c2410_gpio_getpin(DQ)) //read { value |= 0x80; } udelay(40); } spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); return value; } static int ds18b20_open(struct inode *inode, struct file *file) { struct ds18b20_info *dsin = kmalloc(sizeof(* dsin), GFP_KERNEL); if(dsin == NULL){ printk("No memory for ds18b20\n"); return -ENOENT; } memset(dsin, 0, sizeof(*dsin)); file->private_data = dsin; spin_lock_init(&dsin->ds18b20_spinlock); mutex_init(&dsin->ds18b20_mutex); printk("<1>open ds18b20 \n"); return 0; } static int ds18b20_close(struct inode *inode, struct file *file) { struct ds18b20_info *dsin = file->private_data; kfree(dsin); //必需释放 return 0; } static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp) { struct ds18b20_info *dsin = filp->private_data; unsigned char temp1, temp2; unsigned char buffer[2]; unsigned long err; mutex_lock(&dsin->ds18b20_mutex); if(ds18b20_init_work(dsin) < 0) printk("ds18b20 unavailable\n"); ds18b20_write_byte(SKIP_ROM, dsin); ds18b20_write_byte(CONVERT_T, dsin); ssleep(1); ds18b20_init_work(dsin); ds18b20_write_byte(SKIP_ROM, dsin); ds18b20_write_byte(READ_SCRATCHPAD, dsin); temp1 = ds18b20_read_byte(dsin); temp2 = ds18b20_read_byte(dsin); mutex_unlock(&dsin->ds18b20_mutex); buffer[0] = temp1; buffer[1] = temp2; err = copy_to_user(buff, (void *)buffer, count); return err? -EFAULT:count ; } static struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .open = ds18b20_open, .read = ds18b20_read, .release = ds18b20_close, }; struct cdev my_cdev; dev_t dev_num; static char __initdata banner[]="ds18b20 sensor forth edition, made by yanjun\n"; static struct class *led_class; static int __init ds18b20_init(void) { int ret, err; //get the device number in a dynamic way if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0) printk("alloc_chdrev_region error"); printk("dev_num = %d\n", MAJOR(dev_num)); //create and initialize a cdev in a dynamic way cdev_init(&my_cdev, &ds18b20_fops); my_cdev.owner = THIS_MODULE; // my_cdev.ops = &ds18b20_fops; err = cdev_add(&my_cdev, dev_num, 1); if (err < 0) printk("cdev error"); printk(banner); //create a device node led_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(led_class)){ printk("Err:failed in embedsky_leds class.\n"); return -1; } device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME); printk(DEVICE_NAME"initialized\n"); return 0; } static void __exit ds18b20_exit(void) { cdev_del(&my_cdev); unregister_chrdev_region(dev_num, 1); device_destroy(led_class, dev_num); class_destroy(led_class); } module_init(ds18b20_init); module_exit(ds18b20_exit);
该代码基于代码一,进行了简单的修改,使用platform机制,修正了不能多进程访问的bug。
//using cdev to create device #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> //#include </opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach/regs-gpio.h> //#include <asm/arch/regs-gpio.h> //#include </opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach/hardware.h> #include <linux/device.h> #include <linux/kdev_t.h> #include <linux/cdev.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <asm/uaccess.h> #include <linux/platform_device.h> #define DEVICE_NAME "ds18b20" #define SKIP_ROM 0xcc #define CONVERT_T 0x44 #define READ_SCRATCHPAD 0xbe #define DQ S3C2410_GPB5 MODULE_LICENSE("GPL"); struct ds18b20_info { struct mutex ds18b20_mutex; spinlock_t ds18b20_spinlock; }; struct ds18b20_info *dsin; static int ds18b20_init_work(struct ds18b20_info *dsin) { char n; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); s3c2410_gpio_setpin(DQ, 1); udelay(70); s3c2410_gpio_setpin(DQ, 0); udelay(500); s3c2410_gpio_setpin(DQ, 1); udelay(70); n = s3c2410_gpio_getpin(DQ); if(n == 1) n = -1; udelay(80); spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); return n; } static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){ unsigned char i; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); for (i = 0; i < 8; i++){ s3c2410_gpio_setpin(DQ, 0); udelay(10); s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0); udelay(40); s3c2410_gpio_setpin(DQ, 1); data >>= 1; udelay(1); } spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); } static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){ unsigned char i, value= 0; unsigned long flag; spin_lock_irqsave(&dsin->ds18b20_spinlock,flag); for(i = 0; i < 8; i++){ value >>= 1; s3c2410_gpio_setpin(DQ, 0); //pull low s3c2410_gpio_setpin(DQ, 1); //pull high if (s3c2410_gpio_getpin(DQ)) //read { value |= 0x80; } udelay(40); } spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag); return value; } static int ds18b20_open(struct inode *inode, struct file *file) { file->private_data = dsin; printk("<1>open ds18b20 \n"); return 0; } static int ds18b20_close(struct inode *inode, struct file *file) { printk("Now kfree has been executed"); return 0; } static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp) { struct ds18b20_info *dsin = filp->private_data; unsigned char temp1, temp2; unsigned char buffer[2]; unsigned long err; mutex_lock(&dsin->ds18b20_mutex); if(ds18b20_init_work(dsin) < 0) printk("ds18b20 unavailable\n"); ds18b20_write_byte(SKIP_ROM, dsin); ds18b20_write_byte(CONVERT_T, dsin); ssleep(1); ds18b20_init_work(dsin); ds18b20_write_byte(SKIP_ROM, dsin); ds18b20_write_byte(READ_SCRATCHPAD, dsin); temp1 = ds18b20_read_byte(dsin); temp2 = ds18b20_read_byte(dsin); mutex_unlock(&dsin->ds18b20_mutex); buffer[0] = temp1; buffer[1] = temp2; msleep(20); err = copy_to_user(buff, (void *)buffer, count); return err? -EFAULT:count ; } static struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .open = ds18b20_open, .read = ds18b20_read, .release = ds18b20_close, }; struct cdev my_cdev; dev_t dev_num; static char __initdata banner[] = "Ds18b20 sensor: fifth edition using platform, made by yanjun\n"; static struct class *led_class; static int s3c2410_ds18b20_probe(struct platform_device *pdev) { int ret, err; //get the device number in a dynamic way if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0) printk("alloc_chdrev_region error"); printk("dev_num = %d\n", MAJOR(dev_num)); //create and initialize a cdev in a dynamic way cdev_init(&my_cdev, &ds18b20_fops); my_cdev.owner = THIS_MODULE; err = cdev_add(&my_cdev, dev_num, 1); if (err < 0) printk("cdev error"); printk(banner); //create a device node led_class = class_create(THIS_MODULE, DEVICE_NAME); if (IS_ERR(led_class)){ printk("Err:failed in embedsky_leds class.\n"); return -1; } device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME); dsin = kmalloc(sizeof(* dsin), GFP_KERNEL); if(dsin == NULL){ printk("No memory for ds18b20\n"); return -ENOENT; } memset(dsin, 0, sizeof(*dsin)); spin_lock_init(&dsin->ds18b20_spinlock); mutex_init(&dsin->ds18b20_mutex); printk(DEVICE_NAME"initialized\n"); return 0; } static void __exit s3c2410_ds18b20_remove(void) { kfree(dsin); cdev_del(&my_cdev); unregister_chrdev_region(dev_num, 1); device_destroy(led_class, dev_num); class_destroy(led_class); } #ifdef CONFIG_PM static int s3c2410_ds18b20_suspend(struct platform_device *pdev, pm_message_t msg) { /*nothing to do*/ return 0; } static int s3c2410_ds18b20_resume(struct platform_device *pdev, pm_message_t msg) { /*nothing to do*/ return 0; } #else #define s3c2410_ds18b20_suspend NULL #define s3c2410_ds18b20_resume NULL #endif static struct platform_driver s3c2410_ds18b20_driver = { .probe = s3c2410_ds18b20_probe, .remove = s3c2410_ds18b20_remove, .suspend = s3c2410_ds18b20_suspend, .resume = s3c2410_ds18b20_resume, .driver = { .name = "s3c2410-ds18b20", .owner = THIS_MODULE, }, }; static int __init s3c2410_ds18b20_init(void) { return platform_driver_register(&s3c2410_ds18b20_driver); } static void __exit s3c2410_ds18b20_exit(void) { platform_driver_unregister(&s3c2410_ds18b20_driver); } module_init(s3c2410_ds18b20_init); module_exit(s3c2410_ds18b20_exit);
在ds18b20_read函数中,添加了20ms的休眠,该句用于实现多进程的访问。不添加的话,在我的平台上,只有一个进程读取到温度值。
第一个:arch/arm/plat-s3c24xx/devs.c
添加内容:
/*DS18b20 temperature sensor, added by yj423*/
static u64 s3c_device_ds18b20_dmamask = 0xffffffffUL;
struct platform_device s3c_device_ds18b20 = {
.name = "s3c2410-ds18b20",
.id = -1,
.num_resources = 0,
.resource = NULL,
.dev = {
.dma_mask = &s3c_device_ds18b20_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
EXPORT_SYMBOL(s3c_device_ds18b20);
第二个: arch/arm/plat-s3c/include/plat/devs.h
添加内容:
/*added by yj423*/ extern struct platform_device s3c_device_ds18b20;第三个:arch/arm/mach-s3c2440/mach-smdk2440.c
在smdk2440_devices中添加&s3c_device_ds18b20,
编译,加载模块后,我们去sysfs文件系统去验证一下:
[root@yj423 s3c2410-ds18b20]#pwd
/sys/bus/platform/devices/s3c2410-ds18b20
[root@yj423 s3c2410-ds18b20]#ll
lrwxrwxrwx 1 root root 0 Jan 1 00:33 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 00:33 driver -> ../../../bus/platform/drivers/s3c2410-ds18b20
-r--r--r-- 1 root root 4096 Jan 1 00:33 modalias
drwxr-xr-x 2 root root 0 Jan 1 00:33 power
lrwxrwxrwx 1 root root 0 Jan 1 00:33 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 00:33 uevent
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> int main(void) { int i, temper_fd; unsigned char value[2]; unsigned char temp; float temperature; i = 0; if((temper_fd = open("/dev/ds18b20", 0)) < 0){ perror("open device ds18b20 error"); printf("exit\n"); exit(1); } // printf("before syscall read\n"); for(; ;){ int ret = read(temper_fd, value, sizeof(value)); if (ret != 2){ printf("read wrong\n"); exit(0); } temp = value[0] & 0x0f; value[1] = value[1] << 4 | value[0] >> 4; temperature = (float)value[1] + temp * 6.0 / 100.0; printf("temperature = %.2f ", temperature); fflush(stdout); i ++; if (i == 4){ printf("\n"); i = 0; } } close(temper_fd); return 0; }
运行结果:
[root@yj423 modules]#insmod ds18b20.ko
dev_num = 252
ds18b20 sensor forth edition, made by yanjun
ds18b20initialized
[root@yj423 modules]#./sensor2
open ds18b20
temperature = 32.48 temperature = 32.48 temperature = 32.48 temperature = 32.54
temperature = 32.54 temperature = 32.54 temperature = 32.54 temperature = 32.54
temperature = 32.60 temperature = 32.54 temperature = 32.60 temperature = 32.60
temperature = 32.72 temperature = 32.84 temperature = 33.00 temperature = 33.18
temperature = 33.36 temperature = 33.48 temperature = 33.60 temperature = 33.66
temperature = 33.72 temperature = 33.78 temperature = 33.72 ^CNow kfree has been executed