【51单片机】【protues仿真】基于51单片机智能窗帘系统

目录

一、主要功能

二、使用步骤

三、硬件资源

四、软件设计

五、实验现象

一、主要功能

1、LCD1602液晶显示时间、模式、光照值

2、按键设置模式、阈值、时间​

3、手动模式按键开关晾衣架

4、光敏模式按照光照阈值开关晾衣架

5、时间模式按设置时间开关晾衣架

二、使用步骤

本设计以51单片机为核心,结合光敏传感器、数模转换芯片、LCD1602显示屏及步进电机,实现智能窗帘的自动与手动控制。系统支持光照强度阈值调节、温度感知及定时功能,适用于智能家居场景。‌

三、硬件资源

1、51单片机核心模块

2、按键模块

3、数模转换传感器、MQ烟雾传感器模块

4、时钟模块

5、步进电机模块

6、LCD1602显示模块

四、软件设计

#include <reg52.h>

#include <intrins.h>

#define uchar unsigned char // 以后unsigned char就可以用uchar代替

#define uint unsigned int // 以后unsigned int 就可以用uint 代替

sbit ADC_CS = P1^6; // ADC0832的CS引脚

sbit ADC_CLK = P1^7; // ADC0832的CLK引脚

sbit ADC_DAT = P3^2; // ADC0832的DI/DO引脚

sbit SCK_P = P1^0; // 时钟芯片DS1302的SCK管脚

sbit SDA_P = P1^1; // 时钟芯片DS1302的SDA管脚

sbit RST_P = P1^2; // 时钟芯片DS1302的RST管脚

sbit LcdRs_P = P1^3; // 1602液晶的RS管脚

sbit LcdRw_P = P1^4; // 1602液晶的RW管脚

sbit LcdEn_P = P1^5; // 1602液晶的EN管脚

sbit KeyMode_P = P3^3; // 模式切换

sbit KeySet_P = P3^4; // 设置时间按键

sbit KeySet2_P = P3^5; // 设置时间模式的开关时间和光照控制强度

sbit KeyDown_P = P3^6; // 减按键

sbit KeyUp_P = P3^7; // 加按键

sbit Led_P = P2^0; // 指示灯

sbit YYOPEN = P3^0;

uchar gMode=1; // 1是手动模式,2是时间自动模式,3是亮度自动模式

uchar OpenHour = 18; // 开启窗帘的小时

uchar OpenMinute = 20; // 开启窗帘的分钟

uchar CloseHour = 10; // 关闭窗帘的小时

uchar CloseMinute = 30; // 关闭窗帘的分钟

uchar gLight = 80; // 窗帘开关的阈值

uchar gLightmax=99;

uchar code Clock[]={0x80,0x40,0x20,0x10}; // 步进电机顺时针旋转数组

uchar code AntiClock[]={0x10,0x20,0x40,0x80}; // 步进电机逆时针旋转数组

uchar TimeBuff[7]={25,9,1,6,18,30,40}; // 时间数组,默认2025年9月1日,星期五,18:30:40

// TimeBuff[0] 代表年份,范围00-99

// TimeBuff[1] 代表月份,范围1-12

// TimeBuff[2] 代表日期,范围1-31

// TimeBuff[3] 代表星期,范围1-7

// TimeBuff[4] 代表小时,范围00-23

// TimeBuff[5] 代表分钟,范围00-59

// TimeBuff[6] 代表秒钟,范围00-59

/*********************************************************/

// 毫秒级的延时函数,time是要延时的毫秒数

/*********************************************************/

void DelayMs(uint time)

{

uint i,j;

for(i=0;i<time;i++)

for(j=0;j<112;j++);

}

/*********************************************************/

// 1602液晶写命令函数,cmd就是要写入的命令

/*********************************************************/

void LcdWriteCmd(uchar cmd)

{

LcdRs_P = 0;

LcdRw_P = 0;

LcdEn_P = 0;

P0=cmd;

DelayMs(2);

LcdEn_P = 1;

DelayMs(2);

LcdEn_P = 0;

}

/*********************************************************/

// 1602液晶写数据函数,dat就是要写入的数据

/*********************************************************/

void LcdWriteData(uchar dat)

{

LcdRs_P = 1;

LcdRw_P = 0;

LcdEn_P = 0;

P0=dat;

DelayMs(2);

LcdEn_P = 1;

DelayMs(2);

LcdEn_P = 0;

}

