设备树规定GPIO
在设备树规定使用的IO口
/*DT11 gpio4 23*/
/{
dht11 {
compatible = "imx6ull,dht11";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_dht11>;
dht11-gpios = <&gpio4 23 GPIO_ACTIVE_HIGH>; // 新添加的 GPIO,用于 DHT11 数据
status = "okay";
};
}
&iomuxc{
pinctrl_dht11: dht11grp {
fsl,pins = <
MX6UL_PAD_CSI_DATA02__GPIO4_IO23 0x10B0
>;
};
}
驱动程序
dth11协议
判断dht11的响应信号(低脉冲持续 80us,高脉冲持续 80us)
static int dht11_wait_for_ready(void)
{
int timeout_us = 20000;
// 检查 GPIO 描述符是否有效
if (!is_dht11_gpio_valid()) {
printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_wait_for_ready (start)\n");
return -EFAULT;
}
// 等待低电平(主机开始信号)
while (gpiod_get_value(dht11_gpio) && --timeout_us)
{
udelay(1);
}
if (!timeout_us)
{
printk("%s %s line %d timeout waiting for low\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
// 等待高电平(DHT11响应信号开始)
timeout_us = 80; // 高电平持续时间为 80us
while (!gpiod_get_value(dht11_gpio) && --timeout_us)
{
udelay(1);
}
if (!timeout_us)
{
printk("%s %s line %d timeout waiting for high\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
// 等待低电平(DHT11响应信号结束)
timeout_us = 80; // 低电平持续时间为 80us
while (gpiod_get_value(dht11_gpio) && --timeout_us)
{
udelay(1);
}
if (!timeout_us)
{
printk("%s %s line %d timeout waiting for low after high\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
return 0;
}
判断dht11接受到的字节是0或1
/* 读取 DHT11 一个字节的数据 */
static int dht11_read_byte(unsigned char *buf)
{
int i;
int us = 0;
unsigned char data = 0;
// 检查 GPIO 描述符是否有效
if (!is_dht11_gpio_valid()) {
printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_read_byte (start)\n");
return -EFAULT;
}
for (i = 0; i < 8; i++)
{
int timeout_us = 400; // 重置超时
us = 0;
// 等待高电平
while (!gpiod_get_value(dht11_gpio) && --timeout_us)
{
udelay(1);
us++;
}
if (!timeout_us)
{
printk("%s %s line %d timeout waiting for high in bit %d\n", __FILE__, __FUNCTION__, __LINE__, i);
return -1;
}
udelay(40); // 高电平持续时间,用于判断位是 0 还是 1
// 检查 GPIO 值,判断是 0 还是 1
if (gpiod_get_value(dht11_gpio))
{
data = (data << 1) | 1; // 获取 bit 1
timeout_us = 400; // 重置超时
us = 0;
while (gpiod_get_value(dht11_gpio) && --timeout_us)
{
udelay(1);
us++;
}
if (!timeout_us)
{
printk("%s %s line %d timeout waiting for low after bit 1 in bit %d\n", __FILE__, __FUNCTION__, __LINE__, i);
return -1;
}
}
else
{
data = (data << 1) | 0; // 获取 bit 0
}
}
*buf = data; // 返回读取的字节数据
return 0;
}
与用户空间交互
严格执行协议的a到e步骤
static ssize_t dht11_read(struct file *file, char __user *buf, size_t size, loff_t *offset){
int i;
int re;
unsigned char data[5];
unsigned long flags;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
// 检查读取的数据大小
if(size != 4){
printk(KERN_ERR "DHT11: Read size must be 4 bytes, got %zu\n", size);
return -EINVAL;
}
// 检查 dht11_gpio 是否有效
if (!is_dht11_gpio_valid()) {
printk(KERN_ERR "DHT11: dht11_gpio invalid in dht11_read (top)\n");
return -EFAULT;
}
local_irq_save(flags); // 关中断,保护时序敏感操作
// 设置 GPIO 引脚方向为输出并拉高
gpiod_direction_output(dht11_gpio, 1);
mdelay(30); // 等待传感器准备
gpiod_set_value(dht11_gpio, 0); // 拉低总线,发送启动信号
mdelay(20); // 延时 20ms
gpiod_set_value(dht11_gpio, 1); // 拉高总线
udelay(40); // 延时 40us
gpiod_direction_input(dht11_gpio); // 切换为输入模式
udelay(2); // 稍作延时
if (dht11_wait_for_ready()) {
local_irq_restore(flags); // 恢复中断
printk("%s %s line %d dht11_wait_for_ready failed\n", __FILE__, __FUNCTION__, __LINE__);
return -EAGAIN;
}
// 读取 5 字节数据
for (i = 0; i < 5; i++)
{
if (dht11_read_byte(&data[i])) {
local_irq_restore(flags); // 恢复中断
printk("%s %s line %d dht11_read_byte failed for byte %d\n", __FILE__, __FUNCTION__, __LINE__, i);
return -EAGAIN;
}
}
// 校验码验证
if (data[4] != (data[0] + data[1] + data[2] + data[3])) {
printk(KERN_WARNING "DHT11: Checksum mismatch! Data: %d %d %d %d, Checksum: %d, Expected: %d\n",
data[0], data[1], data[2], data[3], data[4], (data[0] + data[1] + data[2] + data[3]));
}
printk("data %d %d\n", data[0], data[2]); // 打印湿度和温度数据
// 将数据拷贝到用户空间
re = copy_to_user(buf, data, 4);
if (re != 0) {
printk(KERN_ERR "DHT11: Failed to copy data to user space: %d\n", re);
return -EFAULT;
}
return 4; // 返回读取的字节数
}
probe函数获取硬件资源
/* 5.1 实现 probe 函数 */
static int dht11_probe(struct platform_device *dev)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 获取硬件资源 */
dht11_gpio = gpiod_get(&dev->dev, "dht11", GPIOD_OUT_HIGH);
if (IS_ERR(dht11_gpio)) {
int err = PTR_ERR(dht11_gpio);
dev_err(&dev->dev, "Failed to get dht11 GPIO: %d\n", err);
return err;
}
dev_info(&dev->dev, "Successfully got dht11 GPIO\n");
/* 创建设备节点,用户空间可以访问 */
device_create(dht11_class, NULL, MKDEV(major, 0), NULL, "querydht11");
return 0;
}
入口函数与出口函数
注册字符设备、创建类、注册平台驱动
注销字符设备、注销类、注销平台驱动
应用函数
open打开设备节点,然后再用read读传感器的信息
#include "dht11.h"
#include <QFile>
temper::temper()
{
}
temper::~temper()
{
}
void temper::run()
{
QFile file;
QString fileName = "/dev/querydht11";
file.setFileName(fileName);
file.open(QIODevice::ReadWrite | QIODevice::Unbuffered); //非缓冲读取
while(!stop){
file.read(reinterpret_cast<char*>(databuf), sizeof(databuf));
if(databuf[0] > 0 && databuf[0] < 100 && databuf[1] > 0 && databuf[1]){ //判断数据是否正确
for(int i = 0; i < 100; i++){
if(!stop){
msleep(100); //每10s读一次数据
}
}
}else{ //数据不对重读
msleep(1000);
continue;
}
}
file.close();
}
void temper::getData(int delay)
{
//获取一次数据后退出
while(1){
QFile file;
QString fileName = "/dev/dth11";
file.setFileName(fileName);
file.open(QIODevice::ReadWrite | QIODevice::Unbuffered);
file.read(reinterpret_cast<char*>(databuf), sizeof(databuf));
file.close();
if(databuf[0] > 0 && databuf[0] < 100 && databuf[1] > 0 && databuf[1]){
break;
}
msleep(delay);
}
}
void temper::temperStop()
{
stop = 1;
}
QT中
启动线程,并且设计定时器定时更新界面
// 启动温湿度读取线程
myTemper = new temper(); // 创建温湿度线程对象
myTemper->getData(500); // 启动线程进行温湿度数据获取
myTemper->start(); // 启动线程
ui->lineEdit->setText("湿度:" + QString::number(myTemper->databuf[0]) + "%"); // 显示湿度
ui->lineEdit_2->setText("温度:" + QString::number(myTemper->databuf[1]) + "℃"); // 显示温度
// 每60秒更新一次温湿度数据
if(timerNums > 600){ // 60s测一次
myTimer->stop(); // 停止定时器
myTemper->getData(500); // 获取温湿度数据
if(myTemper->databuf[0] > 0 && myTemper->databuf[0] < 100 && myTemper->databuf[1] > 0 && myTemper->databuf[1]){
ui->lineEdit->setText("湿度:" + QString::number(myTemper->databuf[0]) + "%"); // 更新湿度显示
ui->lineEdit_2->setText("温度:" + QString::number(myTemper->databuf[1]) + "℃"); // 更新温度显示
}
timerNums = 0; // 重置计时器计数
myTimer->start(); // 重启定时器