基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)

来源:互联网 发布:淘宝房产司法拍卖 编辑:程序博客网 时间:2024/06/10 15:22

此代码为本人原创。该代码仍有不完善之处,有可能还要再次修改!仅供参考!

若有错误、疑问和意见请留言,非常感谢!

该驱动程序基于TQ2440开发板,内核2.6.30。

驱动程序比较简单,使用字符设备来实现。要注意的是在模拟DS18B20的总线时序时,处理器不能抢占当前线程从而造成时序错乱,因此使用了自旋锁来禁止处理器抢占。

代码比较简单,所以代码注释也比较少。如果有不明白的请参考DS18B20的datasheet以及<<Linux设备驱动程序>>的第三章。

1. 驱动代码一

NOTE:请使用驱动代码二,代码一不支持多进程访问。

[cpp] view plaincopy
  1. //using cdev to create device  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/fs.h>  
  5. #include <linux/init.h>  
  6. #include <linux/delay.h>  
  7. #include <asm/irq.h>  
  8. #include <mach/regs-gpio.h>  
  9. #include <mach/hardware.h>  
  10. #include <linux/device.h>  
  11. #include <linux/kdev_t.h>  
  12. #include <linux/cdev.h>  
  13. #include <linux/spinlock.h>  
  14. #include <linux/mutex.h>  
  15. #include <asm/uaccess.h>  
  16.   
  17. #define DEVICE_NAME "ds18b20"  
  18. #define SKIP_ROM 0xcc  
  19. #define CONVERT_T 0x44  
  20. #define READ_SCRATCHPAD 0xbe  
  21. #define DQ  S3C2410_GPB5  
  22.   
  23. MODULE_LICENSE("GPL");  
  24.   
  25. static struct ds18b20_info  
  26. {  
  27.     struct mutex    ds18b20_mutex;  
  28.     spinlock_t  ds18b20_spinlock;         
  29.   
  30. };  
  31.   
  32. static int ds18b20_init_work(struct ds18b20_info *dsin)  
  33. {  
  34.     char n;  
  35.     unsigned long flag;  
  36.   
  37.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  38.     s3c2410_gpio_setpin(DQ, 1);  
  39.     udelay(70);  
  40.     s3c2410_gpio_setpin(DQ, 0);  
  41.     udelay(500);  
  42.     s3c2410_gpio_setpin(DQ, 1);  
  43.     udelay(70);  
  44.     n = s3c2410_gpio_getpin(DQ);  
  45.     if(n == 1)  
  46.         n = -1;  
  47.     udelay(80);  
  48.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  49.     return n;  
  50. }  
  51.   
  52. static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){  
  53.     unsigned char i;  
  54.     unsigned long flag;  
  55.       
  56.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  57.     for (i = 0; i < 8; i++){  
  58.         s3c2410_gpio_setpin(DQ, 0);  
  59.         udelay(10);  
  60.         s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0);  
  61.         udelay(40);  
  62.         s3c2410_gpio_setpin(DQ, 1);  
  63.         data >>= 1;  
  64.         udelay(1);  
  65.     }  
  66.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  67. }  
  68.   
  69. static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){  
  70.     unsigned char i, value= 0;  
  71.     unsigned long flag;  
  72.       
  73.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  74.     for(i = 0; i < 8; i++){  
  75.         value >>= 1;  
  76.         s3c2410_gpio_setpin(DQ, 0);     //pull low  
  77.         s3c2410_gpio_setpin(DQ, 1);     //pull high  
  78.         if (s3c2410_gpio_getpin(DQ))    //read  
  79.         {  
  80.             value |= 0x80;  
  81.         }  
  82.         udelay(40);  
  83.     }  
  84.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  85.     return value;  
  86. }  
  87.   
  88. static int ds18b20_open(struct inode *inode, struct file *file)  
  89. {  
  90.     struct ds18b20_info *dsin = kmalloc(sizeof(* dsin), GFP_KERNEL);  
  91.       
  92.     if(dsin == NULL){  
  93.         printk("No memory for ds18b20\n");  
  94.         return -ENOENT;  
  95.     }  
  96.     memset(dsin, 0, sizeof(*dsin));  
  97.     file->private_data = dsin;  
  98.     spin_lock_init(&dsin->ds18b20_spinlock);  
  99.     mutex_init(&dsin->ds18b20_mutex);  
  100.     printk("<1>open ds18b20 \n");  
  101.     return 0;  
  102. }  
  103.   
  104. static int ds18b20_close(struct inode *inode, struct file *file)  
  105. {  
  106.     struct ds18b20_info *dsin = file->private_data;  
  107.     kfree(dsin);    //必需释放  
  108.     return 0;  
  109. }  
  110.   
  111. static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp)  
  112. {  
  113.       
  114.     struct ds18b20_info *dsin = filp->private_data;  
  115.     unsigned char temp1, temp2;  
  116.     unsigned char buffer[2];  
  117.     unsigned long err;  
  118.       
  119.     mutex_lock(&dsin->ds18b20_mutex);  
  120.     if(ds18b20_init_work(dsin) < 0)  
  121.         printk("ds18b20 unavailable\n");  
  122.     ds18b20_write_byte(SKIP_ROM, dsin);  
  123.     ds18b20_write_byte(CONVERT_T, dsin);  
  124.       
  125.     ssleep(1);  
  126.       
  127.     ds18b20_init_work(dsin);  
  128.     ds18b20_write_byte(SKIP_ROM, dsin);  
  129.     ds18b20_write_byte(READ_SCRATCHPAD, dsin);  
  130.     temp1 = ds18b20_read_byte(dsin);  
  131.     temp2 = ds18b20_read_byte(dsin);  
  132.     mutex_unlock(&dsin->ds18b20_mutex);  
  133.       
  134.     buffer[0] = temp1;  
  135.     buffer[1] = temp2;  
  136.       
  137.     err = copy_to_user(buff, (void *)buffer, count);  
  138.     return err? -EFAULT:count ;  
  139.           
  140. }  
  141.   
  142. static struct file_operations ds18b20_fops =  
  143. {  
  144.     .owner = THIS_MODULE,  
  145.     .open  = ds18b20_open,  
  146.     .read = ds18b20_read,  
  147.     .release = ds18b20_close,  
  148. };  
  149.   
  150. struct cdev my_cdev;  
  151. dev_t dev_num;  
  152. static char __initdata banner[]="ds18b20 sensor forth edition, made by yanjun\n";  
  153. static struct class *led_class;  
  154. static int __init ds18b20_init(void)  
  155. {     
  156.     int ret, err;  
  157.   
  158.     //get the device number in a dynamic way  
  159.     if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0)     
  160.         printk("alloc_chdrev_region error");  
  161.     printk("dev_num = %d\n", MAJOR(dev_num));     
  162.       
  163.     //create and initialize a cdev in a dynamic way  
  164.     cdev_init(&my_cdev, &ds18b20_fops);           
  165.     my_cdev.owner = THIS_MODULE;          
  166. //  my_cdev.ops = &ds18b20_fops;          
  167.     err = cdev_add(&my_cdev, dev_num, 1);     
  168.     if (err < 0)  
  169.         printk("cdev error");  
  170.     printk(banner);  
  171.       
  172.     //create a device node  
  173.     led_class = class_create(THIS_MODULE, DEVICE_NAME);               
  174.     if (IS_ERR(led_class)){  
  175.         printk("Err:failed in embedsky_leds class.\n");  
  176.         return -1;  
  177.     }  
  178.     device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME);  
  179.       
  180.     printk(DEVICE_NAME"initialized\n");  
  181.     return 0;  
  182. }  
  183.   
  184. static void __exit ds18b20_exit(void)  
  185. {  
  186.     cdev_del(&my_cdev);                           
  187.     unregister_chrdev_region(dev_num, 1);         
  188.     device_destroy(led_class, dev_num);       
  189.     class_destroy(led_class);           }  
  190.   
  191. module_init(ds18b20_init);  
  192. module_exit(ds18b20_exit);  


