用51单片机和LD3320语音模块DIY一个会听话的智能温控器(附完整代码)
在智能家居逐渐普及的今天,自己动手制作一个能听懂语音指令的温控设备不仅有趣,还能学到不少实用的电子技术知识。这个项目将带你一步步完成一个基于51单片机的智能温控器,它能通过语音指令控制温度测量,设置温度阈值,并在温度超标时发出警报。
1. 项目所需材料清单
要完成这个项目,你需要准备以下硬件组件:
- 主控芯片:AT89C52单片机(或兼容的51系列单片机)
- 语音识别模块:LD3320语音识别模块
- 温度传感器:DS18B20数字温度传感器
- 显示模块:LCD1602液晶显示屏
- 报警装置:有源蜂鸣器
- 其他元件:
- 10kΩ电阻
- 4.7kΩ电阻
- 面包板或PCB板
- 杜邦线若干
- 5V电源适配器
硬件连接示意图如下:
[VCC 5V] ---- [AT89C52 VCC] |---- [LD3320 VCC] |---- [LCD1602 VCC] |---- [DS18B20 VCC] [GND] ---- [AT89C52 GND] |---- [LD3320 GND] |---- [LCD1602 GND] |---- [DS18B20 GND] |---- [蜂鸣器负极] [P1.1] ---- [LD3320 PA1] [P1.0] ---- [蜂鸣器正极] [P2.0-P2.7] ---- [LCD1602 D0-D7] [P3.7] ---- [LCD1602 RS] [P3.6] ---- [LCD1602 RW] [P3.5] ---- [LCD1602 EN] [P3.4] ---- [DS18B20 DQ]2. 硬件连接详解
2.1 单片机最小系统搭建
AT89C52单片机最小系统需要以下基本电路:
- 电源电路:VCC接5V,GND接地
- 复位电路:10kΩ电阻接在RST和VCC之间,10μF电容接在RST和GND之间
- 时钟电路:11.0592MHz晶振连接XTAL1和XTAL2,两个30pF电容分别接晶振两端到GND
2.2 LD3320语音模块连接
LD3320模块与单片机的连接非常简单:
- VCC接5V
- GND接GND
- PA1接单片机P1.1(用于触发温度测量)
- RXD接单片机TXD(P3.1)
- TXD接单片机RXD(P3.0)
注意:LD3320模块需要预先通过上位机软件设置识别关键词和对应的输出信号
2.3 DS18B20温度传感器连接
DS18B20采用单总线协议,连接方式如下:
- VCC接5V
- GND接GND
- DQ接单片机P3.4,并通过4.7kΩ上拉电阻接VCC
2.4 LCD1602显示屏连接
LCD1602采用8位并行接口连接:
- VSS接GND
- VDD接5V
- VO接10kΩ电位器中点(用于调节对比度)
- RS接P3.7
- RW接P3.6
- EN接P3.5
- D0-D7接P2.0-P2.7
- A接5V(背光正极)
- K接GND(背光负极)
3. 软件设计与代码实现
3.1 主程序框架
主程序主要负责初始化各模块并循环检测语音触发信号:
#include <reg52.h> #include <intrins.h> #include "LCD1602.h" #include "DS18B20.h" sbit VOICE_TRIG = P1^1; // 语音触发引脚 sbit BUZZER = P1^0; // 蜂鸣器控制引脚 void main() { unsigned int threshold = 2500; // 默认阈值25.00℃ unsigned int current_temp; // 初始化各模块 LCD_Init(); DS18B20_Init(); LCD_ShowString(0, 0, "Temp: . C"); LCD_ShowString(0, 1, "Thresh: 25.0C"); while(1) { if(VOICE_TRIG == 1) { // 检测语音触发 current_temp = DS18B20_GetTemp(); Display_Temperature(current_temp); if(current_temp > threshold) { Alarm(); // 温度超过阈值报警 } } } }3.2 DS18B20驱动代码
DS18B20的驱动程序需要严格按照单总线时序操作:
// DS18B20.h #ifndef __DS18B20_H__ #define __DS18B20_H__ #include <reg52.h> #include <intrins.h> sbit DQ = P3^4; // 温度传感器数据线 unsigned int DS18B20_GetTemp(void); void DS18B20_Init(void); #endif// DS18B20.c #include "DS18B20.h" // 延时函数 void Delay_us(unsigned int us) { while(us--) { _nop_(); _nop_(); _nop_(); _nop_(); } } // DS18B20初始化 void DS18B20_Init(void) { unsigned char x = 0; DQ = 1; Delay_us(8); DQ = 0; Delay_us(80); DQ = 1; Delay_us(14); x = DQ; Delay_us(20); } // 从DS18B20读取一个字节 unsigned char DS18B20_ReadByte(void) { unsigned char i = 0; unsigned char dat = 0; for(i=0; i<8; i++) { DQ = 0; dat >>= 1; DQ = 1; if(DQ) dat |= 0x80; Delay_us(30); } return dat; } // 向DS18B20写入一个字节 void DS18B20_WriteByte(unsigned char dat) { unsigned char i = 0; for(i=0; i<8; i++) { DQ = 0; DQ = dat & 0x01; Delay_us(5); DQ = 1; dat >>= 1; } } // 获取温度值 unsigned int DS18B20_GetTemp(void) { unsigned char tempL = 0; unsigned char tempH = 0; unsigned int temp = 0; DS18B20_Init(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0x44); // 启动温度转换 Delay_us(100); DS18B20_Init(); DS18B20_WriteByte(0xCC); // 跳过ROM DS18B20_WriteByte(0xBE); // 读取暂存器 tempL = DS18B20_ReadByte(); tempH = DS18B20_ReadByte(); temp = tempH; temp <<= 8; temp |= tempL; return temp; }3.3 LCD1602显示驱动
LCD1602的驱动程序需要实现基本的显示功能:
// LCD1602.h #ifndef __LCD1602_H__ #define __LCD1602_H__ #include <reg52.h> #include <intrins.h> #define LCD_DB P2 sbit LCD_RS = P3^7; sbit LCD_RW = P3^6; sbit LCD_EN = P3^5; void LCD_Init(void); void LCD_WriteCmd(unsigned char cmd); void LCD_WriteData(unsigned char dat); void LCD_ShowString(unsigned char x, unsigned char y, unsigned char *str); void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len); #endif// LCD1602.c #include "LCD1602.h" // 忙检测 void LCD_BusyCheck(void) { unsigned char sta; LCD_DB = 0xFF; LCD_RS = 0; LCD_RW = 1; do { LCD_EN = 1; sta = LCD_DB; LCD_EN = 0; } while(sta & 0x80); } // 写命令 void LCD_WriteCmd(unsigned char cmd) { LCD_BusyCheck(); LCD_RS = 0; LCD_RW = 0; LCD_DB = cmd; LCD_EN = 1; LCD_EN = 0; } // 写数据 void LCD_WriteData(unsigned char dat) { LCD_BusyCheck(); LCD_RS = 1; LCD_RW = 0; LCD_DB = dat; LCD_EN = 1; LCD_EN = 0; } // 初始化 void LCD_Init(void) { LCD_WriteCmd(0x38); // 8位数据接口,两行显示,5x7点阵 LCD_WriteCmd(0x0C); // 显示开,光标关,闪烁关 LCD_WriteCmd(0x06); // 读写后AC自动增一,画面不动 LCD_WriteCmd(0x01); // 清屏 } // 显示字符串 void LCD_ShowString(unsigned char x, unsigned char y, unsigned char *str) { unsigned char addr; if(y == 0) { addr = 0x80 + x; } else { addr = 0xC0 + x; } LCD_WriteCmd(addr); while(*str != '\0') { LCD_WriteData(*str++); } } // 显示数字 void LCD_ShowNum(unsigned char x, unsigned char y, unsigned int num, unsigned char len) { unsigned char i, temp; unsigned char buf[5]; for(i=0; i<len; i++) { buf[i] = num % 10; num /= 10; } if(y == 0) { LCD_WriteCmd(0x80 + x); } else { LCD_WriteCmd(0xC0 + x); } for(i=len; i>0; i--) { temp = buf[i-1] + '0'; LCD_WriteData(temp); } }3.4 温度显示与报警功能
温度数据处理和显示报警功能的实现:
// 显示温度值 void Display_Temperature(unsigned int temp) { unsigned char buf[7]; unsigned int temp_val; if(temp & 0x8000) { // 负温度 temp_val = ~temp + 1; buf[0] = '-'; } else { // 正温度 temp_val = temp; buf[0] = ' '; } // 计算整数部分 buf[1] = temp_val / 1000 + '0'; buf[2] = (temp_val % 1000) / 100 + '0'; buf[3] = '.'; // 计算小数部分 buf[4] = (temp_val % 100) / 10 + '0'; buf[5] = temp_val % 10 + '0'; buf[6] = '\0'; LCD_ShowString(5, 0, buf); } // 报警功能 void Alarm(void) { unsigned char i; for(i=0; i<100; i++) { BUZZER = ~BUZZER; Delay_us(500); } BUZZER = 0; }4. 项目调试与优化
4.1 常见问题排查
在项目实施过程中可能会遇到以下问题:
LCD1602无显示:
- 检查对比度调节电位器
- 确认电源电压是否为5V
- 检查使能信号EN是否有高低电平变化
DS18B20温度读取失败:
- 确认上拉电阻(4.7kΩ)是否正确连接
- 检查时序是否符合要求,特别是延时时间
- 尝试更换DS18B20模块
LD3320语音识别不灵敏:
- 调整麦克风灵敏度
- 确保环境噪音较小
- 重新训练关键词,确保发音清晰
4.2 性能优化建议
降低功耗:
- 在不需要测量时让单片机进入空闲模式
- 使用LCD1602的显示开关命令控制背光
- 考虑使用低功耗版本的51单片机
提高测量精度:
- 对DS18B20进行多点采样取平均值
- 添加温度补偿算法
- 使用更高精度的参考电压
扩展功能:
- 添加蓝牙模块实现手机控制
- 增加多个温度传感器实现多点监测
- 添加继电器模块控制加热/制冷设备
5. 完整项目代码
以下是整合后的完整项目代码:
// main.c #include <reg52.h> #include <intrins.h> #include "LCD1602.h" #include "DS18B20.h" sbit VOICE_TRIG = P1^1; // 语音触发引脚 sbit BUZZER = P1^0; // 蜂鸣器控制引脚 unsigned int threshold = 2500; // 默认阈值25.00℃ void Delay_ms(unsigned int ms) { unsigned int i, j; for(i=0; i<ms; i++) { for(j=0; j<110; j++); } } void Alarm(void) { unsigned char i; for(i=0; i<100; i++) { BUZZER = ~BUZZER; Delay_ms(1); } BUZZER = 0; } void Display_Temperature(unsigned int temp) { unsigned char buf[7]; unsigned int temp_val; if(temp & 0x8000) { // 负温度 temp_val = ~temp + 1; buf[0] = '-'; } else { // 正温度 temp_val = temp; buf[0] = ' '; } // 计算整数部分 buf[1] = temp_val / 1000 + '0'; buf[2] = (temp_val % 1000) / 100 + '0'; buf[3] = '.'; // 计算小数部分 buf[4] = (temp_val % 100) / 10 + '0'; buf[5] = temp_val % 10 + '0'; buf[6] = '\0'; LCD_ShowString(5, 0, buf); } void Display_Threshold(void) { unsigned char buf[6]; buf[0] = threshold / 1000 + '0'; buf[1] = (threshold % 1000) / 100 + '0'; buf[2] = '.'; buf[3] = (threshold % 100) / 10 + '0'; buf[4] = threshold % 10 + '0'; buf[5] = '\0'; LCD_ShowString(8, 1, buf); } void main() { unsigned int current_temp; // 初始化各模块 LCD_Init(); DS18B20_Init(); LCD_ShowString(0, 0, "Temp: . C"); LCD_ShowString(0, 1, "Thresh: 25.0C"); while(1) { if(VOICE_TRIG == 1) { // 检测语音触发 current_temp = DS18B20_GetTemp(); Display_Temperature(current_temp); if(current_temp > threshold) { Alarm(); // 温度超过阈值报警 } Delay_ms(500); // 防止频繁触发 } } }这个项目完整展示了如何利用51单片机和LD3320语音模块构建一个实用的智能温控系统。通过这个实践,你不仅能掌握基本的单片机编程技巧,还能了解语音识别、温度传感等实用技术的实现原理。