基于51单片机的智能家居窗帘控制系统仿真

仿真图:

在这里插入图片描述

芯片/模块的特点:

AT89C52简介:
AT89C52是一款经典的8位单片机,是意法半导体(STMicroelectronics)公司生产的一系列单片机之一。它基于8051内核,并具有许多与其兼容的特性。

AT89C52的主要特点如下:

内部存储器:AT89C52具有8KB的闪存(Flash)存储器,可用于存储用户程序和数据。这些存储器的内容可以通过编程器进行编程和擦除。

RAM存储器:AT89C52配备了256字节的随机存取存储器(RAM),用于暂存数据和程序的变量。

外部扩展性:AT89C52支持多种外部扩展设备的连接,包括外部存储器(如RAM、EEPROM)和外设(如ADC、LCD、UART等),通过外部硬件连接,可以扩展单片机的功能和应用。

通用I/O引脚:AT89C52拥有32个可编程的通用输入/输出引脚,可用于连接外部设备和与其他芯片进行通信。

定时器/计数器:AT89C52内置了3个16位定时器/计数器和一个可编程的串行定时器/计数器。这些计时器/计数器可用于实现定时功能、生成脉冲信号、测量时间间隔等。0

串行通信:AT89C52支持串行通信接口,包括UART(串行异步通信)和SPI(串行外设接口),便于与其他设备进行数据通信和交互。

低功耗模式:AT89C52具有多种低功耗模式,如空闲模式和电源下模式,在不需要执行任务的时候可以将CPU进入低功耗状态以节省能量。

宽电源电压范围:AT89C52的工作电压范围通常为4.0V至5.5V,可以满足大多数应用需求。

ADC0832特点:

8位分辨率:ADC0832可以将模拟输入信号转换为8位数字输出。这意味着它可以将模拟信号划分为256个不同的离散电平,提供相对较低的分辨率。

双通道输入:ADC0832具有两个模拟输入通道,使其能够同时转换两个模拟信号。这对于需要同时测量多个信号的应用非常有用。

内部参考电压源:ADC0832提供了一个内部的参考电压源,它可以用作模拟输入信号的参考电压。这样可以简化外部电路设计,并提供更稳定和准确的参考电压。

串行输出:ADC0832通过串行接口(SPI或I2C)输出转换结果。这种串行输出形式使其与微控制器或其他数字设备的通信变得更加简单和方便。

低功耗:ADC0832具有较低的功耗特性,适合在低功耗应用中使用。

可编程时钟频率:ADC0832的转换速度可以通过控制输入时钟频率进行编程。这使得可以根据应用的需求调整转换速度,并平衡转换精度和速度。

内部自校准:ADC0832具有内部自校准电路,可以降低转换误差,并提供更准确的转换结果。

ULN2003特点:
高电流驱动能力:ULN2003具有高电流驱动能力,每个输出通道可以提供500mA的峰值输出电流。这使得它可以直接驱动各种继电器、步进电机和其他高功率负载。

集成综合保护:ULN2003集成了综合保护功能,包括输出耐压保护二极管、过电流保护电路和反嵌二极管等。它可以保护芯片和外部设备免受不良电气现象的损害。

多通道输出:ULN2003具有7个独立的输出通道,可以同时控制多个负载。每个通道都具有独立的输入引脚,可以通过输入信号来控制相应的输出通道。

低功耗:ULN2003在工作时功耗较低,适用于对功耗要求较高的应用。它还具有低静态电流消耗,即使在不工作时也能保持低功耗状态。

简单易用:ULN2003非常容易使用,只需要连接输入控制信号和负载即可。它可以直接与TTL或CMOS逻辑电平兼容,并且不需要额外的外部组件。

多种封装形式:ULN2003可以提供不同的封装形式,如多引脚直插式封装(DIP)和表面贴装技术(SMT)封装。这使得ULN2003适应不同应用的安装需求。

DS1302特点:
高精度时间计数:DS1302能够提供高精度的实时时钟计数,可以记录年、月、日、星期、小时、分钟和秒等时间信息。它内部集成了晶体振荡器,提供稳定的时钟信号。

低功耗设计:DS1302采用低功耗设计,可以在低功耗模式下运行,有效延长电池寿命。即使在停电情况下,它也能保持时间数据,并通过外部连接电池继续提供计时功能。

串行接口:DS1302通过串行实时时钟接口(SPI)进行通信和控制。使用少数几个引脚,可以与主控器件进行数据交换和时钟同步。

