[OK210開發(fā)板體驗(yàn)]功能篇(4)Linux字符驅(qū)動之DS18B20溫度傳感器驅(qū)動

原創(chuàng) 2015-12-24 09:30:00 [OK210開發(fā)板體驗(yàn)]功能篇(4)Linux字符驅(qū)動之DS18B20溫度傳感器驅(qū)動
前面進(jìn)行了OK210開發(fā)板體驗(yàn)的入門篇介紹,算是初步入門,分別包含:

    [OK210開發(fā)板體驗(yàn)]入門篇(1):開箱驗(yàn)板
    [OK210開發(fā)板體驗(yàn)]入門篇(2):板載資源

    [OK210開發(fā)板體驗(yàn)]
入門篇(3)開發(fā)環(huán)境(軟件安裝,開發(fā)環(huán)境,燒寫系統(tǒng)

    [OK210開發(fā)板體驗(yàn)]入門篇(4):編程入門(NFS登錄,驅(qū)動入門) 
    [OK210開發(fā)板體驗(yàn)]
功能篇(1):Linux字符驅(qū)動之Led
   
 [OK210開發(fā)板體驗(yàn)]功能(2):Linux字符驅(qū)動之Key按鍵
    [OK210開發(fā)板體驗(yàn)]功能篇(3):Linux Input子系統(tǒng)之Key按鍵
今天是功能篇的第四篇:Linux字符驅(qū)動之DS18B20,本節(jié)主要分3部分:硬件分析,軟件基礎(chǔ),驅(qū)動編程。
一、硬件分析
 [OK210開發(fā)板體驗(yàn)]的第二篇:板載資源中,簡單分析了DS18B20傳感器的功能和作用。其實(shí)對DS18B20的操作,包含兩部分,一是對字符設(shè)備驅(qū)動的深入理解,二是對DS18B20傳感器時序的掌握。前面3篇功能體驗(yàn),分別對GPIO的輸出(LED)和輸入(Key)進(jìn)行了驅(qū)動的編寫,而這篇將同時涉及GPIO的輸入和輸出。
首先從OK210的底板原理圖中可知,OK210開發(fā)板上的DS18B20連接通過一個上拉電阻連接到了核心板的XM0ADDR3引腳上,如下圖所示:

 

XM0ADDR3引腳由S5PV210用戶手冊,可知,該引腳位于MP0_4[3]引腳上,默認(rèn)連接到了EBI接口上,如下圖所示:

 

所以,我們要對DS18B20進(jìn)行操作,就是通過對MP0_4[3]引腳的設(shè)置進(jìn)行實(shí)現(xiàn)
二、軟件基礎(chǔ)
該部分首先總結(jié)一下,字符設(shè)備驅(qū)動的注冊過程,然后簡單介紹一下DS18B20驅(qū)動編寫過程中,注意的事項(xiàng):
1 字符設(shè)備的注冊與注銷
以下的步驟,一般在驅(qū)動初始化函數(shù)中和驅(qū)動退出函數(shù)中實(shí)現(xiàn)。
第一步:注冊設(shè)備號                                    信息#tail -f /var/log/message
        注冊函數(shù):
           register_chrdev_region()                查看#lsmod
           alloc_chrdev_region()                   查看#cat /proc/devices
           register_chrdev()
        注銷函數(shù):
           unregist_chrdev_region() 
           unregister_chrdev()

第二步:初始化cdev并添加到系統(tǒng)
        初始化cdev
            靜態(tài)初始化cdev_init() 
            動態(tài)初始化cdev_alloc()
        添加到系統(tǒng)函數(shù)
            cdev_add()
        從系統(tǒng)刪除函數(shù)
            cdev_del()
第三步:創(chuàng)建設(shè)備節(jié)點(diǎn)
        創(chuàng)建類
           class_create()          將放于/sysfs       查看#ls /sys/class
        刪除類
           class_destroy()      
        創(chuàng)建節(jié)點(diǎn)
           device_create()  class_device_create()  將存放于/dev  查看#ls /dev
        刪除節(jié)點(diǎn)
           device_destroy()  class_device_destroy()

2 DS18B20注意事項(xiàng)
 DS18B20是由DALLAS半導(dǎo)體公司推出的一種的一線總線接口的溫度傳感器。與傳統(tǒng)的熱敏電阻等測溫元件相比,它是一種新型的體積小、適用電壓寬、與微處理器接口簡單的數(shù)字化溫度傳感器。一線總線結(jié)構(gòu)具有簡潔且經(jīng)濟(jì)的特點(diǎn),可使用戶輕松地組建傳感器網(wǎng)絡(luò),從而為測量系統(tǒng)的構(gòu)建引入全新概念,測量溫度范圍為-55~+125℃,精度為±05℃?,F(xiàn)場溫度直接以一線總線的數(shù)字方式傳輸,大大提高了系統(tǒng)的抗干擾性。它能直接讀出被測溫度,并且可根據(jù)實(shí)際要求通過簡單的編程實(shí)現(xiàn)9~l2位的數(shù)字值讀數(shù)方式。它工作在3—55 V的電壓范圍,采用多種封裝形式,從而使系統(tǒng)設(shè)計(jì)靈活、方便,設(shè)定分辨率及用戶設(shè)定的報警溫度存儲在EEPROM中,掉電后依然保存。其內(nèi)部結(jié)構(gòu)如下圖所示:

ROM中的64位序列號是出廠前被光記好的,它可以看作是該DS18B20的地址序列碼,每DS18B2064位序列號均不相同。64ROM的排列是:前8位是產(chǎn)品家族碼,接著48位是DS18B20的序列號,最后8位是前面56位的循環(huán)冗余校驗(yàn)碼(CRC=X8+X5+X4+1)。ROM作用是使每一個DS18B20都各不相同,這樣就可實(shí)現(xiàn)一根總線上掛接多個。
所有的單總線器件要求采用嚴(yán)格的信號時序,以保證數(shù)據(jù)的完整性。DS18B20共有6種信號類型:復(fù)位脈沖、應(yīng)答脈沖、寫0、寫1、讀0和讀1。所有這些信號,除了應(yīng)答脈沖以外,都由主機(jī)發(fā)出同步信號。并且發(fā)送所有的命令和數(shù)據(jù)都是字節(jié)的低位在前。
1)復(fù)位脈沖和應(yīng)答脈沖
單總線上的所有通信都是以初始化序列開始。主機(jī)輸出低電平,保持低電平時間至少480 us,,以產(chǎn)生復(fù)位脈沖。接著主機(jī)釋放總線,4.7K的上拉電阻將單總線拉高,延時1560 us,并進(jìn)入接收模式(Rx)。接著DS18B20拉低總線60~240us,以產(chǎn)生低電平應(yīng)答脈沖,
若為低電平,再延時480 us。
2)寫時序
    寫時序包括寫0時序和寫1時序。所有寫時序至少需要60us,且在2次獨(dú)立的寫時序之間至少需要1us的恢復(fù)時間,兩種寫時序均起始于主機(jī)拉低總線。寫1時序:主機(jī)輸出低電平,延時2us,然后釋放總線,延時60us。寫0時序:主機(jī)輸出低電平,延時60us,然后釋放總線,延時2us。
