这说讲一下DHT11驱动,一共有3个引脚分别是VCC , GND , DATA。
使用起来也是非常的简单,手册里有非常详细的时序图(还是中文的)。
步骤一:
DHT11上电后(DHT11上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,并记录数据,同时 DHT11的DATA数据线由上拉电阻拉高一直保持高电平;此时 DHT11的 DATA 引脚处于输入状态,时刻检测外部信号。
步骤二:
微处理器的I/O设置为输出同时输出低电平,且低电平保持时间不能小于18ms,然后微处理器的I/O设置为输入状态,由于上拉电阻,微处理器的I/O即DHT11的DATA数据线也随之变高,等待DHT11作出回答信号,发送信号如图所示:
步骤三:
DHT11的DATA引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后DHT11的DATA引脚处于输出状态,输出 80微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11回应信号)后,等待80微秒的高电平后的数据接收,发送信号如图所示:
步骤四:
由DHT11的DATA引脚输出40位数据,微处理器根据I/O电平的变化接收40位数据,位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加70微秒的高电平。位数据“0”、“1”格式信号如图所示:
结束信号:
DHT11的DATA引脚输出40位数据后,继续输出低电平50微秒后转为输入状态,由于上拉电阻随之变为高电平。但DHT11内部重测环境温湿度数据,并记录数据,等待外部信号的到来。
以上就是整体的时序操作方法,还要多说一句就是DHT11对是需要求很严格,我最开始怎么调也出不来数据,最后经过网友的博客才想起中断影响时间延时的问题,因此需要屏蔽掉中断在进行操作,就没有问题了。
驱动是直接用我以前写的18b20改的,因此里面有一些忘记改的名称或函数,说明一下不要被迷惑。
/*基本linux头文件,用于定义驱动函数进出口与协议*/
#include
#include
#include
/*驱动注册的头文件,包含驱动的结构体和注册和卸载的函数*/
#include
/*注册杂项设备头文件*/
#include
/*注册设备节点的文件结构体*/
#include
/*inux中申请GPIO的头文件*/
#include
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include
#include
/*三星平台4412平台,GPIO宏定义头文件*/
#include
#include
/*在平台文件中定义的宏里name的命名*/
#define DRIVER_NAME "18b20_train"
#define DEVICE_NAME "ds18b20_train"
/*1,GPL协议,2,作者名*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("C.L.");
static int DHT11_gpios[] = {EXYNOS4_GPX0(1),EXYNOS4212_GPJ1(0)};
static int chose = 0;
unsigned char buff_data[6];
unsigned char check_flag;
#define DHT11_NUM ARRAY_SIZE(DHT11_gpios)
/*io口控制,接受由上层传递的数据cmd、arg*/
#define Delay_us(x) udelay(x)
#define Delay_ms(x) mdelay(x)
/* #define SetDS18B20IOout() s3c_gpio_cfgpin(DHT11_gpios[0], S3C_GPIO_OUTPUT)
#define SetDS18B20IOin() s3c_gpio_cfgpin(DHT11_gpios[0], S3C_GPIO_INPUT)
#define WriteDS18B20IO(data) gpio_set_value(DHT11_gpios[0], data)
#define ReadDS18B20IO() gpio_get_value(DHT11_gpios[0]) */
void SetDS18B20IOout(void){
s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_OUTPUT);
}
void SetDS18B20IOin(void){
s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_INPUT);
}
void WriteDS18B20IO(int data){
gpio_set_value(DHT11_gpios[chose], data);
}
int ReadDS18B20IO(void){
return (gpio_get_value(DHT11_gpios[chose]));
}
static unsigned char DHT11ReadByte(void)
{
int time,i;
unsigned char data;
unsigned char flag;
data = 0x0;
flag = 0x0;
for (i = 0; i < 8; i++)
{
time = 0;
while(!ReadDS18B20IO())
{
time++;
if (time >20)
{
return -1;
printk("Read error1!\n");
}
udelay(5);
}
udelay(30);
flag=0x0;
if(ReadDS18B20IO())
{
flag=0x01;
}
time = 0;
while(ReadDS18B20IO())
{
time++;
if (time > 100)
{
return -1;
printk(">70 error2!!\n");
}
udelay(5);
}
data <<= 1;
data |= flag;
}
return (data);
}
static unsigned char DHT11ReadDate(void)
{
int time, i ;
char sum;
SetDS18B20IOout();
WriteDS18B20IO(0);
Delay_ms(30);
// msleep(16);//msleep是忙等待时间会超过16ms达到18ms
WriteDS18B20IO(1);
Delay_us(30);
SetDS18B20IOin();
time = 0;
if(ReadDS18B20IO()== 0)
{
while (!ReadDS18B20IO())
{
time++;
if (time > 20)
{
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
Delay_us(5);
}
time = 0;
while (ReadDS18B20IO())
{
time++;
if (time > 20)
{
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
udelay(5);
}
for (i = 0; i < 5; i++)
{
buff_data[i] = DHT11ReadByte();
if (buff_data[i] == -1)
{
return -3;
}
}
sum = 0;
for (i = 0; i < 4; i++)
{
sum += buff_data[i];
}
if (sum != buff_data[i])
{
check_flag=0x0;
printk("humidity check fail\n");
return -4;
}else{
check_flag=0xff;
printk("humidity check pass\n");
printk("humidity=[%d],temp=[%d]\n",buff_data[0],buff_data[2]);
}
SetDS18B20IOout();
WriteDS18B20IO(1);
for (i = 0; i < 5; i++)
{
printk(KERN_EMERG "DHdata[%d] = %d\n",i,buff_data[i]);
}
}
return 0;
}
static ssize_t DHT11_Read(struct file *file, char *buff, size_t count, loff_t *f_pos)
{
int ret;
local_irq_disable(); //屏蔽外部中断
DHT11ReadDate();
local_irq_enable(); //矢能外部中断
if(check_flag==0xff){
ret=copy_to_user(buff,(char *)buff_data, count);
if(ret<0){
printk("copy to user err\n");
return -EAGAIN;
}else{
printk("copy to user success\n");
}
return 0;
}
return count;
}
/*io口控制,接受由上层传递的数据cmd、arg*/
static long DHT11_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
printk("cmd is %lu,arg is %lu\n",cmd,arg);
if(cmd > 1){
printk(KERN_EMERG "cmd is 0 or 1\n");
}
if(arg > 2){
printk(KERN_EMERG "arg is 0~2\n");
}
chose = cmd;
return 0;
}
static int DHT11_release(struct inode *inode, struct file *file){
printk(KERN_EMERG "DHT11 release\n");
return 0;
}
static int DHT11_open(struct inode *inode, struct file *file){
printk(KERN_EMERG "DHT11 open\n");
return 0;
}
/*注册设备节点文件结构体*/
static struct file_operations DHT11_ops = {
/*它是一个指向拥有这个结构的模块的指针. 这个成员用来在它的操作还在被使用时阻止模块被卸载. 几乎所有时间中, 它被简单初始化为 THIS_MODULE, 一个在 中定义的宏.*/
.owner = THIS_MODULE,//必选
.open = DHT11_open,//必选 打开文件
.read =DHT11_Read,
.unlocked_ioctl = DHT11_ioctl,
.release = DHT11_release,//必选 关闭文件
};
/*Linux内核使用struct miscdeivce来描述一个混杂设备*/
static struct miscdevice DHT11_dev = {
.minor = MISC_DYNAMIC_MINOR,//minor是这个混杂设备的次设备号,若由系统自动配置,则可以设置为MISC_DYNANIC_MINOR
.name = DEVICE_NAME,//name是设备名
.fops = &DHT11_ops,
};
static int DHT11_probe(struct platform_device *pdv){
int ret,i,init;
printk(KERN_EMERG "\tinitialized\n");
for(i = 0; i< DHT11_NUM;i++){
ret = gpio_request(DHT11_gpios[i],"GPIO");
if(ret){
printk("%s: request GPIO %d for DHT11 failed, ret = %d\n", DRIVER_NAME,
i, ret);
}
else{
printk(" request DHT11'S GPIO success\n");
}
}
init = misc_register(&DHT11_dev);
//生成设备节点,在probe函数中调用,源函数在/*注册杂项设备头文件*/中
printk("DHT11_dev's driver init = %d\n",init);
return 0;
}
/*DHT11_driver init's child*/
static int DHT11_remove(struct platform_device *pdv){
int init;
printk(KERN_EMERG "\tremove\n");
init = misc_deregister(&DHT11_dev);/****************/
//解除设备节点,在remove中调用,源函数在/*注册杂项设备头文件*/中
printk("DHT11's driver exit init = %d\n",init);
return 0;
}
struct platform_driver DHT11_linux_driver = {
.probe = DHT11_probe,
.remove = DHT11_remove,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
};
/*init the DHT11_driver*/
static int DHT11_linux_driver_init(void)
{
int DriverState;
printk(KERN_EMERG "DHT11's driver success enter!\n");
DriverState = platform_driver_register(&DHT11_linux_driver);//在入口函数中添加的初始化的函数。
printk(KERN_EMERG "tDriverState is %d\n",DriverState);//打印出返回值
return 0;
}
/*exit DHT11_driver*/
static void DHT11_linux_driver_exit(void)
{
gpio_free(DHT11_gpios[0]);
gpio_free(DHT11_gpios[1]);
printk(KERN_EMERG "driver success exit!\n");
platform_driver_unregister(&DHT11_linux_driver);//卸载时释放函数占用
}
module_init(DHT11_linux_driver_init);
module_exit(DHT11_linux_driver_exit);
#include
#include
int main(int argc,char *argv[])
{
int humidityfd;
int ret;
char buf[5];
unsigned char tempz = 0;
unsigned char tempx = 0;
unsigned char humidiyz = 0;
unsigned char humidiyx = 0;
humidityfd = open("/dev/ds18b20_train",0);
if(humidityfd<0){
printf("/dev/humidiy open fail\n");
return 0;
}
printf("argv = %d\n",argv[1]);
ret = ioctl(humidityfd,atoi(argv[1]),0);
if(ret<0){
printf("ioctl fail\n");
return 0;
}
while(1){
ret=read(humidityfd,buf,sizeof(buf));
if(ret<0)
printf("read err!\n");
else{
humidiyz =buf[0];
humidiyx =buf[1];
tempz =buf[2];
tempx =buf[3];
printf("humidity = %d.%d%%\n", humidiyz, humidiyx);
printf("temp = %d.%d\n",tempz,tempx);
}
sleep(2);
}
close(humidityfd);
return 0;
}
obj-m += DHT11_driver.o
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0
#当前目录变量
PWD ?= $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
rm -rf *.o *.mod.c *.order *.symvers
#make clean执行的操作是删除后缀为o的文件
clean:
rm -rf *.o
2,驱动中我写了2个DHT11的操作。通过ioctl来控制读取哪一个。从app应用代码也可以看出。
3,硬件注册采用的是平台文件注册的,这个需要注意(想要直接用,就复制read相关函数里的代码改一下引脚就OK)。
4,代码写的有点乱,注释也不是很全,有什么问题评论提出吧。
/ 更新 2017/5/8 /
#include
#include
#include
#include
/*node*/
#include
/* kmalloc() */
#include
/* error codes */
#include
/* size_t */
#include
/*inux中申请GPIO的头文件*/
#include
/*三星平台的GPIO配置函数头文件*/
/*三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件*/
#include
#include
/*三星平台4412平台,GPIO宏定义头文件*/
#include
#include
#include
#include
#include
#include
/*platform_device name*/
#define DRIVER_NAME "18b20_train"
/*platform_driver name*/
#define DEVICE_NAME "DHT11_DEVICE_"
/*设备号 0 自动分配*/
int DHT11_major = 0;
int DHT11_minor = 0;
/*1,GPL协议,2,作者名*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("C.L.");
/*资源,这里应该注册到mach文件中,方便修改放到这了*/
static int DHT11_gpios[] = {EXYNOS4_GPX0(1),EXYNOS4212_GPJ1(0)};
#define DHT11_NUM ARRAY_SIZE(DHT11_gpios)
/*驱动结构信息*/
struct DHT11_dev{
int chose;
unsigned char buff_data[6];
unsigned char check_flag;
struct mutex mutex; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
struct DHT11_dev *dht11_devices;
static struct class *myclass;
#define Set_DHT11_IOout(chose) s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_OUTPUT)
#define Set_DHT11_IOin(chose) s3c_gpio_cfgpin(DHT11_gpios[chose], S3C_GPIO_INPUT)
#define Write_DHT11_IO(chose,data) gpio_set_value(DHT11_gpios[chose], data)
#define Read_DHT11_IO(chose) gpio_get_value(DHT11_gpios[chose])
#define Delay_us(x) udelay(x)
#define Delay_ms(x) mdelay(x)
static unsigned char DHT11ReadByte(struct DHT11_dev *dev)
{
int time,i;
unsigned char data;
unsigned char flag;
data = 0x0;
flag = 0x0;
for (i = 0; i < 8; i++){
time = 0;
while(!Read_DHT11_IO(dev->chose)){
time++;
if (time >20){
return -1;
printk("Read error1!\n");
}
Delay_us(5);
}
Delay_us(30);
flag=0x0;
if(Read_DHT11_IO(dev->chose)){
flag=0x01;
}
time = 0;
while(Read_DHT11_IO(dev->chose)){
time++;
if (time > 100){
return -1;
printk(">70 error2!!\n");
}
Delay_us(5);
}
data <<= 1;
data |= flag;
}
return (data);
}
static unsigned char DHT11ReadDate(struct DHT11_dev *dev)
{
int time, i ;
char sum;
Set_DHT11_IOout(dev->chose);
Write_DHT11_IO(dev->chose,0);
Delay_ms(30);
Write_DHT11_IO(dev->chose,1);
Delay_us(30);
Set_DHT11_IOin(dev->chose);
//printk("DHT11_read_data %d!\n",__LINE__);
time = 0;
if(Read_DHT11_IO(dev->chose)== 0){
while (!Read_DHT11_IO(dev->chose)){
time++;
if (time > 20){
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
Delay_us(5);
}
time = 0;
while (Read_DHT11_IO(dev->chose)){
time++;
if (time > 20){
printk("DHT11_read_data %d err!\n",__LINE__);
return -2;
}
Delay_us(5);
}
for (i = 0; i < 5; i++){
dev->buff_data[i] = DHT11ReadByte(dev);
if (dev->buff_data[i] == -1){
return -3;
}
}
sum = 0;
for (i = 0; i < 4; i++){
sum += dev->buff_data[i];
}
if (sum != dev->buff_data[i]){
dev->check_flag=0x0;
printk("humidity check fail\n");
return -4;
}else{
dev->check_flag=0xff;
printk("humidity check pass\n");
printk("humidity=[%d],temp=[%d]\n",dev->buff_data[0],dev->buff_data[2]);
}
Set_DHT11_IOout(dev->chose);
Write_DHT11_IO(dev->chose,1);
for (i = 0; i < 5; i++){
printk(KERN_EMERG "DHdata[%d] = %d\n",i,dev->buff_data[i]);
}
}else{
printk("DHT11_fail_read_data %d!\n",__LINE__);
return -5;
}
return 0;
}
static ssize_t DHT11_Read(struct file *filp, char *buff, size_t count, loff_t *f_pos)
{
int ret;
struct DHT11_dev *dev = filp->private_data;
printk("dev-chose = %d\n",dev->chose);
if (mutex_lock_interruptible(&dev->mutex))
return -ERESTARTSYS;
local_irq_disable(); //disable_irq
DHT11ReadDate(dev);
local_irq_enable(); //enable_irq
mutex_unlock(&dev->mutex);
if(dev->check_flag==0xff){
ret=copy_to_user(buff,(char *)(dev->buff_data), count);
if(ret<0){
printk("copy to user err\n");
return -EAGAIN;
}else{
printk("copy to user success\n");
}
return 0;
}
return count;
}
static int DHT11_open(struct inode *inode, struct file *filp){
struct DHT11_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct DHT11_dev, cdev);
filp->private_data = dev; /* for other methods */
printk(KERN_EMERG "DHT11 open\n");
return 0; /* success */
}
static int DHT11_release(struct inode *inode, struct file *filp){
printk(KERN_EMERG "DHT11 release\n");
return 0;
}
static long DHT11_ioctl( struct file *files, unsigned int cmd, unsigned long arg){
printk("cmd is %u,arg is %lu\n",cmd,arg);
return 0;
}
/*注册设备节点文件结构体*/
static struct file_operations DHT11_ops = {
.owner = THIS_MODULE,
.open = DHT11_open,
.read =DHT11_Read,
.unlocked_ioctl = DHT11_ioctl,
.release = DHT11_release,
};
static int DHT11_probe(struct platform_device *pdv){
int ret,i;
printk(KERN_EMERG "\tinitialized\n");
for(i = 0; i< DHT11_NUM;i++){
ret = gpio_request(DHT11_gpios[i],"GPIO");
if(ret){
printk("%s: request GPIO %d for DHT11 failed, ret = %d\n", DRIVER_NAME,
i, ret);
}
else{
printk(" request DHT11'S GPIO success\n");
}
}
return 0;
}
static int DHT11_remove(struct platform_device *pdv){
int i;
printk(KERN_EMERG "\tremove\n");
for(i=0;icdev, &DHT11_ops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &DHT11_ops;
err = cdev_add (&dev->cdev, devno, 1);
/*creat dev node */
device_create(myclass, NULL, devno, NULL, DEVICE_NAME"%d",index);
}
/*init the DHT11_driver*/
static int DHT11_linux_driver_init(void)
{
int result,i;
dev_t dev = 0;
printk(KERN_EMERG "DHT11's driver success enter!\n");
/*
* Get a range of minor numbers to work with, asking for a dynamic
* major unless directed otherwise at load time.
*/
if (DHT11_major) {
dev = MKDEV(DHT11_major, DHT11_minor);
result = register_chrdev_region(dev, DHT11_NUM, "DHT11");
} else {
result = alloc_chrdev_region(&dev, DHT11_minor, DHT11_NUM,
"DHT11");
DHT11_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "DHT11: can't get major %d\n", DHT11_major);
return result;
}
dht11_devices = kmalloc(DHT11_NUM * sizeof(struct DHT11_dev), GFP_KERNEL);
if (!dht11_devices) {
result = -ENOMEM;
goto fail; /* Make this more graceful */
}
memset(dht11_devices, 0, DHT11_NUM * sizeof(struct DHT11_dev));
myclass = class_create(THIS_MODULE, "DHT11_DRIVER");
printk(KERN_EMERG "tDriverState is %d\n",platform_driver_register(&DHT11_linux_driver));//打印出返回值
/* Initialize each device. */
for (i = 0; i < DHT11_NUM; i++) {
dht11_devices[i].chose = i;
mutex_init(&dht11_devices[i].mutex);
dht11_setup_cdev(&dht11_devices[i],i);
}
return 0;/* succeed */
fail:
DHT11_linux_driver_exit();
return result;
}
module_init(DHT11_linux_driver_init);
module_exit(DHT11_linux_driver_exit);