2. 驱动代码二(使用platform机制)

该代码基于代码一,进行了简单的修改,使用platform机制,修正了不能多进程访问的bug。

[cpp] view plaincopy
  1. //using cdev to create device  
  2. #include <linux/module.h>  
  3. #include <linux/kernel.h>  
  4. #include <linux/fs.h>  
  5. #include <linux/init.h>  
  6. #include <linux/delay.h>  
  7. #include <asm/irq.h>  
  8. #include <mach/regs-gpio.h>  
  9. #include <mach/hardware.h>  
  10. //#include </opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach/regs-gpio.h>  
  11. //#include <asm/arch/regs-gpio.h>  
  12. //#include </opt/EmbedSky/linux-2.6.30.4/arch/arm/mach-s3c2410/include/mach/hardware.h>  
  13. #include <linux/device.h>  
  14. #include <linux/kdev_t.h>  
  15. #include <linux/cdev.h>  
  16. #include <linux/spinlock.h>  
  17. #include <linux/mutex.h>  
  18. #include <asm/uaccess.h>  
  19. #include <linux/platform_device.h>  
  20.   
  21. #define DEVICE_NAME "ds18b20"  
  22. #define SKIP_ROM 0xcc  
  23. #define CONVERT_T 0x44  
  24. #define READ_SCRATCHPAD 0xbe  
  25. #define DQ  S3C2410_GPB5  
  26.   
  27. MODULE_LICENSE("GPL");  
  28.   
  29. struct ds18b20_info  
  30. {  
  31.     struct mutex    ds18b20_mutex;  
  32.     spinlock_t  ds18b20_spinlock;         
  33.   
  34. };  
  35. struct ds18b20_info *dsin;  
  36.   
  37.   
  38. static int ds18b20_init_work(struct ds18b20_info *dsin)  
  39. {  
  40.     char n;  
  41.     unsigned long flag;  
  42.   
  43.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  44.     s3c2410_gpio_setpin(DQ, 1);  
  45.     udelay(70);  
  46.     s3c2410_gpio_setpin(DQ, 0);  
  47.     udelay(500);  
  48.     s3c2410_gpio_setpin(DQ, 1);  
  49.     udelay(70);  
  50.     n = s3c2410_gpio_getpin(DQ);  
  51.     if(n == 1)  
  52.         n = -1;  
  53.     udelay(80);  
  54.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  55.     return n;  
  56. }  
  57.   
  58. static void ds18b20_write_byte(unsigned char data, struct ds18b20_info *dsin){  
  59.     unsigned char i;  
  60.     unsigned long flag;  
  61.       
  62.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  63.     for (i = 0; i < 8; i++){  
  64.         s3c2410_gpio_setpin(DQ, 0);  
  65.         udelay(10);  
  66.         s3c2410_gpio_setpin(DQ, (data&0x01)? 1:0);  
  67.         udelay(40);  
  68.         s3c2410_gpio_setpin(DQ, 1);  
  69.         data >>= 1;  
  70.         udelay(1);  
  71.     }  
  72.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  73. }  
  74.   
  75. static unsigned char ds18b20_read_byte(struct ds18b20_info *dsin){  
  76.     unsigned char i, value= 0;  
  77.     unsigned long flag;  
  78.       
  79.     spin_lock_irqsave(&dsin->ds18b20_spinlock,flag);  
  80.     for(i = 0; i < 8; i++){  
  81.         value >>= 1;  
  82.         s3c2410_gpio_setpin(DQ, 0);     //pull low  
  83.         s3c2410_gpio_setpin(DQ, 1); //pull high  
  84.         if (s3c2410_gpio_getpin(DQ))    //read  
  85.         {  
  86.             value |= 0x80;  
  87.         }  
  88.         udelay(40);  
  89.     }  
  90.     spin_unlock_irqrestore(&dsin->ds18b20_spinlock, flag);  
  91.     return value;  
  92. }  
  93.   
  94. static int ds18b20_open(struct inode *inode, struct file *file)  
  95. {  
  96.     file->private_data = dsin;  
  97.     printk("<1>open ds18b20 \n");  
  98.     return 0;  
  99. }  
  100.   
  101. static int ds18b20_close(struct inode *inode, struct file *file)  
  102. {     
  103.     printk("Now kfree has been executed");  
  104.     return 0;  
  105. }  
  106.   
  107. static int ds18b20_read (struct file *filp, char *buff, size_t __user count, loff_t *offp)  
  108. {  
  109.       
  110.     struct ds18b20_info *dsin = filp->private_data;  
  111.     unsigned char temp1, temp2;  
  112.     unsigned char buffer[2];  
  113.     unsigned long err;  
  114.       
  115.     mutex_lock(&dsin->ds18b20_mutex);  
  116.     if(ds18b20_init_work(dsin) < 0)  
  117.         printk("ds18b20 unavailable\n");  
  118.     ds18b20_write_byte(SKIP_ROM, dsin);  
  119.     ds18b20_write_byte(CONVERT_T, dsin);  
  120.       
  121.     ssleep(1);  
  122.       
  123.     ds18b20_init_work(dsin);  
  124.     ds18b20_write_byte(SKIP_ROM, dsin);  
  125.     ds18b20_write_byte(READ_SCRATCHPAD, dsin);  
  126.     temp1 = ds18b20_read_byte(dsin);  
  127.     temp2 = ds18b20_read_byte(dsin);  
  128.     mutex_unlock(&dsin->ds18b20_mutex);  
  129.       
  130.     buffer[0] = temp1;  
  131.     buffer[1] = temp2;  
  132.       
  133.         msleep(20);  
  134.             
  135.         err = copy_to_user(buff, (void *)buffer, count);  
  136.     return err? -EFAULT:count ;  
  137.           
  138. }  
  139.   
  140. static struct file_operations ds18b20_fops =  
  141. {  
  142.     .owner = THIS_MODULE,  
  143.     .open  = ds18b20_open,  
  144.     .read = ds18b20_read,  
  145.     .release = ds18b20_close,  
  146. };  
  147.   
  148. struct cdev my_cdev;  
  149. dev_t dev_num;  
  150. static char __initdata banner[] = "Ds18b20 sensor: fifth edition using platform, made by yanjun\n";  
  151. static struct class *led_class;  
  152.   
  153. static int s3c2410_ds18b20_probe(struct platform_device *pdev)  
  154. {     
  155.     int ret, err;  
  156.   
  157.     //get the device number in a dynamic way  
  158.     if ((ret = alloc_chrdev_region(&dev_num, 0, 1, DEVICE_NAME)) < 0)     
  159.         printk("alloc_chdrev_region error");  
  160.     printk("dev_num = %d\n", MAJOR(dev_num));     
  161.       
  162.     //create and initialize a cdev in a dynamic way  
  163.     cdev_init(&my_cdev, &ds18b20_fops);           
  164.     my_cdev.owner = THIS_MODULE;              
  165.     err = cdev_add(&my_cdev, dev_num, 1);     
  166.     if (err < 0)  
  167.         printk("cdev error");  
  168.     printk(banner);  
  169.       
  170.     //create a device node  
  171.     led_class = class_create(THIS_MODULE, DEVICE_NAME);               
  172.     if (IS_ERR(led_class)){  
  173.         printk("Err:failed in embedsky_leds class.\n");  
  174.         return -1;  
  175.     }  
  176.     device_create(led_class, NULL, dev_num, NULL, DEVICE_NAME);  
  177.       
  178.     dsin = kmalloc(sizeof(* dsin), GFP_KERNEL);  
  179.     if(dsin == NULL){  
  180.         printk("No memory for ds18b20\n");  
  181.         return -ENOENT;  
  182.     }  
  183.     memset(dsin, 0, sizeof(*dsin));  
  184.     spin_lock_init(&dsin->ds18b20_spinlock);  
  185.     mutex_init(&dsin->ds18b20_mutex);  
  186.   
  187.     printk(DEVICE_NAME"initialized\n");  
  188.     return 0;  
  189. }  
  190.   
  191. static void __exit s3c2410_ds18b20_remove(void)  
  192. {  
  193.     kfree(dsin);  
  194.     cdev_del(&my_cdev);                       
  195.     unregister_chrdev_region(dev_num, 1);         
  196.     device_destroy(led_class, dev_num);       
  197.     class_destroy(led_class);             
  198. }  
  199.   
  200. #ifdef CONFIG_PM  
  201. static int s3c2410_ds18b20_suspend(struct platform_device *pdev, pm_message_t msg)  
  202. {  
  203.     /*nothing to do*/  
  204.     return 0;  
  205. }  
  206.   
  207. static int s3c2410_ds18b20_resume(struct platform_device *pdev, pm_message_t msg)  
  208. {  
  209.     /*nothing to do*/  
  210.     return 0;  
  211. }  
  212. #else  
  213. #define s3c2410_ds18b20_suspend NULL  
  214. #define s3c2410_ds18b20_resume NULL  
  215. #endif  
  216.   
  217. static struct platform_driver s3c2410_ds18b20_driver = {  
  218.     .probe      = s3c2410_ds18b20_probe,  
  219.     .remove     = s3c2410_ds18b20_remove,  
  220.     .suspend    = s3c2410_ds18b20_suspend,  
  221.     .resume     = s3c2410_ds18b20_resume,     
  222.     .driver     = {  
  223.         .name   = "s3c2410-ds18b20",  
  224.         .owner  = THIS_MODULE,  
  225.     },  
  226. };  
  227.   
  228. static int __init s3c2410_ds18b20_init(void)  
  229. {  
  230.     return platform_driver_register(&s3c2410_ds18b20_driver);  
  231. }  
  232.   
  233. static void __exit s3c2410_ds18b20_exit(void)  
  234. {  
  235.     platform_driver_unregister(&s3c2410_ds18b20_driver);  
  236. }  
  237.   
  238. module_init(s3c2410_ds18b20_init);  
  239. module_exit(s3c2410_ds18b20_exit);  

