DS18B20驱动移植和调试用例

DS18B20时序分析:
DS18B20的一线工作协议流程是:初始化->ROM操作指令->存储器操作指令->数据传输,
其工作时序包括:初始化时序、写时序、读时序。


1.初始化时序:

主机:首先发出一个480~960us的低电平脉冲,然后释放总线变为高电平。
并在随后的480us时间内对总线进行检测:
若有低电平出现则说明总线上有器件做出应答;
若无低电平出现一直为高电平则说明总线上无器件应答。

从器件DS18B20:在一上电就一直检测总线上是否有480~960us的低电平出现:
若有,在总线转为高电平后等待15~60us后将总线电平拉低60~240us做出相应存在脉冲,告知主机器件已经做好准备;
若没有检测到则一直在检测等待。


2.写时序:(写周期为60us~120us)

主机:在写周期一开始先把总线拉低1us表示写周期开始,
随后若主机写0时则继续拉低低电平最少60us直至写周期结束,然后释放总线为高电平;
若主机想写1则一开始拉低总线电平1us后就释放总线为高电平,一直到写周期结束。

从器件DS18B20:则在检查到总线被拉低后等待15us然后从15us~45us开始对总线采样,
在采样周期内总线为高电平则为1;
在采样周期内总线为低电平则为0。
至此完成一个写时序。

说明:/*写一个字节*/
写“1”时隙:
保持总线在低电平1微秒到15微秒之间
然后再保持总线在高电平15微秒到60微秒之间
理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平

写“0”时隙:
保持总线在低电平15微秒到60微秒之间
然后再保持总线在高电平1微秒到15微秒之间
理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平

3.读时序:(读时序过程 >60us)

主机:把总线拉低,在1us之后就必须释放总线为高电平以便让DS18B20把数据传输到总线上。

从器件DS18B20:在检测到总线被拉低1us后便开始送出数据,
若要送出0就把总线拉低为低电平直到读周期结束;
若要送出1则释放总线为高电平。

主机:在一开始拉低总线1us后释放总线,包括前面的拉低总线电平1us在内的15us时间内完成对总线进行采样检测:
在采样期内总线为低电平则确认为0;
在采样期内总线为高电平则确认为1;
至此完成一个读时序。

说明:/*读一个字节*/
读“1”时隙:
若总线状态保持在低电平状态1微秒到15微秒之间
然后跳变到高电平状态且保持在15微秒到60微秒之间
就认为从DS18B20读到一个“1”信号
理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平

读“0”时隙:
若总线状态保持在低电平状态15微秒到30微秒之间
然后跳变到高电平状态且保持在15微秒到60微秒之间
就认为从DS18B20读到一个“0”信号
理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平

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

[root@localhost DS18B20]# ls

app ds18b20.c Makefile
[root@localhost DS18B20]# ls app/
a.out ds18b20_test.c

复制代码

obj-m    :=ds18b20.o    //自己测试用,不能应用于实际工程中!
KERNEL    :=/linux-3.5
all:
    make -C $(KERNEL) M=`pwd`
clean:
    make -C $(KERNEL) M=`pwd` clean

复制代码

复制代码

/*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/
#include <linux/init.h>
/*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/
#include <linux/module.h>
/*三个字符设备函数*/
#include <linux/fs.h>
/*定义字符设备的结构体*/
#include <linux/cdev.h>
/*分配内存空间函数头文件*/
#include <linux/slab.h>
/*定义module_param module_param_array中perm的头文件*/
#include <linux/stat.h>
/*MKDEV转换设备号数据类型的宏定义*/
#include <linux/kdev_t.h>
/*字符定义*/
#include <linux/string.h>
/*linux系统提供的申请端口函数和设置端口状态的函数*/
#include <linux/gpio.h>
/*设置GPIO状态,上下拉,输入输出就,复用等等相关函数*/
#include <plat/gpio-cfg.h>      
/*包含GPIO端口的宏定义*/
//#include <mach/gpio-exynos4.h>
#include <linux/mm.h>
/*dev_t等的定义*/
#include <linux/types.h>
/*包含copy_to_user和copy_from_user的头文件*/
#include <asm/uaccess.h>
/*包含寄存器操作函数的头文件*/
#include <asm/io.h>
/*延时定义*/
#include <linux/delay.h>
/*包含函数device_create 结构体class等头文件*/
#include <linux/device.h>
/*错误诊断和输出需要的头文件*/
#include <linux/errno.h>

#define ds18b20_io (EXYNOS4_GPA0(7))/*4412开发板的gpio部分,gpio第13引脚,控制总线*/
#define DEVICE_NAME "ds18b20"   /*定义的设备节点,于/dev目录下,通过其对具体设备进行访问*/

#define ds18b20_MAJOR 245   /*主设备号*/  //注意:设为0的话是动态分配
#define ds18b20_MINOR 0     /*次设备号*/
#define DEVICE_MINOR_NUM 1  /*次设备个数*/

/*主次设备号赋值*/
int ds18b20_major = ds18b20_MAJOR;
int ds18b20_minor = ds18b20_MINOR;

/*创建了一个总线类型,会在/sys/class下生成myclass目录*/
static struct class *ds18b20_class;

/*字符驱动结构体*/
struct ds18b20_dev{
    struct cdev cdev;
};
struct ds18b20_dev *ds18b20_device;

/*ds18b20的初始化*/
void ds18b20_reset(void)
{
    int ret;

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*配置成输出*/
    gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,并保持高电平状态约100微秒*/
    udelay(100);
    gpio_set_value(ds18b20_io,0);/*向18B20发送一个下降沿,并保持低电平状态约600微秒*/
    udelay(600);
    gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,此时可释放DS18B20总线*/
    udelay(100);//在该时间段可以将引脚配成输入功能接下来进行检测看是否有从机器件拉低响应!
    /*以上操作是给DS18B20一个复位脉冲*/

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));/*配置为输入,可以检测到DS18B20是否复位成功*/
    /*如果低电平出现说明总线上有器件已做出应答*/
    ret = gpio_get_value(ds18b20_io);/*读取io上的电平.若总线在释放后总线状态为高电平,则复位失败*/
    /*if(!ret){
        printk(KERN_EMERG "ds18b20 init is success!\n");
    }
    else{
        printk(KERN_EMERG "ds18b20 init is failed!\n");
    }*/
}

/*写一个字节*/
/*写“1”时隙:
   保持总线在低电平1微秒到15微秒之间
   然后再保持总线在高电平15微秒到60微秒之间
   理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平

写“0”时隙:
    保持总线在低电平15微秒到60微秒之间
    然后再保持总线在高电平1微秒到15微秒之间
    理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
    */
void write_data (unsigned char dat)
{
    unsigned char i;

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*端口设置为输出*/
    for(i=0;i<8;i++){
/*主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。*/
        gpio_set_value(ds18b20_io,0);
        udelay(1);

        /*此处为写"1"*/
        /*若byte变量的D0位是1,则需向总线上写“1”
         根据写“1”时隙规则,电平在此处翻转为高*/
        if(((dat)&(0x01))==1)
            gpio_set_value(ds18b20_io,1);
        udelay(80);//写1/0后直到写时序结束
        gpio_set_value(ds18b20_io,1);
        udelay(15);
        dat = dat>>1;
    }
    gpio_set_value(ds18b20_io,1);//重新释放DS18B20总线
}

/*读一个字节*/
/* 读“1”时隙:
    若总线状态保持在低电平状态1微秒到15微秒之间
    然后跳变到高电平状态且保持在15微秒到60微秒之间
    就认为从DS18B20读到一个“1”信号
    理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平

读“0”时隙:
    若总线状态保持在低电平状态15微秒到30微秒之间
    然后跳变到高电平状态且保持在15微秒到60微秒之间
    就认为从DS18B20读到一个“0”信号
    理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
    */
unsigned char read_data(void)
{
    unsigned char i;
    unsigned char val = 0;

    for(i=0;i<8;i++){
        s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));
        gpio_set_value(ds18b20_io,0);
        udelay(1);
        val >>=1;
        gpio_set_value(ds18b20_io,1);
        s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));
        udelay(1);

        /*若总线在我们设它为低电平之后若1微秒之内变为高
          则认为从DS18B20处收到一个“1”信号
          因此把byte的D7为置“1”   */
        if(gpio_get_value(ds18b20_io))
            val = val | 0x80;
        udelay(60);
    }
    return val;
}

/*打开文件函数*/
static int ds18b20_open(struct inode *inide,struct file *flip)
{
    int ret;
    /*申请gpio*/
    ret = gpio_request(ds18b20_io,"DS18B20");
    if(ret<0){
        printk(KERN_EMERG "gpio_request is failed!\n");
        return 1;
    }
    printk(KERN_EMERG "open DS18B20\n");

    return 0;
}

/*读文件函数*/
static ssize_t ds18b20_read(struct file *flip,char __user *buff,size_t count,loff_t *f_ops)
{
    unsigned char buf[2];/*DS18B20将产生的温度数据以两个字节的形式存储到高速暂存器的温度寄存器中*/

    ds18b20_reset();
    udelay(420);
    write_data(0xcc);/*跳过序列号命令*/
    write_data(0x44);/*发送转换命令44H,完成温度测量和AD转换*/
    mdelay(800);

    ds18b20_reset();
    udelay(400);
    write_data(0xcc);
    write_data(0xbe);/*发送读取命令,从0位到第9位*/
    
    buf[0] = read_data();/*读取低位温度*/
    buf[1] = read_data();/*读取高位温度*/
    /*传输数据到用户空间*/
    if(copy_to_user(buff,buf,sizeof(buf))){
        return -EINVAL;
    }
    
    return 0;
}

static int ds18b20_release(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "ds18b20_release is success!\n");

    return 0;
}

/*文件结构体*/
static struct file_operations ds18b20_fops = {
    .owner  =   THIS_MODULE,
    .open   =   ds18b20_open,
    .read   =   ds18b20_read,
    .release    =   ds18b20_release,
};

/*注册设备到系统*/
static void ds18b20_setup_cdev(struct ds18b20_dev *dev,int index)
{
    int err;
    /*获取设备号*/
    int devno = MKDEV(ds18b20_major,index);

    /*cdev结构体初始化*/
    cdev_init(&dev->cdev,&ds18b20_fops);
    dev->cdev.owner = THIS_MODULE;/*给cdev结构体赋值*/
    dev->cdev.ops = &ds18b20_fops;
    /*注册进系统*/
    err = cdev_add(&dev->cdev,devno,1);
    if(err){
        printk(KERN_EMERG "cdev_add %d is failed! %d\n",index,err);
    }
    else{
        printk(KERN_EMERG "cdev_add %d is success!\n",ds18b20_major);
    }
}

