dth12的工作模式除了单总线外,还可以使用i2c接口的工作模式.
dht12的引脚说明
//
上图说明dht12芯片内部有5个寄存器,寄存器地址从0x00 ~ 0x04, 分别存放湿度数据,温度数据,校验和
同时需要特别注意:上面说dht12的设备地址为0xB8, 此地址已是包含读写位了,通常设备址地址只是7位的. 所以dht12的i2c设备地址为0x5c.
///
绿色的点为开始信号,红色的点为停止信号.
时序说明: 首先控制器发出开始信号, 接着发出0xB8(设备地址和读写位的写模式组成), 收到从机的应答信号后,再发出数据0(表示从地址为0x00的寄存器开始读),收到应答信号后,再接着重新发出开始信号, 应答后,再发出0xB9(设备地址和读组成), 收到应答后,接收从机发回的5上字节数据.最后发出停止信号.
//
在linux内核里声明dht12设备, 设备名为”mydht12”, 设备地址为0x5c.
dht12的简单设备驱动:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
{
struct i2c_msg msg[2];
char addr = 0, data[5], i;
//按dht12的读时序要求, 只需调用i2c_transfer函数一次, 和发出两条消息.
printk("in myprobe ...%s, %x\n", id->name, id->driver_data);
msg[0].addr = cli->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = &addr; //指定从地址为0x00的寄存器开始读
msg[1].addr = cli->addr;
msg[1].flags = I2C_M_RD; //读
msg[1].len = 5; //共接收5字节数据
msg[1].buf = data;
if (i2c_transfer(cli->adapter, msg, 2) < 0)
return -ENODEV;
for (i = 0; i < 5; i++)
printk("%d ", data[i]);
printk("\n");
return 0;
}
int myremove(struct i2c_client *cli)
{
printk("in myremove ...\n");
return 0;
}
struct i2c_device_id ids[] = {
{"mydht12", 0x22},
{},
};
struct i2c_driver mydrv = {
.probe = myprobe,
.remove = myremove,
.driver = {
.name = "mydrv",
.owner = THIS_MODULE,
},
.id_table = ids,
};
module_i2c_driver(mydrv);
MODULE_LICENSE("GPL");
/
加入字符设备接口的设备驱动代码:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/device.h>
#define MYMA 1314
#define COUNT 1
typedef struct {
struct mutex mutex;
struct cdev cdev;
struct i2c_client *cli;
struct class *cls;
}dht12_data_t;
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
struct cdev *cdev = fl->f_path.dentry->d_inode->i_cdev;
dht12_data_t *data = container_of(cdev, dht12_data_t, cdev);
int ret, sum, i;
struct i2c_msg msgs[2];
char data_rcv[5], addr = 0;
struct i2c_client *cli = data->cli;
//上锁,防止重入
mutex_lock(&data->mutex);
msgs[0].addr = cli->addr;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = &addr;
msgs[1].addr = cli->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = 5;
msgs[1].buf = data_rcv;
ret = i2c_transfer(cli->adapter, msgs, 2);
if (ret != 2)
goto out;
//检查校验和
sum = 0;
for (i = 0; i < 4; i++)
sum += data_rcv[i];
if (sum != data_rcv[4])
{
ret = -EINVAL;
goto out;
}
sprintf(buf, "humi: %d.%d ; temp: %d.%d\n", data_rcv[0], data_rcv[1], data_rcv[2], data_rcv[3]);
ret = strlen(buf);
out:
mutex_unlock(&data->mutex);
return ret;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.read = myread,
};
int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
{
static int mi = 0; //记录次设备号
dht12_data_t *data;
dev_t devid;
int ret;
//注册设备号
devid = MKDEV(MYMA, mi);
ret = register_chrdev_region(devid, COUNT, cli->name);
if (ret < 0)
goto err0;
//准备每个匹配设备的数据
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (NULL == data)
{
ret = -ENOMEM;
goto err1;
}
//字符设备初始化
cdev_init(&data->cdev, &fops);
data->cdev.owner = THIS_MODULE;
ret = cdev_add(&data->cdev, devid, COUNT);
if (ret < 0)
goto err2;
//初始化互斥锁
mutex_init(&data->mutex);
//创建设备文件
data->cls = class_create(THIS_MODULE, cli->name);
device_create(data->cls, NULL, devid, NULL, "%s.%d", cli->name, mi++);
data->cli = cli;
i2c_set_clientdata(cli, data); //dev_set_drvdata(&cli->dev, data);
return 0;
err2:
kfree(data);
err1:
unregister_chrdev_region(devid, COUNT);
err0:
return ret;
}
int myremove(struct i2c_client *cli)
{
dht12_data_t *data = i2c_get_clientdata(cli); //dev_get_drvdata(&cli->dev);
//移除设备文件
device_destroy(data->cls, data->cdev.dev);
class_destroy(data->cls);
cdev_del(&data->cdev);
unregister_chrdev_region(data->cdev.dev, COUNT);
kfree(data);
return 0;
}
struct i2c_device_id ids[] = {
{"mykkk", 0x11},
{"mydht12", 0x22},
{},
};
struct i2c_driver mydrv = {
.probe = myprobe,
.remove = myremove,
.driver = {
.name = "mydrv",
.owner = THIS_MODULE,
},
.id_table = ids,
};
module_i2c_driver(mydrv);
MODULE_LICENSE("GPL");