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微秒的高電平
-------------------------------------------------------------------------------------------------------------
[[email protected] DS18B20]# ls
app ds18b20.c Makefile
[[email protected] 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; }
模組編譯測試用:
[[email protected] DS18B20_bak]# pwd
/kernel_v04_king_release/drivers/uea_drv/DS18B20_bak
[[email protected] 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'
[[email protected] 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
[[email protected] app]# pwd
/kernel_v04_king_release/drivers/DS18B20/app
[[email protected] app]# ls
a.out ds18b20_test ds18b20_test.c
[[email protected] app]# arm-none-linux-gnueabi-gcc ds18b20_test.c -static -o ds18b20_test
直接編譯進核心的步驟如下:
[[email protected] DS18B20]# pwd
/kernel_v04_king_release/drivers/DS18B20
[[email protected] DS18B20]# ls
app ds18b20.c Kconfig Makefile
Makefile
obj-y +=ds18b20.o
Kconfig
config DS18B20 tristate "ds18b20 driver" ---help--- gemeng add ds18b20 driver...
[[email protected] 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 #
7
8 obj-y += DS18B20/ #gemeng add 9.21
9 obj-y += gpio/
[[email protected] 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"
[[email protected] kernel_v04_king_release]# make menuconfig
[[email protected] 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
實驗用圖和結果現象:
----------------------------------------------------------------------------------------------------------