static int ds18b20_init(void)
{
    int ret = 0;
    /*dev_t在cdev字符驱动结构体里定义,必须通过dev_t来描述设备号*/
    dev_t ds18b20_dev;

    /*打印输出主次设备号*/
    printk(KERN_EMERG "ds18b20_major is %d!\n",ds18b20_major);
    printk(KERN_EMERG "ds18b20_minor is %d!\n",ds18b20_minor);

    /*申请主次设备号*/
    if(ds18b20_major){
        /*MKDEV为设备号处理宏命令*/
        ds18b20_dev = MKDEV(ds18b20_major,ds18b20_minor);
        /*主设备号不为0则静态注册设备*/
        ret = register_chrdev_region(ds18b20_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
    }
    else{
        /*动态注册设备*/
        ret = alloc_chrdev_region(&ds18b20_dev,ds18b20_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
        ds18b20_major = MAJOR(ds18b20_dev);/*得到主设备号*/
        printk(KERN_EMERG "alloc_chrdev_region is %d\n",ds18b20_major);
    }
    if(ret<0){
        printk(KERN_EMERG "register_chrdev_region is %d failed!\n",ds18b20_major);
    }
    /*创建一个ds18b20_class的总线*/
    ds18b20_class = class_create(THIS_MODULE,DEVICE_NAME);
    /*申请内存空间*/
    ds18b20_device = kmalloc(DEVICE_MINOR_NUM * sizeof(struct ds18b20_dev),GFP_KERNEL);
    if(!ds18b20_device){
        ret = -ENOMEM;
        goto fail;
    }
    /*清空内存空间的数据*/
    memset(ds18b20_device,0,DEVICE_MINOR_NUM*sizeof(struct ds18b20_dev));
    /*注册设备到系统*/
    ds18b20_setup_cdev(ds18b20_device,0);
    /*创建设备节点*/
    device_create(ds18b20_class,NULL,MKDEV(ds18b20_major,ds18b20_minor),NULL,DEVICE_NAME);
    printk(KERN_EMERG "ds18b20 is initation!\n");
    return 0;

    /*注册失败*/
fail:
    unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
    printk(KERN_EMERG "ds18b20 exit!\n");
    return ret;
}

/*设备注销*/
static void __exit ds18b20_exit(void)
{
    /*注销设备号*/
    unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
    /*注销设备*/
    cdev_del(&(ds18b20_device->cdev));
    /*摧毁设备节点*/
    device_destroy(ds18b20_class,MKDEV(ds18b20_major,ds18b20_minor));
    /*释放总线*/
    class_destroy(ds18b20_class);
    /*释放内存*/
    kfree(ds18b20_device);
    /*释放gpiio*/
    gpio_free(ds18b20_io);
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);

MODULE_AUTHOR("crmn");/*作者*/
MODULE_DESCRIPTION("TINY4412 ds18b20 driver");/*模块功能描述*/
MODULE_LICENSE("Dual BSD/GPL");/*开源声明*/
MODULE_VERSION("ds18b20 V1.0");/*代码修订版本*/

复制代码

复制代码

CC=arm-none-linux-gnueabi-gcc
obj-m    :=ds18b20.o
KERNEL    :=/kernel_v04_king_release
all:
    make -C $(KERNEL) M=`pwd`
clean:
    make -C $(KERNEL) M=`pwd` clean

复制代码

复制代码

#include <stdio.h>//标准输入输出
#include <sys/types.h>//open和creat函数需要的头文件
#include <sys/stat.h>//open和creat函数需要的头文件
#include <fcntl.h>//open和creat函数需要的头文件
#include <unistd.h>//close,read,write函数需要的头文件
#include <sys/ioctl.h>//ioctl函数需要的头文件

int main()
{
    int fd;
    unsigned int tem = 0;
    float temperature;
    char buf[10];
    char *ds18b20_node = "/dev/ds18b20";

/*O_RDWR只读打开,O_NDELAY非阻塞方式*/    
    if((fd = open(ds18b20_node,O_RDWR|O_NDELAY))<0)
    {
        printf("ds18b20 open %s failed",ds18b20_node);
        return -1;
    }
    else{
        /*printf("ds18b20 open %s success",ds18b20_node);*/
        while(1)
        {
            read(fd,buf,2);/*从buf中读出数据*/
            tem = buf[1];
            tem <<= 8;/*高位移到前面*/
            tem = tem | buf[0]; /*得到读出的数据*/
            /*18B20是定点数据表示方式,12.4的编码,
            即前12位是温度整数部分,后4位为小数部分,
            4位分辨率就是1/16;
            转换为10进制结果要乘以1/16=0.0625
            */
            temperature = tem*0.0625;/*要求出正数的十进制值,必须将读取到的LSB字节,MSB字节进行整合处理,然后乘以0.0625即可*/
            printf("temperature is :%7.4f\n",temperature);/*打印出温度*/
            tem = 0;
            sleep(1);
        }
    }

    close(fd);
    return 0;
}

复制代码

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

复制代码

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>      
//#include <mach/gpio-exynos4.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>

#define ds18b20_io (EXYNOS4_GPB(4))/*4412开发板的gpio部分*/
#define DEVICE_NAME "ds18b20"   /*定义的设备节点,于/dev目录下,通过其对具体设备进行访问*/

#define ds18b20_MAJOR 245   /*主设备号*/
#define ds18b20_MINOR 0     /*次设备号*/
#define DEVICE_MINOR_NUM 1  /*次设备个数*/

/*主次设备号赋值*/
int ds18b20_major = ds18b20_MAJOR;
int ds18b20_minor = ds18b20_MINOR;

/*创建了一个总线类型,会在/sys/class下生成myclass目录*/
static struct class *ds18b20_class;

/*字符驱动结构体*/
struct ds18b20_dev{
    struct cdev cdev;
};
struct ds18b20_dev *ds18b20_device;

/*ds18b20的初始化*/
void ds18b20_reset(void)
{
    int ret;

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*配置成输出*/
    gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,并保持高电平状态约100微秒*/
    udelay(100);
    gpio_set_value(ds18b20_io,0);/*向18B20发送一个下降沿,并保持低电平状态约600微秒*/
    udelay(600);
    gpio_set_value(ds18b20_io,1);/* 向18B20发送一个上升沿,此时可释放DS18B20总线*/
    udelay(100);//在该时间段可以将引脚配成输入功能接下来进行检测看是否有从机器件拉低响应!
    /*以上操作是给DS18B20一个复位脉冲*/

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));/*配置为输入,可以检测到DS18B20是否复位成功*/
    /*如果低电平出现说明总线上有器件已做出应答*/
    ret = gpio_get_value(ds18b20_io);/*读取io上的电平.若总线在释放后总线状态为高电平,则复位失败*/
    /*if(!ret){
        printk(KERN_EMERG "ds18b20 init is success!\n");
    }
    else{
        printk(KERN_EMERG "ds18b20 init is failed!\n");
    }*/
}