3)讀時序
單總線器件僅在主機(jī)發(fā)出讀時序時,才向主機(jī)傳輸數(shù)據(jù),所以,在主機(jī)發(fā)出讀數(shù)據(jù)命令后,必須馬上產(chǎn)生讀時序,以便從機(jī)能夠傳輸數(shù)據(jù)。所有讀時序至少需要60us,且在2時序之間至少需要1us的恢復(fù)時間。每個讀時序都由主機(jī)發(fā)起,至少拉低總線1us。主機(jī)在讀時序期間必須釋放總線,并且在時序起始后的15us之內(nèi)采樣總線狀態(tài)。典型的讀時序過程為:主機(jī)輸出低電平延時2us,然后主機(jī)轉(zhuǎn)入輸入模式延時12us,然后讀取單總線當(dāng)前的電平,然后延時50us。
三、驅(qū)動編程
如上所述,驅(qū)動的編寫,主要包括兩部分,一是根據(jù)DS18B20時序,編寫讀寫函數(shù),二是根據(jù)字符設(shè)備,編寫設(shè)備驅(qū)動程序。具體的代碼見下,最后附上效果圖:
 
1 驅(qū)動代碼
  1. #include    <linux/module.h>
  2. #include    <linux/fs.h>
  3. #include    <linux/kernel.h>
  4. #include    <linux/init.h>
  5. #include    <linux/delay.h>
  6. #include    <linux/cdev.h>
  7. #include    <linux/device.h>
  8. #include    <linux/gpio.h>
  9. #include    <plat/gpio-cfg.h>

  10. #define    DEVICE_NAME "DS18B20"

  11. static struct cdev cdev;
  12. struct class *tem_class;
  13. static dev_t devno;
  14. static int major = 100;

  15. unsigned int gpio=S5PV210_MP04(3);

  16. void tem_reset(void)
  17. {
  18.     s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  19.     gpio_set_value(gpio, 1);
  20.     //gpio_direction_output(gpio,1);
  21.     udelay(100);
  22.     gpio_set_value(gpio, 0);
  23.     udelay(600);
  24.     gpio_set_value(gpio, 1);
  25.     udelay(100);
  26.     s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
  27. //    gpio_direction_input(gpio);
  28. }

  29. void tem_wbyte(unsigned char data)
  30. {
  31.     int i;

  32.     s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  33.     for (i = 0; i < 8; ++i)
  34.     {
  35.         gpio_set_value(gpio, 0);
  36.         udelay(1);

  37.         if (data & 0x01)
  38.         {
  39.             gpio_set_value(gpio, 1);
  40.         }
  41.         udelay(60);
  42.         gpio_set_value(gpio, 1);
  43.         udelay(15);
  44.         data >>= 1;
  45.     }
  46.     gpio_set_value(gpio, 1);
  47. }

  48. unsigned char tem_rbyte(void)
  49. {
  50.     int i;
  51.     unsigned char ret = 0;

  52.     for (i = 0; i < 8; ++i)
  53.     {
  54.         s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
  55.         gpio_set_value(gpio, 0);
  56. //        gpio_direction_output(gpio,0);
  57.         udelay(1);
  58.         gpio_set_value(gpio, 1);

  59.         s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
  60. //        gpio_direction_input(gpio);
  61.         ret >>= 1;
  62.         if (gpio_get_value(gpio))
  63.         {
  64.             ret |= 0x80;
  65.         }
  66.         udelay(60);
  67.     }
  68.     s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));


  69.     return ret;
  70. }

  71. static ssize_t tem_read(struct file *filp, char *buf, size_t len, loff_t *offset)
  72. {
  73.     unsigned char low, high;

  74.     tem_reset();
  75.     udelay(420);
  76.     tem_wbyte(0xcc);
  77.     tem_wbyte(0x44);

  78.     mdelay(750);
  79.     tem_reset();
  80.     udelay(400);
  81.     tem_wbyte(0xcc);
  82.     tem_wbyte(0xbe);

  83.     low = tem_rbyte();
  84.     high = tem_rbyte();

  85.     *buf = low / 16 + high * 16;

  86.     *(buf + 1) = (low & 0x0f) * 10 / 16 + (high & 0x0f) * 100 / 16 % 10;
  87.     return 0;
  88. }

  89. static struct file_operations tem_fops =
  90. {
  91.     .owner    = THIS_MODULE,
  92.     .read    = tem_read,
  93. };

  94. static int __init tem_init(void)
  95. {
  96.     int result;
  97.     devno = MKDEV(major, 0);

  98.     result = register_chrdev_region(devno, 1, DEVICE_NAME);
  99.     if (result)
  100.     {
  101.         printk("register failed\n");
  102.         return result;
  103.     }
  104.     cdev_init(&cdev, &tem_fops);
  105.     cdev.owner = THIS_MODULE;
  106.     cdev.ops = &tem_fops;
  107.     result = cdev_add(&cdev, devno, 1);
  108.     if (result)
  109.     {
  110.         printk("cdev add failed\n");
  111.         goto fail1;
  112.     }

  113.     tem_class = class_create(THIS_MODULE, "tmp_class");
  114.     if (IS_ERR(tem_class))
  115.     {
  116.         printk("class create failed\n");
  117.         goto fail2;
  118.     }

  119.     device_create(tem_class, NULL, devno, DEVICE_NAME, DEVICE_NAME);
  120.     return 0;
  121. fail2:
  122.     cdev_del(&cdev);
  123. fail1:
  124.     unregister_chrdev_region(devno, 1);
  125.     return result;
  126. }

  127. static void __exit tem_exit(void)
  128. {
  129.     device_destroy(tem_class, devno);
  130.     class_destroy(tem_class);
  131.     cdev_del(&cdev);
  132.     unregister_chrdev_region(devno, 1);
  133. }

  134. module_init(tem_init);
  135. module_exit(tem_exit);

  136. MODULE_LICENSE("GPL");
  137. MODULE_AUTHOR("gjianw217@163.com");
  138. MODULE_DESCRIPTION("DS18B20 driver");