/*********************************************************/

// 1602液晶初始化函数

/*********************************************************/

void LcdInit()

{

LcdWriteCmd(0x38); // 16*2显示,5*7点阵,8位数据口

LcdWriteCmd(0x0C); // 开显示,不显示光标

LcdWriteCmd(0x06); // 地址加1,当写入数据后光标右移

LcdWriteCmd(0x01); // 清屏

}

/*********************************************************/

// 液晶光标定位函数

/*********************************************************/

void LcdGotoXY(uchar line,uchar column)

{

// 第一行

if(line==0)

LcdWriteCmd(0x80+column);

// 第二行

if(line==1)

LcdWriteCmd(0x80+0x40+column);

}

/*********************************************************/

// 液晶显示内容的初始化

/*********************************************************/

void LcdShowInit()

{

LcdGotoXY(0,0);

LcdPrintStr("20 - - : ");

LcdGotoXY(1,0);

LcdPrintStr(" gz: ");

LcdGotoXY(1,0);

LcdPrintMode(gMode);

}

/*********************************************************/

// 初始化DS1302

/*********************************************************/

void DS1302_Init(void)

{

RST_P=0; // RST脚置低

SCK_P=0; // SCK脚置低

SDA_P=0; // SDA脚置低

}

/*********************************************************/

// 从DS1302读出一字节数据

/*********************************************************/

uchar DS1302_Read_Byte(uchar addr)

{

uchar i;

uchar temp;

RST_P=1;

for(i=0;i<8;i++)

{

if(addr&0x01)

SDA_P=1;

else

SDA_P=0;

SCK_P=1;

nop();

SCK_P=0;

nop();

addr=addr>> 1;

}

/* 读出该地址的数据 */

for(i=0;i<8;i++)

{

temp=temp>>1;

if(SDA_P)

temp|= 0x80;

else

temp&=0x7F;

SCK_P=1;

nop();

SCK_P=0;

nop();

}

RST_P=0;

return temp;

}

/*********************************************************/

// 向DS1302写入一字节数据

/*********************************************************/

void DS1302_Write_Byte(uchar addr, uchar dat)

{

uchar i;

RST_P = 1;

for(i=0;i<8;i++)

{

if(addr&0x01)

SDA_P=1;

else

SDA_P=0;

SCK_P=1;

nop();

SCK_P=0;

nop();

addr=addr>>1;

}

/* 写入数据:dat*/

for(i=0;i<8;i++)

{

if(dat&0x01)

SDA_P=1;

else

SDA_P=0;

SCK_P=1;

nop();

SCK_P=0;

nop();

dat=dat>>1;

}

RST_P=0;

}

/*********************************************************/

// 向DS1302写入时间数据

/*********************************************************/

void DS1302_Write_Time()

{

uchar i;

uchar temp1;

uchar temp2;

for(i=0;i<7;i++) // 十进制转BCD码

{

temp1=(TimeBuff[i]/10)<<4;

temp2=TimeBuff[i]%10;

TimeBuff[i]=temp1+temp2;

}

DS1302_Write_Byte(0x8E,0x00); // 关闭写保护

DS1302_Write_Byte(0x80,0x80); // 暂停时钟

DS1302_Write_Byte(0x8C,TimeBuff[0]); // 年

DS1302_Write_Byte(0x88,TimeBuff[1]); // 月

DS1302_Write_Byte(0x86,TimeBuff[2]); // 日

DS1302_Write_Byte(0x8A,TimeBuff[3]); // 星期

DS1302_Write_Byte(0x84,TimeBuff[4]); // 时

DS1302_Write_Byte(0x82,TimeBuff[5]); // 分

DS1302_Write_Byte(0x80,TimeBuff[6]); // 秒

DS1302_Write_Byte(0x80,TimeBuff[6]&0x7F); // 运行时钟

DS1302_Write_Byte(0x8E,0x80); // 打开写保护

}

/*********************************************************/

// 从DS1302读出时间数据

/*********************************************************/

void DS1302_Read_Time()

