[OK210開發板體驗]功能篇(4)Linux字符驅動之DS18B20溫度傳感器驅動
[OK210開發板體驗]入門篇(1):開箱驗板
[OK210開發板體驗]入門篇(2):板載資源
[OK210開發板體驗]入門篇(3):開發環境(軟件安裝,開發環境,燒寫系統)
[OK210開發板體驗]入門篇(4):編程入門(NFS登錄,驅動入門)
[OK210開發板體驗]功能篇(1):Linux字符驅動之Led
[OK210開發板體驗]功能篇(2):Linux字符驅動之Key按鍵
[OK210開發板體驗]功能篇(3):Linux Input子系統之Key按鍵
今天是功能篇的第四篇:Linux字符驅動之DS18B20,本節主要分3部分:硬件分析,軟件基礎,驅動編程。
一、硬件分析
在 [OK210開發板體驗]的第二篇:板載資源中,簡單分析了DS18B20傳感器的功能和作用。其實對DS18B20的操作,包含兩部分,一是對字符設備驅動的深入理解,二是對DS18B20傳感器時序的掌握。前面3篇功能體驗,分別對GPIO的輸出(LED)和輸入(Key)進行了驅動的編寫,而這篇將同時涉及GPIO的輸入和輸出。
首先從OK210的底板原理圖中可知,OK210開發板上的DS18B20連接通過一個上拉電阻連接到了核心板的XM0ADDR3引腳上,如下圖所示:
而XM0ADDR3引腳由S5PV210用戶手冊,可知,該引腳位于MP0_4[3]引腳上,默認連接到了EBI接口上,如下圖所示:
所以,我們要對DS18B20進行操作,就是通過對MP0_4[3]引腳的設置進行實現
二、軟件基礎
該部分首先總結一下,字符設備驅動的注冊過程,然后簡單介紹一下DS18B20驅動編寫過程中,注意的事項:
1 字符設備的注冊與注銷
以下的步驟,一般在驅動初始化函數中和驅動退出函數中實現。
第一步:注冊設備號 信息#tail -f /var/log/message
注冊函數:
register_chrdev_region() 或 查看#lsmod
alloc_chrdev_region() 或 查看#cat /proc/devices
register_chrdev()
注銷函數:
unregist_chrdev_region() 或
unregister_chrdev()
初始化cdev
靜態初始化cdev_init() 或
動態初始化cdev_alloc()
添加到系統函數
cdev_add()
從系統刪除函數
cdev_del()
創建類
class_create() 將放于/sysfs 查看#ls /sys/class
刪除類
class_destroy()
創建節點
device_create() 或 class_device_create() 將存放于/dev 查看#ls /dev
刪除節點
device_destroy() 或 class_device_destroy()
2 DS18B20注意事項
DS18B20是由DALLAS半導體公司推出的一種的“一線總線”接口的溫度傳感器。與傳統的熱敏電阻等測溫元件相比,它是一種新型的體積小、適用電壓寬、與微處理器接口簡單的數字化溫度傳感器。一線總線結構具有簡潔且經濟的特點,可使用戶輕松地組建傳感器網絡,從而為測量系統的構建引入全新概念,測量溫度范圍為-55~+125℃,精度為±0.5℃。現場溫度直接以“一線總線”的數字方式傳輸,大大提高了系統的抗干擾性。它能直接讀出被測溫度,并且可根據實際要求通過簡單的編程實現9~l2位的數字值讀數方式。它工作在3—5.5 V的電壓范圍,采用多種封裝形式,從而使系統設計靈活、方便,設定分辨率及用戶設定的報警溫度存儲在EEPROM中,掉電后依然保存。其內部結構如下圖所示:
如上所述,驅動的編寫,主要包括兩部分,一是根據DS18B20時序,編寫讀寫函數,二是根據字符設備,編寫設備驅動程序。具體的代碼見下,最后附上效果圖:
-
#include <linux/module.h>
-
#include <linux/fs.h>
-
#include <linux/kernel.h>
-
#include <linux/init.h>
-
#include <linux/delay.h>
-
#include <linux/cdev.h>
-
#include <linux/device.h>
-
#include <linux/gpio.h>
-
#include <plat/gpio-cfg.h>
-
-
#define DEVICE_NAME "DS18B20"
-
-
static struct cdev cdev;
-
struct class *tem_class;
-
static dev_t devno;
-
static int major = 100;
-
-
unsigned int gpio=S5PV210_MP04(3);
-
-
void tem_reset(void)
-
{
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
-
gpio_set_value(gpio, 1);
-
//gpio_direction_output(gpio,1);
-
udelay(100);
-
gpio_set_value(gpio, 0);
-
udelay(600);
-
gpio_set_value(gpio, 1);
-
udelay(100);
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
-
// gpio_direction_input(gpio);
-
}
-
-
void tem_wbyte(unsigned char data)
-
{
-
int i;
-
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
-
for (i = 0; i < 8; ++i)
-
{
-
gpio_set_value(gpio, 0);
-
udelay(1);
-
-
if (data & 0x01)
-
{
-
gpio_set_value(gpio, 1);
-
}
-
udelay(60);
-
gpio_set_value(gpio, 1);
-
udelay(15);
-
data >>= 1;
-
}
-
gpio_set_value(gpio, 1);
-
}
-
-
unsigned char tem_rbyte(void)
-
{
-
int i;
-
unsigned char ret = 0;
-
-
for (i = 0; i < 8; ++i)
-
{
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
-
gpio_set_value(gpio, 0);
-
// gpio_direction_output(gpio,0);
-
udelay(1);
-
gpio_set_value(gpio, 1);
-
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(0));
-
// gpio_direction_input(gpio);
-
ret >>= 1;
-
if (gpio_get_value(gpio))
-
{
-
ret |= 0x80;
-
}
-
udelay(60);
-
}
-
s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(1));
-
-
-
return ret;
-
}
-
-
static ssize_t tem_read(struct file *filp, char *buf, size_t len, loff_t *offset)
-
{
-
unsigned char low, high;
-
-
tem_reset();
-
udelay(420);
-
tem_wbyte(0xcc);
-
tem_wbyte(0x44);
-
-
mdelay(750);
-
tem_reset();
-
udelay(400);
-
tem_wbyte(0xcc);
-
tem_wbyte(0xbe);
-
-
low = tem_rbyte();
-
high = tem_rbyte();
-
-
*buf = low / 16 + high * 16;
-
-
*(buf + 1) = (low & 0x0f) * 10 / 16 + (high & 0x0f) * 100 / 16 % 10;
-
return 0;
-
}
-
-
static struct file_operations tem_fops =
-
{
-
.owner = THIS_MODULE,
-
.read = tem_read,
-
};
-
-
static int __init tem_init(void)
-
{
-
int result;
-
devno = MKDEV(major, 0);
-
-
result = register_chrdev_region(devno, 1, DEVICE_NAME);
-
if (result)
-
{
-
printk("register failed\n");
-
return result;
-
}
-
cdev_init(&cdev, &tem_fops);
-
cdev.owner = THIS_MODULE;
-
cdev.ops = &tem_fops;
-
result = cdev_add(&cdev, devno, 1);
-
if (result)
-
{
-
printk("cdev add failed\n");
-
goto fail1;
-
}
-
-
tem_class = class_create(THIS_MODULE, "tmp_class");
-
if (IS_ERR(tem_class))
-
{
-
printk("class create failed\n");
-
goto fail2;
-
}
-
-
device_create(tem_class, NULL, devno, DEVICE_NAME, DEVICE_NAME);
-
return 0;
-
fail2:
-
cdev_del(&cdev);
-
fail1:
-
unregister_chrdev_region(devno, 1);
-
return result;
-
}
-
-
static void __exit tem_exit(void)
-
{
-
device_destroy(tem_class, devno);
-
class_destroy(tem_class);
-
cdev_del(&cdev);
-
unregister_chrdev_region(devno, 1);
-
}
-
-
module_init(tem_init);
-
module_exit(tem_exit);
-
-
MODULE_LICENSE("GPL");
-
MODULE_AUTHOR("gjianw217@163.com");
- MODULE_DESCRIPTION("DS18B20 driver");
2 測試程序
-
#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]={0};
-
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, sizeof(buf));
-
printf("%d.%d C\r\n", buf[0], buf[1]);
-
sleep(1);
-
}
-
close(fd);
-
}
- }
3 Makefile
-
#DS18B20 Makefile
-
ARCH=arm
-
CROSS_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
-
APP_COMPILE=/home/ok210/arm-2009q3/bin/arm-none-linux-gnueabi-
-
#obj-m := app-drv.o
-
obj-m := ds18b20-drv.o
-
#KDIR := /path/to/kernel/linux/
-
KDIR := /home/ok210/android-kernel-samsung-dev/
-
PWD := $(shell pwd)
-
default:
-
make -C $(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules
-
app:ds18b20-app.c
-
$(APP_COMPILE)gcc -o app ds18b20-app.c
-
clean:
- $(MAKE) -C $(KDIR) M=$(PWD) clea
復制代碼