新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 基于S3C2440嵌入式Linux系統(tǒng)下的一個DS18B20驅(qū)動

基于S3C2440嵌入式Linux系統(tǒng)下的一個DS18B20驅(qū)動

作者: 時間:2016-11-20 來源:網(wǎng)絡(luò) 收藏
用Linux驅(qū)動編程的方法寫一個DS18B20的溫度傳感器驅(qū)動,從底層采集溫度信息。以下乃本人所寫的驅(qū)動和測試的源碼,嵌入式Linux內(nèi)核版本為2.6.29,硬件平臺是友善之臂的QQ2440,DS18B20引腳連接S3C2440的GPIOB1,程序難免存在一定的漏洞,希望大家指出。


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

本文引用地址:http://butianyuan.cn/article/201611/318872.htm

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復(fù)位函數(shù)
BYTE DS18b20_reset (void)
{
// 配置GPIOB0輸出模式
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_OUTP);

// 向18B20發(fā)送一個上升沿,并保持高電平狀態(tài)約100微秒
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
udelay(100);

// 向18B20發(fā)送一個下降沿,并保持低電平狀態(tài)約600微秒
s3c2410_gpio_setpin(DS18B20_PIN, LOW);
udelay(600);

// 向18B20發(fā)送一個上升沿,此時可釋放DS18B20總線
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
udelay(100);

// 以上動作是給DS18B20一個復(fù)位脈沖
// 通過再次配置GPIOB1引腳成輸入狀態(tài),可以檢測到DS18B20是否復(fù)位成功
s3c2410_gpio_cfgpin(DS18B20_PIN, DS18B20_PIN_INP);

// 若總線在釋放后總線狀態(tài)為高電平,則復(fù)位失敗
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微秒之間
// 理想狀態(tài): 1微秒的低電平然后跳變再保持60微秒的高電平
//
// 寫“0”時隙:
// 保持總線在低電平15微秒到60微秒之間
// 然后再保持總線在高電平1微秒到15微秒之間
// 理想狀態(tài): 60微秒的低電平然后跳變再保持1微秒的高電平
for (i = 0; i < 8; i++)
{
s3c2410_gpio_setpin(DS18B20_PIN, LOW); udelay(1);
if(byte & HIGH)
{
// 若byte變量的D0位是1,則需向總線上寫“1”
// 根據(jù)寫“1”時隙規(guī)則,電平在此處翻轉(zhuǎn)為高
s3c2410_gpio_setpin(DS18B20_PIN, HIGH);
}
else
{
// 若byte變量的D0位是0,則需向總線上寫“0”
// 根據(jù)寫“0”時隙規(guī)則,電平在保持為低
// s3c2410_gpio_setpin(DS18B20_PIN, LOW);
}
// 電平狀態(tài)保持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”時隙:
// 若總線狀態(tài)保持在低電平狀態(tài)1微秒到15微秒之間
// 然后跳變到高電平狀態(tài)且保持在15微秒到60微秒之間
// 就認為從DS18B20讀到一個“1”信號
// 理想情況: 1微秒的低電平然后跳變再保持60微秒的高電平
//
// 讀“0”時隙:
// 若總線狀態(tài)保持在低電平狀態(tài)15微秒到30微秒之間
// 然后跳變到高電平狀態(tài)且保持在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);

// 若總線在我們設(shè)它為低電平之后若1微秒之內(nèi)變?yōu)楦?br /> // 則認為從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

這個驅(qū)動是基于Linux的2.6.29內(nèi)核樹編譯的,內(nèi)核樹的路徑是當前目錄的../../kernel/linux-2.6.29,若以來其他的Linux的內(nèi)核樹,編譯時可能會出現(xiàn)找不到某些文件的情況,如hardware.h等,只需要在內(nèi)核樹中作一些軟鏈接就可以解決,還有聲明一點的就是:Makefile中的命令操作前的并非空格,而是TAB跳格。

make編譯,編譯完畢后產(chǎn)生許多文件,我們只關(guān)心test_ds18b20和s3c2440_ds18b20.ko這兩個。

把以上兩個文件下載到開發(fā)板中,創(chuàng)建一個設(shè)備節(jié)點:

mknod /dev/DS18B20 c 232 0

把s3c2440_ds18b20這個模塊加載到內(nèi)核:

insmod s3c2440_ds18b20.ko

加載成功后我們可以通過模塊列表查看:

之后把test_ds18b20測試文件改為可執(zhí)行狀態(tài):

chmod 0111 test_ds18b20

執(zhí)行后可以觀察到以下的結(jié)果:



評論


技術(shù)專區(qū)

關(guān)閉