基于51单片机的智能晾衣架系统(keil5代码和proteus仿真)

发布时间:2026/8/1 21:07:34

基于51单片机的智能晾衣架系统(keil5代码和proteus仿真) 课程最后一道大实验需要学生自己选择项目并跑通。本人选择了智能晾衣架系统花费两天时间完成了代码和仿真。下面分别从系统功能、使用模块、代码逻辑等进行阐述希望对大家有帮助。一、系统功能介绍智能晾衣架系统分为手动和自动模式通过按键控制选择。#define KEY_AUTO 1 // 自动模式 #define KEY_MAN 2 // 手动模式1、手动模式在此模式下可以手动升起和放下晾衣架。#define KEY_UP 4 // 手动上升键 #define KEY_DOWN 5 // 手动下降键2、自动模式在此模式下可以自动检测环境数据本人选取了三个参考量分别是温度、光照、湿度当检测到环境数据不处于适宜环境数据时就会收衣服反之则晒衣服。3、设置模式在此模式下可以进行阈值的设置——分为最大值和最小值。#define KEY_INC 6 // 增加阈值键 #define KEY_DEC 7 // 减少阈值键 #define KEY_NEXT 8 // 下一个参数键 #define KEY_SAVE 9 // 保存键 #define KEY_SUB 10 //切换最大最小值最后在主循环里通过mode状态标志位进行模式选择。//模式选择 if(KeyNumKEY_AUTO) { mode1; flag1; } else if(KeyNumKEY_MAN) { mode0; flag1; } else if(KeyNumKEY_SET) { set(); flag1; }当mode1,进入自动模式当mode0,进入手动模式当检测到按键按下KEY_SET则直接进入设置模式。二、模块介绍1温度我使用的是DS18B20温度传感器它具有精度高的优点但是在仿真中不太能体现出来这个优点然后需要单总线通信所以要包含一个“onewire.h”关于它的代码如下。#include REGX52.H #include OneWire.h //DS18B20指令 #define DS18B20_SKIP_ROM 0xCC #define DS18B20_CONVERT_T 0x44 #define DS18B20_READ_SCRATCHPAD 0xBE /** * brief DS18B20开始温度变换 * param 无 * retval 无 */ void DS18B20_ConvertT(void) { OneWire_Init(); OneWire_SendByte(DS18B20_SKIP_ROM); OneWire_SendByte(DS18B20_CONVERT_T); } /** * brief DS18B20读取温度 * param 无 * retval 温度数值 */ float DS18B20_ReadT(void) { unsigned char TLSB,TMSB; int Temp; float T; OneWire_Init(); OneWire_SendByte(DS18B20_SKIP_ROM); OneWire_SendByte(DS18B20_READ_SCRATCHPAD); TLSBOneWire_ReceiveByte(); TMSBOneWire_ReceiveByte(); Temp(TMSB8)|TLSB; TTemp/16.0; return T; }这里是直接使用江协51的代码如果不清楚的可以去看一下江协51视频讲解然后是主函数的代码。void temper_get() //温度读取 { DS18B20_ConvertT(); //转换温度 data_temper(int)DS18B20_ReadT(); //读取温度 if(data_temper0) //如果温度小于0 { LCD_ShowChar(2,1,-); //显示负号 data_temper-data_temper; //将温度变为正数 } else //如果温度大于等于0 { LCD_ShowChar(2,1,); //显示正号 } LCD_ShowString(1,1,Temp ); LCD_ShowNum(2,2,data_temper,2); //显示温度整数部分 LCD_ShowString(2,4,*C ); }2光照这个我是使用光敏电阻配合ADC进行数据采集的光敏电阻很常见我在proteus中使用的是TORCH-LDR这个型号ADC模块则是使用TLC549这是一个串行数据传输占用的引脚少但是要注意这个模块对时序要求很高一定不能撞引脚因为主包犯过这个错误扶额叹息.jpg代码如下。#include REGX52.H #define uchar unsigned char #define uint unsigned int sbit TLC549_CLKP1^7; sbit TLC549_CSP1^6; sbit TLC549_DOP1^5; void ADC_ms(uint m) { uint i,j; for(im;i0;i--) for(j123;j0;j--); } unsigned char TLC549_ADC(void) { uchar i,temp; TLC549_CLK0; TLC549_CS0; for(i0;i8;i) { temp1; temp|TLC549_DO; TLC549_CLK1; TLC549_CLK0; } TLC549_CS1; ADC_ms(10); return temp; }然后是主函数的代码。void light_get() //光照读取 { adcTLC549_ADC(); data_lightadc*2; LCD_ShowString(1,6,light ); LCD_ShowNum(2,7,data_light,3); //显示光照强度部分 LCD_ShowString(2,10, ); }电压和光照强度有一定的数学关系我懒得算了直接用读出的电压值乘以2进行显示仿真结果表现为二者之间呈正比需要注意的是adc是uchar类型的而data_light是uint类型的如果将adc定义为uint类型则无法读出数据这个必须注意3湿度湿度的模块找了很多尝试过DHT11等最后发现一个比较简单好用的至少对于只仿真很友好——SHT21这是一个湿温度传感器至于为什么不用它同时采集温度因为老师要求要有一部分在板子上体现而且这个传感器的温度精度其实比不上DS18B20这里我们只使用检测湿度代码如下。#include sht21.h #include I2c.h #include Delay.h float humidity; /** * brief 初始化SHT21 */ bit SHT21_Init(void) { SHT21_Reset(); // 复位传感器 Delay(15); // 等待复位完成 return 1; } /** * brief 软复位SHT21 */ void SHT21_Reset(void) { I2C_Start(); I2C_SendByte(SHT21_ADDR 1); // 写地址 I2C_ReceiveAck(); I2C_SendByte(SHT21_CMD_RST); // 发送复位命令 I2C_ReceiveAck(); I2C_Stop(); } /** * brief 读取湿度值关键函数 */ float SHT21_ReadHumidity(void) { unsigned int raw_data; unsigned char retry 0; while(retry 3) { // 发送测量命令 I2C_Start(); I2C_SendByte(SHT21_ADDR 1); // 写地址 if(I2C_ReceiveAck()) { retry; continue; } I2C_SendByte(SHT21_CMD_HUM); // 发送湿度测量命令 if(I2C_ReceiveAck()) { retry; continue; } I2C_Stop(); // 等待测量完成 Delay(30); // 读取数据 I2C_Start(); I2C_SendByte((SHT21_ADDR 1) | 1); // 读地址 if(I2C_ReceiveAck()) { retry; continue; } // 读取2字节湿度数据MSBLSB raw_data I2C_ReceiveByte() 8; I2C_SendAck(0); // 应答 raw_data | I2C_ReceiveByte(); I2C_SendAck(1); // 非应答忽略CRC I2C_Stop(); // 计算湿度值 humidity (raw_data / 65536.0) * 125.0 - 6.0; // 验证数据有效性 if(humidity 0 humidity 100) { return humidity; } retry; } return -1; // 读取失败 }它需要使用I2C进行通信大家需要提前移植好I2C.c和.h的代码模块如果没有可以使用我下面提供的。#include REGX52.H //引脚定义 sbit I2C_SCLP2^1; sbit I2C_SDAP2^0; /** * brief I2C开始 * param 无 * retval 无 */ void I2C_Start(void) { I2C_SDA1; I2C_SCL1; I2C_SDA0; I2C_SCL0; } /** * brief I2C停止 * param 无 * retval 无 */ void I2C_Stop(void) { I2C_SDA0; I2C_SCL1; I2C_SDA1; } /** * brief I2C发送一个字节 * param Byte 要发送的字节 * retval 无 */ void I2C_SendByte(unsigned char Byte) { unsigned char i; for(i0;i8;i) { I2C_SDAByte(0x80i); I2C_SCL1; I2C_SCL0; } } /** * brief I2C接收一个字节 * param 无 * retval 接收到的一个字节数据 */ unsigned char I2C_ReceiveByte(void) { unsigned char i,Byte0x00; I2C_SDA1; for(i0;i8;i) { I2C_SCL1; if(I2C_SDA){Byte|(0x80i);} I2C_SCL0; } return Byte; } /** * brief I2C发送应答 * param AckBit 应答位0为应答1为非应答 * retval 无 */ void I2C_SendAck(unsigned char AckBit) { I2C_SDAAckBit; I2C_SCL1; I2C_SCL0; } /** * brief I2C接收应答位 * param 无 * retval 接收到的应答位0为应答1为非应答 */ unsigned char I2C_ReceiveAck(void) { unsigned char AckBit; I2C_SDA1; I2C_SCL1; AckBitI2C_SDA; I2C_SCL0; return AckBit; }然后是主函数的代码。void hum_get() //湿度读取 { data_humidity SHT21_ReadHumidity(); if(data_humidity 0) { LCD_ShowString(1, 13, Hum); LCD_ShowNum(2, 13, (unsigned int)data_humidity, 2); LCD_ShowString(2, 16, %); } else { LCD_ShowString(2, 12, ERROR); } Delay(1000); // 每秒更新一次 }4、步进电机#include REGX52.H #include Delay.h // 定义步进电机控制引脚连接到ULN2003的输入端 sbit MOTOR1P1^0; sbit MOTOR2P1^1; sbit MOTOR3P1^2; sbit MOTOR4P1^3; // 定义变量 unsigned char step_index 0; // 步进电机当前步骤索引 // 步进电机控制函数 void MotorControl(unsigned char motor_running,unsigned char direction) { if(motor_running) { if(step_index0) {MOTOR11;MOTOR20;MOTOR30;MOTOR40;} else if(step_index1) {MOTOR10;MOTOR21;MOTOR30;MOTOR40;} else if(step_index2) {MOTOR10;MOTOR20;MOTOR31;MOTOR40;} else if(step_index3) {MOTOR10;MOTOR20;MOTOR30;MOTOR41;} if(direction 0) // 顺时针 { step_index; if(step_index 4) step_index 0; } else // 逆时针 { if(step_index 0) step_index 4; step_index--; } Delay(500); // 延时控制速度 } else { MOTOR1 0; MOTOR2 0; MOTOR3 0; MOTOR4 0; } }主函数代码如下。// 步进电机控制 void Motor_Up() { MotorControl(1,1); } void Motor_Down() { MotorControl(1,0); }5、储存模块我们使用了24C02进行数据储存模块代码如下。#include REGX52.H #include I2C.h #define AT24C02_ADDRESS 0xA0 /** * brief AT24C02写入一个字节 * param WordAddress 要写入字节的地址 * param Data 要写入的数据 * retval 无 */ void AT24C02_WriteByte(unsigned char WordAddress,Data) { I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_SendByte(Data); I2C_ReceiveAck(); I2C_Stop(); } /** * brief AT24C02读取一个字节 * param WordAddress 要读出字节的地址 * retval 读出的数据 */ unsigned char AT24C02_ReadByte(unsigned char WordAddress) { unsigned char Data; I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_Start(); I2C_SendByte(AT24C02_ADDRESS|0x01); I2C_ReceiveAck(); DataI2C_ReceiveByte(); I2C_SendAck(1); I2C_Stop(); return Data; }这里依旧是江协51的模块。三、手动模式先看代码。//手动控制 if(mode0) { if(flag) { LCD_Clear(); LCD_ShowString(1, 1, MANUAL MODE); flag0; } if(KeyNumKEY_UP) { Motor_Up(); LCD_ShowString(2,1, Motor: UP ); } else if(KeyNumKEY_DOWN) { Motor_Down(); LCD_ShowString(2,1, Motor: DOWN); } }我们定义一个全局变量KeyNum通过检测矩阵按键的返回值对应上面的宏定义“#define KEY_UP 4 ”等进行按键功能的选择这部分逻辑很简单检测到按下“KEY_UP”,就让步进电机上升同时LCD显示检测到按下“KEY_DOWN”,就让步进电机下降同时LCD显示。四、自动模式先看代码。//自动控制 if(mode1) { if(flag) { LCD_Clear(); LCD_ShowString(1, 1, AUTO MODE ); Delay(1500); LCD_Clear(); flag0; light_get(); } temper_get(); light_get(); hum_get(); //当检测到天气变坏自动收衣服 if(data_temper max_data_temper || data_light max_data_light || data_humidity max_data_humidity|| data_temper min_data_temper || data_light min_data_light || data_humidity min_data_humidity) { bad_weather 1; } else bad_weather 0; if(bad_weather1) //天气坏 { if(clothes_position 1) //如果衣服在外面则收衣服 { LCD_Clear(); LCD_ShowString(1, 1, bad weather!); LCD_ShowString(2, 1, up_clothes); for(i0;i5;i)Motor_Up(); // 收回衣服 clothes_position 0; // 更新状态衣服已收回} Delay(1000); } } else if(bad_weather0) //天气好 { if(clothes_position 0) //如果衣服在里面则晒衣服 { LCD_Clear(); LCD_ShowString(1, 1, good weather!); LCD_ShowString(2, 1, up_clothes); for(i0;i5;i)Motor_Down(); // 晒出衣服 clothes_position 1; // 更新状态衣服已晒 Delay(1000); } } }每次按下AUTO键就会将mode1,flag1。mode1则进入自动模式flag1只是一个初始页面显示告诉使用者“我们现在进入自动模式了哦~”但是防止一直重复提示造成闪烁所以增加标志位显示一次后flag0。因为mode1一直存在所以里面传感器数据获取是实时记录然后就是天气判断。设置两个判断标志位。uchar clothes_position1; //0:衣服已收回(初始状态), 1:衣服已晒出 uchar bad_weather2; //天气好坏标志0天气好1天气坏,2:不确定状态逻辑就是当检测到天气变坏让bad_weather1进入坏天气的循环后再进行一次判断“是否收了衣服”如果衣服已经收起来了那么就不用转动电机如果没有则转动电机。为防止电机一直转动每次使用一次则清除标志位。五、设置模式先看代码。void set() { setting_active 1; LCD_Clear(); LCD_ShowString(1, 1, SETTING MODE); // 显示当前设置类型和参数 LCD_ShowString(2, 1, (setting_type 0) ? MAX: : MIN:); LCD_ShowString(2, 5, (param_index 0) ? TEMP : (param_index 1) ? LIGHT : HUMID); // 显示当前值 if(param_index 0) { current_value (setting_type 0) ? max_data_temper : min_data_temper; } else if(param_index 1) { current_value (setting_type 0) ? max_data_light : min_data_light; } else { current_value (setting_type 0) ? max_data_humidity : min_data_humidity; } LCD_ShowNum(2, 11, current_value, 3); while(setting_active) { KeyNum MatrixKey(); if(KeyNum) { // 有按键按下 switch(KeyNum) { case KEY_NEXT: // 切换参数类型温度/光照/湿度 param_index (param_index 1) % 3; break; case KEY_SUB: // 切换最大值/最小值设置 setting_type (setting_type 1) % 2; break; case KEY_INC: // 增加数值 if(param_index 0) { // 温度 if(setting_type 0) max_data_temper; else min_data_temper; } else if(param_index 1) { // 光照 if(setting_type 0) max_data_light 10; else min_data_light 10; } else { // 湿度 if(setting_type 0) max_data_humidity; else min_data_humidity; } break; case KEY_DEC: // 减少数值 if(param_index 0) { // 温度 if(setting_type 0) max_data_temper--; else min_data_temper--; } else if(param_index 1) { // 光照 if(setting_type 0) max_data_light - 10; else min_data_light - 10; } else { // 湿度 if(setting_type 0) max_data_humidity--; else min_data_humidity--; } break; case KEY_SAVE: // 保存设置到EEPROM // 保存温度 AT24C02_WriteByte(0, max_data_temper); AT24C02_WriteByte(1, min_data_temper); // 保存光照16位值 AT24C02_WriteByte(2, max_data_light 8); AT24C02_WriteByte(3, max_data_light 0xFF); AT24C02_WriteByte(4, min_data_light 8); AT24C02_WriteByte(5, min_data_light 0xFF); // 保存湿度 AT24C02_WriteByte(6, max_data_humidity); AT24C02_WriteByte(7, min_data_humidity); LCD_ShowString(1, 1, SETTINGS SAVED!); Delay(1000); setting_active 0; // 退出设置 break; case KEY_SET: // 退出设置不保存 setting_active 0; break; } // 更新显示 LCD_Clear(); LCD_ShowString(1, 1, (setting_type 0) ? SET MAX: : SET MIN:); // 显示参数名称 LCD_ShowString(2, 1, (param_index 0) ? TEMP: : (param_index 1) ? LIGHT: : HUMID:); // 显示当前值 if(param_index 0) { current_value (setting_type 0) ? max_data_temper : min_data_temper; } else if(param_index 1) { current_value (setting_type 0) ? max_data_light : min_data_light; } else { current_value (setting_type 0) ? max_data_humidity : min_data_humidity; } LCD_ShowNum(2, 7, current_value, 3); // 显示操作提示 LCD_ShowString(1, 10, (setting_type 0) ? MAX : MIN); } // 添加短延时防止按键重复触发过快 Delay(50); } }我们这个系统还使用了24C02的存储模块使数据就算掉电也可以存储在芯片内部注释很清楚我就不多说了。然后还需要每次开机读取24C02的存储模块进行数据初始化。void Load_Settings() { // 读取温度设置 max_data_temper AT24C02_ReadByte(0); min_data_temper AT24C02_ReadByte(1); // 读取光照设置16位值 high_byte AT24C02_ReadByte(2); low_byte AT24C02_ReadByte(3); max_data_light (high_byte 8) | low_byte; high_byte AT24C02_ReadByte(4); low_byte AT24C02_ReadByte(5); min_data_light (high_byte 8) | low_byte; // 读取湿度设置 max_data_humidity AT24C02_ReadByte(6); min_data_humidity AT24C02_ReadByte(7); // 设置默认值如果EEPROM未初始化 if(max_data_temper 0xFF) max_data_temper 35; if(min_data_temper 0xFF) min_data_temper 10; if(max_data_light 0xFFFF) max_data_light 400; if(min_data_light 0xFFFF) min_data_light 100; if(max_data_humidity 0xFF) max_data_humidity 50; if(min_data_humidity 0xFF) min_data_humidity 5; }六、主函数主函数代码如下。void main() { LCD_Init(); SHT21_Init(); DS18B20_ConvertT(); //上电先转换一次温度防止第一次读数据错误 Delay(1000); //等待转换完成 Load_Settings(); while(1) { KeyNumMatrixKey(); //模式选择 if(KeyNumKEY_AUTO) { mode1; flag1; } else if(KeyNumKEY_MAN) { mode0; flag1; } else if(KeyNumKEY_SET) { set(); flag1; } //自动控制 if(mode1) { if(flag) { LCD_Clear(); LCD_ShowString(1, 1, AUTO MODE ); Delay(1500); LCD_Clear(); flag0; light_get(); } temper_get(); light_get(); hum_get(); //当检测到天气变坏自动收衣服 if(data_temper max_data_temper || data_light max_data_light || data_humidity max_data_humidity|| data_temper min_data_temper || data_light min_data_light || data_humidity min_data_humidity) { bad_weather 1; } else bad_weather 0; if(bad_weather1) //天气坏 { if(clothes_position 1) //如果衣服在外面则收衣服 { LCD_Clear(); LCD_ShowString(1, 1, bad weather!); LCD_ShowString(2, 1, up_clothes); for(i0;i5;i)Motor_Up(); // 收回衣服 clothes_position 0; // 更新状态衣服已收回} Delay(1000); } } else if(bad_weather0) //天气好 { if(clothes_position 0) //如果衣服在里面则晒衣服 { LCD_Clear(); LCD_ShowString(1, 1, good weather!); LCD_ShowString(2, 1, up_clothes); for(i0;i5;i)Motor_Down(); // 晒出衣服 clothes_position 1; // 更新状态衣服已晒 Delay(1000); } } } //手动控制 if(mode0) { if(flag) { LCD_Clear(); LCD_ShowString(1, 1, MANUAL MODE); flag0; } if(KeyNumKEY_UP) { Motor_Up(); LCD_ShowString(2,1, Motor: UP ); } else if(KeyNumKEY_DOWN) { Motor_Down(); LCD_ShowString(2,1, Motor: DOWN); } } } }好啦就这些第一次发可能有些讲得不太清楚欢迎大家询问。如果要完整的代码通过网盘分享的文件智能晾衣架系统.rar链接: https://pan.baidu.com/s/1hZIC3_-aK47RClQm4HRjpg?pwdujxv 提取码: ujxv--来自百度网盘超级会员v4的分享。

相关新闻