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 <linux/w1-gpio.h>
smdk2440_devices[ ]结构体中加入:
&s3c_ds18b20_device,
构建DS18B20设备的平台数据结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26#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和ds18b20驱动:
好了,重新加载内核。启动target,可以测试温度了:
上述工作表明我焊接的温度传感器可用,下面就是自写的驱动模块代码以及测试程序(参考了网上不少前辈的代码,感谢你们的分享)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225/********************************************************************************* * Copyright: (C) 2015 songyong<handy_skyoutlook.com> * All rights reserved. * * Filename: ds18b20.c * Description: This file * * Version: 1.0.0(2015年08月25日) * Author: sky <handy_sky@outlook.com> * ChangeLog: 1, Release initial version on "2015年08月25日 09时12分04秒" * ********************************************************************************/ #include<linux/module.h> #include<linux/kernel.h> #include<linux/fs.h> #include<linux/init.h> #include<linux/delay.h> #include<linux/gpio.h> #include<linux/types.h> #include<asm/irq.h> #include<mach/regs-gpio.h> #include<mach/hardware.h> #include<linux/device.h> #include<linux/kdev_t.h> #include<linux/cdev.h> #include<linux/errno.h> #include<asm/uaccess.h> #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 %dn",DEV_NAME, ds18b20_major); } printk("%s use major %dn",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 ds18b20n",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("handy_sky@outlook.com");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67/********************************************************************************* * Copyright: (C) 2015 songyong<handy_skyoutlook.com> * All rights reserved. * * Filename: ds18_test.c * Description: This file * * Version: 1.0.0(2015年08月24日) * Author: sky <handy_sky@outlook.com> * ChangeLog: 1, Release initial version on "2015年08月24日 15时24分47秒" * ********************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> /******************************************************************************** * 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 wrongn"); 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14obj-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中的源码。
二、
1
2
3
4
5
6
7
8struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
#define THIS_MODULE (&__this_module),__this_module是一个struct module变量,代表当前模块。所以可以 通过THIS_MODULE宏来引用到struct 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一段内存后使用结构体指针操作,要么就直接使用结构体变量。
最后
以上就是舒心茉莉最近收集整理的关于S3C2440 温度传感器ds18b20的驱动编写与测试的全部内容,更多相关S3C2440内容请搜索靠谱客的其他文章。
发表评论 取消回复