• 51单片机用红外遥控变频空调的程序,带时钟走时DS1302,温度DS18B20带CRC,湿度HTU31D的I2C带CRC,电路图和二千行源代码全公开


    2022.9

    STC51单片机STC12C5A60S2,KEIL C,改了十多年的程序,二千行了。带时钟校正,走时DS1302,温度DS18B20带CRC,湿度HTU31D的I2C带CRC。

    走时有程序上校正功能,可用频率计校到1PPM以下,一年都不用调时间。以前都是调振荡电容调到1ppm以下,累。后来发现可以程序中调,方便多了。

    这次目的是用红外遥控奥克斯变频空调,顺便把所有程序发布一下吧。

    电路图:

    //====================================================/

    主文件:

    //STC12C5A60S2   FLASH 60K,  SRAM 1280字节=内部RAM低128字节+高128字节+内部扩展RAM1024字节

    //温度和湿度值:均按值的10倍,有符号整数

    bit    bit_T0_interrupt_prohibited=0;     // 是否禁用T0中断中的耗时代码

    #define MACRO_WXL_INTERRUPT_PROHIBITED    bit_T0_interrupt_prohibited=1;

    #define MACRO_WXL_INTERRUPT_ALLOWED       bit_T0_interrupt_prohibited=0;

    #include

    #include

    #include "DELAY_STC12_WXL.H"

    //温度  DS18B20

    sbit DQ_DS18_OUTDOOR=P3^4;

    sbit DQ_DS18_INDOOR=P1^4;

    #include "DS18B20_wxl.h"

    int data i_DS18_INDOOR=240;          //温度值(10倍)        设成24度,以免空调制冷、制热控制失误

    int data i_DS18_OUTDOOR=0;

    bit bit_DS18_OUTDOOR_exist=0;

    bit bit_DS18_INDOOR_exist=0;

    unsigned char data  dispbuf_DS18_INDOOR[3]={17,17,17};    //保存各个显示值  

    unsigned char data  dispbuf_DS18_OUTDOOR[3]={17,17,17};

    /*

    数组保存显示值。 [0]存十位及百位(A,b,C等表示)或负号 (可选带点) 。     [1]存个位(可选带点)。   [2]存小数位

    第[0]位: 100 显示 A, 110 B, 120 C ,130 D ,140 E, 150 F,然后就没有了,所以最高是159.9

    第[0]位: -10 显示 A, -20 B, -30 C, -40 D ,-50 E, -60 F,然后就没有了,所以最低是-69.9

    比如:

    25.6℃数组为{2,5,6}    即 2 5 6

    -5.6℃数组为{17,5,6}   即 - 5 6

    -15.6℃数组为{10,5,6}  即 A 5 6

    -25.6℃数组为{10,5,6}  即 B 5 6

    105.6℃数组为{10,5,6}  即 A 5 6

    115.6℃数组为{11,5,6}  即 B 5 6

    */

    //温度  HTU31D  I2C

    //HTU31D模块,芯片焊板上,温度响应慢,用于空调控制一般般,于是仍采用DS18B20来控制温度

    //HTU31D和DS18B20,静态温度HTU31D高0.5度不准,动态HTU31D反应慢

    int idata   i_HTU31D_INDOOR=0;          //温度值(10倍)

    int idata   i_HTU31D_OUTDOOR=0;

    unsigned char idata  dispbuf_HTU31D_INDOOR[3]={17,17,17};    //保存各个显示值

    unsigned char idata  dispbuf_HTU31D_OUTDOOR[3]={17,17,17};

    //湿度 HF3223频率、 HTU31D I2C

    int idata i_HF32_INDOOR=0;       //湿度值10倍       //湿度HF3223采用频率计数,这里淘汰不用了,给HTU31D用

    int idata i_HF32_OUTDOOR=0;

    unsigned char  idata   dispbuf_HF32_INDOOR[3]={14,5,17};    //保存各个显示值       //这里没用到,赋成E5-

    unsigned char  idata   dispbuf_HF32_OUTDOOR[3]={17,17,17};     

    sbit SDA=P3^2;

    sbit SCL=P3^3;

    #include "I2C_HTU31D.H"

    //按键相关

    sbit P1_0=P1^0;   

    sbit P1_1=P1^1;   

    sbit P1_2=P1^2;   

    unsigned char  idata  uc_keycount=0;      //用于判断 20ms内均为高电平,则是键松开

    //时钟相关

    volatile unsigned int   data  tcnt=0;             //计数到1秒的次数(用于时钟)

    unsigned char data second=0;              //时钟

    unsigned char data minute=0;

    unsigned char data hour=0;

    unsigned long idata   ul_time_compensate=0;      //晶振不准,在软件中进行时间补偿,经n秒后,加一秒或减一秒。  最好是晶振调成偏快,然后程序中就不用加秒,比较方便

    //传感器检测相关

    unsigned char  idata do_what=0;                 //传感器检测步序

    volatile unsigned int  data   i_dowhat_interval=1800;    //中断中,do_what的时间计数

    //工作模式

    unsigned char  idata  uc_display_mode=0;         //工作模式   0正常 1制冷 2制热

    unsigned char  idata  uc_display_mode_pre=0;

    unsigned char  code  displaybuf_display_mode[3]={0, 12, 42};     //显示0、C、H

    //设置模式

    unsigned char  idata  g_uc_setting_mode=0;

    volatile  unsigned int data    g_ui_settingmode_timeout_cnt=0;  //设置时,进行计时

    unsigned char idata  g_uc_settingmode_timeout_second=0;

    //显示LED相关

    volatile unsigned char   data  mstcnt=0;        //计数到改变显示LED位的次数

    //dispcode[]是 共“阳”极数码管的 笔划数据!!

    unsigned char  data   dispcode[]={                          //强行放在data中,以加快速度

    0x48, 0xFC, 0x29, 0x38, 0x9C, 0x1A, 0x0A, 0x7C, 0x08, 0x18,    //[0]。。。9

    0x0C, 0x8A, 0x4B, 0xA8, 0x0B, 0x0F,                            //[10](A),11(b),12(C),13(d),14(E),15(F)

    0xFF, 0xBF, 0xBF, 0xBF,                                        //[16](空),17(负号), 18, 19

    0x40, 0xF4, 0x21, 0x30, 0x94, 0x12, 0x02, 0x74, 0x00, 0x10,    //[20](带小数点的0.)。。。29

    0x04, 0x82, 0x43, 0xA0, 0x03, 0x07,                            //[30](A.)  。。。 35(F.)

    0xF7, 0xB7, 0xB7, 0xB7,                                        //[36](空带点.),37(负号带点.), 38, 39

    0xAA, 0x4C, 0x8C                                               //[40](小o), 41(大N), 42(H)

    };

    /*

    8个数码管     时间     温度

            [7]左 8 8 8 8  8 8 8 8 右[0]

    */

    unsigned char data dispbuf[8]={17,17,17,17,17,17,17,17};    //dispbuf保存显示数据,[7]最左数码管 --> [0]最右数码管

    unsigned char data dispbitcode[8]={1,2,4,8,16,32,64,128};   //即选择数码管,依次是[0]最右数码管-->[7]最左数码管,即P2.0接置1(0000 0001)、P2.1置1...

    unsigned char data dispbitcnt=0;       //选择显示哪个数码管, 0表示最右那个数码管(温度小数位),7表示最左那个数码管

    //串口

    unsigned char idata    sbuf[3],  sbufnum;   //串口数据

    //DS1302

    //DS1302 走时不准!  所以从单片机走时间,DS1302只是保存一下时间

    sbit DS1302_CLK=P1^5; 

    sbit DS1302_IO=P1^6; 

    sbit DS1302_RST=P1^7;

    #include "DS1302_wxl.h"

    bit bitDS1302exist=0;      //DS1302是否存在

    unsigned char idata  ds1302_BCDdata[9]={0x59,0x32,0x23, 1, 1,  1,    0x13,  0,      0};     //BCD格式的数据 

                                        //  0     1    2    3  4   5       6     7      8

                                        //  秒    分   时   日 月 星期几  年   写保护   充电

                                        //DS1302数据按BCD格式,0x59即是表示59。

                                        //秒最高位0表示不停止时钟。  小时最高位0表示按24小时制。  0不写保护。  0不充电。

    //空调红外遥控      //发送时,低比特位优先!!   先发送低bit!!

    sbit  P_IR = P1^3;

    volatile  unsigned char data  g_uc_ir_time=0;    //中断T0中执行多少次红外IR发射

    volatile  bit data    g_b_ir_onoff=0;

    unsigned char idata  uc_ir_state=0;           // 空调状态:  0表示关机   1表示开机+制冷    2表示只ECO    3表示ECO+静音   (风都是1档)

    unsigned char code   g_uc_irdata[5][13]=            //红外数据

    {

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x45, 0x15,     //关机0

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x20, 0x0, 0x45, 0x35,    //开机+制冷1

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x28, 0x0, 0x53, 0x4B,    //ECO2

    0xC3 ,0xAD ,0xE0 ,0x0 ,0x60 ,0x80 ,0x20 ,0x0 ,0x0 ,0x28 ,0x0 ,0x53 ,0xCB,     //ECO+静音3

    0xC3, 0xC5, 0xE0, 0x00, 0x60, 0x80, 0x20, 0x00, 0x00, 0x28, 0x00, 0x40, 0xD0//32度ECO+静音3

    };  

    unsigned char code   g_uc_irdata_with_disp[5][13]=         //红外数据   加空调屏显键

    {

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x55, 0x25,     //关机0

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x20, 0x0, 0x55, 0x45,   //开机+制冷1

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x0, 0x20, 0x0, 0x0, 0x28, 0x0, 0x55, 0x4D,   //ECO2

    0xC3, 0xAD, 0xE0, 0x0, 0x60, 0x80, 0x20, 0x0, 0x0, 0x28, 0x0, 0x55, 0xCD,     //ECO+静音3

    0xC3, 0xC5, 0xE0, 0x00, 0x60, 0x80, 0x20, 0x00, 0x00, 0x28, 0x00, 0x55, 0xE5//32度ECO+静音3

    };  

    unsigned char idata  uc_ir_heat_state=0;   // 空调加热状态:  0表示关机   1表示开机+制热    。。。    待完善

    unsigned char code   g_uc_ir_heat_data[5][13];

    bit idata  b_ir_key_delay=0;        //按键检测延时

    unsigned char idata  g_uc_ir_second=0;  //按键延时检测,按秒

    unsigned char idata  g_uc_ir_minute=0;      //空调温度延时时长,按分钟   ,比如1分钟后检测温度

    unsigned char idata  g_uc_ir_minute_pre=0;  //空调温度延时检测

    unsigned int idata  ui_airtemp_set=260;      //空调制冷温度设定值 (10倍)

    unsigned int idata  ui_airtemp_heat_set=200;      //空调制热温度设定值 (10倍)

    unsigned char idata  dispbuf_ui_airtemp_set[3];

    bit idata   b_ECO_ON=0;                 //ECO开7小时检测,开7小时ECO后需要关掉ECO再开,不然会自动消掉

    unsigned char idata  ECO_hour_pre=0;

    ///

    //数值转换到显示值

    void  INT10_TO_dispbuf_nodot(unsigned char  *c,  int  i)        //c[0]存十位及百位(A,b,C等表示),c[1]存个位,c[2]存小数。  负数:c[0]为负号或A,b,C等表示-10、-20、-30等

    {

        if( i>1599  || i<-699) return;  //大于159.9度  小于-69.9度

       

        if( i < 0 )     //如果是负数

        {

            i=-i;

            c[0]=i/100; //最高位

            i=i%100;

            c[1]=i/10;  //个位

            c[2]=i%10;  //小数

            if (c[0] == 0) c[0]=17;    //显示负号

            else c[0]+=9;           //用A,b,C等表示-10、-20、-30等           

        }

        else    //正数

        {

            c[0]=i/100;

            i=i%100;

            c[1]=i/10;

            c[2]=i%10;

        }

    }

    void  INT10_TO_dispbuf(unsigned char  *c,   int  i)  

    {

        if( i>1599  || i<-699) return;  //大于159.9度  小于-69.9度

           

        INT10_TO_dispbuf_nodot( c,  i);

       

        //显示带点

        if( c[2] <3)    ;                     //显示带点的数字

        else if( c[2] < 5 )     c[0]+=20;      //显示带点的数字   [0]是十位

        else if( c[2] < 8) c[1]+=20;        //显示带点的数字

        else  {     c[0]+=20;  c[1]+=20;    }     //显示带点的数字

    }

    void sendSBUF(unsigned char  *a, unsigned char   num)       //串口发送N字节数据

    {

        unsigned char idata  i;

        if(num==0) return;

        TI=0;

        for(i=0;i

        {

            SBUF = a[i]; //输出字符

            while(!TI);     //空语句判断字符是否发完,TI=1表示发完

            TI = 0;         //要人工清TI

        }

    }

    void readDS1302_and_setclock()

    {

        BurstRead1302(ds1302_BCDdata) ;

        second=( (ds1302_BCDdata[0]&0x7f) >>4)*10 + (ds1302_BCDdata[0] & 0x0f);       //去掉最高位"时钟停止"位,再运算

        minute=(ds1302_BCDdata[1]>>4)*10 + (ds1302_BCDdata[1] & 0x0f);

        hour=  ( (ds1302_BCDdata[2]&0x7f) >>4)*10 + (ds1302_BCDdata[2] & 0x0f);       //去掉最高位"24小时"位,再运算

    }

    //每个小时的30分时,写入时间到DS1302中,以使DS1302时间正确   ///

    void DO_WRITETODS1302_EVERY_HALF_HOUR()

    {

        if(bitDS1302exist)

        {

            DS1302_WriteOne(DS1302_SEC_AD, (second/10)*16+second%10 );

            DS1302_WriteOne(DS1302_MIN_AD, (minute/10)*16+minute%10 );

            DS1302_WriteOne(DS1302_HOUR_AD, (hour/10)*16+hour%10 );        

        }

    }

    //传感器检测  /

    void DO_WHAT_FUNC()

    {

            unsigned char idata  uc_DS18_temp1, uc_DS18_temp2,   uc_DS18_temp[10];

            EA=0;

            if(i_dowhat_interval<3600) { EA=1;  return;}   //如果间隔时间未到,则不执行

            i_dowhat_interval=0;        //间隔时间重新计时,即每隔1秒做一次检测

            EA=1;

            switch(do_what)

            {

                case 0:

                    // 内DS18B20测温

                    RESET_DS18_INDOOR(  );

                    SEND_TO_DS18_INDOOR( 0xCC );   //传送数据   0xcc表示SKIP ROM COMMAND指令

                    SEND_TO_DS18_INDOOR( 0x44 );   //传送数据   0X44  CONVERT T指令

                   

                    RESET_DS18_OUTDOOR(  );

                    SEND_TO_DS18_OUTDOOR( 0xCC );   //传送数据   0xcc表示SKIP ROM COMMAND指令

                    SEND_TO_DS18_OUTDOOR( 0x44 );   //传送数据   0X44  CONVERT T指令

                   

                    HTU31D_conversion(  0x40  );   //addr地址接GND是0x40,接VCC是0x41 。   我室内用第一次买的接GND,室外以后用接VCC的

       

                    do_what++;

                    break;

       

       

                case 1:

                    if ( HTU31D_readTRH( &i_HTU31D_INDOOR,  &i_HF32_INDOOR  ,  0x40 )  )     //addr地址接GND是0x40,接VCC是0x41

                    {

                            if(  i_HTU31D_INDOOR >=1100  )  //响应值太大,说明HTU数据异常

                            {

                                i_HTU31D_INDOOR=0;          //设成0

                                dispbuf_HTU31D_INDOOR[0]=15;    //显示F1      [0]是十位

                                dispbuf_HTU31D_INDOOR[1]=1;

                                dispbuf_HTU31D_INDOOR[2]=17;   

                            }

                            else

                            {

                                INT10_TO_dispbuf(dispbuf_HTU31D_INDOOR, i_HTU31D_INDOOR);       //存到显示缓存

                            }

                           

                            if(  i_HF32_INDOOR >=1100  )       //响应值太大,说明HTU数据异常

                            {

                                    i_HF32_INDOOR=0;          //湿度设成0

                                    dispbuf_HF32_INDOOR[0]=15;  //显示F6     [0]是十位

                                    dispbuf_HF32_INDOOR[1]=6;

                                    dispbuf_HF32_INDOOR[2]=17;                                 

                            }

                            else

                                INT10_TO_dispbuf_nodot(dispbuf_HF32_INDOOR, i_HF32_INDOOR);     //存到显示缓存   无点

                    }

                    else        //HTU回应错误

                    {

                            i_HTU31D_INDOOR=0;         //设成0

                            dispbuf_HTU31D_INDOOR[0]=15;    //显示F0         [0]是十位

                            dispbuf_HTU31D_INDOOR[1]=0;

                            dispbuf_HTU31D_INDOOR[2]=17;                           

                           

                            i_HF32_INDOOR=0;         //湿度设成0

                            dispbuf_HF32_INDOOR[0]=15;  //显示F5         [0]是十位

                            dispbuf_HF32_INDOOR[1]=5;

                            dispbuf_HF32_INDOOR[2]=17;     

                    }

                           

                    do_what++;

                    break;

       

       

       

                case 2:

                    // 内DS18B20读温

                    if( RESET_DS18_INDOOR(  ) )     //存在DS18B20

                    {

                        bit_DS18_INDOOR_exist=1;

                        SEND_TO_DS18_INDOOR( 0xCC );   //传送数据   0xcc表示SKIP ROM COMMAND指令

                        SEND_TO_DS18_INDOOR( 0xBE );   //传送数据   0XBE  READ SCRATCHPAD指令

                       

                        //接收9字节数据

                        for(uc_DS18_temp1=0; uc_DS18_temp1<9 ; uc_DS18_temp1++)

                                        uc_DS18_temp[ uc_DS18_temp1 ]=RECEIVE_FROM_DS18_INDOOR(  );   //读取9字节=8字节数据+1字节CRC

                       

                        if( DS18_CRC(uc_DS18_temp,9 ))   //为0则是CRC成功

                        {

                            //失败

                            i_DS18_INDOOR=240;           //设成24度,以免空调控制失误

                            dispbuf_DS18_INDOOR[0]=14;  //显示E1        [0]是十位

                            dispbuf_DS18_INDOOR[1]=1;

                            dispbuf_DS18_INDOOR[2]=17;     

                        }

                        else

                        {

                            uc_DS18_temp1=uc_DS18_temp[0];    // 第1字节是LSB字节

                            uc_DS18_temp2=uc_DS18_temp[1];    // 第2字节是MSB字节

                            i_DS18_INDOOR =  DS18_TEMP_TO_SIGNED_INT10( uc_DS18_temp1, uc_DS18_temp2 ); //转成int

                            INT10_TO_dispbuf(dispbuf_DS18_INDOOR, i_DS18_INDOOR);       //存到显示缓存

                        }

                    }

                    else        //不存在DS18B20

                    {

                        bit_DS18_INDOOR_exist=0;

                       

                        i_DS18_INDOOR=240;           //设成24度,以免空调控制失误

                        dispbuf_DS18_INDOOR[0]=14;  //显示E0        [0]是十位

                        dispbuf_DS18_INDOOR[1]=0;

                        dispbuf_DS18_INDOOR[2]=17;     

                    }

                   

                    do_what++;

                    break;

       

                case 3:

                    // 外DS18B20读温

                    if( RESET_DS18_OUTDOOR(  ) )        //存在DS18B20

                    {

                        bit_DS18_OUTDOOR_exist=1;

                        SEND_TO_DS18_OUTDOOR( 0xCC );   //传送数据   0xcc表示SKIP ROM COMMAND指令

                        SEND_TO_DS18_OUTDOOR( 0xBE );   //传送数据   0XBE  READ SCRATCHPAD指令

                       

                        //接收9字节数据

                        for(uc_DS18_temp1=0; uc_DS18_temp1<9 ; uc_DS18_temp1++)

                                        uc_DS18_temp[ uc_DS18_temp1 ]=RECEIVE_FROM_DS18_OUTDOOR(  );   //读取9字节=8字节数据+1字节CRC

                       

                        if( DS18_CRC(uc_DS18_temp,9 ))   //为0则是CRC成功

                        {

                            //失败

                            i_DS18_OUTDOOR=0;           //设成0

                            dispbuf_DS18_OUTDOOR[0]=14; //显示E1        [0]是十位

                            dispbuf_DS18_OUTDOOR[1]=1;

                            dispbuf_DS18_OUTDOOR[2]=17;    

                        }

                        else

                        {

                            uc_DS18_temp1=uc_DS18_temp[0];    // 第1字节是LSB字节

                            uc_DS18_temp2=uc_DS18_temp[1];    // 第2字节是MSB字节

                            i_DS18_OUTDOOR =  DS18_TEMP_TO_SIGNED_INT10( uc_DS18_temp1, uc_DS18_temp2 );    //转成int

                            INT10_TO_dispbuf(dispbuf_DS18_OUTDOOR, i_DS18_OUTDOOR);     //存到显示缓存

                        }

                    }

                    else        //不存在DS18B20

                    {

                        bit_DS18_OUTDOOR_exist=0;

                       

                        i_DS18_OUTDOOR=0;           //设成0

                        dispbuf_DS18_OUTDOOR[0]=14; //显示E0        [0]是十位

                        dispbuf_DS18_OUTDOOR[1]=0;

                        dispbuf_DS18_OUTDOOR[2]=17;    

                    }

                   

                    do_what++;

                   

                    break;

                case 4:

                    if ( HTU31D_readTRH( &i_HTU31D_OUTDOOR,  &i_HF32_OUTDOOR  ,  0x41 )  )     //addr地址接GND是0x40,接VCC是0x41

                    {

                            if(  i_HTU31D_OUTDOOR >=1100  )  //响应值太大,说明HTU数据异常

                            {

                                i_HTU31D_OUTDOOR=0;          //设成0

                                dispbuf_HTU31D_OUTDOOR[0]=15;   //显示F1      [0]是十位

                                dispbuf_HTU31D_OUTDOOR[1]=1;

                                dispbuf_HTU31D_OUTDOOR[2]=17;  

                            }

                            else

                            {

                                INT10_TO_dispbuf(dispbuf_HTU31D_OUTDOOR, i_HTU31D_OUTDOOR);     //存到显示缓存

                            }

                           

                            if(  i_HF32_OUTDOOR >=1100  )       //响应值太大,说明HTU数据异常

                            {

                                    i_HF32_OUTDOOR=0;          //湿度设成0

                                    dispbuf_HF32_OUTDOOR[0]=15; //显示F6     [0]是十位

                                    dispbuf_HF32_OUTDOOR[1]=6;

                                    dispbuf_HF32_OUTDOOR[2]=17;                                

                            }

                            else

                                INT10_TO_dispbuf_nodot(dispbuf_HF32_OUTDOOR, i_HF32_OUTDOOR);       //存到显示缓存   无点

                    }

                    else        //HTU回应错误

                    {

                            i_HTU31D_OUTDOOR=0;         //设成0

                            dispbuf_HTU31D_OUTDOOR[0]=15;   //显示F0         [0]是十位

                            dispbuf_HTU31D_OUTDOOR[1]=0;

                            dispbuf_HTU31D_OUTDOOR[2]=17;                          

                           

                            i_HF32_OUTDOOR=0;         //湿度设成0

                            dispbuf_HF32_OUTDOOR[0]=15; //显示F5         [0]是十位

                            dispbuf_HF32_OUTDOOR[1]=5;

                            dispbuf_HF32_OUTDOOR[2]=17;    

                    }

                           

                    do_what++;

                    do_what=0;     //回零

                    break; 

       

                default:

                    do_what=0;

            }      

       

    }

    //发送时间和温度 到串口SD记录器  进行记录

    void SENDTXTEMP()

    {

       

            unsigned char idata a[26];

            char idata  i;

           

            for (i=0;i<26;i++)  a[i]=' ';

           

           

            if(bitDS1302exist)

            {

                    BurstRead1302(ds1302_BCDdata) ;

                    wxl_BCDToStr(ds1302_BCDdata,  a);  //17字节

            }

            else

            {

                   

                    a[4]=minute%10+48;

                    a[3]=minute/10+48;

                    a[2]=':';

                    a[1]=hour%10+48;

                    a[0]=hour/10+48;       

                   

            }

           

            a[17]='\t';

            a[18]=0;

            a[19]=0;

            a[20]=0;

           

            INT10_TO_dispbuf_nodot(a+18, i_DS18_INDOOR );

           

            a[18]+=48;

            a[19]+=48;

            a[21]=a[20]+48;

            a[20]='.';

           

            a[22]='\t';

            a[23]=uc_ir_state+'0';

           

            a[24]=0x0D;

            a[25]=0x0A;

           

            sendSBUF(a, 26);

    }

    //刷新显示           ///

    void REFRESH_DISPLAY()

    {          

        switch(g_uc_setting_mode)

        {

            case 0:              //不在设置模式

                dispbuf[4]=minute%10;

                dispbuf[5]=minute/10;

                dispbuf[6]=hour%10;

                dispbuf[7]=hour/10;

               

                switch(uc_display_mode)       // 显示模式,  0=2个温度2个湿度      1=空调控制   2=空调控制

                {

                    case 0:     //正常显示

                        if(second%10==0||second%10==5)       //   在0,5。。。     

                        {

                            dispbuf[0]=dispbuf_DS18_INDOOR[1];        //显示内DS18B20的温度        dispbuf[0]是低位    INDOOR[0]是十位

                            dispbuf[1]=dispbuf_DS18_INDOOR[0];       

                            dispbuf[2]=dispbuf_DS18_OUTDOOR[1];        //显示外DS18B20的温度

                            dispbuf[3]=dispbuf_DS18_OUTDOOR[0];        

                        }

                        else if(second%10==3||second%10==8)  //   在3,8。。。

                        {

                            dispbuf[0]=dispbuf_HTU31D_INDOOR[1];        //显示内HTU31D的温度

                            dispbuf[1]=dispbuf_HTU31D_INDOOR[0];     

                            dispbuf[2]=dispbuf_HTU31D_OUTDOOR[1];       //显示外HTU31D的温度

                            dispbuf[3]=dispbuf_HTU31D_OUTDOOR[0];     

                        }

                        else if(second%10==4||second%10==9)  //   在4,9。。。

                        {

                            dispbuf[0]=dispbuf_HF32_INDOOR[1];        //显示内HF3223的湿度

                            dispbuf[1]=dispbuf_HF32_INDOOR[0];     

                            dispbuf[2]=dispbuf_HF32_OUTDOOR[1];       //显示外HF3223的湿度

                            dispbuf[3]=dispbuf_HF32_OUTDOOR[0];     

                        }                  

                        break;

                       

                    case 1:      //空调制冷显示

                        dispbuf[1]=  12;               //[0]最右数码管   显示C表示制冷

                        dispbuf[0]=uc_ir_state;        //[0]最右数码管

                        goto CASE2_DISPLAY;

                       

                    case 2:      //空调加热显示                

                        dispbuf[1]=  42;                    //[0]最右数码管   显示H表示制热

                        dispbuf[0]=uc_ir_heat_state;        //[0]最右数码管

                       

                    CASE2_DISPLAY:   //在0秒显示内温,3秒外温,5秒内湿,8秒外湿

                           

                        if(second%10==0)          

                        {

                            dispbuf[2]=dispbuf_DS18_INDOOR[1];       //  dispbuf[0]是低位    INDOOR[0]是十位

                            dispbuf[3]=dispbuf_DS18_INDOOR[0];         

                        }

                        else if(second%10==3) 

                        {

                            dispbuf[2]=dispbuf_DS18_OUTDOOR[1];      

                            dispbuf[3]=dispbuf_DS18_OUTDOOR[0];    

                        }

                        else if(second%10==6) 

                        {

                            dispbuf[2]=dispbuf_HF32_INDOOR[1];   

                            dispbuf[3]=dispbuf_HF32_INDOOR[0];    

                        }

                        else if(second%10==8) 

                        {

                            dispbuf[2]=dispbuf_HF32_OUTDOOR[1];   

                            dispbuf[3]=dispbuf_HF32_OUTDOOR[0];     

                        }                  

                        break;

                       

                    default:

                        dispbuf[0]=16;

                        dispbuf[1]=16;

                        dispbuf[2]=16;

                        dispbuf[3]=16;

                }  //SWITCH

                break;

           

           

            //设置中

            case 1:       

                dispbuf[3]=16;

                dispbuf[2]=5  ;  //显示5=S  调节小时

                dispbuf[0]=hour%10;

                dispbuf[1]=hour/10;

                break;

           

            case 2:

                dispbuf[3]=16;

                dispbuf[2]=15  ;  //显示F  调节分钟

                dispbuf[0]=minute%10;

                dispbuf[1]=minute/10;                          

                break;

               

            case 3:

                dispbuf[3]=16;

                dispbuf[2]=40  ;  //显示o  调节工作模式

                dispbuf[0]= displaybuf_display_mode[uc_display_mode];

                dispbuf[1]=16;                             

                break; 

               

            case 4:

                dispbuf[3]=16;

                dispbuf[2]=12  ;  //显示C  调节制冷温度

                INT10_TO_dispbuf( dispbuf_ui_airtemp_set, ui_airtemp_set);

                dispbuf[0]= dispbuf_ui_airtemp_set[1];

                dispbuf[1]= dispbuf_ui_airtemp_set[0];                     

                break; 

               

            case 5:

                dispbuf[3]=16;

                dispbuf[2]=42  ;  //显示H  调节制热温度

                INT10_TO_dispbuf( dispbuf_ui_airtemp_set,   ui_airtemp_heat_set);

                dispbuf[0]= dispbuf_ui_airtemp_set[1];

                dispbuf[1]= dispbuf_ui_airtemp_set[0];                         

                break; 

                           

           

            default:

                dispbuf[3]=16;

                dispbuf[2]=16;

                dispbuf[0]=16;

                dispbuf[1]=16;                             

                break;         

           

            }//SWITCH

    }

    //时间走时  /

    void TIME_PROCESS()

    {  

            //以下本来放在中断T0中,现在移出来,避免中断超时问题

            ET0=0;      //在对tcnt进行操作前,一定要将定时器的中断禁掉

                        //因为tcnt是整型,在运算时要好几条指令,如果被定时器中断,中断中又修改了值!!  则运算结果就不准了,造成时间严重不准!!!

            if(tcnt>=3600)       //3600次,表示过了一秒钟,晶体11.0592

            {

                tcnt-=3600;     // 这句,受中断影响,中断中又修改了值!!   如果只是用tcnt=0;这句,则没有问题。  有可能别的程序在处理时(比如串口),tcnt会多计,故不能清零,不然时间不准

                ET0=1;      //马上开定时器0的中断,这样不会影响到中断服务程序

               

                ul_time_compensate++;                //时间补偿修正

                if (ul_time_compensate >= 151515UL )        //晶振偏快,经计算,XXXX秒后要减少一秒

                { ul_time_compensate=0;  }           //不增秒

                else

                {

                    second++;

                   

                    if(second>=60)

                    {

                        second=0;

                        minute++;

                        if(minute>=60)

                        {

                            minute=0;

                            hour++;

                            if(hour>=24)    hour=0;

                        }

                       

                        SENDTXTEMP();     //每分钟发送温度给TX串口记录仪

       

                        if( minute==30 ) DO_WRITETODS1302_EVERY_HALF_HOUR();  //30分时 记录时间

                    }

                }

               

                //每秒 刷新显示

                REFRESH_DISPLAY();

                   

            }

            ET0=1;

    }

    //发射红外遥控全部信号         /

    //注意:不能用软延时,因为中断ISR会严重影响延时精度,造成红外发射数据错误。  要用定时器中断来统计发射时长!  这里用T0统计发射时长,用T1生成38KHZ。

    void SendIRdata(unsigned char *ir_data)

    {

        unsigned char idata  i,j;

        unsigned char idata  irdata;

       

        //引导码高电平 9ms

        g_uc_ir_time=32;      //实际278us*32=8.9ms

        g_b_ir_onoff=1;

        while(g_uc_ir_time);

       

        //引导码低电平 4.5ms

        g_uc_ir_time=16;      //实际278us*16=4.45ms

        g_b_ir_onoff=0;

        while(g_uc_ir_time);   

       

        //发送13字节数据

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

        {

            irdata=ir_data[i];

           

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

            {

                g_uc_ir_time=2;   //0.56ms 每bit高电平,实际278us*2=0.556ms

                g_b_ir_onoff=1;

                while(g_uc_ir_time);               

           

                if(irdata & 1)    //判断最低位为1还是0。   低位先发送!!

                {

                    g_uc_ir_time=6;         //1为 1.69ms长的低电平,实际278us*6=1.668ms

                    g_b_ir_onoff=0;

                    while(g_uc_ir_time);

                }

                else

                {

                    g_uc_ir_time=2;         //0为 0.565ms长的低电平,实际278us*2=0.556ms

                    g_b_ir_onoff=0;

                    while(g_uc_ir_time);

                }   

                 irdata=irdata>>1;

            }

        }

       

        //发送总的结束位1bit 0.56ms

        g_uc_ir_time=2;             //实际278us*2=0.556ms

        g_b_ir_onoff=1;

        while(g_uc_ir_time);

       

        //此时还在执行发射1中,不能马上退出,故后续再发射一下0

        g_uc_ir_time=2;             //实际278us*2=0.556ms

        g_b_ir_onoff=0;

        while(g_uc_ir_time);

       

        ET1=0;

        P_IR=0;   //关掉红外

    }

    void  P1_0_key_waitfor_release()        //等待按键松开

    {

        Delay50ms(); //延时,用于消除毛刺信号,一般10~50ms

        //判断键松开,判断 连续 20ms内 均为高电平,则是键松开

        uc_keycount=0;

        while(uc_keycount<20)

        {

            if(P1_0==0) uc_keycount=0;

            Delay1ms();

            uc_keycount++;

            TIME_PROCESS();   //处理时间事务,以免按键一直按着就无法处理

        }

    }

    void  P1_1_key_waitfor_release()        //等待按键松开

    {

        //等待按键松开

        Delay50ms(); //延时,用于消除毛刺信号,一般10~50ms

        //判断键松开,判断20ms内均为高电平,则是键松开

        uc_keycount=0;

        while(uc_keycount<20)

        {

            if(P1_1==0) uc_keycount=0;

            Delay1ms();

            uc_keycount++;

            TIME_PROCESS();   //处理时间事务,以免按键一直按着就无法处理

        }  

    }

    void  P1_2_key_waitfor_release()        //等待按键松开

    {

        Delay50ms(); //延时,用于消除毛刺信号,一般10~50ms

        //判断键松开,判断20ms内均为高电平,则是键松开

        uc_keycount=0;

        while(uc_keycount<20)

        {

            if(P1_2==0) uc_keycount=0;

            Delay1ms();

            uc_keycount++;

            TIME_PROCESS();   //处理时间事务,以免按键一直按着就无法处理

        }

    }

    //切换制冷中的各种模式

    void my_aircontrol_cold_turnto(unsigned char i,  unsigned char j)

    {

        SendIRdata( &g_uc_irdata[i][0] ); 

        uc_ir_state=i;

       

        //设置下次检测空调温度时间

        g_uc_ir_minute=j;             //延时j分钟

        g_uc_ir_minute_pre=minute;

       

        if(i<=1)   b_ECO_ON=0;

    }

    //检测按键进入设置状态   

    void DO_KEY_PRESS_SETTING()

    {

        unsigned char idata  i;

       

        if( P1_1==1)  return;

       

        //进入设置模式

        for(i=0;  i<8;  i++)   dispbuf[i]=16;  //全显示空

        g_uc_setting_mode=1;

        REFRESH_DISPLAY();

        P1_1_key_waitfor_release();     //等待按键松开

        g_uc_settingmode_timeout_second=0;   //设置时,只进行时间走时,暂停了其它工作:不进行传感器检测、不进行串口通讯、不进行空调控制,所以需要判断一个超时,以免一直困在设置模式中

        g_ui_settingmode_timeout_cnt=0;   

       

        while(1)  //设置中

        {

                ET0=0;

                if(g_ui_settingmode_timeout_cnt>=3600)         //超过1秒

                {

                    g_ui_settingmode_timeout_cnt=0;         //(有点误差没关系)

                    ET0=1;

                    g_uc_settingmode_timeout_second++;

                    if(g_uc_settingmode_timeout_second>60)   break;    //超时,退出循环  (有点误差没关系)

                }

                ET0=1; 

                   

                TIME_PROCESS();      //处理时间事务,      每秒刷新显示

           

                if( P1_2==0 )

                {  

                    switch(g_uc_setting_mode)

                    {

                        case 1:       //小时加1

                            hour++;

                            if(hour>=24) hour=0;

                            break;

                       

                        case 2:      //分钟加1

                            minute++;

                            if(minute>=60) minute=0;     //小时不变

                           

                            TL0=0;          //定时器重新置数

                            tcnt=0;                 //程序计数清0

                            second=0;

                            break;

                           

                        case 3:      //模式变化

                            uc_display_mode++;

                            if(uc_display_mode > 2) uc_display_mode=0;    

                            break;

                           

                        case 4:      //制冷温度设定

                            ui_airtemp_set+=5;

                            if(ui_airtemp_set > 270) ui_airtemp_set=260;

                            break;

                           

                        case 5:      //制热温度设定

                            ui_airtemp_heat_set+=5;

                            if(ui_airtemp_heat_set > 210) ui_airtemp_heat_set=200;

                            break;                                             

                    }

                    REFRESH_DISPLAY();

                    P1_2_key_waitfor_release();     //等待按键松开

                   

                    g_uc_settingmode_timeout_second=0;

                }

               

                else   if  ( P1_1==0 )        //设置下一项

                {

                    g_uc_setting_mode++;

                    REFRESH_DISPLAY();

                   

                    P1_1_key_waitfor_release();     //等待按键松开

                                   

                    g_uc_settingmode_timeout_second=0;

                   

                    if(g_uc_setting_mode > 5 ) break;

                }  

        }  //WHILE

        //退出设置了,恢复显示,如果工作模式切换,则关空调

        if(uc_display_mode != uc_display_mode_pre)

        {

            //关空调

            if(uc_display_mode_pre==1)

            {

                my_aircontrol_cold_turnto(0, 0); //关空调

            }

            else if(uc_display_mode_pre==2)

            {

                //待完善

            }

            uc_display_mode_pre=uc_display_mode;

        }

       

        g_uc_setting_mode=0;

        REFRESH_DISPLAY();

       

    }

    //检测按键进行红外开关空调on off       

    void DO_KEY_PRESS_AIRCONTROL()

    {

        if( !uc_display_mode )  return;     //没在控制模式

           

       

        //进行时间延时

        if(b_ir_key_delay==1)

        {

            if(second > g_uc_ir_second  &&  second-g_uc_ir_second > 5)  b_ir_key_delay=0;          //没跨分钟

            if(second < g_uc_ir_second  &&  second+60-g_uc_ir_second > 5)  b_ir_key_delay=0;    //跨分钟了

        }

       

        if( b_ir_key_delay)    return;

           

        if(P1_0==1)  return;

        //按键有按下了

        if(uc_display_mode==1)      //制冷

        {

            if( uc_ir_state)    my_aircontrol_cold_turnto(0, 0);      //如果已开机,则关机

            else      my_aircontrol_cold_turnto(1, 1);               //没开机,则开机            //1分钟后检测温度, 以免太快发送下一个

        }

        else if(uc_display_mode==2)   //制热

        {

            //待未来加入程序

            if( uc_ir_heat_state)         //如果已开机,则关机

                ;

            else   //没开机,则开机

                ;

        }

                   

        P1_0_key_waitfor_release();     //等待按键松开

       

        //按键后需要延时后才接受下一次按键,以免一直快速按键

        b_ir_key_delay=1;      

        g_uc_ir_second=second;

    }  

    //红外控制空调制冷   /

    void DO_IR_COLD_AIRCONTROL()

    {

        if( uc_display_mode!=1 )  return;     //没在制冷模式

           

        if( !bit_DS18_INDOOR_exist &&  uc_ir_state  )      my_aircontrol_cold_turnto(0, 0);       // 如果传感器突然掉线

       

       

        if(b_ECO_ON)

        {

            if( hour > ECO_hour_pre  &&  hour - ECO_hour_pre > 7)       my_aircontrol_cold_turnto(1, 1);        //没跨天

            if( hour < ECO_hour_pre  &&  hour+24 - ECO_hour_pre > 7 )   my_aircontrol_cold_turnto(1, 1);        //跨天了

        }

       

        //按键进行切换屏显

        if(P1_2==0)

        {

            SendIRdata( &g_uc_irdata_with_disp[ uc_ir_state ][0] );

            P1_2_key_waitfor_release();

        }

       

       

        //进行时间延时

        if( g_uc_ir_minute )   //需要延时

        {

            if( minute > g_uc_ir_minute_pre  &&  minute - g_uc_ir_minute_pre > g_uc_ir_minute)  g_uc_ir_minute=0;          //没跨小时

            if( minute < g_uc_ir_minute_pre  &&  minute+60 - g_uc_ir_minute_pre > g_uc_ir_minute )  g_uc_ir_minute=0;      //跨小时了

        }

       

        if( g_uc_ir_minute )    return;

        switch( uc_ir_state )

        {

        case 0:   //关机0中

            break;

           

        case 1:   //全力制冷1中

            if( i_DS18_INDOOR <= ui_airtemp_set )   { my_aircontrol_cold_turnto(2, 10);   ECO_hour_pre=hour;  b_ECO_ON=1; }//温度小于,转成ECO   //n分钟后检测温度, 因为温度会先回升,再下降,不能马上检测

            break;

           

        case 2:   //ECO2中

            if( i_DS18_INDOOR <= ui_airtemp_set )   my_aircontrol_cold_turnto(3, 10);    //温度小于,转成静音+ECO    //10分钟后检测温度, 以免太快发送下一个      如果ECO自动消掉,则温度会下降

            else if( i_DS18_INDOOR >= ui_airtemp_set+10 )    my_aircontrol_cold_turnto(1, 1);    //大于,回退至全力制冷   //1分钟后检测温度, 以免太快发送下一个

            break;

        case 3:   //ECO+静音3

            if( i_DS18_INDOOR <= ui_airtemp_set-3   )    my_aircontrol_cold_turnto(4, 10);  //ECO+静音很省电,也可能会温度太低

            else if( i_DS18_INDOOR > ui_airtemp_set+5 )  my_aircontrol_cold_turnto(2, 10);   //大于,回退至ECO            //10分钟后检测温度, 以免太快发送下一个

            break;

        case 4:   //太冷了,32度ECO+静音4

            if( i_DS18_INDOOR > ui_airtemp_set+5 )  my_aircontrol_cold_turnto(2, 10);   //大于,回退至ECO            //10分钟后检测温度, 以免太快发送下一个

            break; 

        }

    }

           

       

    //红外控制空调制热     ///

    void DO_IR_HEAT_AIRCONTROL()

    {

        if( uc_display_mode!=2 )   return;     //没在控制模式

           

        if( !bit_DS18_INDOOR_exist &&  uc_ir_heat_state )      ;                            // 如果传感器突然掉线

        //程序待补充

       

    }  

       

    //检测串口进行通讯       

    //串口接受2个字节为一组,第一字节是命令,第二字节是数据

    //没有用串口中断,4800bps的2个字节之间只有2ms,如果程序做别的事,就会收不全数据,故电脑发送时,一个字节、一个字节慢慢发

    void DO_PROCESS_SERIAL_COM()

    {

       

            if(RI==1)       //串口收到数据,进行处理

            {

                sbuf[sbufnum]=SBUF;

                RI=0;   //要人工清RI

                sbufnum++;     

               

                if(sbufnum>=2) 

                {

                    sbufnum=0;

                    //sendSBUF( "COM=", 4 );    //回显命令

                    sendSBUF( sbuf, 2 );

                    //sendSBUF( "!", 1 );

                   

                    if(sbuf[0]>=0x80)         //第一字节是命令,如果是0X80以上,发送温度、湿度;   0x80以下留给DS1302

                    {

                        sendSBUF( dispbuf_DS18_OUTDOOR, 3 );

                        sendSBUF( dispbuf_DS18_INDOOR, 3 );

                    }

                    else if(bitDS1302exist)

                    {

                        switch (sbuf[0])       //第一字节是0~8,则写入DS1302。   如果是9等,则读出DS1302

                        {

                            case 0:  //second RAW data

                                    if( (sbuf[1]&0x0f)>9 || ((sbuf[1]&0xf0)>>4)>5 ) {sendSBUF("secoER!",7);break;}

                                    DS1302_WriteOne(DS1302_SEC_AD,sbuf[1]);

                                    readDS1302_and_setclock();

                                    sendSBUF("secoOK!",7);

                                    break;

                            case 1:  //minute RAW data

                                    if( (sbuf[1]&0x0f)>9 || ((sbuf[1]&0xf0)>>4)>5 ) {sendSBUF("minuER!",7);break;}

                                    DS1302_WriteOne(DS1302_MIN_AD,sbuf[1]);

                                    readDS1302_and_setclock();

                                    sendSBUF("minuOK!",7);

                                    break;

                            case 2:  //hour RAW data

                                    if(   (sbuf[1]&0x0f)>9 ||   ((sbuf[1]&0xf0)>>4)>2  ||     ( ((sbuf[1]&0xf0)>>4)*10+(sbuf[1]&0x0f) )>23  ) {sendSBUF("hourER!",7);break;}

                                    DS1302_WriteOne(DS1302_HOUR_AD,sbuf[1]);

                                    readDS1302_and_setclock();

                                    sendSBUF("hourOK!",7);

                                    break;

                            case 3:  //date RAW data

                                    if( ( ((sbuf[1]&0xf0)>>4)*10+(sbuf[1]&0x0f) )>31  ) {sendSBUF("dateER!",7);break;}

                                    DS1302_WriteOne(DS1302_DATE_AD,sbuf[1]);

                                    //readDS1302_and_setclock();

                                    sendSBUF("dateOK!",7);

                                    break;

                            case 4:  //month RAW data

                                    if( ( ((sbuf[1]&0xf0)>>4)*10+(sbuf[1]&0x0f) )>12  ) {sendSBUF("montER!",7);break;}

                                    DS1302_WriteOne(DS1302_MONTH_AD,sbuf[1]);

                                    //readDS1302_and_setclock();

                                    sendSBUF("montOK!",7);

                                    break;

                            case 5:  //week RAW data

                                    if( ( ((sbuf[1]&0xf0)>>4)*10+(sbuf[1]&0x0f) )>7  ) {sendSBUF("weekER!",7);break;}

                                    DS1302_WriteOne(DS1302_WEEK_AD,sbuf[1]);

                                    //readDS1302_and_setclock();

                                    sendSBUF("weekOK!",7);

                                    break; 

                            case 6:  //year RAW data

                                    if( ( ((sbuf[1]&0xf0)>>4)*10+(sbuf[1]&0x0f) )>99  ) {sendSBUF("yearER!",7);break;}

                                    DS1302_WriteOne(DS1302_YEAR_AD,sbuf[1]);

                                    //readDS1302_and_setclock();

                                    sendSBUF("yearOK!",7);

                                    break;                                                                                     

                            case 7: //设置CONTROL字节  写保护

                                    sbuf[1]=sbuf[1] & 0x80; //只有最高位bit7有效,其它都是0

                                    DS1302_WriteOne(DS1302_CONTROL, sbuf[1]);

                                    sendSBUF("ContOK!",7);

                                    break;

                            case 8: //设置TRICKLE CHARGER字节  充电

                                    DS1302_WriteOne(DS1302_CHARGER_AD, sbuf[1]);

                                    sendSBUF("charOK!",7);

                                    break;                         

                            default:    //读出全部DS1302字节,并串口发送出去

                                    BurstRead1302(ds1302_BCDdata) ;

                                    ds1302_BCDdata[8]=DS1302_ReadOne(DS1302_CHARGER_AD);

                                    sendSBUF( ds1302_BCDdata, 9 );

                                    break;

                        }

                    }

                    else

                    {

                        sendSBUF("DS1302 NOT exist!",17);

                    }

                }//END 二个字节处理

            } //END RI==1

           

    }

    ///

    void main(void)

    {

    /* 测试晶振的频率,用来校准晶振的误差

    AUXR = (AUXR | 0x04);  //独立波特率发生器工作在1T 模式  0000 0100   --BRTCLKOUT(CLKOUT2)      

    BRT = (256-5);         //预置数,BRT独立波特率发生器的8位重装载值    //输出频率= 晶振11.0592MHz/5/2=1.10592MHz   --BRTCLKOUT(CLKOUT2)

    WAKE_CLKO = (WAKE_CLKO | 0x04);  //允许 独立波特率发生器脚BRTCLKO(CLKOUT2)(P1.0) 输出时钟频率,   输出频率=定时器溢出率/2

    AUXR = (AUXR | 0x10);            //启动独立波特率发生器开始计数工作   --BRTCLKOUT(CLKOUT2)

    while(1) ; 

       

    */

       

       

       

       

        //红外状态

        uc_ir_state=0;

        uc_ir_heat_state=0;

        P_IR=0;

       

       

        uc_display_mode=0;                    //按走时模式

        uc_display_mode_pre=uc_display_mode;

       

        //开机是弱上拉

        P0M1=0;    //P0推挽

        P0M0=0xFF;

       

        P2M1=0;   //P2推挽

        P2M0=0xFF;

        P0=0;       //P0接各个笔划,共阳极,接地时点亮

        P2=0xFF;    //P2,置1时选择数码管

       

       

        P1M1=0;  

        P1M0=0x08;  // 0000 1000  //空调口设成推挽(红外)

        P1=0xF7;    //1111 0111  关闭空调口(红外)

       

        P3=0xFF;  //弱上拉

        DS1302_RST = 0; 

        DS1302_CLK = 0;

        Delay20ms();         //延时,让DS1302等外设上电复位

       

        DS1302_SetProtect(0);                     //上电后要取消写保护,才能对DS1302的RAM进行写入

       

        DS1302_WriteOne( 0xC0 , 0xAB);            //测试DS1302的RAM,判断DS1302是存在

        if (DS1302_ReadOne( 0xC0 ) == 0xAB)    

        {

            bitDS1302exist=1;

            DS1302_WriteOne(DS1302_CHARGER_AD,  0);   //取消对电池充电

        }

        else    bitDS1302exist=0;

        if(bitDS1302exist)  readDS1302_and_setclock();      //读取和设置时间

           

        //先显示一下,以免黑屏

        dispbuf[4]=minute%10;

        dispbuf[5]=minute/10;

        dispbuf[6]=hour%10;

        dispbuf[7]=hour/10;

        //设置定时器T0

        //TMOD=0x52;    // 0101 0010 =  T1的GATE=0选通,C/T选择为外部计数方式=1,工作方式=1 (16位计数器);  T0的GATE选通,C/T选择为C定时方式=0,工作方式=2

        TMOD = 0x22; //(定时器0和1:  GATE选通,定时方式,   工作方式2,自动重装,8位)

       

        TH0=0;     //预置数=0

        TL0=0;     //从0开始计数 即:(256-0)=256次 ,11.0592/12/256=3600 次/S

        TR0=1;        //开始计数

        ET0=1;        //允许T0的中断

       

       

        //设置定时器T1  --用于红外遥控

        TH1=244;        //预置数         //即计数12次中断一次,11.0592MHZ,即:11.0592/12/12=76.8K 次/S

        TL1=244;        //开始计数值

        TR1=1;          //开始计数  

        ET1=0;       

        PT1=1;      //中断优先级IP中的PT1=1,提高中断优先级比T0高!       这样T1中断可以中断掉T0中断

       

       

        //设置 "独立波特率发生器",用于串口 --V2版新增

        AUXR=AUXR | 1;  //不可位AUXR中的BIT0的S1BRS=1,即选用独立波特率发生器,定时器1得到释放!

        SCON=0x50;      //01 0 1 0000 模式1,无多处理机,允许接收,发送8位,接收8位,清TI=0,RI=0

        BRT=250;        //从0开始计数 即:(256-250)=6次 ,11.0592/12/6/32=4800bps,需要 波特率倍增位SMOD=0。   波特率低一些,以免处理不过来

        AUXR=AUXR | 0x10;   //不可位AUXR中的BIT4的BRTR=1,即允许独立波特率发生器运行

        ES=0;               //不允许串口总断   

        EA=1;         //允许总中断

       

       

        //串口接收到一个数据后,这时RI要为0,才会放入SBUF中并且自动置RI=1,否则丢弃这个数据。如果RI为1,则后续数据都丢掉了。

        //串口发送时,就会马上发,发完自动置TI=1。如果TI=1,不影响数据发送。  

        RI=0;       //清理一下串口

        sbufnum=0;  //串口接收到的数量

        I2C_Init();   //HTU31D

    /* 测试 红外发射信号是否正常

    uc_ir_state=1;

    while(1)

    {

        if(P1_0==0)

        {

            SendIRdata( &g_uc_irdata[uc_ir_state][0] );

            uc_ir_state++;     

            if(uc_ir_state>=4) uc_ir_state=0;

           

            P1_0_key_waitfor_release();     //等待按键松开

        }

    }

    */

        while(1)  

        {

            DO_PROCESS_SERIAL_COM();    //检测串口进行通讯

            DO_KEY_PRESS_SETTING();     //检测按键进入设置状态

       

            DO_KEY_PRESS_AIRCONTROL();   //检测按键进行红外开关空调

            DO_IR_COLD_AIRCONTROL();   //红外控制空调制冷

           

            DO_IR_HEAT_AIRCONTROL();   //红外控制空调制热

            TIME_PROCESS();        //走时

           

            DO_WHAT_FUNC();      //传感器检测

        }

    }

    ///

    //中断TSR    中断处理时间不能超过一次计数时间277.777US

    /*

    中断每秒3600次,每次277.7777us。时间计算在主程序中进行。

    //---注意:这个会影响软延时delay函数,执行一次如果是10us,则对于277us来说误差就是4%(实际执行时间未知)

    */

    void t0(void) interrupt 1

    {

        tcnt++;

       

        i_dowhat_interval++;   //如果按键一直按住,则会多计数,或溢出,没关系,就是dowhat间隔时间再延长而已

       

        mstcnt++;

       

       

        if(bit_T0_interrupt_prohibited)    return;    //时序要求强的操作,则后序代码不执行

           

           

        if( mstcnt>=4 )      //计数器每计数n次,改变一次显示位(定时器是3600HZ,数码管闪烁频率=3600/9次/8个管=50HZ,数码管闪烁频率=3600/4次/16个管=56HZ)

        {

            mstcnt=0;

            if(dispbitcnt<8)

            {

                P0= dispcode[dispbuf[dispbitcnt]];    //P0输出笔划。  dispbuf中是显示数据,[0]是最右那个数码管。  dispcode中是笔划。

           

                P2= dispbitcode[dispbitcnt];         //P2选择哪个数码管,dispbitcode是数码管选择码,[0]是最右那个数码管

            }

            else

                P2=0;   //不显示,16管只显示8管,用于减少一半显示亮度,以免太亮

               

            dispbitcnt++;        //选LED的下一显示位

            if(dispbitcnt>=16)  dispbitcnt=0;            //这里数值大,则亮度小,但闪烁。闪烁频率在25HZ,明显会闪。闪烁频率在50HZ,不闪。

        }

       

       

        //红外发射IR控制

        if(g_uc_ir_time)

        {

            if(g_b_ir_onoff)   ET1=1;   //本次发射IR  278us

            else    ET1=0;              //本次不发射IR  278us

            g_uc_ir_time--;

        }

        else    ET1=0;   //把红外IR关了

           

       

        //在设置模式中,需要进行计时

        if(g_uc_setting_mode)    g_ui_settingmode_timeout_cnt++;

       

    }

    ///

    //T1  --用于红外发射38KHZ

    //只进行端口翻转,处理时间要短。   76.8K 次/S,  13.021us一次中断。38KHZ是26.316us周期。 1%的误差,对载波来说,没有问题。

    //---这个会严重影响软延时delay函数,执行一次可能要1us,对于13us来说误差就是10%,软延时就被影响10%!!造成红外发射严重不准或其它问题

    void t1(void) interrupt 3

    {

        P_IR=~P_IR;        //端口翻转就有了38KHZ信号

    }  

    //====================================================/

    DELAY_STC12_WXL.H文件:

    //  STC12C5A60S2 单片机 1T  11.0592MHZ

    void Delay2us()     //@11.0592MHz

    {

        unsigned char i;

        _nop_();

        _nop_();

        i = 2;

        while (--i);

    }

    //

    void Delay8us()     //@11.0592MHz

    {

        unsigned char i;

        i = 19;

        while (--i);

    }

    //

    void Delay10us()        //@11.0592MHz

    {

        unsigned char i;

        _nop_();

        _nop_();

        _nop_();

        i = 24;

        while (--i);

    }

    //

    void Delay80us()        //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        _nop_();

        _nop_();

        i = 1;

        j = 216;

        do

        {

            while (--j);

        } while (--i);

    }

    //

    void Delay600us()       //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        _nop_();

        i = 7;

        j = 112;

        do

        {

            while (--j);

        } while (--i);

    }

    //

    void Delay1ms()     //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        i = 11;

        j = 190;

        do

        {

            while (--j);

        } while (--i);

    }

    ///

    void Delay20ms()        //@11.0592MHz

    {

        unsigned char i, j, k;

        i = 1;

        j = 216;

        k = 35;

        do

        {

            do

            {

                while (--k);

            } while (--j);

        } while (--i);

    }

    void Delay50ms()        //@11.0592MHz

    {

        unsigned char i, j, k;

        i = 3;

        j = 26;

        k = 223;

        do

        {

            do

            {

                while (--k);

            } while (--j);

        } while (--i);

    }

    ///以下是红外遥控用的           

    void Delay9ms()     //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        _nop_();

        _nop_();

        i = 97;

        j = 206;

        do

        {

            while (--j);

        } while (--i);

    }

    void Delay4500us()      //@11.0592MHz

    {

        unsigned char i, j;

        i = 49;

        j = 101;

        do

        {

            while (--j);

        } while (--i);

    }

    void Delay560us()       //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        _nop_();

        _nop_();

        i = 7;

        j = 1;

        do

        {

            while (--j);

        } while (--i);

    }

    void Delay1690us()      //@11.0592MHz

    {

        unsigned char i, j;

        i = 19;

        j = 42;

        do

        {

            while (--j);

        } while (--i);

    }

    //====================================================/

    DS18B20_WXL.H文件:

    /*  先设置端口

    sbit DQ_DS18_OUTDOOR=P3^4;

    sbit DQ_DS18_INDOOR=P1^4;

    */

    //

    unsigned char  DS18_CRC( unsigned char   *addr, unsigned char  len)

    //计算8位CRC,输入是一串字节流数据。  数据不需要扩展CRC位的0。

    //注意:这里的addr[]中是原始数据,不需要扩展的一字节0,len长度为原始数据长度!!  重要!!

    {

        unsigned char  idata  crc = 0, inbyte, i, mix;

        while (len--)

        {

            // inbyte 存储当前参与计算的新字节

            inbyte = *addr++;

            for (i = 8; i; i--)

            {

                mix = (crc ^ inbyte) & 0x01; // CRC寄存器最低位 与 数据的最低位 进行XOR(高位都是忽略的),看结果是1还是0,如果是1,则需要用POLY对CRC寄存器进行XOR

                crc >>= 1;      //高位移入的是0

                if (mix)

                {

                    crc ^= 0x8C;   //颠倒后的POLY

                }

                inbyte >>= 1;  

            }

        }

        return crc;

    }

    //

    int  DS18_TEMP_TO_SIGNED_INT10(unsigned char  uc_DS18_temp1, unsigned char  uc_DS18_temp2) //LSB字节 , MSB字节。

    //返回值:  温度的10倍,有符号整数

    //ds18b20的2个字节:  负数是全部bit取反,再加1,比如:

    //55度:0000 0011 0111 0000

    //取反:1111 1100 1000 1111

    //加1:1111 1100 1001 0000 得-55度

    //ds18b20的低4bit是小数,需要单独处理,不能合在一起转成十进制。   高5bit是符号位。  中间7bit是非小数

    {

        unsigned char  idata  uc_fraction=0;

        int  idata  i_temp=0;

        bit bitminus=0;

        unsigned char  code  DS18_decimaltable[16]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};  //小数换算表,二进制温度数据0001是0.0625度,即小数显示1;0010是0.125度,即小数还是显示1;...

       

        if((uc_DS18_temp2 & 0xf8)!=0x00)     //如果是负数

        {        //是负数,2个字节合在一起整个减一,再取反,就是正数值(小数还要乘以精度)

                //或者,2个字节合在一起整个取反再加1

            bitminus=1;    //表示是负数

            uc_DS18_temp2=~uc_DS18_temp2;    

            uc_DS18_temp1=~uc_DS18_temp1;      //取反

            uc_DS18_temp1++;                 //加1

            if (uc_DS18_temp1==0) uc_DS18_temp2++;  //如果有进位,则要加1

        }

       

        uc_fraction=DS18_decimaltable[ uc_DS18_temp1 & 0x0f ];   //查表得到小数部分

       

        uc_DS18_temp2=uc_DS18_temp2<<4;        

        uc_DS18_temp2=uc_DS18_temp2 & 0x70;     //保证MSB其它位均为0

        uc_DS18_temp1=uc_DS18_temp1>>4;    //移掉LSB中的小数

        uc_DS18_temp1=uc_DS18_temp1 & 0x0f;     //保证LSB其它位均为0

        uc_DS18_temp2=uc_DS18_temp2 | uc_DS18_temp1;    //MSB与LSB的非小数部分合并,这样就是温度的绝对值的整数值

       

        i_temp=uc_DS18_temp2*10;

        i_temp+=uc_fraction;

       

        if(bitminus)    return -i_temp;

        else    return i_temp;

    }

    /*

    //

    unsigned int DS18_TEMP_TO_INT(unsigned char  uc_DS18_temp1, unsigned char  uc_DS18_temp2)   //LSB字节 , MSB字节。

    //返回值 低15位=温度绝对值的10倍。如果是负数,则最高位是1。同AM2321的表达方式

    //ds18b20的2个字节:  负数是全部bit取反,再加1,比如:

    //55度:0000 0011 0111 0000

    //取反:1111 1100 1000 1111

    //加1:1111 1100 1001 0000 得-55度

    //ds18b20的低4bit是小数,需要单独处理,不能合在一起转成十进制。   高5bit是符号位。

    {

        unsigned char  idata  uc_fraction=0;

        unsigned int  idata  ui_temp=0;

        bit bitminus=0;

        unsigned char  idata  DS18_decimaltable[16]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};  //小数换算表,二进制温度数据0001是0.0625度,即小数显示1;0010是0.125度,即小数还是显示1;...

       

        if((uc_DS18_temp2 & 0xf8)!=0x00)     //如果是负数

        {        //是负数,2个字节合在一起整个减一,再取反,就是正数值(小数还要乘以精度)

                //或者,2个字节合在一起整个取反再加1

            bitminus=1;    //表示是负数

            uc_DS18_temp2=~uc_DS18_temp2;    

            uc_DS18_temp1=~uc_DS18_temp1;      //取反

            uc_DS18_temp1++;                 //加1

            if (uc_DS18_temp1==0) uc_DS18_temp2++;  //如果有进位,则要加1

        }

       

        uc_fraction=DS18_decimaltable[ uc_DS18_temp1 & 0x0f ];   //查表得到小数部分

       

        uc_DS18_temp2=uc_DS18_temp2<<4;        

        uc_DS18_temp2=uc_DS18_temp2 & 0x70;     //保证MSB其它位均为0

        uc_DS18_temp1=uc_DS18_temp1>>4;    //移掉LSB中的小数

        uc_DS18_temp1=uc_DS18_temp1 & 0x0f;     //保证LSB其它位均为0

        uc_DS18_temp2=uc_DS18_temp2 | uc_DS18_temp1;    //MSB与LSB的非小数部分合并,这样就是温度的绝对值的整数值

       

        ui_temp=uc_DS18_temp2*10;

        ui_temp+=uc_fraction;

       

        if(bitminus) ui_temp=ui_temp | 0x8000;      //如果是负数,则最高位变1

       

        return ui_temp;

    }

    */

    //

    void SEND_TO_DS18_OUTDOOR( unsigned char  command ) //DQ_DS18_OUTDOOR表示哪一个DS1302,调用前DQ_DS18_OUTDOOR为高电平

    {

        unsigned char  idata  i;

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

        {

            if((command & 0x01)==0)   //command为待发送的数据。 现要发送“0”     60至120us之间

            {

                DQ_DS18_OUTDOOR=0;      //DATA=0,DATA从高到低,表示开始传送数据(传送开始到传送完的整个时间在60-120US之内)

                Delay80us();    //延时120us(不能太大,不然就变成复位了)   (先经过15US,然后DS18会开始检测DATA数据)

                DQ_DS18_OUTDOOR=1;      //“0”发送完成,拉高 ,然后需要至少1US的恢复时间(即高电平时间)

                Delay2us();

            }

            else            //现要发送“1”       最小 60  us

            {

                MACRO_WXL_INTERRUPT_PROHIBITED      //自定义宏,强时序操作时中断服务简化

                DQ_DS18_OUTDOOR=0;      //DATA=0,DATA从高到低,表示开始传送数据(传送开始到传送完的整个时间在60-120US之内)

                Delay2us(); //延时(经过15US后DS18会开始检测DATA数据,因此应尽快变1)

                DQ_DS18_OUTDOOR=1;      //DATA=1,送出DATA值

                MACRO_WXL_INTERRUPT_ALLOWED

                Delay80us();    //延时80~∝us

            }

            command=_cror_(command,1);   //循环移位

        }

    }   //END,调用后DQ_DS18_OUTDOOR为高电平

    //

    unsigned char RECEIVE_FROM_DS18_OUTDOOR( )  //DQ_DS18_OUTDOOR表示哪一个DS1302,调用前DQ_DS18_OUTDOOR为高电平

    {

        unsigned char  idata  i,temp;

        temp=0;

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

        {

            temp=_cror_(temp,1);    //移位,LSb先传送

            MACRO_WXL_INTERRUPT_PROHIBITED       //自定义宏,强时序操作时中断服务简化

            DQ_DS18_OUTDOOR=0;                   //DATA=0,从高到低,表示开始数据接收

            Delay2us();     //开始数据接收后,需先DATA=0至少1US进行INIT

            DQ_DS18_OUTDOOR=1;                   //DATA=1,以进行检测

            Delay8us(); //延时8us    (开始数据接收后,DS18只输出数据15US)

            if(DQ_DS18_OUTDOOR==1)    temp=temp | 0x80;           //读DATA

            MACRO_WXL_INTERRUPT_ALLOWED

            Delay80us();    //延时80~∝us    (整个读取时间至少是60US,再加1US的恢复时间(即高电平时间)

        }

        return temp;

    }   //END,调用后DQ_DS18_OUTDOOR为高电平

    //

    bit RESET_DS18_OUTDOOR(  )  //DQ_DS18_OUTDOOR表示哪一个DS1302,调用前DQ_DS18_OUTDOOR为高电平。   返回0表示出错

    {

        DQ_DS18_OUTDOOR=1;

        Delay2us();

        if(DQ_DS18_OUTDOOR==0) return 0;

       

        DQ_DS18_OUTDOOR=0;

        Delay600us();       //延时750us

        DQ_DS18_OUTDOOR=1;

        Delay80us();        //延时80us 

        if(DQ_DS18_OUTDOOR==1) return 0;   //如果DS18B20有下拉,则存在DS18B20

        Delay600us();       //延时670us

        return 1;

    }   //END,调用后DQ_DS18_OUTDOOR为高电平

    //

    //

    void SEND_TO_DS18_INDOOR( unsigned char  command )  //DQ_DS18_INDOOR表示哪一个DS1302,调用前DQ_DS18_INDOOR为高电平

    {

        unsigned char  idata  i;

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

        {

            if((command & 0x01)==0)   //command为待发送的数据。 现要发送“0”     60至120us之间

            {

                DQ_DS18_INDOOR=0;       //DATA=0,DATA从高到低,表示开始传送数据(传送开始到传送完的整个时间在60-120US之内)

                Delay80us();    //延时120us(不能太大,不然就变成复位了)   (先经过15US,然后DS18会开始检测DATA数据)

                DQ_DS18_INDOOR=1;       //“0”发送完成,拉高 ,然后需要至少1US的恢复时间(即高电平时间)

                Delay2us();

            }

            else            //现要发送“1”       最小 60  us

            {

                MACRO_WXL_INTERRUPT_PROHIBITED    //自定义宏,强时序操作时中断服务简化

                DQ_DS18_INDOOR=0;       //DATA=0,DATA从高到低,表示开始传送数据(传送开始到传送完的整个时间在60-120US之内)

                Delay2us(); //延时(经过15US后DS18会开始检测DATA数据,因此应尽快变1)

                DQ_DS18_INDOOR=1;       //DATA=1,送出DATA值

                MACRO_WXL_INTERRUPT_ALLOWED

                Delay80us();    //延时80~∝us

            }

            command=_cror_(command,1);   //循环移位

        }

    }   //END,调用后DQ_DS18_INDOOR为高电平

    //

    unsigned char RECEIVE_FROM_DS18_INDOOR( )   //DQ_DS18_INDOOR表示哪一个DS1302,调用前DQ_DS18_INDOOR为高电平

    {

        unsigned char  idata  i,temp;

        temp=0;

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

        {

            temp=_cror_(temp,1);    //移位,LSb先传送

            MACRO_WXL_INTERRUPT_PROHIBITED     //自定义宏,强时序操作时中断服务简化

            DQ_DS18_INDOOR=0;                   //DATA=0,从高到低,表示开始数据接收

            Delay2us();     //开始数据接收后,需先DATA=0至少1US进行INIT

            DQ_DS18_INDOOR=1;                   //DATA=1,以进行检测

            Delay8us(); //延时8us    (开始数据接收后,DS18只输出数据15US)

            if(DQ_DS18_INDOOR==1)    temp=temp | 0x80;           //读DATA

            MACRO_WXL_INTERRUPT_ALLOWED

            Delay80us();    //延时80~∝us    (整个读取时间至少是60US,再加1US的恢复时间(即高电平时间)

        }

        return temp;

    }   //END,调用后DQ_DS18_INDOOR为高电平

    //

    bit RESET_DS18_INDOOR(  )   //DQ_DS18_INDOOR表示哪一个DS1302,调用前DQ_DS18_INDOOR为高电平。   返回0表示出错

    {

        DQ_DS18_INDOOR=1;

        Delay2us();

        if(DQ_DS18_INDOOR==0) return 0;

       

        DQ_DS18_INDOOR=0;

        Delay600us();       //延时750us

        DQ_DS18_INDOOR=1;

        Delay80us();        //延时80us 

        if(DQ_DS18_INDOOR==1) return 0;     //如果DS18B20有下拉,则存在DS18B20

        Delay600us();       //延时670us

        return 1;

    }   //END,调用后DQ_DS18_INDOOR为高电平

    //====================================================/

    I2C_HTU31D.H文件:

    //根据别人的程序修改的

    void I2C_Delay(unsigned char t)

    {

        while(t--)

        {

            Delay10us();

        }

    }

    /*================================================================

    【名 称】unsigned char I2CWRByte(unsigned char WriteData)

    【功 能】I2C写一个字节数据,返回ACK或者NACK

    【备 注】从高到低,依次发送

    ================================================================*/

    unsigned char I2C_WriteByte(unsigned char   WriteData)

    {

        unsigned char idata i;

        //SCL = 0;                //写之前,SCL、SDA已经是0

        for(i = 0;i < 8;i++)       //第1~8位,写给对方,  MSb先发

        {

            I2C_Delay(10);   //稳定SCL低电平

            if(WriteData & 0x80)            SDA = 1;   

            else            SDA = 0;

            I2C_Delay(40);   //稳定SDA

           

            SCL = 1;           //输出SDA稳定后,拉高SCL给出上升沿,从机检测到后进行数据采样

            I2C_Delay(50);   //高电平500us

            SCL = 0;

               

            WriteData <<= 1;

        }

       

        I2C_Delay(10);     //稳定SCL低电平

        SDA = 1;

        I2C_Delay(40);

       

        SCL = 1;                 //第9位,读对方发来的是ACK还是NACK。   注意: I2C里,回应ACK是SDA拉低电平,NACK是释放SDA(高电平)!!对于主机设备、外设都是这样!!

        I2C_Delay(50);

        if( SDA )   i=1;            //SDA为高,收到NACK

        else        i=0;            //SDA为低,收到ACK

        SCL = 0;

       

        //_nop_(); _nop_();    _nop_();    _nop_();

        I2C_Delay(1);                            //稳定SCL低电平

        SDA=0;                                    //返回时,SCL、SDA都是0

        return i;

    }

    /*================================================================

    【名 称】unsigned char I2CRDByte(unsigned char AckValue)

    【功 能】I2C读一个字节数据,入口参数用于控制应答状态,ACK或者NACK

    【备 注】从高到低,依次接收

    ================================================================*/

    unsigned char I2C_ReadByte(unsigned char   AckValue)

    {

        unsigned char idata i;

        unsigned char idata ReadData = 0;

       

         //读之前,SCL、SDA已经是0

         

        SDA = 1;            //释放SDA

       

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

        {

           

            I2C_Delay(50);  //稳定SCL低电平   

           

            SCL = 1;               //SCL上升沿

            I2C_Delay(50);     //延时SCL高电平500us等待信号稳定    注:在后期测比较好,相当于速率更慢,SDA信号更稳定

            ReadData <<= 1;

            if(SDA == 1)        //采样获取数据      数据是MSB最高字节在先、MSb最高位在先

                ReadData |= 0x01;

            else

                ReadData &= 0xfe;

            SCL = 0;       

        }  

       

        I2C_Delay(10);    //稳定SCL低电平

        SDA = AckValue;   //应答状态

        I2C_Delay(40);

        SCL = 1;  

      I2C_Delay(50);         

      SCL = 0;

        I2C_Delay(1);

        SDA=0;                           //返回时,SCL、SDA都是0

       

        return ReadData;

    }

    /*================================================================

    【名 称】void I2CStart(void)

    【功 能】I2C启动信号

    【备 注】SCL、SDA同为高,SDA跳变成低之后,SCL跳变成低

    ================================================================*/

    void I2C_Start(void)

    {

        SDA = 1;

        I2C_Delay(1);

        SCL = 1;

        I2C_Delay(20);       // SCL高电平200us

        SDA = 0;

        I2C_Delay(10);       // SCL高电平100us

        SCL = 0;

        I2C_Delay(1);        //返回时,SCL、SDA都是0

    }

    /*================================================================

    【名 称】void I2CStop(void)

    【功 能】I2C停止信号

    【备 注】SCL、SDA同为低,SCL跳变成高之后,SDA跳变成高

    ================================================================*/

    void I2C_Stop(void)

    {

            //之前,SCL、SDA已经是0

            I2C_Delay(10);        // SCL低电平500us

            SDA = 0;

            I2C_Delay(40);

           

        SCL = 1;

        I2C_Delay(10);       // SCL高电平100us

        SDA = 1;               //返回时,SCL、SDA都是0

    }

    /*================================================================

    【名 称】void I2C_Init(void)

    【功 能】I2C初始化,空闲状态

    ================================================================*/

    void I2C_Init(void)

    {

        unsigned char idata  i;

        for(i = 0;i < 99;i++)       //做99次,如果 外设 有发送什么数据什么的,可以让它全部发完

        {

        I2C_Start();

        I2C_Stop();

        }

    }

    /

    unsigned char HTU31D_conversion( unsigned char addr )    //返回  0表示HTU回应错误,  1表示成功

    {

       

        I2C_Start();

       

        if(I2C_WriteByte( addr<<1 ) == 0)   //发送地址+wr,  addr接GND是0x40,接VCC是0x41,  wr是LSB位是0。   HTU会反馈ACK0

        {

            if(I2C_WriteByte( 0x5E ) == 0)   //发送精度要求, 按最大精度,  01011110= 0x5E。   HTU会反馈ACK0

            {

                I2C_Stop();

                return 1;   //成功

            }

        }

        I2C_Stop();

        return 0;  //不成功

    }

    unsigned char  CRC_noadd0(unsigned char    *addr, unsigned char    len)  

    //计算8位CRC,输入是一串字节流数据。  按变种计算方式,数据不需要扩展CRC位的0,可以扩展CRC值。

    //注意:这里的addr[]中是原始数据,不需要扩展的一字节0,len长度为原始数据长度!!  重要!!

    //注意:这里的addr[]中可以扩展一字节CRC值,len长度为原始数据长度+1,这样计算出来CRC值就是0。

    {

        unsigned char  idata   crc = 0, inbyte, i;

        while (len--)

        {

            // inbyte 存储当前参与计算的新字节

            inbyte = *addr++;

            for (i = 8; i; i--)

            {

                 //CRC寄存器移出的高位与待测数据移出的高位相XOR,是1则要把CRC寄存器XOR

                if(    (crc & 0x80) ^ (inbyte & 0x80)  )

                {

                        crc<<=1;

                        crc^=0x31;                             

                }

                else

                      crc<<=1;

                   

                        inbyte<<=1;

            }

        }

        return crc;

    }

    unsigned char HTU31D_CRC(unsigned char  MSB,  unsigned char LSB,   unsigned char   CRC)      //HTU31D发送数据是MSB最高字节在先、MSb最高位在先,计算CRC也这样

    {

        unsigned char  idata  ucdata[3];

        ucdata[0]=MSB;

        ucdata[1]=LSB;

        ucdata[2]=CRC;

        return CRC_noadd0(ucdata, 3);

    }

    unsigned char HTU31D_readTRH(int   *wendu,  int   *shidu,  unsigned char addr )     //返回0表示HTU回应错误,  1表示成功。   值低15位= 温度或湿度 绝对值的10倍。如果是负数,则最高位是1。

    {

       

        unsigned char idata MSB, LSB, CRC;

        unsigned int idata temp;

        float idata ftemp;

       

        I2C_Start();

       

        if(I2C_WriteByte( addr<<1 ) == 0)   //发送地址+wr,  addr接GND是0x40,接VCC是0x41,  wr是LSB位是0。   HTU会反馈ACK0

        {

            if(I2C_WriteByte( 0 ) == 0)   //发送Read T & RH。   HTU会反馈ACK0

            {

                I2C_Stop();

                I2C_Start();

                if(I2C_WriteByte( (addr<<1) + 1 ) == 0)   //发送地址+rd,  addr接GND是0x40,接VCC是0x41,  rd是LSB位是1。   HTU会反馈ACK0

                {

                    MSB = I2C_ReadByte(0);    //读取温度MSB,并发送ACK0来确认

                    LSB = I2C_ReadByte(0);     //读取温度LSB,并发送ACK0来确认

                    CRC = I2C_ReadByte(0);          //读取CRC,并发送ACK0来确认

                    if(HTU31D_CRC(MSB,LSB,CRC))          //CRC出错

                    {

                        *wendu=1200;

                    }      

                    else    //CRC正常

                    {

                        temp = MSB;

                        temp<<=8;

                        temp += LSB;

                       

                        ftemp= -40. + 165. * ( (float)temp / 65535. ) ;        //温度:T ℃= -40 + 165 * ST / 65535    ,float 约精确到6位数,可以满足               

                        if(ftemp>=0)

                            *wendu=  (int)(  (ftemp+0.05) *10.);               //四舍五入 

                        else

                            *wendu=  (int)(  (ftemp-0.05) *10.);                 

                    }

                   

                    MSB = I2C_ReadByte(0);    //读取湿度MSB,并发送ACK0来确认

                    LSB = I2C_ReadByte(0);     //读取湿度LSB,并发送ACK0来确认

                    CRC = I2C_ReadByte(1);          //读取CRC,并发送 NACK1 来确认要停止了

                    I2C_Stop();

                    if(HTU31D_CRC(MSB,LSB,CRC))          //CRC出错

                    {

                        *shidu=1200;

                    }      

                    else    //CRC正常              

                    {

                        temp = MSB;

                        temp<<=8;

                        temp += LSB;

                        *shidu=(int)( (100.*( (float)temp / 65535.) + 0.05) *10. ) ;   //四舍五入     //湿度: RH%= 100 * SRH / 65535   ,float 约精确到6位数,可以满足

                    }

                   

                    //在主程序判断数值是否异常。比如温度值太大,说明数据异常

                   

                    return 1;  //成功

                }

            }

        }

        I2C_Stop();

        return 0;  //不成功

    }

    //====================================================/

    DS1302_wxl.H文件:

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

    文件名称: DS1302.H

    创建人:kuloqiu

    描述:

       完成于2010.9.9 硬件测试通过

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

    //硬件接口定义

    /*

    sbit DS1302_CLK=P1^5; 

    sbit DS1302_IO=P1^6; 

    sbit DS1302_RST=P1^7;

    */

    //DS1302内部寄存器地址

    #define DS1302_SEC_AD    0x80 //秒数据地址   //秒寄存器地址   1000 000X    BIT0是1表示读,是0表示写。  BIT7恒为1。   BIT6是1表示内存RAM区,0表示CLOCK区。

    #define DS1302_MIN_AD   0x82 //分数据地址

    #define DS1302_HOUR_AD   0x84 //时数据地址

    #define DS1302_DATE_AD    0x86 //日数据地址

    #define DS1302_MONTH_AD   0x88 //月数据地址

    #define DS1302_WEEK_AD    0x8A //星期数据地址   

    #define DS1302_YEAR_AD    0x8C //年数据地址

    #define DS1302_CONTROL    0x8E //WP写保护控制

    #define DS1302_CHARGER_AD 0x90 //充电涓电流                  1001 000X

    //传输方式

    #define DS1302_NWCLOCK    0xBE   //多字节突发方式写时钟

    #define DS1302_NRCLOCK    0xBF   //多字节突发方式读时钟

    #define DS1302_NWRAM    0xFE   //多字节突发方式写RAM

    #define DS1302_NRRAM    0xFF   //多字节突发方式读RAM

    #define DS1302_RAM(X) (0xC0+(X)*2)   //用于计算 RAM 地址的宏。  1100 000X  ~ 1111 111X  ,即 写C0/读C1、写C2/读C3、写C4/读C5、 ~  写FE/读FF ,即地址按C0、C2、C4。。。再赋BIT0

    #define BCD(X) ((X/10)<<4|(X%10))     //数值  转成 BCD格式

    //地址命令0位

    #define DS1302_READ       0X01 //读

    #define DS1302_WRITE      0X00 //写

    //函数定义

    extern void DS1302_innerWriteByte(unsigned char cdata);

    extern unsigned char DS1302_innerReadByte();

    extern void DS1302_WriteOne(unsigned char address,unsigned char cdata);

    extern unsigned char DS1302_ReadOne(unsigned char address);

    extern void DS1302_SetProtect(bit flag);

    extern void DS1302_SetRtc(unsigned char *tible);

    extern void DS1302_ReadRtc(unsigned char *timeread);

    extern void Display_TimePROESS(unsigned char dplay[8],unsigned char *tible); 

    extern void DS1302_NReadRam(unsigned char *rstr);

    extern void DS1302_NWriteRam(unsigned char *wstr);

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

    文件名称: DS1302.c

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

    void DS1302delay(void )

    {

    _nop_();

    _nop_();

    _nop_();

    _nop_();

       

    }

    void DS1302_innerWriteByte(unsigned char cdata) //实时时钟写入一字节(内部函数),调用时需要CLK=0,返回时CLK=0, IO=1

    //写入DS1302是CLK=0时准备数据,上升沿写入;

    //读取DS1302是CLK下降沿后的CLK=0时可以读

    {

        unsigned char  idata  i;

        unsigned char  idata  acc;

        acc = cdata;

        for(i=8; i>0; i--)

        {

            DS1302_IO  =acc & 1;            //低位先传送

            DS1302delay();                              //(进入时,CLK先进行延时一下)

            DS1302_CLK = 1;        //上升沿写入

            DS1302delay();

            DS1302_CLK = 0;               

            acc = acc >> 1;

        }

        DS1302_IO  =1;

    }  //返回时CLK=0,IO=1,没有延时一下

    unsigned char  DS1302_innerReadByte() //实时时钟读取一字节(内部函数),调用时需要CLK=0,数据IO处于可读状态,返回时CLK=0, IO=1

    //写入DS1302是CLK=0时准备数据,上升沿写入;

    //读取DS1302是CLK下降沿后的CLK=0时可以读

    {

        unsigned char  idata  i;

        unsigned char  idata  acc,temp;

        for(i=8; i>0; i--)

        {   

            acc = acc >>1;            //低位先传送

            DS1302_IO=1;

            DS1302delay();                                   //(进入时,CLK先进行延时一下)

            temp=DS1302_IO;          //先读数,再 CLK 上升沿

            acc = (temp<<7) | acc;

            DS1302_CLK = 1;

            DS1302delay();

            DS1302_CLK = 0;         

        }

        return(acc);

    }  //返回时CLK=0,IO=1,没有延时一下

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

    函数名称: DS1302_WriteOne(unsigned char address,unsigned char cdata)

    函数功能: 向指定地址写单个数据

    输入参数: address要写入对象的地址, cdata 要写的数据

    输出参数:     

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

    void DS1302_WriteOne(unsigned char address,unsigned char cdata)

    //ucAddr: DS1302地址,   ucData: 要写的数据

    {

        DS1302_RST = 0;

        DS1302_CLK = 0;

        DS1302delay();    //RST变上升要有1us以上间隔

        DS1302_RST = 1;

       

        DS1302_innerWriteByte(address & 0xFE);       // 地址 | "写命令"   BIT0是0

        DS1302_innerWriteByte(cdata);       // 写1Byte数据

       

        DS1302delay();      //延时一下CLK低电平

        DS1302_CLK = 0;  

        DS1302_RST = 0;

    }

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

    函数名称: DS1302_ReadOne(unsigned char address)

    函数功能: 向指定地址读出单个数据

    输入参数: address要读出对象的地址

    输出参数:     

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

    unsigned char DS1302_ReadOne(unsigned char address) //读取DS1302某地址的数据

    {

        unsigned char  idata  ucData;

        DS1302_RST = 0;

        DS1302_CLK = 0;

        DS1302delay();    //RST变上升要有1us以上间隔

        DS1302_RST = 1;

       

        DS1302_innerWriteByte(address | 0x01);        // 地址 | "读命令"    BIT0是1

        ucData = DS1302_innerReadByte();         // 读1Byte数据

       

        DS1302delay();        //延时一下CLK低电平

        DS1302_CLK = 0;

        DS1302_RST = 0;

        return(ucData);

    }

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

    函数名称: DS1302_SetProtect(bit flag)

    函数功能: wp保护开关

    输入参数: flag:1保护,0不保护

    输出参数:     

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

    void DS1302_SetProtect(bit flag)        //是否写保护 

    {

    if(flag)

       DS1302_WriteOne(DS1302_CONTROL,0x80);    //DS1302_CONTROL=0x8E,WP写保护

    else

       DS1302_WriteOne(DS1302_CONTROL,0x00);

    }

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

    函数名称: DS1302_SetRtc(unsigned char *settime)

    函数功能: 设置实时时钟

    输入参数: *settime:设置的具体时间数组首地址

    输出参数:     

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

    /* 未使用

    void DS1302_SetRtc(unsigned char *settime)  

    {

    unsigned char i;

    unsigned char tible[7];

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

    {      

       tible[i]=BCD(settime[i]); //BCD转换

    }

    DS1302_SetProtect(0);   //关写保护

    DS1302_WriteOne((DS1302_YEAR_AD|DS1302_WRITE),tible[6]);   //年

    DS1302_WriteOne((DS1302_WEEK_AD|DS1302_WRITE),tible[5]);   //周

    DS1302_WriteOne((DS1302_MONTH_AD|DS1302_WRITE),tible[4]); //月

    DS1302_WriteOne((DS1302_DATE_AD|DS1302_WRITE),tible[3]); //日

    DS1302_WriteOne((DS1302_HOUR_AD|DS1302_WRITE),tible[2]); //时

    DS1302_WriteOne((DS1302_MIN_AD|DS1302_WRITE),tible[1]); //分

    DS1302_WriteOne((DS1302_SEC_AD|DS1302_WRITE),tible[0]); //秒

    //DS1302_SetProtect(1);   //开写保护

    }

    */

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

    函数名称: DS1302_ReadRtc(unsigned char *timeread)

    函数功能: 读取时钟

    输入参数: *timeread:读到的时间数据存放的地址

    输出参数:     

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

    /* 未使用

    void DS1302_ReadRtc(unsigned char *timeread)

    {    

    timeread[6]=DS1302_ReadOne(DS1302_YEAR_AD);

    timeread[5]=DS1302_ReadOne(DS1302_WEEK_AD);

    timeread[4]=DS1302_ReadOne(DS1302_MONTH_AD);

    timeread[3]=DS1302_ReadOne(DS1302_DATE_AD);

    timeread[2]=DS1302_ReadOne(DS1302_HOUR_AD);

    timeread[1]=DS1302_ReadOne(DS1302_MIN_AD);

    timeread[0]=DS1302_ReadOne(DS1302_SEC_AD);

    }

    */

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

    函数名称: Display_TimePROESS(unsigned char dplay[8],unsigned char tible[7])

    函数功能: 处理显示的数据 __仅显示时分秒 ,因为时间寄存器内存放的是BCD码

    输入参数: dplay[8]:处理后的数据   tible[7]:读到后未处理的数据

    输出参数:        

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

    /* 未使用

    void Display_TimePROESS(unsigned char dplay[8],unsigned char tible[7]) //BCD分成两部分转十六进制

    {

    dplay[0]=tible[2]/16; //时

    dplay[1]=tible[2]%16;

    dplay[2]=10;

    dplay[3]=tible[1]/16; //分

    dplay[4]=tible[1]%16;

    dplay[5]=10;

    dplay[6]=tible[0]/16; //秒

    dplay[7]=tible[0]%16;

    }   

    */

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

    函数名称: DS1302_NReadRam(unsigned char *rstr)

    函数功能: 多字节突发模式读RAM,

        DS1302_NRRAM 一次可进行31个片内RAM单元读

    输入参数: *rstr:存放读到的N个数据

    输出参数:        

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

    void DS1302_NReadRam(unsigned char *rstr)

    {

    unsigned char  idata  i;

    DS1302_RST=0;

    DS1302_CLK=0;

    DS1302delay();    //RST变上升要有1us以上间隔

    DS1302_RST=1;       

    DS1302_innerWriteByte(DS1302_NRRAM);   // 写0xFF,多字节突发方式读RAM

    for(i=0;i<31;i++)     //连续31 字节

    {

       *rstr=DS1302_innerReadByte() ;

       rstr++;

    }

    DS1302delay();        //延时一下CLK低电平

    DS1302_RST=0;

    DS1302_CLK=0;

    }

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

    函数名称: DS1302_NReadRam(unsigned char *rstr)

    函数功能: 多字节突发模式写RAM,

        DS1302_NRRAM 一次可进行31个片内RAM单元写

    输入参数: *wstr:要被写入的N个数据

    输出参数:        

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

    void DS1302_NWriteRam(unsigned char *wstr)

    {

    unsigned char  idata  i;

    unsigned char  idata  *tmpstr;  

    tmpstr=wstr ;

    DS1302_SetProtect(0);   //关写保护

    DS1302_RST=0;

    DS1302_CLK=0;

    DS1302delay();    //RST变上升要有1us以上间隔

    DS1302_RST=1;

    DS1302_innerWriteByte(DS1302_NWRAM);   //写 0xFE ,多字节突发方式写RAM

    for(i=0;i<31;i++)     //连续31 字节

    {

       DS1302_innerWriteByte(*tmpstr) ;      

       tmpstr++;

    }

    DS1302delay();      //延时一下CLK低电平

    DS1302_RST=0;

    DS1302_CLK=0;

    //DS1302_SetProtect(1);   //开写保护

    }

    //

    void BurstWrite1302(unsigned char *pWClock)  //往DS1302写入时钟数据(多字节方式)

    {

        unsigned char  idata  i;

       

        DS1302_SetProtect(0);   //关写保护

       

        DS1302_RST = 0;

        DS1302_CLK = 0;

        DS1302delay();    //RST变上升要有1us以上间隔

        DS1302_RST = 1;

       

        DS1302_innerWriteByte(0xbe);         // 0xbe为时钟多字节写时钟命令

        for (i = 8; i>0; i--)         //8Byte = 7Byte 时钟数据 + 1Byte 控制       //时钟块写时,必须至少写8个字节。

        {

            DS1302_innerWriteByte(*pWClock);  // 写1Byte数据   //返回时CLK=0

            pWClock++;

        }

       

        DS1302delay();      //延时一下CLK低电平

        DS1302_CLK = 0;

        DS1302_RST = 0;     //  RST  0=复位  1=运行

    }   //返回时  RST=0

    //

    void BurstRead1302(unsigned char *pRClock) //读取DS1302时钟数据(时钟多字节方式)

    {

        unsigned char  idata  i;

       

        DS1302_RST = 0;             //  RST  0=复位  1=运行。复位后都是先写

        DS1302_CLK = 0;     //  CLK

                //写入DS1302是CLK=0时准备数据,上升沿写入;

                //读取DS1302是CLK下降沿后的CLK=0时可以读

        DS1302delay();    //RST变上升要有1us以上间隔

        DS1302_RST = 1;             //  RST  0=复位  1=运行

       

        DS1302_innerWriteByte(0xbf);        // 0xbf:   时钟多字节读命令

        for (i=8; i>0; i--)

        {

           *pRClock = DS1302_innerReadByte();   // 读1Byte数据    //返回时CLK=0

           pRClock++;

        }

       

        DS1302delay();      //延时一下CLK低电平

        DS1302_CLK = 0;

        DS1302_RST = 0;     //  RST  0=复位  1=运行

    }       //返回时  RST=0

    //

    void  wxl_BCDToStr(unsigned char  *ds1302_BCDdata,  char  *ds1302_strtime)     //ds1302_strtime[18]时间字串,用于串口输出。时间18字节。

    {

    ds1302_strtime [0] = ((ds1302_BCDdata[6]>>4) & 0x0F) + '0';  //年

    ds1302_strtime [1] = ((ds1302_BCDdata[6]) & 0x0F) + '0';

    ds1302_strtime [2] ='.';

    ds1302_strtime [3] = ((ds1302_BCDdata[4]>>4) & 0x0F) + '0';  //月

    ds1302_strtime [4] = ((ds1302_BCDdata[4]) & 0x0F) + '0';

    ds1302_strtime [5] ='.';

    ds1302_strtime [6] = ((ds1302_BCDdata[3]>>4) & 0x0F) + '0';  //日

    ds1302_strtime [7] = ((ds1302_BCDdata[3]) & 0x0F) + '0';

    ds1302_strtime [8] =' ';

    ds1302_strtime [9] = ((ds1302_BCDdata[2]>>4) & 0x0F) + '0';  //时

    ds1302_strtime [10] = ((ds1302_BCDdata[2]) & 0x0F) + '0';

    ds1302_strtime [11] =':';

    ds1302_strtime [12] = ((ds1302_BCDdata[1]>>4) & 0x0F) + '0';  //分

    ds1302_strtime [13] = ((ds1302_BCDdata[1]) & 0x0F) + '0';

    ds1302_strtime [14] =':';

    ds1302_strtime [15] = ((ds1302_BCDdata[0]>>4) & 0x0F) + '0';  //秒

    ds1302_strtime [16] = ((ds1302_BCDdata[0]) & 0x0F) + '0';

    ds1302_strtime [17] =0;

    }

     

  • 相关阅读:
    UNIapp实现局域网内在线升级
    YJ的2022CSP游记
    面试突击:@Autowired 和 @Resource 有什么区别?你学会了吗?
    langchain主要模块(五):Agent以及Wandb
    【uniapp】JavaScript基础学习-20231027
    vue路由之路由的两种模式
    ImageNet1K的下载与使用
    百度Echarts实现饼图,较官网示例更多项显示
    个人简历内容
    网络安全新资讯-《关键信息基础设施安全保护要求》于明年五月正式实施
  • 原文地址:https://blog.csdn.net/wxlfreewind/article/details/126836793