容易集成:DS1302集成了时钟计数和RAM存储器功能,并具有简单的接口和命令,容易与各种微控制器和单片机集成。它不需要复杂的控制信号,可以通过简单的读写命令进行操作。

可编程控制功能:DS1302具有可编程的控制功能,可以设置闹钟、写保护等特殊功能。它还支持多种时间格式的选择,例如24小时制或12小时制。

温度补偿:DS1302内置温度补偿功能,可以校正温度对时钟频率的影响,提高时钟计数的准确性。

高稳定性和抗震动能力:DS1302具有高稳定性和抗震动能力,适用于各种工业和消费类应用场景。

主程序:

/*************************************************************
                        智能窗帘

补充说明:
***************************************************************/
#include "reg52.h"
#include "LCD1602.h"
#include "DS1302.h"
#include "28BYJ48.h"
#include "eeprom52.h"
#include "tlc0832.h"
#include "infrared.h"
#include "delay.h"

#define uchar unsigned char //宏定义
#define uint unsigned int

/*******************引脚定义*********************/
sbit KEY_MODE    = P3^3;   //设置键
sbit KEY_ADD     = P3^4;   //加值键
sbit KEY_SUB     = P3^6;   //减值键
sbit KEY_ENTER   = P3^5;   //确定键
sbit BUZZER      = P2^0;   //蜂鸣器

sbit SW1      = P1^3;   //全开 限位开关
/*******************变量定义*********************/
uchar light_up = 80;
uchar light_down = 30; //存储光强上、下限值
uchar set_f = 0;            //设置选择标记,=0非设置,=1设置年,=2设置月,=3设置日,=4设置时,=5设置分,=6设置秒
                            //              =7设置定时-时,=8设置定时-分,=9设置定时-开关,=10设置窗帘打开/关闭
                            //				=11设置光强上限,=12设置光强下限,=13设置校准窗帘位置

