0 Replies Latest reply on Aug 23, 2011 7:20 PM by wensenyuan

    [资料转发分享]基于S3C2440嵌入式Linux系统下的一个DS18B20驱动和测试程序

    Brown Belt

      嵌入式Linux内核版本为2.6.29,硬件平台是友善之臂的MICRO2440,DS18B20引脚连接S3C2440的GPIOF0,程序难免存在一定的漏洞,希望大家指出,顺便说说,使用以下代码的朋友帮忙留一下言,谢谢。

      /********************* s3c2440_ds18b20.c文件开始 **************************/
      #include "linux/config.h"
      #include "linux/module.h"
      #include "linux/kernel.h"
      #include "linux/fs.h"
      #include "linux/init.h"
      #include "linux/devfs_fs_kernel.h"
      #include "linux/miscdevice.h"
      #include "linux/delay.h"
      #include "asm/irq.h"
      #include "asm/arch/regs-gpio.h"
      #include "asm/hardware.h"

       

      typedef unsigned char BYTE;

      #define DS18B20_PIN   S3C2410_GPF0
      #define DS18B20_PIN_OUTP S3C2410_GPF0_OUTP
      #define DS18B20_PIN_INP   S3C2410_GPF0_INP
      #define HIGH 1
      #define LOW 0

      #define DEV_NAME "DS18B20"
      #define DEV_MAJOR 232

      static BYTE data[2];

      // DS18B20复位函数
      BYTE DS18b20_reset (void)
      {
          // 配置GPIOB0输出模式
          s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);
         
          // 向18B20发送一个上升沿,并保持高电平状态约100微秒
          s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
          udelay(100);
         
          // 向18B20发送一个下降沿,并保持低电平状态约600微秒
          s3c2410_gpio_setpin(DS18B20_PIN, LOW);
          udelay(600);
         
          // 向18B20发送一个上升沿,此时可释放DS18B20总线
          s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
          udelay(100);
         
          // 以上动作是给DS18B20一个复位脉冲
          // 通过再次配置GPIOB1引脚成输入状态,可以检测到DS18B20是否复位成功
          s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);
         
          // 若总线在释放后总线状态为高电平,则复位失败
          if(s3c2410_gpio_getpin(DS18B20_PIN))
      { printk("DS18b20 reset failed.\r\n"); return 1;}

          return 0;
      }

       

      void DS18b20_write_byte (BYTE byte)
      {
          BYTE i;
          // 配置GPIOB1为输出模式
          s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);

          // 写“1”时隙:
          //     保持总线在低电平1微秒到15微秒之间
          //     然后再保持总线在高电平15微秒到60微秒之间
          //     理想状态: 1微秒的低电平然后跳变再保持60微秒的高电平
          //
          // 写“0”时隙:
          //     保持总线在低电平15微秒到60微秒之间
          //     然后再保持总线在高电平1微秒到15微秒之间
          //     理想状态: 60微秒的低电平然后跳变再保持1微秒的高电平
          for (i = 0; i < 8; i++)
          {
              s3c2410_gpio_setpin(DS18B20_PIN, LOW);
      udelay(1);
              if(byte & HIGH)
              {
                   // 若byte变量的D0位是1,则需向总线上写“1”
                   // 根据写“1”时隙规则,电平在此处翻转为高
                   s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
              }
              else
              {
                   // 若byte变量的D0位是0,则需向总线上写“0”
                   // 根据写“0”时隙规则,电平在保持为低
                   // s3c2410_gpio_setpin(DS18B20_PIN, LOW);
              }
              // 电平状态保持60微秒
              udelay(60);

              s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
              udelay(15);

              byte >>= 1;
          }
          s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
      }

      BYTE DS18b20_read_byte (void)
      {
          BYTE i = 0;
          BYTE byte = 0;
          // 读“1”时隙:
          //     若总线状态保持在低电平状态1微秒到15微秒之间
          //     然后跳变到高电平状态且保持在15微秒到60微秒之间
          //      就认为从DS18B20读到一个“1”信号
          //     理想情况: 1微秒的低电平然后跳变再保持60微秒的高电平
          //
          // 读“0”时隙:
          //     若总线状态保持在低电平状态15微秒到30微秒之间
          //     然后跳变到高电平状态且保持在15微秒到60微秒之间
          //     就认为从DS18B20读到一个“0”信号
          //     理想情况: 15微秒的低电平然后跳变再保持46微秒的高电平
          for (i = 0; i < 8; i++)
          {
              s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);
              s3c2410_gpio_setpin(DS18B20_PIN, LOW);

              udelay(1);
              byte >>= 1;

              s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
              s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);

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

      void DS18b20_proc(void)        
      {
          while(DS18b20_reset());
         
          udelay(120);
         
          DS18b20_write_byte(0xcc);
          DS18b20_write_byte(0x44);
         
          udelay(5);
         
          while(DS18b20_reset());
          udelay(200);
         
          DS18b20_write_byte(0xcc);
          DS18b20_write_byte(0xbe);
         
          data[0] = DS18b20_read_byte();
          data[1] = DS18b20_read_byte();
      }

      static ssize_t s3c2440_18b20_read(struct file *filp, char *buf, size_t len, loff_t *off)
      {
          DS18b20_proc();

          buf[0] = data[0];
          buf[1] = data[1];
         
          return 1;
      }

      static struct file_operations s3c2440_18b20_fops =
      {
          .owner = THIS_MODULE,
          .read = s3c2440_18b20_read,
      };

      static int __init s3c2440_18b20_init(void)
      {
          if (register_chrdev(DEV_MAJOR, DEV_NAME, &s3c2440_18b20_fops) < 0)
          {
              printk(DEV_NAME ": Register major failed.\r\n");
              return -1;
          }
         
          devfs_mk_cdev(MKDEV(DEV_MAJOR, 0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEV_NAME);
         
          while(DS18b20_reset());  
      }

      static void __exit s3c2440_18b20_exit(void)
      {
          devfs_remove(DEV_NAME);
          unregister_chrdev(DEV_MAJOR, DEV_NAME);
      }

      module_init(s3c2440_18b20_init);
      module_exit(s3c2440_18b20_exit);

      /************************* s3c2440_ds18b20.c文件结束 **************************/

       

       

       

      /************************* test_ds18b20.c文件开始 **************************/

      #include "stdio.h"
      #include "sys/types.h"
      #include "sys/ioctl.h"
      #include "stdlib.h"
      #include "termios.h"
      #include "sys/stat.h"
      #include "fcntl.h"
      #include "sys/time.h"

      main()
      {
          int fd;
          unsigned char buf[2];
          float result;

          if ((fd=open("/dev/DS18B20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0)
          {
              printf("Open Device DS18B20 failed.\r\n");
              exit(1);
          }
          else
          {
              printf("Open Device DS18B20 successed.\r\n");
              while(1)
              {
                  read(fd, buf, 1);
                  result = (float)buf[0];
                  result /= 16;
                  result += ((float)buf[1] * 16);
          
                  printf("%.1f `C\r\n", result);
                  sleep(1);
              }
              close(fd);
          }
      }

      /************************* test_ds18b20.c文件结束 **************************/

       

       

      /************************* Makefile文件开始 **************************/

      obj-m := s3c2440_ds18b20.o

      KERNELDIR ?= ../../kernel/linux-2.6.29
      PWD := $(shell pwd)
      CC := arm-linux-gcc
      CLEAN := rm -rf


      all : s3c2440_ds18b20.c test_ds18b20
          $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

      test_ds18b20 : test_ds18b20.c
          $(CC) test_ds18b20.c -o test_ds18b20

      clobber :
          $(CLEAN) test_ds18b20 s3c2440_ds18b20.ko
      clean :
          $(CLEAN) *.mod.* *.o *~ modules.order Module.symvers

      /************************* Makefile文件结束 **************************/

          这个驱动是基于Linux的2.6.29内核树编译的,内核树的路径是当前目录的../../kernel/linux-2.6.29,若以来其他的Linux的内核树,编译时可能会出现找不到某些文件的情况,如hardware.h等,只需要在内核树中作一些软链接就可以解决,还有声明一点的就是:Makefile中的命令操作前的并非空格,而是TAB跳格。

          make编译,编译完毕后产生许多文件,我们只关心test_ds18b20和s3c2440_ds18b20.ko这两个。

          把以上两个文件下载到开发板中,创建一个设备节点:

          mknod /dev/DS18B20 c 232 0

          把s3c2440_ds18b20这个模块加载到内核:

          insmod s3c2440_ds18b20.ko

         

       

          之后把test_ds18b20测试文件改为可执行状态:

          chmod 0111 test_ds18b20

          执行后可以观察到结果: