arm+linux fl2440 ds18b20 温度传感器驱动编写及测试

来源:互联网 发布:迪优美特网络机顶盒多少钱 编辑:程序博客网 时间:2024/06/04 18:21

---------------------------------------------------------------------------------------

                                   主机操作系统:centos 6.7
                                   交叉编译器版本:arm-linux-gcc-4.5.4
                                   开发板平台:fl2440
                                    linux内核版本:Linux-3.0

                                    Author:  shaocongshuai <916962705@qq.com>

-----------------------------------------------------------------------------------------------------------------

 ds18b20简介: 

   DS18B20是Dallas公司生产的数字温度传感器,具有体积小、适用电压宽、经济灵活的特点。它内部使用了onboard专利技术,全部传感元件及转换电路集成在一个形如三极管的集成电路内。DS18B20有电源线、地线及数据线3根引脚线,工作电压范围为3~5.5 V,支持单总线接口。


结构及原理:

  


详细的信息参考ds18b20的datasheet


ds18b20.c代码

#include<linux/module.h>  

#include<linux/kernel.h>  
#include<linux/fs.h>  
#include<linux/init.h>  
#include<linux/delay.h>  
#include<linux/gpio.h>  
#include<linux/types.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/errno.h>  
#include<asm/uaccess.h>  
  
#define DQ S3C2410_GPG(0)  
#define INPUT S3C2410_GPIO_INPUT  
#define OUTPUT S3C2410_GPIO_OUTPUT  

#ifndef  DS18B20_MAJOR
#define  DS18B20_MAJOR       0
#endif

#define DRV_AUTHOR                "shaocongshuai" 
#define DRV_DESC                  "S3C24XX DS18B20 driver"
#define DEV_NAME                  "ds18b20"   
  
static int ds18b20_major = DS18B20_MAJOR;  
static int ds18b20_minor = 0;  
  
static struct cdev      *ds18b20_cdev; //记得给指针分配内存
static struct class     *ds18b20_class;

void ds18b20_reset(void) //重置ds18b20  
{  
    int err;  
  
    s3c2410_gpio_cfgpin(DQ, OUTPUT); //配置端口的GPIO的功能  
    s3c2410_gpio_pullup(DQ, 1); //配置上拉电阻.0不需要上拉电阻,1需要上拉电阻。 
  
    s3c2410_gpio_setpin(DQ, 0);  //写数据到端口 。输入值是0,就是低电平,to为1,表示该pin输出为1 
    //拉低总线保持480us以发出一个复位脉冲
    udelay(480);  
    s3c2410_gpio_setpin(DQ, 1);//释放总线  
    udelay(60);  //DS18B20拉低信号,60~240us表示应答 
  
    s3c2410_gpio_cfgpin(DQ, INPUT);//读入DS18B20拉低信号  
    while(s3c2410_gpio_getpin(DQ));  //等待DS18B20应答
    while(!s3c2410_gpio_getpin(DQ)); //等待DS18B20释放总线 
}  
  
static unsigned int ds18b20_write(unsigned char data)  
{  
    unsigned int i;  
    s3c2410_gpio_cfgpin(DQ, OUTPUT); //设置为输出 
    s3c2410_gpio_pullup(DQ, 1);   //上拉 

    for (i = 0; i < 8; i++)   //只能一位一位的读写  
    {  
        s3c2410_gpio_setpin(DQ, 0);//拉低,写时序开始  
        udelay(15); //写时序开始后的15us释放总线 
        if(data & 0x01) //线上如果是高电平则写1时序 
        {  
            s3c2410_gpio_setpin(DQ ,1);  
            udelay(60); 
        }  
        else //线上如果是低电平则写0时序
        {
            s3c2410_gpio_setpin(DQ ,0);  
            udelay(60); //所有写时序必须60us以上 
        }
        s3c2410_gpio_setpin(DQ, 1); //释放总线  
        udelay(1);  
        data >>= 1;     //从最低位开始判断;每比较完一次便把数据向右移,获得新的最低位状态  
    }  
    return 2;  
}  
  
