STM32开发——传感器
·
STM32开发——传感器
一、DHT11温湿度传感器
1.基本概念
传感器参数
| 参数 | 范围/精度 |
|---|---|
| 温度测量范围 | 0°C ~ 50°C |
| 湿度测量范围 | 20%RH ~ 90%RH |
| 温度精度 | ±2°C |
| 湿度精度 | ±5%RH |
| 通信接口 | 单总线(GPIO) |
DHT11电路特性
-
线与特性:多个设备可以共享同一总线,通过开漏输出实现
-
总线状态真值表:
| DHT11状态 | STM32状态 | 总线实际电平 |
|---|---|---|
| 1(释放) | 0(拉低) | 0 |
| 0(拉低) | 1(释放) | 0 |
| 1(释放) | 1(释放) | 1(上拉) |
DHT11通信时序
(1) 起始和应答信号
起始信号(主机发送):
总线默认高电平(上拉)
↓
主机拉低总线至少18ms
↓
主机拉高总线20-40us(释放总线)
↓
代表主机发送了一个起始信号
应答信号(DHT11回复):
DHT11检测到起始信号后
↓
DHT11拉低总线80us左右
↓
DHT11拉高总线80us左右(释放总线)
↓
代表回复了一个应答信号
(2) 数据传输
发送bit '0':
DHT11拉低总线50us
↓
DHT11拉高总线26-28us
↓
代表发送了一个bit '0'
发送bit '1':
DHT11拉低总线50us
↓
DHT11拉高总线70us左右
↓
代表发送了一个bit '1'
(3) 结束信号
-
数据传输完毕后,主机将总线拉为高电平
DHT11数据格式
总共40bit,5个字节: 字节0:湿度整数部分(8bit) 字节1:湿度小数部分(8bit,通常为0) 字节2:温度整数部分(8bit) 字节3:温度小数部分(8bit,通常为0) 字节4:校验和(8bit)= 字节0+字节1+字节2+字节3
2.具体编译过程
CubeMX配置引脚
-
将连接DHT11的GPIO引脚配置为开漏输出模式
-
使能上拉电阻
-
在代码中需要切换输入/输出模式
keil实现dht11.c
设置为输出模式
void set_gpio_output(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_10; // PB10引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
return;
}
参数说明:
-
GPIO_PIN_10:使用PB10引脚 -
GPIO_MODE_OUTPUT_PP:推挽输出模式(能输出高电平和低电平) -
GPIO_NOPULL:无上下拉电阻 -
GPIO_SPEED_FREQ_LOW:低速模式(降低功耗和噪声)
设置为输入模式
void set_gpio_input(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
return;
}
DHT11数据读取函数
int read_dht11(char *phum, char *ptemp)
{
int timeout = 0;
int i = 0;
int n = 0;
int cnt = 0;
unsigned char data[5] = {0};
// 1. 开启TIM3定时器开始计数
HAL_TIM_Base_Start(&htim3);
// 2. 等待传感器工作稳定
HAL_Delay(100);
// 3. 发送起始信号:拉低至少18ms
set_gpio_output(); // 设置为输出模式
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, 0); // 拉低
HAL_Delay(25); // 保持低电平25ms
// 4. 拉高总线,准备接收响应
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, 1); // 拉高
set_gpio_input(); // 切换为输入模式
// 5. 等待DHT11拉低总线(响应信号开始)
timeout = 0;
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10) && timeout < 5)
{
HAL_Delay(10); // 延时10us
timeout++;
}
// 6. 超时判断
if (timeout >= 5)
{
return -1; // -1代表DHT11没有应答
}
// 7. 等待高电平到来(数据开始)
while (!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10));
// 8. 循环接收5个字节数据(40位)
for (i = 0; i < 5; i++)
{
data[i] = 0;
for (n = 7; n >= 0; n--) // 从高位到低位接收
{
cnt = 0;
// 等待高电平到来(数据位开始)
while (!HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10));
// 测量高电平持续时间
while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_10))
{
HAL_Delayus(10); // 每10us计数一次
cnt++;
}
// 判断数据位:高电平时间大于50us代表bit'1'
if (cnt > 5) // 5*10us = 50us
{
data[i] |= (0x1 << n); // 对应位置1
}
// 否则bit'0'(data[i]该位保持0)
}
}
// 9. 校验数据
if (data[0] + data[1] + data[2] + data[3] != data[4])
{
return -2; // -2代表数据校验出错
}
// 10. GPIO设置为输出,拉高总线(结束)
set_gpio_output();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, 1);
// 11. 格式化温湿度数据
sprintf(phum, "%d.%d\n", data[0], data[1]); // 湿度
sprintf(ptemp, "%d.%d\n", data[2], data[3]); // 温度
return 0; // 读取成功
}
keil实现主函数
int main(void)
{
/* 初始化HAL库 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化外设 */
MX_GPIO_Init(); // GPIO初始化
MX_TIM3_Init(); // 定时器3初始化(用于微秒延时)
MX_UART1_UART_Init(); // 串口1初始化
char hum[32] = {0}; // 湿度字符串缓冲区
char temp[32] = {0}; // 温度字符串缓冲区
char s[32] = {0}; // 状态字符串缓冲区
int ret = 0;
while (1)
{
// 读取DHT11数据
ret = read_dht11(hum, temp);
// 延时2秒(DHT11读取间隔至少1秒)
HAL_Delay(2000);
// 通过串口发送状态信息
sprintf(s, "ret = %d\r\n", ret);
HAL_UART_Transmit(&huart1, (uint8_t *)s, strlen(s), 1000);
// 发送湿度数据
HAL_UART_Transmit(&huart1, (uint8_t *)hum, strlen(hum), 1000);
// 发送温度数据
HAL_UART_Transmit(&huart1, (uint8_t *)temp, strlen(temp), 1000);
}
}
3. 代码执行流程
(1) 发送起始信号
set_gpio_output() → 设置为输出模式 HAL_GPIO_WritePin(,0) → 拉低总线 HAL_Delay(25) → 保持低电平25ms(>18ms) HAL_GPIO_WritePin(,1) → 拉高总线 set_gpio_input() → 切换为输入,准备接收
(2) 等待DHT11响应
timeout计数等待拉低
↓
超时(timeout>=5) → 返回-1(无应答)
↓
等待拉高(响应结束)
(3) 接收数据位原理
每个数据位开始:等待高电平
↓
测量高电平持续时间(每10us计数)
↓
cnt > 5(>50us)→ 判定为bit'1'
cnt ≤ 5(≤50us)→ 判定为bit'0'
↓
数据位存入对应字节的对应位
(4) 数据校验
data[0] + data[1] + data[2] + data[3] == data[4] → 数据正确 否则返回-2(校验错误)
4. 超时和错误码说明
| 返回值 | 含义 |
|---|---|
| 0 | 读取成功 |
| -1 | DHT11无应答(未检测到拉低信号) |
| -2 | 数据校验失败 |
5. 逻辑分析仪使用
-
作用:观察DHT11通信时序,验证时序是否正确
-
观察点:起始信号长度、应答信号、数据位0和1的波形差异
二、LM75温度传感器
1.具体概念
I2C时序基础
(1) 起始信号
当SCL为高电平时
↓
SDA由高电平变为低电平
↓
代表主机发送了一个起始信号
(2) 从机地址 + 读写位
主机发送:7位从机地址 + 读写位
读写位:0表示写,1表示读
↓
从机应答:ACK(低电平)或 NACK(高电平)
(3) 数据传输规则
| SCL状态 | SDA操作 | 说明 |
|---|---|---|
| SCL高电平 | SDA要保持稳定 | 主机采样数据 |
| SCL低电平 | 从机可以进行数据传输 | SDA可以变化 |
(4) 结束信号
当SCL为高电平时
↓
SDA由低电平变为高电平
↓
代表发送了一个结束信号
I2C写时序(主机向从机写入数据)
1. 发送起始信号 2. 发送7位从机地址 + 写命令(0) 3. 等待从机应答(ACK) 4. 主机发送数据 5. 等待从机应答(ACK) 6. 发送结束信号
I2C读时序(主机从从机读取数据)
1. 发送起始信号 2. 发送7位从机地址 + 写命令(0) 3. 等待从机应答(ACK) 4. 发送寄存器地址 5. 等待从机应答(ACK) 6. 发送起始信号(重新起始) 7. 发送7位从机地址 + 读命令(1) 8. 等待从机应答(ACK) 9. 接收数据并发送应答(ACK) 10. 发送结束信号
LM75从机地址
-
默认地址:0x48
-
地址组成:7位地址(A2、A1、A0默认为0)
-
HAL库使用:需要将7位地址左移1位(变成8位地址)
LM75温度寄存器
-
寄存器地址:0x00
-
数据格式:
-
总共16bit数据
-
先发高位,再发低位
-
需要舍弃掉低7bit(右移7位)
-
分辨率:0.5°C
-
2.具体编译过程
CubeMX配置
-
配置I2C引脚(SCL、SDA)
-
使能I2C外设
-
设置I2C速度模式(标准模式100KHz或快速模式400KHz)
主函数实现
int main(void)
{
/* 初始化HAL库 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化外设 */
MX_GPIO_Init(); // GPIO初始化
MX_I2C1_Init(); // I2C1初始化(连接LM75)
MX_USART1_UART_Init(); // 串口1初始化
uint8_t tmpbuff[2] = {0}; // 存放读取到的2字节温度数据
float temp = 0; // 计算后的温度值
char send_buff[32] = {0}; // 发送缓冲区
while (1)
{
// 1. 读取LM75温度寄存器
HAL_I2C_Mem_Read(
&hi2c1, // I2C控制器
(0x48 << 1), // LM75地址(7位0x48左移1位)
0x00, // 温度寄存器地址
1, // 寄存器地址长度(1字节)
(uint8_t *)tmpbuff, // 存放读取数据的缓冲区
sizeof(tmpbuff), // 读取2字节
1000 // 超时1000ms
);
// 2. 温度数据转换
// tmpbuff[0]是高8位,tmpbuff[1]是低8位
// 合并后右移7位,舍弃低7位,再乘以0.5
temp = ((tmpbuff[0] << 8 | tmpbuff[1]) >> 7) * 0.5;
// 3. 格式化温度数据
sprintf(send_buff, "lm75_temp = %.2f\r\n", temp);
// 4. 通过串口发送
HAL_UART_Transmit(&huart1, (uint8_t *)send_buff, strlen(send_buff), 1000);
// 5. 延时1秒
HAL_Delay(1000);
}
}
I2C读取函数详解
HAL_I2C_Mem_Read(
&hi2c1, // I2C1外设
(0x48 << 1), // 地址:0x48左移1位 = 0x90(HAL库需要8位地址)
0x00, // 寄存器地址:LM75温度寄存器
1, // 寄存器地址长度:1字节
(uint8_t *)tmpbuff, // 数据缓冲区指针
2, // 要读取的字节数:2字节
1000 // 超时时间:1000ms
);
温度数据转换
// 原始数据:tmpbuff[0] = 0x1A, tmpbuff[1] = 0xC0 // 二进制: 00011010 11000000 // 1. 合并2字节 uint16_t raw = tmpbuff[0] << 8 | tmpbuff[1]; // 0x1A << 8 = 0x1A00 // 0x1A00 | 0xC0 = 0x1AC0 // 二进制:00011010 11000000 // 2. 右移7位(舍弃低7位) uint16_t value = raw >> 7; // 0x1AC0 >> 7 = 0x0358 // 二进制:00000011 01011000 // 十进制:856 // 3. 乘以分辨率0.5 float temp = value * 0.5; // 856 * 0.5 = 428.0°C?(实际温度应在此范围内) // 注意:LM75实际温度范围-55°C~125°C,这里示例数据需要根据实际调整
3. LM75数据格式说明
16位温度数据寄存器(地址0x00):
D15 D14 D13 D12 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
温度数据(11位有效) 未使用(5位)
↓
右移7位后得到11位温度数据
↓
乘以0.5得到实际温度
三、两个传感器代码对比
1. 通信方式对比
| 传感器 | 通信方式 | 代码特点 |
|---|---|---|
| DHT11 | GPIO单总线 | 需要精确时序,微秒级延时,手动切换输入输出 |
| LM75 | I2C总线 | 使用HAL库函数,时序由硬件处理 |
2. 数据处理对比
| 传感器 | 数据读取 | 数据转换 | |
|---|---|---|---|
| DHT11 | 5字节原始数据 | data[0]=湿度整数,data[1]=湿度小数,data[2]=温度整数,data[3]=温度小数 | |
| LM75 | 2字节原始数据 | ((high<<8 | low) >> 7) * 0.5 |
3. 错误处理对比
| 传感器 | 错误码 | 含义 |
|---|---|---|
| DHT11 | -1 | 无应答 |
| -2 | 校验错误 | |
| LM75 | HAL_OK=0 | 成功 |
| HAL_TIMEOUT | 超时 |
四、关键函数总结
DHT11关键函数
| 函数 | 作用 |
|---|---|
set_gpio_output() |
设置PB10为输出模式 |
set_gpio_input() |
设置PB10为输入模式 |
HAL_Delayus() |
微秒级延时(基于TIM3) |
read_dht11() |
读取温湿度数据 |
LM75关键函数
| 函数 | 作用 |
|---|---|
HAL_I2C_Mem_Read() |
读取I2C设备寄存器 |
HAL_UART_Transmit() |
串口发送数据 |
共用函数
| 函数 | 作用 |
|---|---|
HAL_Delay() |
毫秒级延时 |
sprintf() |
格式化字符串 |
总结:
-
DHT11驱动:需要精确时序控制,通过测量高电平持续时间判断0/1
-
LM75驱动:使用HAL库I2C函数,简化通信过程
-
数据校验:DHT11有校验和验证,LM75无校验
-
错误处理:DHT11返回自定义错误码,LM75返回HAL库状态
-
数据格式:DHT11返回整数和小数,LM75需计算得到浮点数
更多推荐



所有评论(0)