void write_data (unsigned char dat)
{
    unsigned char i;

    s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));/*端口设置为输出*/
    for(i=0;i<8;i++){
/*主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。*/
        gpio_set_value(ds18b20_io,0);
        udelay(1);

        /*此处为写"1"*/
        /*若byte变量的D0位是1,则需向总线上写“1”
         根据写“1”时隙规则,电平在此处翻转为高*/
        if(((dat)&(0x01))==1)
            gpio_set_value(ds18b20_io,1);
        udelay(80);//写1/0后直到写时序结束
        gpio_set_value(ds18b20_io,1);
        udelay(15);
        dat = dat>>1;
    }
    gpio_set_value(ds18b20_io,1);//重新释放DS18B20总线
}

unsigned char read_data(void)
{
    unsigned char i;
    unsigned char val = 0;

    for(i=0;i<8;i++){
        s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(1));
        gpio_set_value(ds18b20_io,0);
        udelay(1);
        val >>=1;
        gpio_set_value(ds18b20_io,1);
        s3c_gpio_cfgpin(ds18b20_io,S3C_GPIO_SFN(0));
        udelay(1);

        /*若总线在我们设它为低电平之后若1微秒之内变为高
          则认为从DS18B20处收到一个“1”信号
          因此把byte的D7为置“1”   */
        if(gpio_get_value(ds18b20_io))
            val = val | 0x80;
        udelay(60);
    }
    return val;
}

