xu.sunrise 发表于 2011-1-12 19:37:47

arm linux下的DS18B20驱动和测试程序

这个程序是从网上找的,我只是修改了一下GPIO端口,电路连接也十分简单,直接连S3C2410_GPF4,把电源地线连好就行了,linux的驱动程序很好弄的,大家千万别被一些概念吓到
/************************* s3c2440_ds18b20.c文件开始 **************************/
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/autoconf.h>
#include <linux/module.h>


typedef unsigned char BYTE;

#define DS18B20_PIN   S3C2410_GPF4
#define DS18B20_PIN_OUTP S3C2410_GPF4_OUTP
#define DS18B20_PIN_INP   S3C2410_GPF4_INP
#define HIGH 1
#define LOW 0
#define DEV_NAME "DS18B20"
#define DEV_MAJOR 232
static BYTE data;

MODULE_AUTHOR("sunrise");
MODULE_LICENSE("Dual BSD/GPL");

// 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 = DS18b20_read_byte();
    data = DS18b20_read_byte();
}

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

    buf = data;
    buf = data;
   
    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文件结束 **************************/

测试程序:
#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;
    unsigned short temp=0;
        double result=0;
        int flag=0;

    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);
                        printf("read data is 0x%02X-0x%02X\n",buf,buf);
            temp=((unsigned short)buf)<<8;
                        temp|=(unsigned short)buf;
                        printf("no error here\n");
                        result=0.0625*((double)temp);
                        printf("%f \r\n", result);
            sleep(1);
      }
      close(fd);
    }
}

编译方法:驱动放到内核的driver/char下面,修改driver/char的Kconfig和Makefile文件,在Kconfig下添加
config GT2440_DS18B20_DRIVER
        tristate "GT2440_DS18B20_DRIVER"
        depends on ARCH_S3C2440
        default m if ARCH_S3C2440
        help
          By internet
在Makefile中添加
obj-$(CONFIG_GT2440_DS18B20_DRIVER)   +=gt2440_ds18b20.o
gt2440_ds18b20就是驱动的名字啦

测试时先insmod gt2440_ds18b20.ko
mknod /dev/DS18B20 c 232 0
./test1820

测试数据如下
11.8125
read data is 0x00-0xBD
the temp is 00BD
11.8125
read data is 0x00-0xBD
the temp is 00BD
11.8125
read data is 0x00-0xBD
the temp is 00BD
11.8125
read data is 0x00-0xBE
the temp is 00BE
11.8750
read data is 0x00-0xBD
the temp is 00BD
11.8125
read data is 0x00-0xBD
the temp is 00BD
11.8125
read data is 0x00-0xBD
the temp is 00BD
11.8125

lbc___ 发表于 2011-1-12 20:14:26

这个...18b20对时间要求比较苛刻,不知道有其他程序同时在运行的情况下还能读到正确的温度么?

xu.sunrise 发表于 2011-1-12 20:28:03

完全可以,驱动是工作在内核态的,优先级高,况且ARM的速度本来就快

zhych314 发表于 2011-1-12 20:46:08

mark一下
页: [1]
查看完整版本: arm linux下的DS18B20驱动和测试程序