TQ2440-2.6.30.4内核下的ds28b20模块驱动 from:
http://keyemb.com/?p=12
因为要做个温度采集的东西,所以用到了ds18b20温度传感器。但是以前只在单片机上写过ds18b20的程序,现在对arm-linux下的驱动程序得编写还不是很熟练,所以在网络上找了个程序下了下来用了一下。文章的地址忘了。。。在这里感谢原作者。网上有许多TQ2440的驱动,但是我试了好几个,只有这个是好使的..
驱动程序:ds18b20.c
#include <linux/platform_device.h> #include <linux/delay.h> #include <linux/fs.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/uaccess.h> #include <linux/clk.h> #include <mach/regs-gpio.h> #include <mach/io.h> #include <mach/map.h> #include <mach/regs-clock.h> #define DQ S3C2410_GPG11 #define CPG_IN S3C2410_GPG11_INP #define CPG_OUT S3C2410_GPG11_OUTP #define DEVICE_NAME "tq18b20" //自定义驱动称为“tq18b20”。 #define DS18B20_MAJOR 210 static int opencount = 0; static char data[2]; //ds18b20复位,返回0成功,返回1失败 static unsigned char init_ds18b20(void) { unsigned char ret = 0; s3c2410_gpio_cfgpin(DQ, CPG_OUT); // 配置GPG11输出模式 //s3c2410_gpio_pullup(DQ, 0); s3c2410_gpio_setpin(DQ, 1); // 向18B20发送一个上升沿,并保持高电平状态约100微秒 udelay(100); s3c2410_gpio_setpin(DQ, 0); // 向18B20发送一个下降沿,并保持低电平状态约500微秒 udelay(500); s3c2410_gpio_setpin(DQ, 1); //将18b20总线拉高,以便在15~60us后接收18b20发出的存在脉冲 udelay(50); s3c2410_gpio_cfgpin(DQ, CPG_IN); // 通过再次配置GPG11引脚成输入状态,可以检测到DS18B20是否复位成功 //若存在脉冲是一个60~240us的低电平信号,则通信双方已达成基本的协议 ret = s3c2410_gpio_getpin(DQ); //接下来是控制器与18b20的数字通信 udelay(200); return ret; } //向18b20写一个字节 static void write_onechar(char data) { unsigned char i = 0; s3c2410_gpio_cfgpin(DQ, CPG_OUT); //配置GPG11输出模式 s3c2410_gpio_pullup(DQ, 1); for(i=0; i<8; i++) { s3c2410_gpio_setpin(DQ, 0); //每一位的发送前至少15us低电平起始位 udelay(15); s3c2410_gpio_setpin(DQ, data&0x01); //在采样时间内,如果控制器将总线拉高,表示写1,拉低表示写0 udelay(60); s3c2410_gpio_setpin(DQ, 1); udelay(2); data >>= 1; } } //从18b20读一个字节 static unsigned char read_onechar(void) { unsigned char i; unsigned char data=0; for(i=0; i<8; i++) { s3c2410_gpio_cfgpin(DQ, CPG_OUT); // 配置GPG11输出模式 s3c2410_gpio_setpin(DQ, 0); //读时间间隙时也是必须先有主机产生至少1us的低电平,表示读时间的起始 udelay(1); data >>= 1; s3c2410_gpio_setpin(DQ, 1); s3c2410_gpio_cfgpin(DQ, CPG_IN); if(s3c2410_gpio_getpin(DQ)) // 若总线在我们设它为低电平之后若1微秒之内变为高 // 则认为从DS18B20处收到一个“1”信号 data |= 0x80; udelay(50); } return data; } //18b20的读函数,读出温度 static ssize_t read_ds18b20(struct file *filp, char *buffer, size_t count, loff_t *ppos) { if(init_ds18b20()) //初始化成功,init_ds18b20()返回值为0,否则为1 return -1; write_onechar(0x0cc); //跳过读序列号的操作 write_onechar(0x44); //启动温度转换 udelay(5); while(init_ds18b20()); udelay(200); write_onechar(0x0cc); //跳过读序列号的操作 write_onechar(0x0be); //读取温度寄存器 data[0] = read_onechar(); //读低8位 data[1] = read_onechar(); //读高8位 // copy_to_user(buffer, &data, 2); buffer[0]=data[0]; buffer[1]=data[1]; return 1; } //对应应用程序的open函数 static int open_ds18b20(struct inode *node, struct file *file) { unsigned char flag; if(opencount == 1) return -EBUSY; flag = init_ds18b20(); if(flag&0x01) { printk("uable to open device!\n"); return -1; } else { opencount++; printk("device opened!\n"); return 0; } } static int release_ds18b20(struct inode *node, struct file *file) { opencount--; printk("device released!\n"); return 0; } static struct file_operations ds18b20_fops = { .owner = THIS_MODULE, .read = read_ds18b20, .release = release_ds18b20, .open = open_ds18b20, }; static char __initdata banner[] = "TQ2440/SKY2440 ds18b20\n";//打印信息 static struct class *ds18b20_class; static int __init ds18b20_init(void) { int ret; printk(banner); ret = register_chrdev(DS18B20_MAJOR, DEVICE_NAME, &ds18b20_fops); if (ret < 0) { printk(DEVICE_NAME " can't register major number\n"); return ret; }//错误处理 ds18b20_class = class_create(THIS_MODULE, DEVICE_NAME); if(IS_ERR(ds18b20_class)) { printk("Err: failed in tope-leds class. \n"); return -1; } device_create(ds18b20_class, NULL, MKDEV(DS18B20_MAJOR, 0), NULL, DEVICE_NAME);//创建一个设备节点,节点名为DEVICE_NAME printk(DEVICE_NAME " initialized\n");//打印信息,内核中的打印用printk函数 return 0; } static void __exit ds18b20_exit(void) { printk(DEVICE_NAME " exit\n");//打印信息,内核中的打印用printk函数 unregister_chrdev(DS18B20_MAJOR, DEVICE_NAME);//取消注册设备 device_destroy(ds18b20_class, MKDEV(DS18B20_MAJOR, 0)); //删掉设备节点 class_destroy(ds18b20_class); //注销类 } module_init(ds18b20_init); module_exit(ds18b20_exit); MODULE_LICENSE("GPL");//遵循的协议
编译的方法是在当前目录下写一个Makefile文件,内容如下
obj-m := ds18b20.o
然后执行:
make -C 内核路径 M=`pwd` modules
就会生成ds18b20.ko的驱动模块文件。
测试程序:ds18b20_test.c
#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/tq18b20",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, 1); result = (float)buf[0]; result /= 16; result += ((float)buf[1] * 16); if(result<100) { printf("%f .C\r\n", result); sleep(1); } } close(fd); } }
直接用交叉编译器编译即可。执行后就能在终端上看到不断输出的温度值。