基于S3C2440嵌入式Linux系统下的一个DS18B20驱动

    用Linux驱动编程的方法写一个DS18B20的温度传感器驱动,从底层采集温度信息。以下乃本人所写的驱动和测试的源码,嵌入式Linux内核版本为2.6.29,硬件平台是友善之臂的QQ2440,DS18B20引脚连接S3C2440的GPIOB1,程序难免存在一定的漏洞,希望大家指出。

#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_GPB1 #define DS18B20_PIN_OUTP S3C2410_GPB1_OUTP #define DS18B20_PIN_INP   S3C2410_GPB1_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.rn"); 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.rn");        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);

 

 

 

 

 

#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.rn");        exit(1);    }    else    {        printf("Open Device DS18B20 successed.rn");        while(1)        {            read(fd, buf, 1);            result = (float)buf[0];            result /= 16;            result += ((float)buf[1] * 16);                 printf("%.1f `Crn", result);            sleep(1);        }        close(fd);    } }

 

 

 

 

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

 

    这个驱动是基于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   执行后可以观察到以下的结果:

[转载]基于S3C2440嵌入式Linux系统下的一个DS18B20驱动和命

 

原文地址:基于S3C2440嵌入式Linux系统下的一个DS18B20驱动和命作者:李焕东

标签: DS18B20驱动, 嵌入式DS18B20驱动

添加新评论