智能硬件行业资讯,IoT、机器人、AR/VR、智能家居前沿

51单片机智能家居系统:从零实现温湿度监测、蓝牙遥控与PWM风扇调速

51单片机智能家居系统:从零实现温湿度监测、蓝牙遥控与PWM风扇调速 - 图片1

51单片机智能家居系统:从零实现温湿度监测、蓝牙遥控与PWM风扇调速 - 图片2

51单片机智能家居系统:从零实现温湿度监测、蓝牙遥控与PWM风扇调速 - 图片3

手把手拆解一个完整的嵌入式小项目,代码、电路、原理全都有 很多初学者学完51单片机的语法和基础外设后,都会面临一个尴尬的问题:单独点亮一个LED、单独驱动一个传感器都会,但怎么把这些东西组合成一个“有点意思”的小系统? 今天这篇文章,我就带你完整拆解一个智能家居原型项目——它不复杂,但涵盖了嵌入式开发中最核心的几块内容:传感器数据采集、PWM电机调速、蓝牙串口遥控、OLED屏幕显示、光敏自动控制。我会把每一部分的设计思路、代码细节、硬件连接全部讲清楚。 先看系统框图,对整个项目有个整体印象: 一、这个项目能做什么? 先给你一个直观感受,这套系统实现了以下功能: 功能模块实现方式实际用途温湿度检测DHT11传感器采集环境温湿度,显示在OLED和串口自动补光光敏传感器 + LED环境变暗时自动开灯风扇调速PWM控制电机通过蓝牙或串口无级调速灯光控制蓝牙串口指令手机APP远程开关灯数据监控OLED屏幕显示本地实时查看温湿度、风扇转速数据上传串口发送上传到PC或手机APP 一句话概括:采集环境数据 → 本地显示 + 逻辑控制 → 支持远程干预,这就是一个完整物联网节点的雏形。 二、硬件清单与引脚分配 2.1 元器件清单 器件型号/规格数量作用单片机STC89C52RC1主控温湿度传感器DHT111采集温湿度OLED屏幕SSD1306 (I²C接口)1本地显示蓝牙模块HC-05/HC-061无线串口通信光敏传感器光敏电阻模块(数字输出)1光线检测LED灯普通LED + 限流电阻2照明指示直流电机小功率5V直流电机1模拟风扇电机驱动L298N或L9110S1驱动电机晶振11.0592MHz1串口通信精准波特率 2.2 引脚连接表 对照代码的main.h,引脚定义如下: 外设引脚说明LED2(灯光)P1.1蓝牙控制灯光(低电平点亮)LED4(光控灯)P1.3光敏传感器自动控制PWM风扇P1.0同时作为PWM输出引脚DHT11P1.2单总线数据线OLED_SCLP1.5I²C时钟线OLED_SDAP1.4I²C数据线光敏传感器P3.7数字信号输入(0=有光/1=无光)串口TXP3.1蓝牙/串口发送串口RXP3.0蓝牙/串口接收 三、软件架构分层解读 整个工程的代码结构很清晰,分为以下几层: main.c/h → 主逻辑、状态管理、中断服务 ├── uart.c/h → 串口初始化、收发(蓝牙通信) ├── timer.c/h → 定时器初始化(PWM时基) ├── dht11.c/h → DHT11驱动(时序读取) ├── oled.c/h → OLED屏幕驱动(I²C指令/数据) ├── iic.c/h → I²C底层协议(起始、停止、收发) ├── delay.c/h → 延时函数(微秒/毫秒级) ├── key.c/h → 按键扫描(预留) └── main.h → 全局引脚定义、类型别名 这个分层的好处是:每一层只做一件事,上层调用下层接口,修改底层驱动不影响上层逻辑。 四、逐个模块深度解析 4.1 串口与蓝牙通信(uart.c) 串口是这套系统的“神经”——它既要接收蓝牙指令,又要发送温湿度数据。 波特率计算 代码中使用了定时器1工作模式2(8位自动重装),波特率9600: TH1 = 0xFD; TL1 = 0xFD; // 11.0592MHz下,9600波特率 为什么是0xFD?这是计算出来的初值: 定时器溢出速率 = 晶振频率 / (12 × (256 - TH1)) 波特率 = 定时器溢出速率 / 32(模式1) 9600 = 11059200 / (12 × (256 - TH1) × 32) → TH1 = 256 - 11059200/(9600×12×32) ≈ 256 - 3 = 253 = 0xFD 所以用11.0592MHz晶振,就是为了串口通信时波特率误差极小。 串口中断处理 void UART_Routine(void) interrupt 4 { if(RI) { RI = 0; recv = SBUF; switch(recv) { case 0x01: LED2 = 0; break; // 开灯 case 0x02: LED2 = 1; break; // 关灯 case 0x03: level++; break; // 加速 case 0x04: level--; break; // 减速 } } } 这里定义了一个简单的二进制通信协议: 0x01 → 开灯 0x02 → 关灯 0x03 → 风扇加速(占空比+1) 0x04 → 风扇减速(占空比-1) 补充知识:实际产品中不会用这种单字节协议(易出错、无校验),通常会加上帧头帧尾和校验和,比如 AA 01 01 02 这种格式。但作为学习项目,简洁优先。 4.2 PWM风扇调速(timer.c) 51单片机没有硬件PWM,所以用定时器中断配合软件计数实现。 原理 定时器0每10ms触发一次中断,中断服务函数里维护两个计数器: void Timer0_Routine(void) interrupt 1 { TH0 = 0xDC; TL0 = 0x00; // 10ms重装值 pwm_count++; pwm_count = pwm_count % 10; // 周期 = 10 × 10ms = 100ms if(pwm_count < level) { PWM_IO = 1; // 高电平 } else { PWM_IO = 0; // 低电平 } // 每300个中断 = 3000ms = 3秒读取一次DHT11 tim_count++; tim_count = tim_count % 300; if(0 == tim_count) read_dht11_flag = 1; } 工作机制: level值高电平占比风扇转速00/10 = 0%停转33/10 = 30%低速55/10 = 50%中速88/10 = 80%高速1010/10 = 100%全速 pwm_count从0到9循环,level决定了在一个周期内高电平持续多少次中断。当level=10时,10个时间片全为高电平,相当于直通全速。 为什么周期选100ms? 因为人耳对100ms以上的频率不敏感,电机不会发出可听见的嗡嗡声。实际电机调速通常用1kHz~20kHz,但51单片机用定时器做高频率PWM会占用大量CPU资源,100ms是在资源有限下的折中方案。 4.3 DHT11温湿度读取(dht11.c) DHT11是单总线传感器,通信时序非常讲究。一次完整读取流程如下: 主机拉低 ≥ 18ms → 拉高 20~40us → DHT11拉低 80us → 拉高 80us → 开始传数据 每个数据位是这样区分的: 位"0":DHT11拉低 50us → 拉高 26~28us(高电平持续时间短) 位"1":DHT11拉低 50us → 拉高 70us(高电平持续时间长) 代码用Delay30us()后读取引脚电平来区分0和1: Delay30us(); if(1 == DHT11_IO) { data_bit = 1; // 30us后还是高电平 → 位"1" while(DHT11_IO && count < 80) count++; } else { data_bit = 0; // 30us后已变低 → 位"0" } DHT11返回的数据格式是40位(5字节): 字节0字节1字节2字节3字节4湿度整数湿度小数温度整数温度小数校验和 校验公式:字节0 + 字节1 + 字节2 + 字节3 == 字节4 代码中只取了整数部分(humi = dht_buf[0],temp = dht_buf[2]),舍弃了小数——因为DHT11的湿度精度也就±5%,小数没实际意义。 4.4 OLED显示(oled.c + iic.c) OLED用的是SSD1306驱动芯片,7位I²C地址是0x78(含读写位是0x78写/0x79读)。 I²C通信的关键是起始条件、停止条件、应答位: void IIC_Start(void) { IIC_SCL = 1; IIC_SDA = 1; Delay5us(); IIC_SDA = 0; // SDA在SCL高电平时拉低 → 起始 Delay5us(); IIC_SCL = 0; } void IIC_Stop(void) { IIC_SCL = 1; IIC_SDA = 0; Delay5us(); IIC_SDA = 1; // SDA在SCL高电平时拉高 → 停止 } 向OLED写数据分两步: 写命令(控制指令,如设置光标位置):IIC_Send_Byte(0x00) 写数据(像素内容):IIC_Send_Byte(0x40) 代码中的OLED_WR_Byte(dat, cmd)封装了这一过程: void OLED_WR_Byte(unsigned dat, unsigned cmd) { IIC_Start(); IIC_Send_Byte(0x78); // 设备地址 + 写 IIC_Recv_Ack(); if(cmd) { IIC_Send_Byte(0x40); // 数据命令 } else { IIC_Send_Byte(0x00); // 控制命令 } IIC_Recv_Ack(); IIC_Send_Byte(dat); IIC_Recv_Ack(); IIC_Stop(); } 显示原理:OLED内部有8个页(Page0~Page7),每页128列,每列对应一个字节(8个像素)。要显示一个字符,就是把对应的点阵数据(F8X16或F6x8字库)逐字节写入对应位置。 ![OLED显示原理示意] 比如显示"Fan Speed:",就是循环调用OLED_ShowChar(),每个字符是16×8像素,x坐标每写一个字符+8。 4.5 光敏传感器控制(main.c) 光敏传感器模块通常输出TTL电平: 光线足够 → 输出低电平(0) 光线不足 → 输出高电平(1) 代码逻辑极其简单: if(SUN == 0) { // 有光 LED4 = 1; // 关灯 } else { // 暗 LED4 = 0; // 开灯(低电平点亮) } 注意代码中LED4是低电平点亮(sbit LED4 = P1^3),所以条件判断是反的:SUN == 0时关灯,SUN == 1时开灯。 五、主程序逻辑流程图 整个main()的执行逻辑: 系统初始化(定时器、串口、OLED) ↓ 显示标题 "智能家居系统" ↓ ┌──────────────────────────────────┐ │ 循环执行: │ │ ├── 每3秒读一次DHT11 │ │ │ ├── 成功 → OLED显示 + 串口发送 │ │ │ └── 失败 → 忽略 │ │ ├── 光敏检测 → 控制LED4 │ │ └── OLED刷新显示风扇速度 │ └──────────────────────────────────┘ ↑ │ └─────────────── 中断驱动 ────┘ 定时器中断:PWM输出 + DHT11触发标志 串口中断:蓝牙指令响应 状态标志位read_dht11_flag起到了关键作用——DHT11读取需要时间,不能在主循环里阻塞,所以用定时器每3秒置位一次标志,主循环检测到标志才去读取,实现了非阻塞的定时采集。 六、代码中隐藏的小细节 6.1 数据类型重定义 typedef unsigned char uchar; typedef unsigned int uint; 后面所有的代码都用uchar和uint,比直接写unsigned char更简洁。这是51开发中的常见习惯。 6.2 延时函数的精度 void Delay30us(void) { unsigned char data i; i = 11; while (--i); } 注意这里用了data关键字,把变量放在片内RAM中,访问速度更快,延时更准确。如果不加data,编译器可能把它分配到xdata区,访问变慢导致延时不准。 Delay5us()是空函数,因为5us太短,用空操作_nop_()积累,I²C时序是靠Delay5us()配合IIC_SCL/IIC_SDA电平切换来实现的。 6.3 中断服务函数的注意事项 void Timer0_Routine(void) interrupt 1 { TH0 = 0XDC; TL0 = 0X00; // ... } 定时器0工作在模式1(16位),每次溢出都需要手动重装初值。如果用模式2(8位自动重装)就不用手动重装,但最大计数值只有256,不够10ms。 6.4 sprintf的使用风险 sprintf(buf, "H:%u%% T:%uC", (uint)humity, (uint)temperature); sprintf是标准库函数,在51单片机上会占用大量程序空间(ROM)。如果ROM不够用,建议改成手动拼接字符串。不过STC89C52有8KB Flash,这点代码还好。 七、扩展与改进方向 这个项目还有很大的扩展空间,你可以试试: 改进方向实现思路增加Wi-Fi模块用ESP8266取代蓝牙,接入阿里云/OneNET增加手机APP用MIT App Inventor或微信小程序做界面数据存储外接AT24C02 EEPROM,存储历史数据增加按键控制代码里已有key.c,可接入按键手动控制增加报警功能温度超限时蜂鸣器报警(P1.6已有BEEP定义)改进协议定义帧格式:AA 命令 数据 校验和 BB 八、常见问题排查 现象可能原因解决方法OLED不显示I²C地址不对/连接错误检查0x78地址,确认SDA/SCL接线DHT11读失败时序不对/上拉电阻加4.7kΩ上拉电阻,检查Delay30us精度蓝牙收不到数据波特率不匹配HC-05默认9600,确保和代码一致风扇不转驱动模块供电不足电机要用外部5V供电,不能从单片机取电串口乱码晶振频率不匹配确认用的是11.0592MHz,不是12MHz 九、总结 这个项目麻雀虽小五脏俱全,涵盖了: GPIO控制(LED、电机) 定时器中断(PWM、定时任务) 串口中断(蓝牙通信) 单总线协议(DHT11) I²C协议(OLED) 模数混合控制(光敏传感器) 对于51单片机学习者来说,这是一个非常好的综合练习项目。当你把这些模块全部调通的那一刻,你对嵌入式系统的理解会上一个台阶。 如果你在实操中遇到问题,欢迎在评论区留言,我会尽力解答。 项目代码已在文中完整给出,需要工程文件的可以直接复制整理。硬件连接以文中表格为准。祝你调试顺利!