Keil5+STM32F103C8T6:DHT11温湿度检测系统与OLED显示代码全解析及注意事项
以下是一个基于Keil5,使用STM32F103C8T6实现DHT11温湿度检测并在OLED屏幕显示的代码示例。 代码思路 1. DHT11驱动:实现读取DHT11传感器的温湿度数据。 2. OLED驱动:实现OLED屏幕的初始化和数据显示。 3. 主函数:循环读取DHT11数据并显示在OLED屏幕上。 代码示例 ```c #include "stm32f10x.h" #include "dht11.h" #include "oled.h" // 毫秒延时函数 void delay_ms(uint32_t ms) { uint32_t i; SysTick->LOAD = 72000 - 1; SysTick->VAL = 0; SysTick->CTRL = 0x05; for (i = 0; i < ms; i++) { while ((SysTick->CTRL & 0x10000) == 0); } SysTick->CTRL = 0; } int main(void) { uint8_t humidity, temperature; // 初始化OLED OLED_Init(); OLED_Clear(); // 初始化DHT11 DHT11_Init(); while (1) { // 读取DHT11数据 if (DHT11_Read_Data(&humidity, &temperature) == SUCCESS) { // 清屏 OLED_Clear(); // 显示湿度 OLED_ShowString(0, 0, "Humidity:"); OLED_ShowNum(80, 0, humidity, 2, 16); OLED_ShowChar(100, 0, '%', 16); // 显示温度 OLED_ShowString(0, 2, "Temperature:"); OLED_ShowNum(80, 2, temperature, 2, 16); OLED_ShowChar(100, 2, 'C', 16); } // 延时2秒 delay_ms(2000); } } ``` DHT11驱动代码(dht11.h) ```c #ifndef __DHT11_H #define __DHT11_H #include "stm32f10x.h" #define DHT11_GPIO_PORT GPIOA #define DHT11_GPIO_PIN GPIO_Pin_0 #define DHT11_RCC_GPIO_CLK RCC_APB2Periph_GPIOA #define SUCCESS 1 #define ERROR 0 void DHT11_Init(void); uint8_t DHT11_Read_Data(uint8_t *humidity, uint8_t *temperature); #endif ``` DHT11驱动代码(dht11.c) ```c #include "dht11.h" #include "delay.h" // 配置DHT11引脚为输出模式 void DHT11_GPIO_Output(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(DHT11_RCC_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure); } // 配置DHT11引脚为输入模式 void DHT11_GPIO_Input(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(DHT11_RCC_GPIO_CLK, ENABLE); GPIO_InitStructure.GPIO_Pin = DHT11_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStructure); } // 初始化DHT11 void DHT11_Init(void) { DHT11_GPIO_Output(); GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); } // 读取一个字节 static uint8_t DHT11_Read_Byte(void) { uint8_t i, byte = 0; for (i = 0; i < 8; i++) { // 等待低电平结束 while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 0); delay_us(40); if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 1) { // 等待高电平结束 while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 1); byte |= (1 << (7 - i)); } else { byte &= ~(1 << (7 - i)); } } return byte; } // 读取DHT11数据 uint8_t DHT11_Read_Data(uint8_t *humidity, uint8_t *temperature) { uint8_t buf[5]; uint8_t i; // 主机拉低18ms DHT11_GPIO_Output(); GPIO_ResetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); delay_ms(18); // 主机拉高20-40us GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); delay_us(30); // 切换到输入模式 DHT11_GPIO_Input(); // 等待DHT11响应 if (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 0) { // 等待低电平结束 while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 0); // 等待高电平结束 while (GPIO_ReadInputDataBit(DHT11_GPIO_PORT, DHT11_GPIO_PIN) == 1); // 读取40位数据 for (i = 0; i < 5; i++) { buf[i] = DHT11_Read_Byte(); } // 切换到输出模式 DHT11_GPIO_Output(); GPIO_SetBits(DHT11_GPIO_PORT, DHT11_GPIO_PIN); // 校验数据 if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) { *humidity = buf[0]; *temperature = buf[2]; return SUCCESS; } } return ERROR; } ``` OLED驱动代码(oled.h) ```c #ifndef __OLED_H #define __OLED_H #include "stm32f10x.h" #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据 void OLED_Init(void); void OLED_Clear(void); void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size); void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2); void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size); #endif ``` OLED驱动代码(oled.c) ```c #include "oled.h" #include "oledfont.h" #include "delay.h" // I2C写一个字节 static void I2C_WriteByte(uint8_t addr, uint8_t data) { I2C_GenerateSTART(I2C1, ENABLE); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, 0x78, I2C_Direction_Transmitter); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANITTER_MODE_SELECTED)); I2C_SendData(I2C1, addr); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANITTED)); I2C_SendData(I2C1, data); while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANITTED)); I2C_GenerateSTOP(I2C1, ENABLE); } // 写命令 void OLED_WR_CMD(uint8_t cmd) { I2C_WriteByte(0x00, cmd); } // 写数据 void OLED_WR_DATA(uint8_t data) { I2C_WriteByte(0x40, data); } // 初始化OLED void OLED_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 配置I2C引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 配置I2C I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 400000; I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); // OLED初始化命令 OLED_WR_CMD(0xAE); // 关闭显示 OLED_WR_CMD(0x20); // 设置内存地址模式 OLED_WR_CMD(0x10); // 水平寻址模式 OLED_WR_CMD(0xb0); // 设置页地址 OLED_WR_CMD(0xc8); // 扫描方向:从上到下 OLED_WR_CMD(0x00); // 设置列地址低4位 OLED_WR_CMD(0x10); // 设置列地址高4位 OLED_WR_CMD(0x40); // 设置显示开始行 OLED_WR_CMD(0x81); // 设置对比度 OLED_WR_CMD(0xff); // 对比度值 OLED_WR_CMD(0xa1); // 设置段重映射 OLED_WR_CMD(0xa6); // 设置正常显示 OLED_WR_CMD(0xa8); // 设置多路复用率 OLED_WR_CMD(0x3f); // 1/64 OLED_WR_CMD(0xa4); // 全局显示 OLED_WR_CMD(0xd3); // 设置显示偏移 OLED_WR_CMD(0x00); // 无偏移 OLED_WR_CMD(0xd5); // 设置显示时钟分频比/振荡器频率 OLED_WR_CMD(0xf0); // 分频比/振荡器频率 OLED_WR_CMD(0xd9); // 设置预充电周期 OLED_WR_CMD(0x22); // 预充电周期 OLED_WR_CMD(0xda); // 设置COM引脚硬件配置 OLED_WR_CMD(0x12); // COM引脚硬件配置 OLED_WR_CMD(0xdb); // 设置VCOMH取消选择级别 OLED_WR_CMD(0x20); // VCOMH取消选择级别 OLED_WR_CMD(0x8d); // 设置充电泵 OLED_WR_CMD(0x14); // 启用充电泵 OLED_WR_CMD(0xaf); // 开启显示 } // 清屏 void OLED_Clear(void) { uint8_t i, n; for (i = 0; i < 8; i++) { OLED_WR_CMD(0xb0 + i); OLED_WR_CMD(0x00); OLED_WR_CMD(0x10); for (n = 0; n < 128; n++) { OLED_WR_DATA(0); } } } // 显示一个字符 void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size) { uint8_t c = 0, i = 0; c = chr - ' '; if (x > 128 - 1) { x = 0; y = y + 2; } if (Char_Size == 16) { for (i = 0; i < 16; i++) { OLED_WR_DATA(F8X16[c * 16 + i]); } } else { for (i = 0; i < 8; i++) { OLED_WR_DATA(F6x8[c][i]); } } } // 显示数字 void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size2) { uint8_t t, temp; uint8_t enshow = 0; for (t = 0; t < len; t++) { temp = (num / OLED_Pow(10, len - t - 1)) % 10; if (enshow == 0 && t < (len - 1)) { if (temp == 0) { OLED_ShowChar(x + (size2 / 2) * t, y, ' ', size2); continue; } else { enshow = 1; } } OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2); } } // 显示字符串 void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size) { unsigned char j = 0; while (chr[j] != '\0') { OLED_ShowChar(x, y, chr[j], Char_Size); x += 8; if (x > 120) { x = 0; y += 2; } j++; } } ``` 注意事项 我在以往的开发过程中,碰到过以下几点需要格外小心处理的地方: 1. DHT11数据读取需要严格按照时序进行,延时时间的准确性会影响数据读取的成功率。 2. OLED屏幕的初始化和数据显示需要按照其协议进行操作,不同型号的OLED可能有不同的初始化命令。 3. 注意GPIO引脚的配置,确保DHT11和OLED的引脚没有冲突。 ######[AI写代码神器 | 3605点数解答 | 2025-11-12 18:19:26]
- STM32F10x代码解析:多模块引用与变量定义及功能探讨(字节豆包 | 25点数解答 | 2025-05-06 01:13:43)197
- Keil编译报错:找不到core_cm3.h文件的原因及解决办法(字节豆包 | 666点数解答 | 2025-11-12 18:26:21)169
- Keil MDK编译报错“无法打开 core_cm3.h”,解决办法及注意事项全揭秘!(字节豆包 | 560点数解答 | 2025-11-12 18:31:47)80
- Java 继承实现:重温经典剪刀石头布游戏(字节豆包 | 451点数解答 | 2024-10-20 19:57:58)468
- Python 实现课程信息输入存储:含输入检测与字典构建全流程 (GPT | 448点数解答 | 2024-05-30 15:08:47)231
- 51 单片机+Keil5:YL - 69 土壤湿度采集并在 LCD1602 显示的示例代码及注意事项(字节豆包 | 1239点数解答 | 2025-11-02 17:53:34)63
- Golang开发:解析JSON数据时数值默认类型及示例解析(百度文心 | 393点数解答 | 2023-11-09 18:13:06)282
- SQL Working Areas Sizing: Manual vs. Automatic Policy - A Comprehensive Analysis(阿里通义 | 530点数解答 | 2024-05-13 10:55:58)310
- Java:设计圆类与圆柱体类并计算属性及体积表面积(字节豆包 | 470点数解答 | 2024-10-20 10:03:11)183
- Java 实现矩形与长方体类,精准计算底面积与体积(字节豆包 | 319点数解答 | 2024-10-20 10:25:46)291
- Java 实现矩形与长方体类,轻松计算底面积和体积(字节豆包 | 308点数解答 | 2024-10-20 10:34:27)273
- Java 实现矩形与长方体类:计算底面积与体积的完整代码示例(字节豆包 | 306点数解答 | 2024-10-20 18:34:47)277