/*打开文件函数*/
static int ds18b20_open(struct inode *inide,struct file *flip)
{
    int ret;
    /*申请gpio*/
    ret = gpio_request(ds18b20_io,"DS18B20");
    if(ret<0){
        printk(KERN_EMERG "gpio_request is failed!\n");
        return 1;
    }
    printk(KERN_EMERG "open DS18B20\n");

    return 0;
}

/*读文件函数*/
static ssize_t ds18b20_read(struct file *flip,char __user *buff,size_t count,loff_t *f_ops)
{
    unsigned char buf[2];/*DS18B20将产生的温度数据以两个字节的形式存储到高速暂存器的温度寄存器中*/

    ds18b20_reset();
    udelay(420);
    write_data(0xcc);/*跳过序列号命令*/
    write_data(0x44);/*发送转换命令44H,完成温度测量和AD转换*/
    mdelay(800);

    ds18b20_reset();
    udelay(400);
    write_data(0xcc);
    write_data(0xbe);/*发送读取命令,从0位到第9位*/
    
    buf[0] = read_data();/*读取低位温度*/
    buf[1] = read_data();/*读取高位温度*/
    /*传输数据到用户空间*/
    if(copy_to_user(buff,buf,sizeof(buf))){
        return -EINVAL;
    }
    
    return 0;
}

static int ds18b20_release(struct inode *inode, struct file *filp)
{
    printk(KERN_EMERG "ds18b20_release is success!\n");

    return 0;
}

/*文件结构体*/
static struct file_operations ds18b20_fops = {
    .owner  =   THIS_MODULE,
    .open   =   ds18b20_open,
    .read   =   ds18b20_read,
    .release    =   ds18b20_release,
};

/*注册设备到系统*/
static void ds18b20_setup_cdev(struct ds18b20_dev *dev,int index)
{
    int err;
    /*获取设备号*/
    int devno = MKDEV(ds18b20_major,index);

    /*cdev结构体初始化*/
    cdev_init(&dev->cdev,&ds18b20_fops);
    dev->cdev.owner = THIS_MODULE;/*给cdev结构体赋值*/
    dev->cdev.ops = &ds18b20_fops;
    /*注册进系统*/
    err = cdev_add(&dev->cdev,devno,1);
    if(err){
        printk(KERN_EMERG "cdev_add %d is failed! %d\n",index,err);
    }
    else{
        printk(KERN_EMERG "cdev_add %d is success!\n",ds18b20_major);
    }
}

static int ds18b20_init(void)
{
    int ret = 0;
    /*dev_t在cdev字符驱动结构体里定义,必须通过dev_t来描述设备号*/
    dev_t ds18b20_dev;

    /*打印输出主次设备号*/
    printk(KERN_EMERG "ds18b20_major is %d!\n",ds18b20_major);
    printk(KERN_EMERG "ds18b20_minor is %d!\n",ds18b20_minor);

    /*申请主次设备号*/
    if(ds18b20_major){
        /*MKDEV为设备号处理宏命令*/
        ds18b20_dev = MKDEV(ds18b20_major,ds18b20_minor);
        /*主设备号不为0则静态注册设备*/
        ret = register_chrdev_region(ds18b20_dev,DEVICE_MINOR_NUM,DEVICE_NAME);
    }
    else{
        /*动态注册设备*/
        ret = alloc_chrdev_region(&ds18b20_dev,ds18b20_minor,DEVICE_MINOR_NUM,DEVICE_NAME);
        ds18b20_major = MAJOR(ds18b20_dev);/*得到主设备号*/
        printk(KERN_EMERG "alloc_chrdev_region is %d\n",ds18b20_major);
    }
    if(ret<0){
        printk(KERN_EMERG "register_chrdev_region is %d failed!\n",ds18b20_major);
    }
    /*创建一个ds18b20_class的总线*/
    ds18b20_class = class_create(THIS_MODULE,DEVICE_NAME);
    /*申请内存空间*/
    ds18b20_device = kmalloc(DEVICE_MINOR_NUM * sizeof(struct ds18b20_dev),GFP_KERNEL);
    if(!ds18b20_device){
        ret = -ENOMEM;
        goto fail;
    }
    /*清空内存空间的数据*/
    memset(ds18b20_device,0,DEVICE_MINOR_NUM*sizeof(struct ds18b20_dev));
    /*注册设备到系统*/
    ds18b20_setup_cdev(ds18b20_device,0);
    /*创建设备节点*/
    device_create(ds18b20_class,NULL,MKDEV(ds18b20_major,ds18b20_minor),NULL,DEVICE_NAME);
    printk(KERN_EMERG "ds18b20 is initation!\n");
    return 0;

    /*注册失败*/
fail:
    unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
    printk(KERN_EMERG "ds18b20 exit!\n");
    return ret;
}