static unsigned int ds18b20_read(void)  
{  
    unsigned int i;  
    unsigned char data = 0x00;  
  
    for (i =0; i < 8 ; i++)  
    {  
        data >>= 1;  
        s3c2410_gpio_cfgpin(DQ, OUTPUT);  
        s3c2410_gpio_setpin(DQ, 0); //拉低总线,启动输入  
        udelay(1); //至少1us,然后总线释放 
        s3c2410_gpio_setpin(DQ, 1); //释放总线 
        s3c2410_gpio_cfgpin(DQ, INPUT);  
        if(0 != s3c2410_gpio_getpin(DQ))  
            data |= 0x80;   //最低位数据从data的最高位放起,边放边右移直到读取位完毕。  
        udelay(60); //读时序必须至少60us 
    }  
    return data;  
}  
  
/*unsigned int s3c2410_gpio_getpin(unsigned int pin)
{
        void __iomem *base = S3C24XX_GPIO_BASE(pin);
        unsigned long offs = S3C2410_GPIO_OFFSET(pin);
        return __raw_readl(base + 0x04) & (1<< offs);
}
    s3c2410_gpio_getpin()的返回值是GPxDAT寄存器的值与所要读取的GPIO对应的bit mask相与以后的值,0表示该GPIO对应的bit为0, 非0表示该bit为1,所以s3c2410_gpio_getpin(S3C2410_GPG(9))如果GPG9为低电平则返回的是0,如果是高电平则返回的是GPxDAT中的GPG9对应位的值为0x0100而不是0x0001,查处问题后修改也很简单了。*/

static ssize_t read_ds18b20(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)  
{  
    unsigned char Data[2] = {0x00, 0x00};  
  
    ds18b20_reset();  
    ds18b20_write(0xcc);  //忽略rom指令
    ds18b20_write(0x44);  //温度转换
  
    ds18b20_reset();  
    ds18b20_write(0xcc);  
    ds18b20_write(0xbe);  //读存储器
  
    Data[0] = ds18b20_read();  //读低8位,存放在result[0]  
    Data[1] = ds18b20_read();  //读高8位,存放在result[1] 

    ds18b20_reset();  
    if ( copy_to_user(buf, Data, sizeof(Data)))  
        return -EFAULT;  //return -EFAULT代表返回一个错误代码;
    else
        printk("copy to user is ok.\n");
}  
/*#include <linux/uaccess.h>
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
如果数据拷贝成功,则返回零;否则,返回没有拷贝成功的数据字节数。
*to是用户空间的指针,
*from是内核空间指针,
n表示从内核空间向用户空间拷贝数据的字节数*/
  
static int open_ds18b20(struct inode *inode, struct file *filp)  
{  
    int flag = 0;  
  
    flag = ds18b20_reset();  
    if(flag)  
    {  
        printk("open ds18b20 successful!\n");  
    }  
    else printk("open ds18b20 failed!\n");  
    return 0;  
}  
  
static int release_ds18b20(struct inode *inode, struct file *filp)  
{  
    return 0;  
}  
  
static struct file_operations ds18b20_fops={  
    .owner  = THIS_MODULE,  
    .read   = read_ds18b20,  
    .open   = open_ds18b20,  
    .release = release_ds18b20,  
};  
  