unsigned char dis[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
uchar num;   //计数变量
uchar ds[4] = {8, 30, 16, 20}; //定时控制开时、分、控制关时、分

uchar mode = 0; //模式标记。=0手动,=1定时,=2光控
uchar light;    //存储光强

uint moto_num = 0;                                        //记录窗帘位置
uchar motor_state = 0;                                     //窗帘状态,=0已经关闭,=1处于运转过程中,=2已经打开
bit moto_f = 0;                                           //标记当前控制状态,=0控制关,=1控制开
uchar code display_mode[3][3] = {"SD\0", "DS\0", "GK\0"}; //显示模式

void WriteData(void)
{
    SectorErase(0x2000);    //擦除扇区
    WriteByte(0x2000, ds[0]);  //写入【定时时间】数据保存
    WriteByte(0x2001, ds[1]); //写入【定时时间】数据保存存
    WriteByte(0x2002, ds[2]); //写入【定时时间】数据保存存
    WriteByte(0x2003, ds[3]); //写入【定时时间】数据保存存
    WriteByte(0x2004, light_up); //写入【光强上限值】数据保存
    WriteByte(0x2005, light_down); //写入【光强下限值】数据保存
    WriteByte(0x2006, mode); //写入【模式】数据保存
}

void ReadData(void)
{
    ds[0] = ReadByte(0x2000);      //读取存储的【定时时间】数据
    ds[1] = ReadByte(0x2001);      //读取存储的【定时时间】数据
    ds[2] = ReadByte(0x2002);      //读取存储的【定时时间】数据
    ds[3] = ReadByte(0x2003);      //读取存储的【定时时间】数据
    light_up = ReadByte(0x2004);   //读取存储的【光强上限值】数据
    light_down = ReadByte(0x2005); //读取存储的【光强下限值】数据
    mode = ReadByte(0x2006);       //读取存储的【模式】数据
}

/********************************************************
函数名称:void display(void)
函数作用:正常显示日期、时间、光强等函数
参数说明:
********************************************************/
void display(void)
{
    uint t[3];

    //显示时间
    LCD_DispOneChar(0, 1, dis[time[2] / 10]); //显示时【0:表示第1列,2:表示第2行,所有关于显示都一样】
    LCD_DispOneChar(1, 1, dis[time[2] % 10]);
    LCD_DispOneChar(2, 1, ':');
    LCD_DispOneChar(3, 1, dis[time[1] / 10]); //显示分【3:表示第4列,2:表示第2行,所有关于显示都一样】
    LCD_DispOneChar(4, 1, dis[time[1] % 10]);
    LCD_DispOneChar(5, 1, ':');
    LCD_DispOneChar(6, 1, dis[time[0] / 10]); //显示秒
    LCD_DispOneChar(7, 1, dis[time[0] % 10]);

    //显示日期
    LCD_DispOneChar(0, 0, dis[time[6] / 10]); //显示年
    LCD_DispOneChar(1, 0, dis[time[6] % 10]);
    LCD_DispOneChar(2, 0, '/');
    LCD_DispOneChar(3, 0, dis[time[4] / 10]); //显示月
    LCD_DispOneChar(4, 0, dis[time[4] % 10]);
    LCD_DispOneChar(5, 0, '/');
    LCD_DispOneChar(6, 0, dis[time[3] / 10]); //显示日
    LCD_DispOneChar(7, 0, dis[time[3] % 10]);

    //显示模式
    LCD_DispStr(9, 0, display_mode[mode]);

    //显示光强
    if (light > 99)
        LCD_DispOneChar(12, 0, dis[light / 100]);
    else
        LCD_DispOneChar(12, 0, ' ');
    LCD_DispOneChar(13, 0, dis[light % 100 / 10]);
    LCD_DispOneChar(14, 0, dis[light % 10]);
    LCD_DispOneChar(15, 0, '%');

    //光强控制
    if (mode == 2) //光控模式
    {
        if ((light >= light_up || light < light_down)) //判断实际光强是否【大于等于上限 或 小于下限值】、当前处于打开状态
        {
            moto_f = 0;     //标记当前控制窗帘关闭
            motor_state = 1; //标记窗帘处于运转过程中
        }
        else if ((light < light_up && light >= light_down)) //判断实际光强是否【大于等于下限值 并且 小于上限】、当前处于关闭状态
        {
            moto_f = 1;     //标记当前控制窗帘打开
            motor_state = 1; //标记窗帘处于运转过程中
        }
    }
    else if (mode == 1) //定时模式
    {
        t[0] = time[2] * 60 + time[1]; //当前时间
        t[1] = ds[0] * 60 + ds[1];     //窗帘开启时间点
        t[2] = ds[2] * 60 + ds[3];     //窗帘关闭时间点

        if (t[1] < t[2]) //0时 --> t[1] --> t[2] --> 24时
        {
            if (t[0] >= t[1] && t[0] <= t[2])
                moto_f = 1; //标记当前需要打开窗帘
            else
                moto_f = 0; //标记当前需要关闭窗帘
        }
        else //0时 --> t[2] --> t[1] --> 24时
        {
            if (t[0] >= t[1] || t[0] <= t[2])
                moto_f = 1; //标记当前需要打开窗帘
            else
                moto_f = 0; //标记当前需要关闭窗帘
        }
        motor_state = 1; //标记窗帘处于运转过程中
    }
    
    if (moto_f == 0 && motor_state != 0 && SW1 != 0) //控制关闭窗帘
    {
        if (moto_num == 0) //控制次数减到0时,表示窗户已经关闭完成
        {
            BYJ48 = BYJ48 & 0xf0; //关闭步进电机
            motor_state = 0;    //标记窗帘处于关闭状态
            LCD_DispStr(10, 1, "ZT:OFF");
        }
        else
        {
            motor_z();  //步进电机正转
            moto_num--; //控制次数-1
            LCD_DispStr(10, 1, "-->OFF");
        }
    }
    else if (moto_f == 1 && motor_state != 2 && SW1 != 0) //控制打开窗帘
    {
        if (moto_num >= 192) //控制次数加到192时,表示窗户已经打开完成
        {
            BYJ48 = BYJ48 & 0xf0; //关闭步进电机
            motor_state = 2;    //标记窗帘处于打开状态
            LCD_DispStr(10, 1, "ZT:ON ");
        }
        else
        {
            motor_f();  //步进电机反转
            moto_num++; //控制次数+1
            LCD_DispStr(10, 1, "-->ON ");
        }
    }
    else //窗户处于停止状态
    {
        BYJ48 = BYJ48 & 0xf0; //关闭步进电机
        if (motor_state == 0)
            LCD_DispStr(10, 1, "ZT:OFF");
        else if (motor_state == 2)
            LCD_DispStr(10, 1, "ZT:ON ");
    }
}
/********************************************************
函数名称:void display2(void)
函数作用:显示调整日期、时间函数
参数说明:
********************************************************/
void display2()
{
    num++;

    LCD_DispStr(1, 0, "Date:");
    LCD_DispStr(1, 1, "Time:");

    if (num % 2 == 0) //偶数次显示,奇数次不显示。这样就会有闪烁效果,可以清楚看到当前设置的是哪个值
    {
        LCD_DispOneChar(6, 0, dis[time[6] / 10]); //显示年
        LCD_DispOneChar(7, 0, dis[time[6] % 10]);
        LCD_DispOneChar(8, 0, '/');
        LCD_DispOneChar(9, 0, dis[time[4] / 10]); //显示月
        LCD_DispOneChar(10, 0, dis[time[4] % 10]);
        LCD_DispOneChar(11, 0, '/');
        LCD_DispOneChar(12, 0, dis[time[3] / 10]); //显示日
        LCD_DispOneChar(13, 0, dis[time[3] % 10]);

        LCD_DispOneChar(6, 1, dis[time[2] / 10]); //显示时
        LCD_DispOneChar(7, 1, dis[time[2] % 10]);
        LCD_DispOneChar(8, 1, ':');
        LCD_DispOneChar(9, 1, dis[time[1] / 10]); //显示分
        LCD_DispOneChar(10, 1, dis[time[1] % 10]);
        LCD_DispOneChar(11, 1, ':');
        LCD_DispOneChar(12, 1, dis[time[0] / 10]); //显示秒
        LCD_DispOneChar(13, 1, dis[time[0] % 10]);
    }
    else //奇数次不显示
    {
        switch (set_f) //根据当前设置的内容,对应位置闪烁
        {
        case 1:
            LCD_DispStr(6, 0, "  ");
            break;
        case 2:
            LCD_DispStr(9, 0, "  ");
            break;
        case 3:
            LCD_DispStr(12, 0, "  ");
            break;
        case 4:
            LCD_DispStr(6, 1, "  ");
            break;
        case 5:
            LCD_DispStr(9, 1, "  ");
            break;
        case 6:
            LCD_DispStr(12, 1, "  ");
            break;
        default:
            break;
        }
    }
}
/********************************************************
函数名称:void display3(void)
函数作用:显示调整定时时间函数
参数说明:
********************************************************/
void display3()
{
    num++;
    LCD_DispStr(0, 0, "Sets timer time:");
    LCD_DispStr(7, 1, "--");
    if (num % 2 == 0) //偶数次显示,奇数次不显示。这样就会有闪烁效果,可以清楚看到当前设置的是哪个值
    {
        LCD_DispOneChar(1, 1, dis[ds[0] / 10]); //显示定时开-时
        LCD_DispOneChar(2, 1, dis[ds[0] % 10]);
        LCD_DispOneChar(3, 1, ':');
        LCD_DispOneChar(4, 1, dis[ds[1] / 10]); //显示定时开-分
        LCD_DispOneChar(5, 1, dis[ds[1] % 10]);

        LCD_DispOneChar(10, 1, dis[ds[2] / 10]); //显示定时关-时
        LCD_DispOneChar(11, 1, dis[ds[2] % 10]);
        LCD_DispOneChar(12, 1, ':');
        LCD_DispOneChar(13, 1, dis[ds[3] / 10]); //显示定时关-分
        LCD_DispOneChar(14, 1, dis[ds[3] % 10]);
    }
    else //奇数次不显示
    {
        switch (set_f) //根据当前设置的内容,对应位置闪烁
        {
        case 7:
            LCD_DispStr(1, 1, "  ");
            break;
        case 8:
            LCD_DispStr(4, 1, "  ");
            break;
        case 9:
            LCD_DispStr(10, 1, "  ");
            break;
        case 10:
            LCD_DispStr(13, 1, "  ");
            break;
        default:
            break;
        }
    }
}
/********************************************************
函数名称:void display4(void)
函数作用:显示调整光强控制函数
参数说明:
********************************************************/
void display4(void)
{
    num++;

    LCD_DispStr(0, 0, "Light_Up  :");
    LCD_DispStr(0, 1, "Light_Down:");
    LCD_DispOneChar(15, 0, '%');
    LCD_DispOneChar(15, 1, '%');
    if (num % 2 == 0) //偶数次显示,奇数次不显示。这样就会有闪烁效果,可以清楚看到当前设置的是哪个值
    {
        LCD_DispOneChar(12, 0, dis[light_up / 100]); //显示光强上限值
        LCD_DispOneChar(13, 0, dis[light_up % 100 / 10]);
        LCD_DispOneChar(14, 0, dis[light_up % 10]);

        LCD_DispOneChar(12, 1, dis[light_down / 100]); //显示光强下限值
        LCD_DispOneChar(13, 1, dis[light_down % 100 / 10]);
        LCD_DispOneChar(14, 1, dis[light_down % 10]);
    }
    else //奇数次不显示
    {
        switch (set_f) //根据当前设置的内容,对应位置闪烁
        {
        case 11:
            LCD_DispStr(12, 0, "   ");
            break;
        case 12:
            LCD_DispStr(12, 1, "   ");
            break;
        default:
            break;
        }
    }
}
/********************************************************
函数名称:void KeyScan(void)
函数作用:按键查询处理函数
参数说明:
********************************************************/
void KeyScan(void)
{
    //设置键
    if (hw_data == 'B' || KEY_MODE == 0) //按键按下
    {
        DelayMs(50);                        //延时消抖
        if (hw_data == 'B' || KEY_MODE == 0) //再次确认按键按下
        {
            BUZZER = 0;
            DelayMs(130);
            BUZZER = 1;
            BYJ48 = BYJ48 & 0xf0; //关闭步进电机
            if (set_f == 6)    //日期、时间校准完
            {
                LCD_Clear(); //清除屏幕显示
                DelayMs(4);;        //延时等待全部清除完毕
                set_f = 13;              //跳到校准窗帘状态
            }
            else if (set_f == 0) //进入设置先清除屏幕,显示设置部分
            {
                Write_DS1302(WRITE_PROTECT, 0X00); //禁止DS1302写保护
                LCD_Clear();          //清除屏幕显示
                DelayMs(4);;                 //延时等待全部清除完毕
                switch (mode)                     //根据对应的模式,设置对应的参数
                {
                case 0:
                    set_f = 1;
                    break; //手动模式
                case 1:
                    set_f = 7;
                    break; //定时模式
                case 2:
                    set_f = 11;
                    break; //光控模式
                default:
                    break;
                }
            }
            else if (set_f == 10 || set_f == 12) //进入设置日期时间
            {
                LCD_Clear(); //清除屏幕显示
                DelayMs(4);;        //延时等待全部清除完毕
                set_f = 1;               //进入校准时间
            }
            else
                set_f++; //设置变量+1
            if (set_f > 12)
            {
                set_f = 0; //重置设置变量

                Write_DS1302(WRITE_MINUTE, (time[1] / 10) * 16 + time[1] % 10); //将设置好的时间写入DS1302
                Write_DS1302(WRITE_SECOND, (time[0] / 10) * 16 + time[0] % 10);

                WriteData();

                LCD_Clear(); //清除屏幕显示
                DelayMs(4);;        //延时等待全部清除完毕
            }
        }
        while (!KEY_MODE)
            ; //等待按键松开
    }
    //确定
    if (hw_data == '|' || KEY_ENTER == 0) //按键按下
    {
        DelayMs(50);                                        //延时消抖
        if ((hw_data == '|' || KEY_ENTER == 0) && set_f != 0) //再次确认按键按下
        {
            BUZZER = 0;
            DelayMs(130);
            BUZZER = 1;
            if (set_f < 7)
            {
                Write_DS1302(WRITE_MINUTE, (time[1] / 10) * 16 + time[1] % 10); //将设置好的时间写入DS1302
                Write_DS1302(WRITE_SECOND, (time[0] / 10) * 16 + time[0] % 10);
            }
            set_f = 0; //重置设置变量

            WriteData();

            LCD_Clear(); //清除屏幕显示
            DelayMs(4);;        //延时等待全部清除完毕
        }
        else if (KEY_ENTER == 0 && set_f == 0) //切换模式
        {
            mode++;              //模式变量+1
            if (mode >= 3)       //最大3个模式
                mode = 0;        //重新回到第1个模式
            if (motor_state == 1) //如果处于运转过程中
            {
                if (moto_f == 0) //控制步进电机关闭
                    motor_state = 0;
                else //控制步进电机打开
                    motor_state = 2;
            }
            WriteData();
        }
        while (!KEY_ENTER)
            ; //等待按键松开
    }
    //加键	,下面减键内容一样
    if (hw_data == '+' || KEY_ADD == 0)
    {
        DelayMs(150);
        if ((hw_data == '+' || KEY_ADD == 0) && set_f != 0)
        {
            BUZZER = 0;
            DelayMs(130);
            BUZZER = 1;
            if (set_f == 1) //设置年
            {
                time[6]++;                                    //年份+1
                time[6] = (time[6] / 10) * 16 + time[6] % 10; //将年份转换成16进制,例如:16年-->0x16
                if (time[6] > 0x99)                           //判断是否达到最大年份79年
                    time[6] = 0x00;                           //重新回到00年
                Write_DS1302(WRITE_YEA, time[6]);              //将设置好的年份写入到DS1302存储起来
            }

            if (set_f == 2) //设置月
            {
                time[4]++;                                    //月份+1
                time[4] = (time[4] / 10) * 16 + time[4] % 10; //将月份转换成16进制,例如:08月-->0x08
                if (time[4] > 0x12)                           //判断是否达到最大月份12月
                    time[4] = 0x01;                           //重新回到01月
                Write_DS1302(WRITE_MONTH, time[4]);            //将设置好的月份写入到DS1302存储起来
            }

            if (set_f == 3) //设置日
            {
                time[3]++;                                                                                                          //日份+1
                time[3] = (time[3] / 10) * 16 + time[3] % 10;                                                                       //将日份转换成16进制,例如:15日-->0x15
                if (time[4] == 1 || time[4] == 3 || time[4] == 5 || time[4] == 7 || time[4] == 8 || time[4] == 10 || time[4] == 12) //1,3,5,7,8,10,12为大月,每月31天
                {
                    if (time[3] > 0x31) //判断是否达到最大日份31日
                        time[3] = 0x01; //重新回到01日
                }
                else //否者2,4,6,9,11为小月
                {
                    if (time[4] == 0x02) //如果是2月,需要区分闰、平年
                    {
                        if (((!(time[6] % 4) && (time[6] % 100)) || !(time[6] % 400)) == 0) //如果闰年
                        {
                            if (time[3] > 0x28) //判断是否达到最大日份28日
                                time[3] = 0x01; //重新回到01日
                        }
                        else //否者平年
                        {
                            if (time[3] > 0x29) //判断是否达到最大日份29日
                                time[3] = 0x01; //重新回到01日
                        }
                    }
                    else //否者为剩下的月份
                    {
                        if (time[3] > 0x30) //判断是否达到最大日份30日
                            time[3] = 0x01; //重新回到01日
                    }
                }
                Write_DS1302(WRITE_DAY, time[3]); //将设置好的日份写入到DS1302存储起来
            }

            if (set_f == 4) //设置时,同上
            {
                time[2]++;
                time[2] = (time[2] / 10) * 16 + time[2] % 10;
                if (time[2] > 0x23)
                    time[2] = 0x00;
                Write_DS1302(WRITE_HOUR, time[2]);
            }

            if (set_f == 5) //设置分,同上
            {
                time[1]++;
                time[1] = (time[1] / 10) * 16 + time[1] % 10;
                if (time[1] > 0x59)
                    time[1] = 0x00;
                Write_DS1302(WRITE_MINUTE, time[1]);
            }

            if (set_f == 6) //设置秒,同上
            {
                time[0]++;
                time[0] = (time[0] / 10) * 16 + time[0] % 10;
                if (time[0] > 0x59)
                    time[0] = 0x00;
                Write_DS1302(WRITE_SECOND, time[0]);
            }

            if (set_f == 7) //设置定时起始时间——时
            {
                if (ds[0] < 23)
                    ds[0]++;
                else
                    ds[0] = 0;
            }

            if (set_f == 8) //设置定时起始时间——分
            {
                if (ds[1] < 59)
                    ds[1]++;
                else
                    ds[1] = 0;
            }

            if (set_f == 9) //设置定时结束时间——时
            {
                if (ds[2] < 23)
                    ds[2]++;
                else
                    ds[2] = 0;
            }

            if (set_f == 10) //设置定时结束时间——分
            {
                if (ds[3] < 59)
                    ds[3]++;
                else
                    ds[3] = 0;
            }
            if (set_f == 11) //设置 光强上限值
            {
                if (light_up < 99) //最大值可设置99%
                    light_up++;    //光强上限值+1%
            }

            if (set_f == 12) //设置 光强下限值
            {
                if (light_down < 99 && light_down + 1 < light_up) //最大值可设置99%,并且下限不能超过上限
                    light_down++;                                 //光强下限值+1%
            }
        }
        else if ((hw_data == '+' || KEY_ADD == 0) && set_f == 0 && mode == 0) //手动控制窗帘打开
        {
            if (motor_state == 1 && moto_f == 1) //已经在控制状态。并且控制打开
                motor_state = 2;                 //停止运作
            else
            {
                if (moto_num < 192) //判断是否不处于打开状态
                {
                    moto_f = 1;     //控制开闭窗
                    motor_state = 1; //开启运作
                }
            }
            while (!KEY_ADD)
                ; //等待按键松开
        }
    }
    //减键
    if (hw_data == '-' || KEY_SUB == 0)
    {
        DelayMs(150);
        if ((hw_data == '-' || KEY_SUB == 0) && set_f != 0)
        {
            BUZZER = 0;
            DelayMs(130);
            BUZZER = 1;
            if (set_f == 1) //设置年
            {
                if (time[6] == 0) //判断当前是否为00年
                    time[6] = 99; //如果是回到最大年份:79年
                else
                    time[6]--;                                //否者,年份-1
                time[6] = (time[6] / 10) * 16 + time[6] % 10; //将年份转换成16进制,例如:16年-->0x16
                Write_DS1302(WRITE_YEA, time[6]);              //将设置好的年份写入到DS1302存储起来
            }

            if (set_f == 2) //设置月,同上
            {
                if (time[4] == 1)
                    time[4] = 12;
                else
                    time[4]--;
                time[4] = (time[4] / 10) * 16 + time[4] % 10;
                Write_DS1302(WRITE_MONTH, time[4]);
            }

            if (set_f == 3) //设置日,同上
            {
                time[3]--;
                time[3] = (time[3] / 10) * 16 + time[3] % 10;
                if (time[4] == 1 || time[4] == 3 || time[4] == 5 || time[4] == 7 || time[4] == 8 || time[4] == 10 || time[4] == 12)
                {
                    if (time[3] < 0x01)
                        time[3] = 0x31;
                }
                else
                {
                    if (time[4] == 0x02)
                    {
                        if (((!(time[6] % 4) && (time[6] % 100)) || !(time[6] % 400)) == 0)
                        {
                            if (time[3] < 0x01)
                                time[3] = 0x28;
                        }
                        else
                        {
                            if (time[3] < 0x01)
                                time[3] = 0x29;
                        }
                    }
                    else
                    {
                        if (time[3] < 0x01)
                            time[3] = 0x30;
                    }
                }
                Write_DS1302(WRITE_DAY, time[3]);
            }

            if (set_f == 4) //设置时,同上
            {
                if (time[2] == 0)
                    time[2] = 23;
                else
                    time[2]--;
                time[2] = (time[2] / 10) * 16 + time[2] % 10;
                Write_DS1302(WRITE_HOUR, time[2]);
            }

            if (set_f == 5) //设置分,同上
            {
                if (time[1] == 0)
                    time[1] = 59;
                else
                    time[1]--;
                time[1] = (time[1] / 10) * 16 + time[1] % 10;
                Write_DS1302(WRITE_MINUTE, time[1]);
            }

            if (set_f == 6) //设置秒,同上
            {
                if (time[0] == 0)
                    time[0] = 59;
                else
                    time[0]--;
                time[0] = (time[0] / 10) * 16 + time[0] % 10;
                Write_DS1302(WRITE_SECOND, time[0]);
            }
            if (set_f == 7) //设置定时起始时间——时
            {
                if (ds[0] > 0)
                    ds[0]--;
                else
                    ds[0] = 23;
            }

            if (set_f == 8) //设置定时起始时间——分
            {
                if (ds[1] > 0)
                    ds[1]--;
                else
                    ds[1] = 59;
            }

            if (set_f == 9) //设置定时结束时间——时
            {
                if (ds[2] > 0)
                    ds[2]--;
                else
                    ds[2] = 23;
            }

            if (set_f == 10) //设置定时结束时间——分
            {
                if (ds[3] > 0)
                    ds[3]--;
                else
                    ds[3] = 59;
            }

            if (set_f == 11) //设置 光强上限值
            {
                if (light_up != 0 && light_up > light_down + 1) //最小值可设置成0,并且上限值要大于下限值
                    light_up--;                                 //光强上限值-1%
            }

            if (set_f == 12) //设置 光强下限值
            {
                if (light_down != 0) //最小值可设置成0
                    light_down--;    //光强下限值-1%
            }
        }
        else if ((hw_data == '-' || KEY_SUB == 0) && set_f == 0 && mode == 0) //手动控制窗户关闭
        {
            if (motor_state == 1 && moto_f == 0) //已经在控制状态。并且控制打开
                motor_state = 0;                 //停止运作
            else
            {
                if (moto_num > 0) //判断是否不处于打开状态
                {
                    moto_f = 0;     //控制开闭窗
                    motor_state = 1; //开启运作
                }
            }
            while (!KEY_SUB)
                ; //等待按键松开
        }
    }
    //红外遥控切换模式
    if (hw_data == 'Q' && set_f == 0)
    {
        DelayMs(50);
        if (hw_data == 'Q' && set_f == 0)
        {
            BUZZER = 0;
            DelayMs(130);
            BUZZER = 1;
            mode++;              //模式变量+1
            if (mode >= 3)       //最大3个模式
                mode = 0;        //重新回到第1个模式
            if (motor_state == 1) //如果处于运转过程中
            {
                if (moto_f == 0) //控制步进电机关闭
                    motor_state = 0;
                else //控制步进电机打开
                    motor_state = 2;
            }
            WriteData();
        }
    }

    //红外遥控切换设置上一个参数
    if (hw_data == '<' && set_f != 0)
    {
        BUZZER = 0;
        DelayMs(130);
        BUZZER = 1;
        if (set_f == 7 || set_f == 11)
        {
            LCD_Clear(); //清除屏幕显示
            DelayMs(4);;        //延时等待全部清除完毕
            set_f = 6;
        }
        else if (set_f == 1)
        {
            LCD_Clear(); //清除屏幕显示
            DelayMs(4);;        //延时等待全部清除完毕
            switch (mode)            //根据对应的模式,设置对应的参数
            {
            case 0:
                set_f = 6;
                break; //手动模式
            case 1:
                set_f = 10;
                break; //定时模式
            case 2:
                set_f = 12;
                break; //光控模式
            default:
                break;
            }
        }
        else
            set_f--;
    }

    //红外遥控切换设置下一个参数
    if (hw_data == '>' && set_f != 0)
    {
        BUZZER = 0;
        DelayMs(130);
        BUZZER = 1;
        if (set_f == 6) //日期、时间校准完
        {
            LCD_Clear(); //清除屏幕显示
            DelayMs(4);;        //延时等待全部清除完毕
            set_f = 13;              //跳到校准窗帘状态
        }
        else if (set_f == 10 || set_f == 12)
        {
            LCD_Clear(); //清除屏幕显示
            DelayMs(4);;        //延时等待全部清除完毕
            set_f = 1;               //进入校准时间
        }
        else
            set_f++; //设置变量+1
        if (set_f > 12)
        {
            Write_DS1302(WRITE_PROTECT, 0X00); //禁止DS1302写保护
            LCD_Clear();          //清除屏幕显示
            DelayMs(4);;                 //延时等待全部清除完毕
            switch (mode)                     //根据对应的模式,设置对应的参数
            {
            case 0:
                set_f = 1;
                break; //手动模式
            case 1:
                set_f = 7;
                break; //定时模式
            case 2:
                set_f = 11;
                break; //光控模式
            default:
                break;
            }
        }
    }
    hw_data = 0xff; //清除红外接收的数据
}


/********************************************************
函数名称:void mian()
函数作用:主函数
参数说明:
********************************************************/
void main()
{
    uchar lowtime; //记录上次采集光强的时间
    uchar nums;    //循环计数变量
    
    Init_DS1302();
    IR_Init();  //红外接收初始化
    LCD_Init(); //LCD1602初始化

    DelayMs(50);
    if (KEY_ENTER == 0) //开机过程按键可初始化
    {
        DelayMs(50);
        if (KEY_ENTER == 0)
        {
            WriteData();
        }
    }
    else
    {
        ReadData();
    }
    
    

    while (1) //死循环
    {

        if (set_f == 0) //正常显示
        {
            nums++;         //循环次数+1
            if (nums == 10) //每循环10次,读取一次日期、时间
            {
                nums = 0;    //重置循环次数
                Read_time(); //读取日期、时间
            }
            if (time[0] != lowtime) //时间过去一秒
            {
                lowtime = time[0];         //重新记录时间
                light = ReadADC(AIN0_GND);   //读取AD值
                light = 100 - light * 100 / 255; //转换为光强
            }
            display(); //更新显示数据
        }
        else if (set_f < 7) //校准日期、时间
        {
            Read_time(); //读取日期、时间
            display2();
        }
        else if (set_f < 11) //设置定时
            display3();
        else if (set_f < 13) //设置光强控制值
            display4();
            
        KeyScan(); //按键检测处理
    }
}

设计文件:

链接:https://pan.baidu.com/s/1oNj5I3Kad1A4pVNlaUVeTg?pwd=8if2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值