基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
来源:互联网 发布:淘宝房产司法拍卖 编辑:程序博客网 时间:2024/06/10 15:22
此代码为本人原创。该代码仍有不完善之处,有可能还要再次修改!仅供参考!
若有错误、疑问和意见请留言,非常感谢!
该驱动程序基于TQ2440开发板,内核2.6.30。
驱动程序比较简单,使用字符设备来实现。要注意的是在模拟DS18B20的总线时序时,处理器不能抢占当前线程从而造成时序错乱,因此使用了自旋锁来禁止处理器抢占。
代码比较简单,所以代码注释也比较少。如果有不明白的请参考DS18B20的datasheet以及<<Linux设备驱动程序>>的第三章。
1. 驱动代码一
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);
2. 驱动代码二(使用platform机制)
该代码基于代码一,进行了简单的修改,使用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
添加内容:
- <span style="font-size:12px;">/*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);</span>
第二个: arch/arm/plat-s3c/include/plat/devs.h
添加内容:
- /*added by yj423*/
- extern struct platform_device s3c_device_ds18b20;
在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. 应用程序
- #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
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- 基于ARM-LINUX的温度传感器驱动-DS18B20
- 基于ARM-LINUX的温度传感器驱动(DS18B20)
- 嵌入式Linux驱动——s3c2410的ds18b20(基于linux-2.6.24.4内核)
- linux ds18b20 温度传感器驱动
- FL2440—基于platform 模型的DS18B20驱动实例
- FL2440—基于platform 模型的DS18B20驱动实例
- S3C2440 温度传感器ds18b20的驱动编写与测试
- 基于Linux的温度传感器DS18B20驱动程序设计
- 基于S3C2440芯片linux系统下的ds18b20设备驱动
- 基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
- 基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
- 基于S3C2440的嵌入式Linux驱动——看门狗(watchdog)驱动解读
- 基于linux platform总线的LED驱动
- s3c2440添加ds18b20驱动
- 09-S3C2440驱动学习(三)嵌入式linux-platform平台总线驱动程序及分离分层构建驱动框架
- 【C语言】getchar函数的控制台输入原理,回车符的处理
- android 使用 ndk生成.so文件
- selenium2---XPATH语法
- ORACLE:中文排序
- cocos2d-x 一些简便方法
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- memcached server LRU 深入分析
- java 静态变量和实例变量的区别
- iPhone5和iOS6上HTML5开发的新增功能
- 微软安全新闻聚焦-双周刊第三十期
- WinDbg 源码模式调试
- NoUniqueBeanDefinitionException异常
- Java学习笔记35:Java常用字符串操作函数
- win7以管理员身份运行设置图解(转)