在ds18b20_read函数中,添加了20ms的休眠,该句用于实现多进程的访问。不添加的话,在我的平台上,只有一个进程读取到温度值。

上面是驱动的代码,还需要修改另外3个文件。

第一个:arch/arm/plat-s3c24xx/devs.c

添加内容:

[html] view plaincopy
  1. <span style="font-size:12px;">/*DS18b20 temperature sensor, added by yj423*/  
  2. static u64 s3c_device_ds18b20_dmamask = 0xffffffffUL;  
  3.   
  4. struct platform_device s3c_device_ds18b20 = {  
  5.     .name       = "s3c2410-ds18b20",  
  6.     .id     = -1,  
  7.     .num_resources  = 0,  
  8.     .resource   = NULL,  
  9.     .dev        = {  
  10.         .dma_mask = &s3c_device_ds18b20_dmamask,  
  11.         .coherent_dma_mask = 0xffffffffUL  
  12.     }  
  13. };  
  14.   
  15. EXPORT_SYMBOL(s3c_device_ds18b20);</span>  

第二个: arch/arm/plat-s3c/include/plat/devs.h

添加内容:

[html] view plaincopy
  1. /*added by yj423*/  
  2. 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

3. 应用程序

[cpp] view plaincopy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/ioctl.h>  
  5. #include <sys/types.h>  
  6. #include <sys/stat.h>  
  7. #include <fcntl.h>  
  8. #include <sys/select.h>  
  9. #include <sys/time.h>  
  10. #include <errno.h>  
  11.   
  12. int main(void)  
  13. {  
  14.     int i, temper_fd;  
  15.     unsigned char value[2];  
  16.     unsigned char temp;  
  17.     float temperature;  
  18.     i = 0;  
  19.     if((temper_fd = open("/dev/ds18b20", 0)) < 0){  
  20.         perror("open device ds18b20 error");  
  21.         printf("exit\n");  
  22.         exit(1);  
  23.     }  
  24. //      printf("before syscall read\n");      
  25.     for(; ;){     
  26.         int ret = read(temper_fd, value, sizeof(value));  
  27.         if (ret != 2){  
  28.             printf("read wrong\n");  
  29.             exit(0);  
  30.         }  
  31.         temp = value[0] & 0x0f;  
  32.         value[1] = value[1] << 4 | value[0] >> 4;   
  33.         temperature = (float)value[1] + temp * 6.0 / 100.0;  
  34.         printf("temperature = %.2f ", temperature);  
  35.         fflush(stdout);  
  36.         i ++;  
  37.         if (i == 4){  
  38.             printf("\n");  
  39.             i = 0;  
  40.         }  
  41.     }         
  42.     close(temper_fd);  
  43.     return 0;  
  44. }  


运行结果:

 [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

原创粉丝点击