ds18b20驱动程序
有了之前延时的方法,那么实现一个单总线数据传输的传感器驱动程序就非常简单了。下面我们套用杂项驱动框架来编写ds18b20驱动程序。
实现需要明确的是:**ds18b20驱动的本质是通过2440的gpio,通过给定的时序对ds18b20的读写数据访问,最后将数据传回应用程序。**由于我们实验的linux内核是三星公司根据2440公板修改后的内核,所以有关gpio的操作,三星公司提供了几个api函数,从而大幅度降低了程序的编写难度
- void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);配置gpio的功能,这里的功能主要就是输出/输入。该函数需要两个参数,第一个参数是需要控制的引脚,系统提供了几个带参宏,例如#define S3C2410_GPF(_nr) 表示GPF组的第_nr引脚。 S3C2410_GPF(1)就表示GPF1引脚。第二个参数表示功能,系统提供了几个不带参宏,如S3C2410_GPIO_OUTPUT表示输出,S3C2410_GPIO_INPUT表示输入;
- void s3c2410_gpio_pullup(unsigned int pin, unsigned int to);设置gpio引脚的内部上拉电阻。第一个参数表示要设置的引脚,第二个参数为1时表示使能上拉,0表示禁止上拉。本实验要全程使用内部上拉,因为我们没有外接上拉电阻。
- **unsigned int s3c2410_gpio_getpin(unsigned int pin);**读取某引脚的电平,1表示高电平,0表示低电平。
- void s3c2410_gpio_setpin(unsigned int pin, unsigned int to);控制某引脚的电平。第一个参数是要控制的引脚,第二个参数为1表示输出高电平,为0表示输出低电平。
我们将ds18b20的数据引脚接到2440的GPF3引脚:
先定义几个宏便于之后的操作:
复位函数
首先编写ds18b20的复位函数
第29行:使能上拉电阻
第33-34行:在释放总线前,要把功能设置为输入并将引脚设置为高
之后和裸机驱动部分裸机类似
向ds18b20写数据函数
第69行的延时是有必要的,因为引脚从低电平变为高电平需要时间。
读取一个字节的函数
温度采集函数
最后就是温度采集了,跟裸机程序一样。需要注意的是这里不能使用浮点运算把温度值算好再传递给应用程序。这是因为内核本身是不支持浮点运算的。这个计算的过程应该放在应用程序中。
代码
#include <linux/init.h>
#include <linux/module.h>
#include <mach/hardware.h>
#include <mach/gpio-fns.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>
#include <linux/fs.h>
#include <linux/timer.h>#define DQ S3C2410_GPF(3)
#define DQ_OUT S3C2410_GPIO_OUTPUT
#define DQ_IN S3C2410_GPIO_INPUTstatic int ds18b20_reset(void)
{int k;s3c2410_gpio_pullup(DQ,1);s3c2410_gpio_cfgpin(DQ,DQ_OUT);s3c2410_gpio_setpin(DQ,0);udelay(500);s3c2410_gpio_cfgpin(DQ,DQ_IN);s3c2410_gpio_setpin(DQ,1);k = 0;while(k < 30 && s3c2410_gpio_getpin(DQ)){udelay(10);++k;}if(k >= 30){return 0;}k = 0;while(k < 30 && !s3c2410_gpio_getpin(DQ)){udelay(10);++k;}if(k >= 30){return 0;}return 1;
}static void ds18b20_write(unsigned char dat)
{int i;s3c2410_gpio_cfgpin(DQ,DQ_OUT);for(i = 0;i < 8;++i){s3c2410_gpio_setpin(DQ,0);udelay(10);if(dat & 0x01){s3c2410_gpio_setpin(DQ,1);}else{s3c2410_gpio_setpin(DQ,0);}udelay(40);s3c2410_gpio_setpin(DQ,1);udelay(3);dat >>= 1;}
}static unsigned char ds18b20_read(void)
{unsigned char ret = 0;int i;for(i = 0;i < 8;++i){s3c2410_gpio_cfgpin(DQ,DQ_OUT);s3c2410_gpio_setpin(DQ,0);udelay(3);s3c2410_gpio_setpin(DQ,1);s3c2410_gpio_cfgpin(DQ,DQ_IN);udelay(1);if(s3c2410_gpio_getpin(DQ)){ret |= 0x01 << i;}udelay(45);}return ret;
}unsigned short getTemperature(void)
{unsigned short ret;ds18b20_reset();ds18b20_write(0xCC);ds18b20_write(0x44);mdelay(500);ds18b20_reset();ds18b20_write(0xCC);ds18b20_write(0xBE);ret = ds18b20_read();ret |= ds18b20_read();return ret;
}static int ds18b20_driver_open(struct inode *node, struct file *fp)
{return 0;
}static int ds18b20_driver_close(struct inode *node, struct file *fp)
{return 0;
}static ssize_t ds18b20_driver_read(struct file *fp, char __user *user_buffer, size_t len, loff_t *offset)
{unsigned ret = getTemperature();copy_to_user(user_buffer,&ret,4);return sizeof(ret);
}static ssize_t ds18b20_driver_write(struct file *fp, const char __user *user_buffer, size_t len, loff_t *offset)
{return 0;
}static struct file_operations fops =
{.owner = THIS_MODULE,.open = ds18b20_driver_open,.read = ds18b20_driver_read,.write = ds18b20_driver_write,.release = ds18b20_driver_close
};static struct miscdevice ds18b20_dev =
{.minor = MISC_DYNAMIC_MINOR,.name = "ds18b20",.fops = &fops
};static int __init ds1820_init(void)
{int ret;ret = misc_register(&ds18b20_dev);if(ret < 0){printk("misc_register is failed\n");return -1;}return 0;
}static void __exit ds1820_exit(void)
{
}module_init(ds1820_init);
module_exit(ds1820_exit);MODULE_LICENSE("GPL");
应用层代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main(void)
{int fd = open("/dev/ds18b20",O_RDWR);if(fd < 0){printf("file open error\n");return 0;}int i = 0;float j = 0;while(1){sleep(1);read(fd,&i,4);j = i * 0.0625; printf("%.2f\n",j);}return 0;
}