/*设备注销*/
static void __exit ds18b20_exit(void)
{
    /*注销设备号*/
    unregister_chrdev_region(MKDEV(ds18b20_major,ds18b20_minor),DEVICE_MINOR_NUM);
    /*注销设备*/
    cdev_del(&(ds18b20_device->cdev));
    /*摧毁设备节点*/
    device_destroy(ds18b20_class,MKDEV(ds18b20_major,ds18b20_minor));
    /*释放总线*/
    class_destroy(ds18b20_class);
    /*释放内存*/
    kfree(ds18b20_device);
    /*释放gpiio*/
    gpio_free(ds18b20_io);
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);

MODULE_AUTHOR("crmn");/*作者*/
MODULE_DESCRIPTION("TINY4412 ds18b20 driver");/*模块功能描述*/
MODULE_LICENSE("Dual BSD/GPL");/*开源声明*/
MODULE_VERSION("ds18b20 V1.0");/*代码修订版本*/

复制代码

复制代码

#include <stdio.h>//标准输入输出
#include <sys/types.h>//open和creat函数需要的头文件
#include <sys/stat.h>//open和creat函数需要的头文件
#include <fcntl.h>//open和creat函数需要的头文件
#include <unistd.h>//close,read,write函数需要的头文件
#include <sys/ioctl.h>//ioctl函数需要的头文件

#define FILE "/dev/ds18b20"

int main()
{
    int fd;
    unsigned int tem = 0;
    float temperature;
    char buf[10];
    //char *ds18b20_node = "/dev/ds18b20";

    /*O_RDWR只读打开,O_NDELAY非阻塞方式*/    
    fd = open(FILE,O_RDWR|O_NDELAY);
    if(fd < 0){
        printf("ds18b20 open %s failed!",FILE);
        return -1;
    }else{
        /*printf("ds18b20 open %s success",ds18b20_node);*/
        while(1)
        {
            read(fd,buf,2);/*从buf中读出数据*/
            tem = buf[1];
            tem <<= 8;/*高位移到前面*/
            tem = tem | buf[0]; /*得到读出的数据*/
            /*18B20是定点数据表示方式,12.4的编码,
              即前12位是温度整数部分,后4位为小数部分,
              4位分辨率就是1/16;
              转换为10进制结果要乘以1/16=0.0625
              */
            temperature = tem*0.0625;/*要求出正数的十进制值,必须将读取到的LSB字节,MSB字节进行整合处理,然后乘以0.0625即可*/
            printf("temperature is :%7.4f\n",temperature);/*打印出温度*/
            tem = 0;
            sleep(1);
        }
    }

    close(fd);
    return 0;
}

复制代码

模块编译测试用:

