嵌入式学习 day67:DHT11 温湿度传感器和 LM75 温度传感器 一、DHT11 温湿度传感器1. 什么是 DHT11DHT11 是一个温湿度传感器用单根数据线通信单总线协议。测量范围温度 0~50°C湿度 20~90%RH。数据格式一次传输 40 位5 字节。第 1 字节湿度整数。第 2 字节湿度小数DHT11 恒为 0。第 3 字节温度整数。第 4 字节温度小数DHT11 恒为 0。第 5 字节校验和前 4 字节之和。2. 通信协议单总线协议一根线主机和从机分时控制。通信流程text1. 主机拉低 20ms然后拉高 30us → 起始信号 2. 从机拉低 80us然后拉高 80us → 响应信号 3. 从机发送 40 位数据每位以 50us 低电平开始 4. 高电平持续 26~28us 表示 0持续 70us 表示 13. 代码详解1发送起始信号cvoid master_send_dht11_reset(void) { gpio_set_value(dht11gpiono, 0); // 拉低 msleep(20); // 保持 20ms gpio_set_value(dht11gpiono, 1); // 拉高 }主机拉低 20ms再拉高作为起始信号。2接收响应信号cstatic int master_recv_dht11_respone(void) { int timeout 0; gpio_direction_input(dht11gpiono); // 切换为输入 // 等待从机拉低 while (timeout 20 gpio_get_value(dht11gpiono)) { udelay(10); timeout; } if (timeout 20) { return -1; // 超时无响应 } // 等待从机拉低结束 while (!gpio_get_value(dht11gpiono)); // 等待从机拉高结束 while (gpio_get_value(dht11gpiono)); return 0; }等待从机的响应信号拉低 80us再拉高 80us。3接收 40 位数据cstatic void master_recv_dht11_data(unsigned char *pdata, int len) { int i 0; int n 0; int cnt 0; for (i 0; i len; i) { for (n 7; n 0; n--) { cnt 0; while (!gpio_get_value(dht11gpiono)); // 等待低电平结束 while (gpio_get_value(dht11gpiono)) { // 统计高电平时间 udelay(10); cnt; } if (cnt 5) { pdata[i] | (0x1 n); // 高电平长是 1 } } } gpio_direction_output(dht11gpiono, 1); // 切回输出 }每位数据以低电平开始高电平持续时间决定是 0 还是 1。高电平短26~28us→ 0。高电平长70us→ 1。4校验数据cstatic int master_check_dht11_data(unsigned char *pdata, int len) { if (pdata[4] pdata[0] pdata[1] pdata[2] pdata[3]) return 0; else return -2; }第 5 字节 前 4 字节之和校验数据是否正确。5dht11_read核心cstatic ssize_t dht11_read(struct file *fp, char __user *puser, size_t n, loff_t *off) { int ret 0; unsigned char data[5] {0}; unsigned long nret 0; unsigned long flags 0; master_send_dht11_reset(); // 1. 发送起始信号 spin_lock_irqsave(lock, flags); // 2. 加锁关中断 ret master_recv_dht11_respone(); // 3. 接收响应 if (-1 ret) return ret; master_recv_dht11_data(data, 5); // 4. 接收 40 位数据 spin_unlock_irqrestore(lock, flags); // 5. 解锁 ret master_check_dht11_data(data, 5); // 6. 校验 if (-2 ret) return ret; nret copy_to_user(puser, data, sizeof(unsigned char) * 4); // 7. 拷贝到用户空间 if (nret) { pr_info(copy_to_user failed\n); return -3; } return (sizeof(unsigned char) * 4); }4. 为什么用自旋锁DHT11 的单总线协议对时序要求很严格微秒级。如果传输过程中被中断打断时序就乱了。用spin_lock_irqsave关中断保证传输过程中不被打断。cspin_lock_irqsave(lock, flags); // 保存中断状态关中断 // ... 时序敏感的代码 ... spin_unlock_irqrestore(lock, flags); // 恢复中断状态函数作用spin_lock_irqsave关中断 加锁spin_unlock_irqrestore解锁 恢复中断5. 设备树dtsdht11 { compatible pute,putedht11; pinctrl-names default; pinctrl-0 pinctrl_dht11; gpio-dht11 gpio1 2 1; status okay; };6. 应用程序cint fd open(/dev/dht11_misc, O_RDWR); unsigned char data[4] {0}; read(fd, data, sizeof(data)); printf(湿度: %d.%d%%\n, data[0], data[1]); printf(温度: %d.%d°C\n, data[2], data[3]);读 4 字节前 2 字节湿度后 2 字节温度。二、LM75 温度传感器1. 什么是 LM75LM75 是一个I2C 接口的温度传感器。接口I2C两线SCL、SDA。从机地址0x48~0x4F可配置。温度寄存器地址 0x002 字节。高 8 位整数部分。低 8 位小数部分每 0.5°C 一个单位。2. I2C 通信流程LM75 的读流程写主机发送寄存器地址0x00表示温度寄存器。读主机读 2 字节数据。3. 代码详解1核心结构体i2c_msgcstruct i2c_msg { __u16 addr; // 从机地址 __u16 flags; // 标志I2C_M_RD 读 __u16 len; // 数据长度 __u8 *buf; // 数据缓冲区 };2lm75_read核心cstatic ssize_t lm75_read(struct file *fp, char __user *puser, size_t n, loff_t *off) { struct i2c_msg sendmsg; struct i2c_msg recvmsg; int ret 0; unsigned char tmpbuff[2] {0}; unsigned long nret 0; memset(sendmsg, 0, sizeof(sendmsg)); memset(recvmsg, 0, sizeof(recvmsg)); // 1. 发送寄存器地址0x00 温度寄存器 sendmsg.addr lm75_client-addr; tmpbuff[0] 0x00; sendmsg.buf tmpbuff; sendmsg.len 1; ret lm75_client-adapter-algo-master_xfer(lm75_client-adapter, sendmsg, 1); if (ret ! 1) { pr_info(master_xfer sendmsg failed ret %d\n, ret); return -1; } // 2. 读 2 字节温度数据 recvmsg.addr lm75_client-addr; recvmsg.flags | I2C_M_RD; // 读标志 recvmsg.buf tmpbuff; recvmsg.len 2; ret lm75_client-adapter-algo-master_xfer(lm75_client-adapter, recvmsg, 1); if (ret ! 1) { pr_info(master_xfer recvmsg failed ret %d\n, ret); return -1; } // 3. 拷贝到用户空间 nret copy_to_user(puser, tmpbuff, sizeof(tmpbuff)); if (nret) { pr_info(copy_to_user failed\n); return -1; } return sizeof(tmpbuff); }通信流程写发送0x00告诉 LM75 要读温度寄存器。读读 2 字节温度数据。3master_xferclm75_client-adapter-algo-master_xfer(lm75_client-adapter, sendmsg, 1);参数含义adapterI2C 控制器sendmsg消息数组1消息数量这是最底层的 I2C 传输函数通常用i2c_transfer更规范。4. i2c_driver 结构体cstruct i2c_driver lm75_drv { .probe lm75_probe, .remove lm75_remove, .driver { .name lm75_drv, .owner THIS_MODULE, .of_match_table lm75_of_match_tables, }, .id_table lm75_id_tables, };和 platform 驱动类似只是总线类型不同。5. 注册和注销cstatic int __init lm75_drv_init(void) { return i2c_add_driver(lm75_drv); // 注册 I2C 驱动 } static void __exit lm75_drv_exit(void) { i2c_del_driver(lm75_drv); // 注销 I2C 驱动 }6. 设备树dtsi2c1 { status okay; lm7548 { compatible pute,putelm75; reg 0x48; // I2C 从机地址 }; };7. 应用程序cint fd open(/dev/lm75_misc, O_RDWR); unsigned char temp[2] {0}; read(fd, temp, sizeof(temp)); // 温度计算 int raw (temp[0] 8) | temp[1]; int temp_int raw 8; // 整数部分 int temp_frac (raw 0xFF) 7; // 小数部分0.5 度 printf(Temperature: %d.%d C\n, temp_int, temp_frac * 5);三、DHT11 和 LM75 对比项目DHT11LM75接口单总线1 根线I2C2 根线协议单总线协议I2C 协议数据格式40 位5 字节2 字节测量内容温度 湿度温度通信速率慢毫秒级快微秒级驱动框架platformi2c时序要求严格微秒级标准 I2C同步机制自旋锁关中断I2C 控制器自动处理四、总结DHT11要点说明接口单总线通信主机起始 → 从机响应 → 40 位数据数据湿度整数、湿度小数、温度整数、温度小数、校验和关键时序敏感用自旋锁关中断保护框架platformLM75要点说明接口I2C通信写寄存器地址 → 读数据数据2 字节高 8 位整数低 8 位小数关键用i2c_msg和master_xfer传输框架i2cDHT11 是单总线协议对时序要求严格需要关中断保护。LM75 是 I2C 协议标准总线控制器自动处理时序。两者的驱动框架不同但都通过 misc 设备向用户空间提供接口。