ds18b20的详细信息百度百科都有比较详细的介绍:
http://www.baidu.com/link?url=tgDW0RZumyv2JQbC7ohrnKj_jtFZmqkrnJd7vp3V24KCiG8bkOHqD7vRoQev3OjBPFgIGUvUyVpfdtgSzJUwm_&wd=&eqid=cb79eb5b0000da5a0000000555e061f4
再有就是特别感谢这位提供datasheet时序的博主:http://www.cnblogs.com/wangyuezhuiyi/archive/2012/10/12/2721839.html
=================================================================================================
因为Linux内核3.0自带Dallas 1-wires设备驱动,路径为:drivers/w1,所以在写驱动之前我首先进行了内核自带的单总线设备通用驱动移植(其实主要原因是温度传感器是我自己刚刚焊接上去的,想确认下其能否正常工作);此类驱动为Master/Slave模式:Master目录下为主控制器驱动,我们用到的是w1-gpio.c;Slave目录下是从设备驱动,我们使用的DS18B20属于温度传感器,所以使用w1_therm.c这个驱动。w1-gpio.c是单总线的IO操作方法,用于模拟单总线时序;w1_therm.c是DS18B20的内部操作方法(读写寄存器),和IO时序无关;我们可以将驱动结构看成是将“w1_therm”挂接到“w1-gpio”总线上,由w1-gpio控制w1_therm工作。
=================================================================================================
一、在mach-smdk2440.c中添加对设备DS18B20的支持:
加入w1-gpio.h头文件,以使用w1_gpio_platform_data结构体:
#include
smdk2440_devices[ ]结构体中加入:
&s3c_ds18b20_device,
构建DS18B20设备的平台数据结构:
#if 1
/* DS1820B add by Handy 2015.8.17*/
static void w1_enable_external_pullup(int enable)
{
if(enable)
s3c_gpio_setpull(S3C2410_GPG(0), S3C_GPIO_PULL_UP);
else
s3c_gpio_setpull(S3C2410_GPG(0), S3C_GPIO_PULL_NONE);
}
static struct w1_gpio_platform_data ds18b20_w1_gpio = {
.pin = S3C2410_GPG(0),
.is_open_drain = 0,
.enable_external_pullup = w1_enable_external_pullup,
};
static struct platform_device s3c_ds18b20_device ={
.name = "w1-gpio",
.id = -1,
.dev = {
.platform_data = &ds18b20_w1_gpio,
},
};
#endif
说明:设备名称是“w1-gpio”,这个和w1-gpio.c驱动中的驱动名称一致,才能注册这个设备驱动。通过FL2440开发板底板原理图可以看到只用到了GPG(0)这个管脚。
好了我们接下来配置内核,支持W1-gpio和ds18b20驱动:
好了,重新加载内核。启动target,可以测试温度了:
上述工作表明我焊接的温度传感器可用,下面就是自写的驱动模块代码以及测试程序(参考了网上不少前辈的代码,感谢你们的分享)
/*********************************************************************************
* Copyright: (C) 2015 songyong
* All rights reserved.
*
* Filename: ds18b20.c
* Description: This file
*
* Version: 1.0.0(2015年08月25日)
* Author: sky
* ChangeLog: 1, Release initial version on "2015年08月25日 09时12分04秒"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DQ S3C2410_GPG(0)
#define INPUT S3C2410_GPIO_INPUT
#define OUTPUT S3C2410_GPIO_OUTPUT
#define D_MAJOR 0
#define D_MINOR 0
#define DEV_NAME "ds18b20"
static int ds18b20_major = D_MAJOR;
static int ds18b20_minor = D_MINOR;
struct ds18b20_device
{
struct class *sy_class;
struct cdev cdev;
};
static struct ds18b20_device dev;//若使用指针,记得给指针开辟空间。
static unsigned int ds18b20_reset(void)
{
int err;
s3c2410_gpio_cfgpin(DQ, OUTPUT);
s3c2410_gpio_pullup(DQ, 0);
s3c2410_gpio_setpin(DQ, 1);
udelay(10);
s3c2410_gpio_setpin(DQ, 0);
udelay(600);
s3c2410_gpio_setpin(DQ, 1);
udelay(60);
s3c2410_gpio_cfgpin(DQ, INPUT);
udelay(400);
err = s3c2410_gpio_getpin(DQ);
return err;
}
static unsigned int ds18b20_write(unsigned char data)
{
unsigned int i;
s3c2410_gpio_cfgpin(DQ, OUTPUT);
for (i = 0; i < 8; i++) //只能一位一位的读写
{
s3c2410_gpio_setpin(DQ, 0);
udelay(5);
if(data & 0x01)
{
s3c2410_gpio_setpin(DQ ,1);
udelay(60);
}
else udelay(60);
data >>= 1; //从最低位开始判断;每比较完一次便把数据向右移,获得新的最低位状态
s3c2410_gpio_setpin(DQ, 1);
udelay(1);
}
return 2;
}
static unsigned int ds18b20_read(void)
{
unsigned int i ;
unsigned char data = 0x00;
for (i =0; i < 8 ; i++)
{
s3c2410_gpio_cfgpin(DQ, OUTPUT);
s3c2410_gpio_setpin(DQ, 1);
udelay(1);
s3c2410_gpio_setpin(DQ, 0);
udelay(2);
s3c2410_gpio_setpin(DQ, 1);
s3c2410_gpio_cfgpin(DQ, INPUT);
data >>= 1;
if(0 != s3c2410_gpio_getpin(DQ))
data |= 0x80; //最低位数据从data的最高位放起,边放边右移直到读取位完毕。
udelay(60);
}
return data;
}
static ssize_t read_ds18b20(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
unsigned char Data[2] = {0x00, 0x00};
unsigned long err;
int flag;
flag = ds18b20_reset();
if(flag <= 0)
{
printk("ds18b20 init fail!\n");
return -1;
}
ds18b20_write(0xcc);
ds18b20_write(0x44);
ds18b20_reset();
if(flag <= 0)
{
printk("ds18b20 init fail!\n");
return -1;
}
ds18b20_write(0xcc);
ds18b20_write(0xbe);
Data[0] = ds18b20_read();
Data[1] = ds18b20_read();
ds18b20_reset();
err = copy_to_user(buf, Data, sizeof(Data));
return err? -EFAULT:count;
}
#if 1
static int open_ds18b20(struct inode *inode, struct file *filp)
{
int flag = 0;
flag = ds18b20_reset();
if(flag)
{
printk("open ds18b20 successful!\n");
}
else printk("open ds18b20 failed!\n");
return 0;
}
#endif
static int release_ds18b20(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations fops={
.owner = THIS_MODULE,
.read = read_ds18b20,
.open = open_ds18b20,
.release = release_ds18b20,
};
static int __init ds18b20_init(void)
{
int result,err;
dev_t devno = 0;
if(ds18b20_major)
{
devno = MKDEV(ds18b20_major, ds18b20_minor);
result = register_chrdev_region(devno, 1, DEV_NAME);
}
else{
result = alloc_chrdev_region(&devno, ds18b20_minor, 1, DEV_NAME);
ds18b20_major = MAJOR(devno);
}
if(result < 0)
{
printk(KERN_ERR "%s can't use major %d\n",DEV_NAME, ds18b20_major);
}
printk("%s use major %d\n",DEV_NAME, ds18b20_major);
cdev_init(&dev.cdev,&fops);
dev.cdev.owner = THIS_MODULE;
err = cdev_add(&dev.cdev, devno, 1);
if(err)
{
printk(KERN_NOTICE"ERROR %d add ds18b20\n",err);
goto ERROR;
}
dev.sy_class = class_create(THIS_MODULE, DEV_NAME);
device_create(dev.sy_class, NULL, MKDEV(ds18b20_major, ds18b20_minor), NULL, DEV_NAME);
printk(KERN_NOTICE"Ds18b20 is ok!\n");
return 0;
ERROR:
printk(KERN_ERR"%s driver installed failure.\n",DEV_NAME);
cdev_del(&dev.cdev);
unregister_chrdev_region(devno, 1);
return err;
}
static void __exit ds18b20_exit(void)
{
dev_t devno = MKDEV(ds18b20_major, 0);
cdev_del(&dev.cdev);
device_destroy(dev.sy_class,devno);
class_destroy(dev.sy_class);
unregister_chrdev_region(devno, 1);
printk(KERN_NOTICE"bye ds18b20!\n");
}
module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("[email protected]");
测试程序:
/*********************************************************************************
* Copyright: (C) 2015 songyong
* All rights reserved.
*
* Filename: ds18_test.c
* Description: This file
*
* Version: 1.0.0(2015年08月24日)
* Author: sky
* ChangeLog: 1, Release initial version on "2015年08月24日 15时24分47秒"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
int fd;
int i = 0;
int data = 0;
unsigned char result[2];
float temperature = 0;
printf("will open fd... \n");
if((fd = open("/dev/ds18b20",O_RDWR|O_NONBLOCK)) < 0 )
{
perror("open device fail.\n");
return -1;
}
else printf("Open Device Ds18b20 Successful!!\n");
while(1)
{
int ret;
printf("\nWill read temperature...\n");
usleep(100);
ret = read(fd, result, sizeof(result));
if(ret != 2)
{
printf("read wrong\n");
exit(0);
}
else printf("read success !\n");
data = (int)result[1];
data <<=8;
data = data | result[0];
temperature =data * 0.0625 ;
printf("Temperature = %.2f ℃\n",temperature);
fflush(stdout);
sleep(1);
}
close(fd);
return 0;
} /* ----- End of main() ----- */
Makefile:
obj-m := ds18b20.o
KERNELDIR ?=/home/pikaqiu/minefl2440/kernel/linux-3.0_song
PWD := $(shell pwd)
CC := /opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
CLEAN := rm -rf
all : ds18b20.c ds18_test
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
ds18b20 : ds18b20.c
$(CC) ds18_test.c -o ds18_test
clobber :
$(CLEAN) ds18_test ds18b20.ko
clean :
$(CLEAN) *.mod.* *.o *~ modules.order Module.symvers
在做完之后我的终端时不时还会打印出:
WARNING: at drivers/gpio/gpiolib.c:101 gpio_ensure_requested+0x54/0xd4()
autorequest GPIO-192
Modules linked in: ds18b20
在网上找了资料之后怀疑是GPIO端口复用所致,在内核配置里面关闭了自带的单线设备通用驱动
<> Dallas's 1-wire support --->,即可解决。
小结:
一、
首先这次写完驱动后对比之前所学的驱动代码,知道了操纵GPIO管脚的方式有两种:一种是像之前LED灯的驱动里面那样通过对寄存器地址的操作来控制,另一种便是直接通过调用内核中提供的相关函数来操纵具体的硬件GPIO管脚。本文的驱动便是直接使用了内核中的函数来操控GPIO管脚。
这些函数在linux内核源代码的/arch/arm/plat_s3c24xx/gpio.c中实现:
1.void s3c2410_gpio_cfgpin(unsigned int pin,unsigned int function)
第一个参数pin 是对应的io引脚(这里用宏S3C2410_GPG(0), 0不是固定的,看你需要引用的引脚而定)第二个参数是设置该引脚的功能的(由S3C2410_GPIO_INPUT,S3C2410_GPIO_OUTPUT,S3C2410_GPIO_SFN2,S3C2410_GPIO_SFN3这4个宏进行定义)
例如:s3c2410_gpio_cfgpin(S3C2410_GPG(5),S3C2410_GPIO_INPUT)设置GPG5引脚为输入。
2.void s3c2410_gpio_pullup(unsigned int pin,unsigned int to)
作用:设置相应的的GPIO的上拉电阻。第一个参数:相应的引脚,和1里面的用法一致。第二个参数:设置为1或者0,1表示上拉,0表示不上拉。
3.void s3c2410_gpio_setpin(unsigned int pin,unsigned int to)
作用:将相应的引脚输出为1或者0。第一个参数:相应的引脚第二个参数:1或者0
例子:s3c2410_gpio_setpin(S3C2410_GPB(5),1)将引脚GPB5输出为1
4.unsigned int s3c2410_gpio_getpin(unsigned int pin)功能:获取相应的引脚的状态 高为1,低为0
更多的gpio操作详见gpio.c中的源码。
二、
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
最大的收获的是更加熟悉相关结构与脉络。比如动静态申请设备号,通过struct class结构体变量对应的类来创建设备节点等。以前学的时候总是看到说必须要填充
fops结构体,然后嵌入到
cdev之中去。以前的理解是:常说用cdev来描述一个字符设备驱动,然后有关这个驱动的系统应用函数接口自然就需要填充进去。错虽然没错,但是直到找到
cdev结构体之后才算是真正有了代码依据。
dev_t dev就是设备号。还解释了每次都要写
owner=THIS_MODULE。可以看到
owner指针是
struct module结构体类型。
struc module在内核中
代表一个内核模块,我们插入一个内核模块,一般会使用工具
insmod,该工具实际上调用了系统调用
init_module,在该系统调用函数中,首先调用
load_module,
把用户空间传入的整个内核模块文件创建成一个内核模块,
返回一个struct module结构体。
内核中便以这个结构体代表这个内核模块。对于
THIS_MODULE来说,它是一个宏定义:
cdev路径:linux-3.0_song/include/linux/cdev.h
资料链接:http://blog.csdn.net/jk110333/article/details/8563647 THIS_MODULE
三、
起初我看到WARNING上面的gpio_request误以为是操作GPIO之前没有为gpio管脚申请资源。然后使用了gpio_request申请后依旧没有效果;现在大概浅显的知道gpio_request是用在虚拟地址映射时需要申请资源。并不是在驱动正常使用gpio引脚时申请。后面还通过grep来查找GPG(0)在内核中的哪些地方有占用,发现在plat-s3c24xx/pm.c里面有用到,进去将GPG(0)短暂的Disable之后重新加载内核依旧没有解决。此时我又去看mach-smdk2440里面的GPG(0),当然这是我自己添加的。然后我瞬间知道原因了:肯定是自带的通用驱动编译进内核后与自己编写的驱动IO端口偶尔冲突而引发的warning。自己之前在做wifi驱动的时候。也是内核提供的mac80211通用驱动与厂商提供的.ko驱动冲突。这次又是这种情况..囧。还有就是自己在使用结构体指针的时候,程序又跑飞了。再次提醒自己要知道此机器上指针所能存放的地址大小。驱动里牵扯到结构体操作,要么先给一个指针kmalloc一段内存后使用结构体指针操作,要么就直接使用结构体变量。