{

uchar i;

TimeBuff[0]=DS1302_Read_Byte(0x8D); // 年

TimeBuff[1]=DS1302_Read_Byte(0x89); // 月

TimeBuff[2]=DS1302_Read_Byte(0x87); // 日

TimeBuff[3]=DS1302_Read_Byte(0x8B); // 星期

TimeBuff[4]=DS1302_Read_Byte(0x85); // 时

TimeBuff[5]=DS1302_Read_Byte(0x83); // 分

TimeBuff[6]=(DS1302_Read_Byte(0x81))&0x7F; // 秒

for(i=0;i<7;i++) // BCD转十进制

{

TimeBuff[i]=(TimeBuff[i]/16)*10+TimeBuff[i]%16;

}

}

/*********************************************************/

// 按键扫描(设置当前时间)

/*********************************************************/

void KeyScanf1()

{

if(KeySet_P==0)

{

LcdGotoXY(0,13); // 显示秒钟的冒号

LcdWriteData(':');

LcdWriteCmd(0x0f); // 启动光标闪烁

LcdGotoXY(0,3); // 定位光标到年份闪烁

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

/* 调整年份 */

while(1)

{

if(KeyDown_P==0) // 如果减按键被下去

{

if(TimeBuff[0]>0) // 判断年份是否大于0

TimeBuff[0]--; // 是的话就减去1

LcdGotoXY(0,2); // 光标定位到年份的位置

LcdPrintNum(TimeBuff[0]); // 刷新显示改变后的年份

LcdGotoXY(0,3); // 定位光标到年份闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeyUp_P==0) // 如果加按键被下去

{

if(TimeBuff[0]<99) // 判断年份是否小于99

TimeBuff[0]++; // 是的话就加上1

LcdGotoXY(0,2); // 光标定位到年份的位置

LcdPrintNum(TimeBuff[0]); // 刷新显示改变后的年份

LcdGotoXY(0,3); // 定位光标到年份闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeySet_P==0)

{

break;

}

}

LcdGotoXY(0,6); // 定位光标到月份闪烁

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

/* 调整月份 */

while(1)

{

if(KeyDown_P==0) // 如果减按键被下去

{

if(TimeBuff[1]>1) // 判断月份是否大于1

TimeBuff[1]--; // 是的话就减去1

LcdGotoXY(0,5); // 光标定位到月份的位置

LcdPrintNum(TimeBuff[1]); // 刷新显示改变后的月份

LcdGotoXY(0,6); // 定位光标到月份闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeyUp_P==0) // 如果加按键被下去

{

if(TimeBuff[1]<12) // 判断月份是否小于12

TimeBuff[1]++; // 是的话就加上1

LcdGotoXY(0,5); // 光标定位到月份的位置

LcdPrintNum(TimeBuff[1]); // 刷新显示改变后的月份

LcdGotoXY(0,6); // 定位光标到月份闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeySet_P==0)

{

break;

}

}

LcdGotoXY(0,9); // 定位光标到日期闪烁

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

/* 调整日期 */

while(1)

{

if(KeyDown_P==0) // 如果减按键被下去

{

if(TimeBuff[2]>1) // 判断日期是否大于1

TimeBuff[2]--; // 是的话就减去1

LcdGotoXY(0,8); // 光标定位到日期的位置

LcdPrintNum(TimeBuff[2]); // 刷新显示改变后的日期

LcdGotoXY(0,9); // 定位光标到日期闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeyUp_P==0) // 如果加按键被下去

{

if(TimeBuff[2]<31) // 判断日期是否小于31

TimeBuff[2]++; // 是的话就加上1

LcdGotoXY(0,8); // 光标定位到日期的位置

LcdPrintNum(TimeBuff[2]); // 刷新显示改变后的日期

LcdGotoXY(0,9); // 定位光标到日期闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeySet_P==0)

{

break;

}

}

LcdGotoXY(0,12); // 定位光标到小时闪烁

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

/* 调整小时 */

while(1)

{

if(KeyDown_P==0) // 如果减按键被下去

{

if(TimeBuff[4]>0) // 判断小时是否大于0

TimeBuff[4]--; // 是的话就减去1

LcdGotoXY(0,11); // 光标定位到小时的位置

LcdPrintNum(TimeBuff[4]); // 刷新显示改变后的小时

LcdGotoXY(0,12); // 定位光标到小时闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeyUp_P==0) // 如果加按键被下去

{

if(TimeBuff[4]<23) // 判断小时是否小于23

TimeBuff[4]++; // 是的话就加上1

LcdGotoXY(0,11); // 光标定位到小时的位置

LcdPrintNum(TimeBuff[4]); // 刷新显示改变后的小时

LcdGotoXY(0,12); // 定位光标到小时闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeySet_P==0)

{

break;

}

}

LcdGotoXY(0,15); // 定位光标到分钟闪烁

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

/* 调整分钟 */

while(1)

{

if(KeyDown_P==0) // 如果减按键被下去

{

if(TimeBuff[5]>0) // 判断分钟是否大于0

TimeBuff[5]--; // 是的话就减去1

LcdGotoXY(0,14); // 光标定位到分钟的位置

LcdPrintNum(TimeBuff[5]); // 刷新显示改变后的分钟

LcdGotoXY(0,15); // 定位光标到分钟闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeyUp_P==0) // 如果加按键被下去

{

if(TimeBuff[5]<59) // 判断分钟是否小于59

TimeBuff[5]++; // 是的话就加上1

LcdGotoXY(0,14); // 光标定位到分钟的位置

LcdPrintNum(TimeBuff[5]); // 刷新显示改变后的分钟

LcdGotoXY(0,15); // 定位光标到分钟闪烁

DelayMs(300); // 延时0.3秒左右

}

if(KeySet_P==0)

{

break;

}

}

/* 退出前的设置 */

LcdWriteCmd(0x0C); // 关闭光标闪烁

DS1302_Write_Time(); // 把新设置的时间值存入DS1302芯片

DelayMs(10); // 延时等待,消除按键按下的抖动

while(!KeySet_P); // 等待按键释放

DelayMs(10); // 延时等待,消除按键松开的抖动

}

}

/*********************************************************/

// 按键扫描(模式切换)

/*********************************************************/

void KeyScanf3()

{

if(KeyMode_P==0)

{

gMode++; // 切换到下一模式

if(gMode==4) // 如果到尽头了

gMode=1; // 回到第一种模式

LcdGotoXY(1,0); // 光标定位

LcdPrintMode(gMode); // 显示模式

DelayMs(10); // 去除按键按下的抖动

while(!KeyMode_P); // 等待按键是否

DelayMs(10); // 去除按键松开的抖动

}

}

void main()

{

uchar light;

LcdInit(); // 执行液晶初始化

DS1302_Init(); // 时钟芯片的初始化

LcdShowInit(); // 液晶显示内容的初始化

while(1)

{

DS1302_Read_Time(); // 获取当前时钟芯片的时间,存在数组time_buf中

FlashTime(); // 刷新时间显示

light=Get_ADC0832(); // 读取光照强度

light=light/2.5; // 缩小光照检测结果(在0-99)

if(light>99) // 如果大于99

light=99; // 则依然保持99

LcdGotoXY(1,14); // 光标定位

LcdPrintNum(light); // 显示光照强度

KeyScanf1(); // 按键扫描(时间的设置)

KeyScanf2(); // 按键扫描(阈值的设置)

KeyScanf3(); // 按键扫描(模式切换)

DelayMs(100); // 延时0.1秒

}

}

五、实验现象

演示视频:

【51单片机-B063】【protues仿真】基于51单片机智能窗帘仿真

相关推荐
懒羊羊不懒@3 小时前
Java基础语法—最小单位、及注释
java·c语言·开发语言·数据结构·学习·算法
SundayBear4 小时前
嵌入式进阶:C语言内联汇编
c语言·开发语言·汇编
我先去打把游戏先5 小时前
ESP32学习笔记(基于IDF):IOT应用——WIFI连接
笔记·单片机·嵌入式硬件·mcu·物联网·学习·esp32
云里物里5 小时前
云里物里智能物联电子标签系统助力零售品牌力提升
物联网·零售·电子价签·esl电子标签·电子标签
北京耐用通信6 小时前
破解工业通信瓶颈:耐达讯自动化Modbus转Profinet网关连接驱动器的奥秘
人工智能·物联网·网络协议·自动化·信息与通信
小龙报7 小时前
《算法通关指南---C++编程篇(2)》
c语言·开发语言·数据结构·c++·程序人生·算法·学习方法
清风6666668 小时前
基于单片机的简易智能衣架控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
北京耐用通信8 小时前
电力自动化新突破:Modbus如何变身Profinet?智能仪表连接的终极解决方案
人工智能·物联网·网络安全·自动化·信息与通信
酷飞飞8 小时前
I2C软实现基于GD32F407VE的天空星的配置
单片机·嵌入式硬件