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 <hch@infradead.org>
5 # Rewritten to use lists instead of if-statements.
6 #
7
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測量溫度

http://blog.csdn.net/wwt18811707971/article/details/52662772


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM