ARM40-A5应用程序——温度传感器DS18B20的驱动与应用

ARM40-­A5应用程序——温度传感器DS18B20的驱动与应用

2018.10.31
  DS18B20是常用的温度传感器,具有体积小,GPIO占用少,抗干扰能力强,精度高的特点。工作电压3.0~5.5VDC,静态功耗极低,测温范围 -55℃~+125℃。
  DS18B20的驱动是GPIO驱动的一个很好的示例。

一、DS18B20驱动

1.1、驱动源码

  见附(1)。主要封装了如下1-wire相关的操作函数:

int w1_reset_bus(void);
static inline void w1_write_bit(int bit);
void w1_write_8(unsigned char byte);
static inline unsigned char w1_read_bit(void);
unsigned char w1_read_8(void);

  其它函数都是驱动程序框架内的函数。

1.2、Makefile

obj-m    := mod_ds18b20_PE20.o

PWD      := $(shell pwd)
KDIR     := /home/arm40_a5d3_kernel/linux-at91-master
CC       := /home/arm-2014.05/bin/arm-none-linux-gnueabi-gcc
all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        rm -rf *.o *.mod.c *.ko *.symvers *.order

  make后生成 mod_ds18b20_PE20.ko文件。

1.3、安装驱动文件

  将mod_ds18b20_PE20.ko文件拷贝到ARM40:

rmmod mod_ds18b20_PE20.ko
insmod mod_ds18b20_PE20.ko
lsmod

  即完成驱动的安装。

二、DS18B20驱动的测试程序

  DS18B20驱动的测试程序为test_ds18b20_PE20.c,内容如下:

#include <stdio.h>
#include <fcntl.h>

int main(void)
{
        int fd;
        unsigned char buf[2];
        float result;
        
        if ((fd=open("/dev/ds18b20",O_RDWR | O_NDELAY | O_NOCTTY)) < 0) {
                printf("Open ds18b20 fail.\n");
                return -1;
        } else {
                printf("Open ds18b20 success.\n");
                while(1) {
                        read(fd, buf, 1);
                        
                        result = (float)buf[0];       // 转换读到的数据
                        result /= 16;
                        result += ((float)buf[1] * 16);

                        printf("%.1f C\n", result);
                        sleep(1);
                }
                close(fd);
        }
        return 0;
}

  编译后得到test_ds18b20_PE20文件,拷贝到ARM40上运行:

root@ARM40:~# ./test_ds18b20_PE20.c
open ds18b20 success.
Open ds18b20 success.
23.2 C
23.3 C
23.2 C
23.3 C
^Cds18b20 release.

参考文章

  用软件实现1-Wire®通信
  https://www.maximintegrated.com/cn/app-notes/index.mvp/id/126
  Linux内核drivers/w1/w1_io.c
  1-Wire总线与DS18B20应用仿真
  https://blog.csdn.net/u010339445/article/details/49277713
  1-Wire®双向电平转换器(1.8V至5V)参考设计
  https://www.maximintegrated.com/cn/app-notes/index.mvp/id/4477
  DS18S20 1-Wire寄生供电数字温度计
  https://www.maximintegrated.com/cn/products/sensors/DS18S20.html
  AM3354增加DS18B20
  https://blog.csdn.net/zuoyioo7/article/details/73608624

附:

(1)DS18B20驱动源码:(mod_ds18b20_PE20.c)

#include <linux/delay.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>

#define DEBUG 
#if defined(DEBUG)			
#define DPRINTK(fmt,arg...) printk(fmt,##arg); 
#else
#define DPRINTK(fmt,arg...)
#endif

#define DQ              148              //148=PE20,57=PB25
#define DEV_NAME        "ds18b20"

/*
During the initialization sequence the bus master transmits (TX) the reset pulse by pulling the 1-Wire 
bus low for a minimum of 480µs. The bus master then releases the bus and goes into receive mode
(RX). When the bus is released, the 5kΩ pullup resistor pulls the 1-Wire bus high. When the DS18B20 
detects this rising edge, it waits 15µs to 60µs and then transmits a presence pulse by pulling the 1-Wire 
bus low for 60µs to 240µs.
 */
int w1_reset_bus(void)
{
        //向ds18b20发送一个500us低电平复位信号
        gpio_direction_output(DQ,0);    //Drives DQ low
        udelay(500);

        gpio_direction_input(DQ);       //Releases the bus
        udelay(70);
        
		//检测到DQ上为高, 复位失败; 为低电平,复位成功
        if(gpio_get_value(DQ))          // Sample for presence pulse from slave
                return -1;

        //等待复位时隙完毕后,继续将DQ置为高电平
        udelay(410);                    // Complete the reset sequence recovery
        gpio_direction_output(DQ,1);

        return 0;
}

static int ds18b20_open(struct inode *inode, struct file *filp)
{
        int ret = 0;

        gpio_direction_output(DQ,1);		//初始时DQ为1
        udelay(1);
        ret = w1_reset_bus();
        if(ret == 0) {         		        //若能够正常复位,表明1-wire设备是在线的
                DPRINTK("open ds18b20 success.\n");
        } else
                DPRINTK("open ds18b20 fail.\n");
        
        return ret;
}