[root@localhost DS18B20_bak]# pwd
/kernel_v04_king_release/drivers/uea_drv/DS18B20_bak
[root@localhost DS18B20_bak]# make
make -C /kernel_v04_king_release M=`pwd`
make[1]: Entering directory `/kernel_v04_king_release'
LD /kernel_v04_king_release/drivers/uea_drv/DS18B20_bak/built-in.o
CC [M] /kernel_v04_king_release/drivers/uea_drv/DS18B20_bak/ds18b20.o
Building modules, stage 2.
MODPOST 1 modules
CC /kernel_v04_king_release/drivers/uea_drv/DS18B20_bak/ds18b20.mod.o
LD [M] /kernel_v04_king_release/drivers/uea_drv/DS18B20_bak/ds18b20.ko
make[1]: Leaving directory `/kernel_v04_king_release'
[root@localhost DS18B20_bak]# ll
总用量 196
drwxr-xr-x. 2 root root 4096 9月 20 18:27 app
-rw-r--r--. 1 root root 8 9月 21 11:02 built-in.o
-rw-r--r--. 1 root root 8072 9月 20 15:30 ds18b20.c
-rw-r--r--. 1 root root 83118 9月 21 11:02 ds18b20.ko
-rw-r--r--. 1 root root 497 9月 21 11:02 ds18b20.mod.c
-rw-r--r--. 1 root root 14988 9月 21 11:02 ds18b20.mod.o
-rw-r--r--. 1 root root 69304 9月 21 11:02 ds18b20.o
-rw-r--r--. 1 root root 154 9月 20 16:16 Makefile
-rw-r--r--. 1 root root 71 9月 21 11:02 modules.order
-rw-r--r--. 1 root root 0 9月 21 11:02 Module.symvers

[root@localhost app]# pwd
/kernel_v04_king_release/drivers/DS18B20/app
[root@localhost app]# ls
a.out ds18b20_test ds18b20_test.c
[root@localhost app]# arm-none-linux-gnueabi-gcc ds18b20_test.c -static -o ds18b20_test

直接编译进内核的步骤如下:

[root@localhost DS18B20]# pwd
/kernel_v04_king_release/drivers/DS18B20
[root@localhost DS18B20]# ls
app ds18b20.c Kconfig Makefile

Makefile

obj-y    +=ds18b20.o

 Kconfig

config DS18B20
    tristate "ds18b20 driver"
    ---help---
    gemeng add ds18b20 driver...

[root@localhost drivers]# vim Makefile //driver层的Makefile添加如下:

2 # Makefile for the Linux kernel device drivers.
3 #
4 # 15 Sep 2000, Christoph Hellwig <[email protected]>
5 # Rewritten to use lists instead of if-statements.
6 #

8 obj-y += DS18B20/     #gemeng add 9.21
9 obj-y += gpio/

[root@localhost drivers]# vim Kconfig  //driver层的Kconfig添加如下:

49 # input before char - char/joystick depends on it. As does USB.
50 
51 source "drivers/input/Kconfig"
52 source "drivers/DS18B20/Kconfig"   #gemeng add 9.21
53 source "drivers/char/Kconfig"

[root@localhost kernel_v04_king_release]# make menuconfig

[root@localhost kernel_v04_king_release]# make 

CC kernel/configs.o
LD kernel/built-in.o
CC drivers/DS18B20/ds18b20.o
LD drivers/DS18B20/built-in.o
CHK gator_events.h

。。。。。。

LD vmlinux
SYSMAP System.map
SYSMAP .tmp_System.map
OBJCOPY arch/arm/boot/Image
Kernel: arch/arm/boot/Image is ready
GZIP arch/arm/boot/compressed/piggy.gzip
AS arch/arm/boot/compressed/piggy.gzip.o
SHIPPED arch/arm/boot/compressed/lib1funcs.S
AS arch/arm/boot/compressed/lib1funcs.o
LD arch/arm/boot/compressed/vmlinux
OBJCOPY arch/arm/boot/zImage
Kernel: arch/arm/boot/zImage is ready
Building modules, stage 2.
MODPOST 5 modules
LD [M] drivers/gator/gator.ko

实验用图和结果现象:

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

基于迅为Itop4412开发板上控制ds18b20测量温度

https://www.cnblogs.com/crmn/articles/7526563.html

猜你喜欢

转载自blog.csdn.net/xzx208/article/details/83151646