static int __init ds18b20_init(void)  
{  
    int     result;
    int     err;  
    dev_t   devno = 0;  
  
    if(ds18b20_major)  
    {  
        devno = MKDEV(ds18b20_major, ds18b20_minor);  
        result = register_chrdev_region(devno, 1, DEV_NAME);  
    }  
    else
    {  
        result = alloc_chrdev_region(&devno, ds18b20_minor, 1, DEV_NAME);  
        ds18b20_major = MAJOR(devno);  
    }  
    if(result < 0)  
    {  
        printk(KERN_ERR "%s can't use major %d\n",DEV_NAME, ds18b20_major);  
        return -ENODEV;
    }  
    printk("%s use major %d\n",DEV_NAME, ds18b20_major);  
  
    if(NULL == (ds18b20_cdev=cdev_alloc()) )
    {
        printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
        unregister_chrdev_region(devno, 1);
        return -ENOMEM;
    }

    ds18b20_cdev->owner = THIS_MODULE;
    cdev_init(ds18b20_cdev, &ds18b20_fops);  


    err = cdev_add(ds18b20_cdev, devno, 1);  
    if(err)  
    {  
        printk(KERN_NOTICE"ERROR %d add ds18b20\n",err);  
        goto ERROR;  
    }  

       内核中定义了struct class结构体,一个struct class 结构体类型变量对应一个类,内核同时提供了class_create()函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建了这个类,再调用device_create()函数在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create()函数,去/sysfs下寻找对应的类而创建设备节点.
~ >: ls sys/class/
bdi/           i2c-dev/       net/           scsi_host/     vc/
block/         ieee80211/     ppp/           sound/         vtconsole/
display/       input/         rfkill/        spi_master/    xt_idletimer/
ds18b20/       mem/           rtc/           spidev/
firmware/      misc/          scsi_device/   tty/
graphics/      mmc_host/      scsi_disk/     ubi/

i2c-adapter/   mtd/           scsi_generic/  usb_device/


    ds18b20_class = class_create(THIS_MODULE, DEV_NAME);  

(1).第一个参数指定类的所有者是那个模块,一般为THIS_MODULE
(2)第二个参数是类目录名,在/sys/class下创建类目录
(3)调用class_create()函数后,要用IS_ERR()函数判断创建的类是否正确。


    if ( IS_ERR(ds18b20_class) ) 
    {
        printk(KERN_ERR "S3C %s driver can't create a class.\n", DEV_NAME);
        return -1;
    }

    device_create(ds18b20_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEV_NAME);  
    printk(KERN_NOTICE"Ds18b20 is ok!\n");  
    return 0;  
ERROR:  
    printk(KERN_ERR"%s driver installed failure.\n",DEV_NAME);  
    cdev_del(ds18b20_cdev);  
    unregister_chrdev_region(devno, 1);  
    return err;  
  
}  

static void __exit ds18b20_exit(void)  
{  
    dev_t devno = MKDEV(ds18b20_major, 0);  
  
    cdev_del(ds18b20_cdev);  
    device_destroy(ds18b20_class,devno);  
    class_destroy(ds18b20_class);  
  
    unregister_chrdev_region(devno, 1);  
    printk(KERN_NOTICE"Goodbye ds18b20!\n");  
}  
  
module_init(ds18b20_init);  
module_exit(ds18b20_exit);  

MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);

MODULE_LICENSE("Dual BSD/GPL");  


测试代码 ds18b20_test.c

#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <linux/ioctl.h>  
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
  
int main(int    argc, char  **argv)  
{  
    int                 fd;  
    unsigned char       result[2];    // 从ds18b20读出的结果,result[0]存放低八位  
    unsigned short      temp = 0;  
    double              temperature = 0;  
    
    if ((fd=open("/dev/ds18b20", O_RDWR|O_NONBLOCK|O_NOCTTY))<0)  
    {
        perror("open device failed\n");  
        exit(1);  
    }  
    printf ("open ds18b20 success.\n");


    while (1) 
    {  
        read(fd, result, sizeof(result));  
        temp = ((unsigned short)result[1])<<8;
        temp |= (unsigned short)result[0];
        temperature = ((double)temp) * 0.0625;
        printf("Current Temperature:%6.2f\n", temperature);  
        sleep(2);  
    }  
    close(fd);


    return 0;  

注意

         二进制中的前面5位是符号位,如果测得的温度大于0, 这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反加1再乘于0.0625即可得到实际 温度。 例如+125℃的数字输出为07D0H,+25.0625℃的数字输出为0191H,-25.0625℃的数字输出为FE6FH,-55℃的数字输出为FC90H 

0 0