static int ds18b20_release(struct inode *inode, struct file *filp)
{
        DPRINTK("ds18b20 release.\n");
        return 0;
}

/*
(1)To generate a Write 1 time slot, after pulling the 1-Wire bus low, the bus master must release the 
1-Wire bus within 15µs. When the bus is released, the 5kΩ pullup resistor will pull the bus high. 
(2)To generate a Write 0 time slot, after pulling the 1-Wire bus low, the bus master must continue to 
hold the bus low for the duration of the time slot (at least 60µs).
(3)The DS18B20 samples the 1-Wire bus during a window that lasts from 15µs to 60µs after the master
initiates the write time slot. If the bus is high during the sampling window, a 1 is written to the DS18B20. 
If the line is low, a 0 is written to the DS18B20.
 */
static inline void w1_write_bit(int bit)
{
	if (bit) {                       // Write '1' bit
		gpio_direction_output(DQ,0); // Drives DQ low
		udelay(6);
		gpio_direction_input(DQ);    // Releases the bus
		udelay(64);
	} else {                         // Write '0' bit
		gpio_direction_output(DQ,0); // Drives DQ low
		udelay(60);
		gpio_direction_input(DQ);    // Releases the bus
		udelay(10);
	}
}

void w1_write_8(unsigned char byte)
{
        int i;

        for (i = 0; i < 8; i++) {
                w1_write_bit((byte >> i) & 0x1);
        }
}

/*
A read time slot is initiated by the master device pulling the 1-Wire bus low for a minimum of 1µs and 
then releasing the bus. After the master initiates the read time slot, the DS18B20 will begin transmitting 
a 1 or 0 on bus. The DS18B20 transmits a 1 by leaving the bus high and ransmits a 0 by pulling the bus 
low. 
When transmitting a 0, the DS18B20 will release the bus by the end of the time slot, and the bus will be 
pulled back to its high idle state by the pullup resister. 
Output data from the DS18B20 is valid for 15µs after the falling edge that initiated the read time slot. 
Therefore, the master must release the bus and then sample the bus state within 15µs from the start of 
the slot.
    another:
A read-data time slot begins like a write-one time slot. The voltage on the data line must remain low 1us. 
During the 1us window, when responding with a 0, the DS2408 starts pulling the data line low; its 
internal timing generator determines when this pulldown ends and the voltage starts rising again(15us 
in). When responding with a 1, the DS2408 does not hold the data line low at all, and the voltage starts 
rising as soon as 1us is over.
 */
static inline unsigned char w1_read_bit(void)
{
        int result;

        //sample timing is critical here
        gpio_direction_output(DQ,0);            //Drives DQ low
        udelay(6);
        gpio_direction_input(DQ);               //Releases the bus
        udelay(2);                              //udelay(9); for some situation
        result = gpio_get_value(DQ) ? 1 : 0;    //Sample the bit value from the slave
        udelay(55);                             //Complete the time slot and 10us recovery

        return result & 0x1;
}

/**
 * w1_read_8() - Reads 8 bits.
 * Return:        the byte read
 */
unsigned char w1_read_8(void)
{
        int i;
        unsigned char result = 0;

        for (i = 0; i < 8; ++i)
                result |= (w1_read_bit() << i);

        return result;
}

static ssize_t ds18b20_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
        unsigned char data[2];
        unsigned long ret;
        int flag;

        flag = w1_reset_bus();
        if(flag < 0) {
                DPRINTK("ds18b20 reset fail.\n");
                return -1;
        }
        w1_write_8(0xcc);
        w1_write_8(0x44);

        flag = w1_reset_bus();
        if(flag < 0) {
                DPRINTK("ds18b20 reset fail.\n");
                return -1;
        }
        w1_write_8(0xcc);
        w1_write_8(0xbe);

        data[0] = w1_read_8();
        data[1] = w1_read_8();

        ret = copy_to_user(buf, data, sizeof(data));
        return ret ? -EFAULT : sizeof(data);
}

static struct file_operations ds18b20_fops =
{
        .owner = THIS_MODULE,
        .open = ds18b20_open,
        .release = ds18b20_release,
        .read = ds18b20_read,
};

static struct miscdevice ds18b20_miscdev =
{
        .minor  = MISC_DYNAMIC_MINOR,
        .name   = DEV_NAME,
        .fops   = &ds18b20_fops,
};

static int __init ds18b20_init(void)
{
        int ret;

        ret = gpio_request(DQ, "W1LED");
        if (ret) {
                DPRINTK("%s: w1-gpio unable to request GPIO %d for W1LED, ret = %d\n", DEV_NAME, DQ, ret);
                return ret;
        }

        ret = misc_register(&ds18b20_miscdev);
        return ret;
}

static void __exit ds18b20_exit(void)
{
        DPRINTK("ds18b20 free\n");
        gpio_free(DQ);
        misc_deregister(&ds18b20_miscdev);
}

module_init(ds18b20_init);
module_exit(ds18b20_exit);

MODULE_AUTHOR("rit <[email protected]>");
MODULE_DESCRIPTION("ds18b20 driver");
MODULE_LICENSE("GPL");

猜你喜欢

转载自blog.csdn.net/vonchn/article/details/83586870