復(fù)制代碼

2 測試程序

  1. #include "stdio.h"
  2. #include "sys/types.h"
  3. #include "sys/ioctl.h"
  4. #include "stdlib.h"
  5. #include "termios.h"
  6. #include "sys/stat.h"
  7. #include "fcntl.h"
  8. #include "sys/time.h"

  9. main()
  10. {
  11.     int fd;
  12.     unsigned char buf[2]={0};
  13.     float result;

  14.     if ((fd=open("/dev/DS18B20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
  15.     {
  16.         printf("Open Device DS18B20 failed.\r\n");
  17.         exit(1);
  18.     }
  19.     else
  20.     {
  21.         printf("Open Device DS18B20 successed.\r\n");
  22.         while(1)
  23.         {
  24.             read(fd, buf, sizeof(buf));
  25.             printf("%d.%d C\r\n", buf[0], buf[1]);
  26.             sleep(1);
  27.         }
  28.         close(fd);
  29.     }
  30. }
復(fù)制代碼

3 Makefile


  1. #DS18B20 Makefile
  2. ARCH=arm
  3. CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  4. APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
  5. #obj-m := app-drv.o
  6. obj-m := ds18b20-drv.o
  7. #KDIR := /path/to/kernel/linux/
  8. KDIR := /home/ok210/android-kernel-samsung-dev/
  9. PWD := $(shell pwd)
  10. default:
  11.         make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
  12. app:ds18b20-app.c
  13.         $(APP_COMPILE)gcc -o app ds18b20-app.c
  14. clean:
  15.         $(MAKE) -C $(KDIR) M=$(PWD) clea

復(fù)制代碼



相關(guān)產(chǎn)品 >

  • OKMX6UL-C開發(fā)板

    飛凌嵌入式專注imx6系列imx6ul開發(fā)板、飛思卡爾imx6ul核心板等ARM嵌入式核心控制系統(tǒng)研發(fā)、設(shè)計(jì)和生產(chǎn),i.mx6UL系列產(chǎn)品現(xiàn)已暢銷全國,作為恩智浦imx6ul,imx6ul開發(fā)板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。

    了解詳情
    OKMX6UL-C開發(fā)板
  • OKMX6ULL-C開發(fā)板

    40*29mm,雙網(wǎng)雙CAN,8路串口| i.MX6ULL開發(fā)板是基于NXP i.MX6ULL設(shè)計(jì)開發(fā)的的一款Linux開發(fā)板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對板連接器,適應(yīng)場景豐富。 了解詳情
    OKMX6ULL-C開發(fā)板

推薦閱讀 換一批 換一批