• DIY单片机STC51控制海尔热水器,带电量计量,走时DS1302,温度DS18B20带CRC,程序全公开


    2022.9

    海尔热水器50L的数显热水器。控制温度简直是傻X,温差10度才开始加热,比如设定55度,要降到45度才开始加热,太难用了。

    另外,自己想再加一点功能,设定不同时间可以有不同温度。

    于是进行改造DIY。

    拆机发现有2块电路板,一块是继电器强电板(带5V变压器、NTC热敏电阻感温探头、继电器),另一块是单片机微电脑控制板。

    微电脑控制板如下:

    SENS就是从NTC热敏电阻感温探头上取电压值,单片机程序ADC判断电压值转成温度就可以了。

    RL1和RL2是控制2个加热继电器,二个连在一起接通就是2KW加热功率。

    DIY就单独做个STC51单片机STC15F2K60S2控制板,替代这块微电脑板。

    DIY程序带电量计量功能。发现全家用热水还是挺多的,这台热水器夏天一个月要耗100多度电,那冬天就要200多度以上了。

    另外,加热时检测温度会高些,不加热时温度会低1~2度,比如加热到70度,停止加热后,温度会掉1~2度才平稳。  海尔的程序有做了补偿,自己的程序就不管了。 DIY按加热时的温度电压做的曲线公式,见程序,不同热水器公式不一样,需要自己先用万用表测量摸索。

    另外,提醒一下,一直高温75度保温的话,热水器坏得快会漏水,因为温度高腐蚀快。我上一个海尔热水器改造后,就是这样,用了七八年没问题,改造1年多后,就漏水了,因为长期接近75度在运行。另外温度70多度,对PPR管道的腐蚀也快,管道坏了就非常麻烦了,PPR管道一般60多度是可以长期用的,但70多度就腐蚀快了。所以,海尔的程序温差10度加热也有道理的,但低温下温差10度就没天理了。  所以热水器温度不要长期设那么高,同时要把家里的水管压力降下来会比较保险。

    注意,热水器加热时,会有热胀,家里水管压力有可能会一直升高!热水器自带的安全阀泄压压力是8公斤,太高了,不太好。最保险的是自己再安装一个4BAR的安全泄压阀。 但这个要根据情况,有的地方小区的供水总管有泄压了,就不会有这个问题。

    所以,所有事情都没这么简单,一切都是有技术含量的,不要瞎搞。热水器是有爆炸过的案例的,非常危险。

    电路图:

    程序:

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

    主文件:

    //参考我的走时程序2022.8来修改了

    //STC15F2K60S2,60K ROM,256RAM+1792扩展RAM  11.0592MHZ

    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_STC15_WXL.H"

    //温度  DS18B20

    sbit DQ_DS18_INDOOR=P1^4;

    sbit DQ_DS18_OUTDOOR=P2^4;

    #include "DS18B20_wxl.h"

    int data i_DS18_INDOOR=240;          //温度值(10倍)    //温度按值的10倍,有符号整数

    bit bit_DS18_INDOOR_exist=0;

    unsigned char data  dispbuf_DS18_INDOOR[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

    */

    //按键相关

    sbit P1_2=P1^2;   

    sbit P5_5=P5^5;   

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

    sbit P_LED=P5^4;

    sbit P_OUT1=P1^5;

    //时钟相关

    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  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相关

    sbit P2_0=P2^0;

    sbit P2_1=P2^1;

    sbit P2_2=P2^2;

    sbit P2_3=P2^3;

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

    unsigned char data  dispcode[]={          //共阴

        0xFA,0x22,0xB9,0xAB,  //0 1 2 3

        0x63,0xCB,0xDB,0xA2,

        0xFB,0xEB,

        0xF3,0x5B,0xD8,  //A b C

        0x3B,0xD9,0xD1,  //d E F

        0x0,0x1,0xF2,0x0,       // [16]空 负号 大n 空

        0xFE, 0x26, 0xBD, 0xAF, 0x67, 0xCF, 0xDF, 0xA6, 0xFF, 0xEF, //[20]带点0

        0xF7, 0x5F, 0xDC, 0x3F, 0xDD, 0xD5                          //带点A.

    };

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

    unsigned char data  dispbitcnt=0;   //显示LED的哪一位:0-8位。选择哪个数码管,例如dispbitcnt为0是表示最右那个数码管

    //ADC和温度控制

    unsigned int idata  uiADCresult=0;

    unsigned char idata  ucTEMPnow=80;         

    unsigned char idata  ucTEMPset=70;           //开机时,预设70

    unsigned char idata  uc_temp_day=46;   

    unsigned char idata  uc_temp_night=70;

    volatile bit   bit_displaysettemp_cnt=0;     //设置温度时,显示时间延时

    volatile unsigned int  data  displaysettemp_cnt=0;     //延时计数

    //串口

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

    //DS1302

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

    sbit DS1302_CLK=P2^5; 

    sbit DS1302_IO=P2^6; 

    sbit DS1302_RST=P2^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不充电。

                                     

                                     

    //功率计量    热水器2KW, 一秒是5.5E-4度电

    #define  KWH_PER_SECOND  (2.0/3600.)

    float idata  KWHtotal=0;            //总共用了多少度电

    float idata  KWHtoday=0;            //今天用了多少度电

    float idata  KWHyesterday=0;        //昨天用了多少度电

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

    volatile bit   bit_KWH_cnt=0;      //是否进行用电统计

    volatile unsigned int   data  KWH_cnt=0;           //计数到1秒的次数(用于计量)

    bit  bit_clear_KWH=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  ) return;    //大于159.9度

        if( i<-699 ) return; //小于-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)  

    {

        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;

        S2CON &= ~0x02;       //清除S2的TI中断请求位

        for(i=0;i

        {

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

           while( !(S2CON & 0x02) );     //判断字符是否发完,中断请求位TI=1表示发完

           S2CON &= ~0x02;         //要人工清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小时"位,再运算

    }

    void readDS1302_and_settemp()      //读取和设置温度

    {

        uc_temp_day=DS1302_ReadOne( 0xC2 );

        if(uc_temp_day>73)

        {

           uc_temp_day=46;

           DS1302_WriteOne( 0xC2 , uc_temp_day);

        }

       

        uc_temp_night=DS1302_ReadOne( 0xC4 );

        if(uc_temp_night>73)

        {

           uc_temp_night=70;

           DS1302_WriteOne( 0xC4 , uc_temp_night);

        }

       

    }

    void DS1302_save_float(unsigned char addr, float f)

    {

        unsigned char idata  *p;

        p=(unsigned char *)&f;

       

        DS1302_WriteOne(addr,   *p);

        DS1302_WriteOne(addr+2, *(p+1));

        DS1302_WriteOne(addr+4, *(p+2));

        DS1302_WriteOne(addr+6, *(p+3));

    }

    //保存电量值 昨天电量4字节 今天电量4字节 总电量4字节              ///

    void writeDS1302_KWH()

    {

        if(bitDS1302exist)

        {

           DS1302_save_float(0xD0, KWHyesterday);

           DS1302_save_float(0xE0, KWHtoday);       

           DS1302_save_float(0xF0, KWHtotal);

        }

    }

    float DS1302_read_float(unsigned char addr)

    {

        float idata  f;

        unsigned char idata  *p;

       

        p=(unsigned char *)&f;

       

        *p    =DS1302_ReadOne(addr);

        *(p+1)=DS1302_ReadOne(addr+2 );

        *(p+2)=DS1302_ReadOne(addr+4 );

        *(p+3)=DS1302_ReadOne(addr+6 );

       

        return f;

    }

    void readDS1302_KWH()

    {

        if(bitDS1302exist)

        {

           KWHyesterday=DS1302_read_float(0xD0);

           KWHtoday=DS1302_read_float(0xE0);        

           KWHtotal=DS1302_read_float(0xF0);

        }

    }

    //每个小时的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 InitADC()

    {

        //CLK_DIV是上电默认0,不需要修改。即主时钟不对外输出时钟,主时钟频率不分频(即不除以12),ADRJ=0即ADC_RES[7:0]存放高8位ADC结果,ADC_RESL[1:0]存放低2位ADC结果

        /*CLK_DIV 的 ADRJ:ADC转换结果调整

        0:ADC_RES[7:0]存放高8位ADC结果,ADC_RESL[1:0]存放低2位ADC结果

        1:ADC_RES[1:0]存放高2位ADC结果,ADC_RESL[7:0]存放低8位ADC结果*/

        P1ASF = 8;            //设置P1.3口为ADC口     需作为A/D使用的口需先将P1ASF特殊功能寄存器中的相应位置为‘1’

       

        ADC_RES = 0;          //清除结果寄存器

        ADC_RESL=0;

       

        ADC_CONTR = 0x83;    //1000 0011: 1打开ADC 电源,00设540个时钟周期转换一次(精度最高),0清ADC_FLAG,   0未启动转换,011输入通道选P1.3

                             //当ADC转换完成后,ADC_FLAG = 1,一定要软件清0

        Delay20ms();      //ADC上电时要延时一下,使ADC稳定

    }

    void GetADCResult()

    {

        ADC_CONTR = 0x83 | 0x08;        //启动ADC     BIT3位ADC_START位置1  

        _nop_();                        //等待4个NOP

        _nop_();

        _nop_();

        _nop_();

        while (!(ADC_CONTR & 0x10));    //等待ADC转换完成     bit4“ADC完成标志位”

        ADC_CONTR &= ~0x10;             //bit3 ADC_START位转换结束后会自动清0。   需要人工清零bit4“ADC完成标志位ADC_FLAG”

        //计算温度   

        uiADCresult = ( (unsigned int) ADC_RES ) <<2;      //ADC_RES存放高8位ADC结果,ADC_RESL[1:0]存放低2位ADC结果

        uiADCresult |= (unsigned int)(ADC_RESL & 0x03);

        ucTEMPnow=(unsigned char)( (5.2565 - (float)uiADCresult*0.004882813 )/0.0441+0.5 ); //转换成电压,再用公式算成温度,再四舍五入取整(float有效数字是6~7位) (ADC按加热时的阻值曲线)

    }

    //传感器检测  /

    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指令

       

                  do_what++;

                  break;

       

       

               case 1:     //ADC

                  GetADCResult();

                        

                  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++;

                  do_what=0;     //回零

                  break;

       

               default:

                  do_what=0;

           }     

       

    }

    //刷新显示           ///

    void REFRESH_DISPLAY()

    {         

        char idata  second_level;

        int idata   i;

       

        second_level=second%10;

       

        switch(g_uc_setting_mode)

        {

           case 0:              //不在设置模式

               if(  bit_displaysettemp_cnt==1 ) break;      //如果在设置温度中,则有显示延时

                 

               if( second_level==0  )          

               {

                  dispbuf[0]=minute%10;

                  dispbuf[1]=minute/10;

                  dispbuf[2]=hour%10;

                  dispbuf[3]=hour/10;            

               }

               else if(second_level==3)          //显示热水器温度

               {

                  dispbuf[0]=ucTEMPset%10;

                  dispbuf[1]=ucTEMPset/10;

                  dispbuf[2]=ucTEMPnow%10;

                  dispbuf[3]=ucTEMPnow/10;

               }

               else if(second_level==6)           //显示环境温度

               {

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

                  dispbuf[1]=dispbuf_DS18_INDOOR[0];

                  dispbuf[2]=16;

                  dispbuf[3]=16;   

               }

               else if(second_level==8)         //显示一天用电量

               {

                  INT10_TO_dispbuf_nodot( dispbuf_KWH  ,  (int)(KWHtoday*10) );

                  dispbuf[0]=dispbuf_KWH[2];       //  dispbuf[0]是低位    KWH[0]是十位

                  dispbuf[1]=dispbuf_KWH[1]+20;   //带点,就只显示个位+小数位,因为一天用电量不会超过10度

                 

                  INT10_TO_dispbuf_nodot( dispbuf_KWH  ,  (int)(KWHyesterday*10) );

                  dispbuf[2]=dispbuf_KWH[2];

                  dispbuf[3]=dispbuf_KWH[1]+20;            

               }

               else if(second_level==9)         //显示总用电量

               {

                  i=(int)(KWHtotal*10.);

                  dispbuf[0]=i%10;       //  dispbuf[0]是低位

                  i=i/10;

                  dispbuf[1]=i%10+20;     //带点

                  i=i/10;

                  dispbuf[2]=i%10;

                  i=i/10;

                  dispbuf[3]=i%10;           

               }         

               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]=13  ;  //显示d  调节白天温度

               dispbuf[0]= uc_temp_day%10;        //dispbuf[0]是低位

               dispbuf[1]= uc_temp_day/10;                         

               break;

          

           case 4:

               dispbuf[3]=16;   

               dispbuf[2]=18  ;  //显示大n  调节晚上温度

               dispbuf[0]= uc_temp_night%10;        //dispbuf[0]是低位

               dispbuf[1]= uc_temp_night/10;                          

               break;

              

           case 5:              //电量清零,另外DS1302第一次使用时不知道RAM中存的是什么数,这样读出来就乱了

               dispbuf[3]=16;   

               dispbuf[2]=12;   //显示C 

               dispbuf[0]= bit_clear_KWH;        //dispbuf[0]是低位

               dispbuf[1]=16;                           

               break;    

          

           default:

               dispbuf[0]=minute%10;

               dispbuf[1]=minute/10;

               dispbuf[2]=hour%10;

               dispbuf[3]=hour/10;                          

               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 >= 20492UL )        //晶振偏快,经计算,XXXX秒后要减少一秒

               { ul_time_compensate=0;  }           //不增秒

               else

               {

                  second++;

                 

                  if(second>=60)

                  {

                      second=0;

                      minute++;

                      if(minute>=60)

                      {

                         minute=0;

                         hour++;

                         if(hour>=24)     

                         {

                             hour=0;

                            

                             //跨天了,进行电量记录

                             KWHyesterday=KWHtoday;

                             KWHtoday=0;

                             writeDS1302_KWH();

                         }

                      }

                     

                     

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

                        

                      if(hour==23 && minute==55)            //00:00 后不用热水了,不加热

                      {

                         ucTEMPset=0;

                      }

                      if(hour==5 && minute==0)          //设成白天温度

                      {

                         ucTEMPset=uc_temp_day;

                      }

                      if(hour==17 && minute==30)          //设成最大温度,以方便烧开水冲下水道

                      {

                         ucTEMPset=73;

                      }

                      if(hour==20 && minute==0)         //设成夜间温度

                      {

                         ucTEMPset=uc_temp_night;

                      }

                        

                  }

               }

              

               //每秒 刷新显示

               REFRESH_DISPLAY();

                 

           }  

           ET0=1;

    }

    //热水器加热控制                 /

    void DO_TEMP_CONTROL_HEAT()

    {

        float idata   KWH_tem;

       

        ET0=0;

        if( bit_KWH_cnt  &&  KWH_cnt>=3600)         //正在统计用电,超过1秒

        {

           KWH_cnt-=3600;

           ET0=1;

           //加1秒的电量

           KWHtotal+=KWH_PER_SECOND;

           KWHtoday+=KWH_PER_SECOND;

        }

        ET0=1;

       

       

       

        GetADCResult();

       

        if(ucTEMPnow >= 75 || ucTEMPnow >= ucTEMPset+2 || ucTEMPset<40 ) 

           //加热完后会掉2度,所以加热到高2度(按加热时的ADC曲线)。   温度高,NTC小,NTC上电压下降。如果没电,NTC上电压会是0,得119℃。

        {//关闭加热

           P_OUT1=0;    

           P_LED=1;

          

           ET0=0;

           if(bit_KWH_cnt==1) 

           {

               bit_KWH_cnt=0;   //结束统计用电

               KWH_tem= (KWH_cnt / 3600.) * KWH_PER_SECOND;   

               KWH_cnt=0;

               ET0=1;

              

               KWHtotal+=  KWH_tem;

               KWHtoday+=  KWH_tem;

               if(KWHtotal>990)  KWHtotal=0;    //显示不了太大的数

               if(KWHtoday>15)  KWHtoday=0;     //显示不了太大的数

               writeDS1302_KWH();   //保存电量

           }

           ET0=1;

        }

        if( ucTEMPset>=40  && ucTEMPnow>=10 &&  ucTEMPnow <= ucTEMPset-2 )    //NTC断开,则ADC电压5.0V,得5.8℃。 检测温度小于10度,则说明NTC异常,就不加热。

        {//开始加热

           P_OUT1=1;

           P_LED=0;

          

           if(bit_KWH_cnt==0) 

           {

               ET0=0;

               KWH_cnt=0;

               bit_KWH_cnt=1;   //开始统计用电

               ET0=1;

           }

        }

       

    }

    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();   //处理时间事务,以免按键一直按着就无法处理

           DO_TEMP_CONTROL_HEAT();

        }  

    }

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

    {

        //等待按键松开

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

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

        uc_keycount=0;

        while(uc_keycount<20)

        {

           if(P5_5==0) uc_keycount=0;

           Delay1ms();

           uc_keycount++;

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

           DO_TEMP_CONTROL_HEAT();

        }  

    }

    unsigned char TEMP_INCREASE(unsigned char i)

    {

        if( i==0 ) return 40;

        if( i>=40 && i<=48  ) return i+2;

        if( i>=50 && i<=65  ) return i+5;

        if( i==70 ) return 73;

        if( i==73 ) return 0;

        return 0;

    }

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

    void DO_KEY_PRESS_SETTING()

    {

        unsigned char idata  i;

       

        if( P1_2==1)  return;

       

        //进入设置模式

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

        g_uc_setting_mode=1;

        bit_clear_KWH=0;

        REFRESH_DISPLAY();

        P1_2_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();      //处理时间事务,      每秒刷新显示

               DO_TEMP_CONTROL_HEAT();

          

               if( P5_5==0 )

               {  

                  switch(g_uc_setting_mode)

                  {

                      case 1:       //小时加1

                         hour++;

                         if(hour>=24) hour=0;

                         dispbuf[0]=hour%10;

                         dispbuf[1]=hour/10;

                         break;

                     

                      case 2:      //分钟加1

                         minute++;

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

                        

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

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

                         second=0;

                        

                         dispbuf[0]=minute%10;

                         dispbuf[1]=minute/10;                           

                         break;

                        

                      case 3:      //白天5:00自动设温多少度

                         uc_temp_day=TEMP_INCREASE(uc_temp_day);

                         DS1302_WriteOne( 0xC2 , uc_temp_day);

                         break;

                        

                      case 4:      //晚上20:00自动设温多少度

                         uc_temp_night=TEMP_INCREASE(uc_temp_night);

                         DS1302_WriteOne( 0xC4 , uc_temp_night);

                         break;

                        

                      case 5:      //清空功率计量

                         bit_clear_KWH=1;

                         KWHtotal=0;  

                         writeDS1302_KWH();

                         break;                  

                  }

                  REFRESH_DISPLAY();

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

                 

                  g_uc_settingmode_timeout_second=0;

               }

              

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

               {

                  g_uc_setting_mode++;

                  REFRESH_DISPLAY();

                 

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

                               

                  g_uc_settingmode_timeout_second=0;

                 

                  if(g_uc_setting_mode > 5 ) break;

               }

        }//WHILE

        g_uc_setting_mode=0;

       

    }

    //检测按键进行进行当前温度调整       

    void DO_KEY_PRESS_TEMP_MODIFY()

    {

        ET0=0;

        if(  bit_displaysettemp_cnt==1 && displaysettemp_cnt>18000)   bit_displaysettemp_cnt=0;     //设置温度时,显示时间延时

        ET0=1;

       

        if(P5_5==1)  return;

        //按键有按下了

        ucTEMPset=TEMP_INCREASE(ucTEMPset);

          

        dispbuf[3]=16;

        dispbuf[2]=16;

        dispbuf[0]= ucTEMPset %10;        //dispbuf[0]是低位

        dispbuf[1]= ucTEMPset /10; 

       

        ET0=0; displaysettemp_cnt=0;   ET0=1;

        bit_displaysettemp_cnt=1;       //设置温度时,显示时间延时

       

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

    }  

    //检测串口进行通讯       

    void DO_PROCESS_SERIAL_COM()

    {

          

           if(  (S2CON & 0x01)  )      //中断请求RI位表示串口收到数据,进行处理

           {

               sbuf[sbufnum]=S2BUF;

               S2CON &= ~0x01;     //清除S2的中断请求RI位   //要人工清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_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);

                  }

               }

           }

    }

    //本次:这里只有4个数码管,改成用函数来选择数码管,这样就不会改变别的针脚的值。  

    void SELECT_LED(char led)       

    {

        if(led ==3 )  // 最左 LED共阴   先赋0,再赋1,以免同时亮

        {      P2_1=1;       P2_2=1;       P2_3=1;       P2_0=0;    }

        else if(led ==2 ) 

        {      P2_0=1;       P2_2=1;       P2_3=1;       P2_1=0;    }

        else if(led ==1 ) 

        {      P2_0=1;       P2_1=1;       P2_3=1;       P2_2=0;    }

        else if(led ==0 )  // 最右 LED

        {      P2_0=1;       P2_1=1;       P2_2=1;       P2_3=0;    }

        else

        {      P2_0=1;       P2_1=1;       P2_2=1;       P2_3=1;    }

    }

    ///

    void main(void)

    {

       

       

       

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

    //采用定时器T2将时钟输出到P3.0。  输出时钟频率 = T2溢出率/2

    AUXR |= 0x04; //T2设为1T模式(bit2设1)      // AUXR &= ~0x04; //T2为12T模式(bit2设0)

    AUXR &= ~0x08; //T2的C/T=0, 即用作定时器(bit3设0)

    T2L = (65536-11059200L/2/1105920L);        //预置数

    T2H = (65536-11059200L/2/1105920L) >> 8;

    AUXR |= 0x10;      //T2开始运行(bit4设1)

    INT_CLKO = 0x04;   //使能T2的时钟输出功能。INT_CLKO中的各位是:EX4 EX3 EX2 MCKO_S2 T2CLKO T1CLKO T0CLKO

    while (1);

       

    */ 

       

       

       

       

       

    //上电后为准双向口、弱上拉、高电平

        P_OUT1=0;  //不输出  

        P_LED=1;    //不亮

        P2=0xff;    //不亮

    //P1.5设成推挽,其它仍是弱上拉, ADC也是弱

        P1M1=0;    

        P1M0=0x20;  //P1.5推挽,  0010 0000

        P_OUT1=0;  //不输出

       

    //P2选共阴数码管,可以弱

       

    //P3推挽,笔划 共阳

        P3M1=0;  

        P3M0=0xff;

        InitADC();

        GetADCResult();

       

       

        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();      //读取和设置时间

           readDS1302_and_settemp();       //读取和设置温度

           readDS1302_KWH();

        }

       

        if(  hour <= 4 )  ucTEMPset=0;

        if(hour>=5 && hour <=17 )  ucTEMPset=uc_temp_day;

        if(hour>=18 && hour<=19 )  ucTEMPset=73;

        if(hour>=20 && hour <=23 )  ucTEMPset=uc_temp_night;

       

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

        dispbuf[0]=minute%10;

        dispbuf[1]=minute/10;

        dispbuf[2]=hour%10;

        dispbuf[3]=hour/10; 

          

      

    //复位后定时器是传统8051的速度,即12分频。可设置AUXR设置成1T不分频。

    //设置定时器T0

        TMOD=0x02;    // 0000 0010, 即 T0的GATE选通,C/T选择为C定时方式=0,工作方式=2

        TH0=0;        //预置数=0

        TL0=0;        //从0开始计数 即:(256-0)=256次 ,11.0592/12/256=3600 HZ,3600次就是一秒

        TR0=1;        //开始计数

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

       

        EA=1;         //允许总中断

       

    //采用串口2,串口2固定使用 定时器T2 作波特率发生器!  串行口2波特率=T2溢出率/4 !    ---不使用T0和T1

        S2CON = 0x50; //0101 0000  可读,不可位寻址。

                    //0为8位UART,1保留,0非多机通信,1允许串行口2接收,  

                    //0未用(第9位数据),0未用(第9位数据),0清中断请求TI,0清中断请求RI

    //T2固定为16位自动重装载模式,没有其它模式

        T2L = (65536 - (11059200L/4/4800));    //预置数低8位。  波特率设为4800bps。  11059200L/4/4800=576。  预置数=64960。

        T2H = (65536 - (11059200L/4/4800)) >> 8;        //预置数高8位

        AUXR = 0x14;        // 0001 0100: T2设成1T模式, 并启动运行  (AUXR上电是0x01)

                         //0为T0是12分频,0为T1是12分频12T,0为串口1模式0的速度是12分频,  1为允许T2运行

                         //0为T2用作定时器,1为T2不分频1T,    0为允许使用内部扩展RAM,0为T1作为串口1的波特率发生器

        IE2 = 0;         //不使能串口2中断。      IE可位寻址,IE2不可以位寻址

                       //IE2上电是0,其中的位是:ET4 ET3 ES4 ES3 ET2 ESPI ES2, 这里全置0

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

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

        S2CON &= ~0x01;     //清除S2的中断请求RI位    //清理一下串口

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

        while(1)  

        {

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

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

       

           DO_KEY_PRESS_TEMP_MODIFY();   //检测按键进行当前温度调整

          

           DO_TEMP_CONTROL_HEAT();   //热水器加热控制

           TIME_PROCESS();         //走时

          

           DO_WHAT_FUNC();      //传感器检测

        } //end while

    } //end MAIN

    /*

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

    */

    ///

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

    void t0(void) interrupt 1

    {  

        tcnt++;            //时钟计数

       

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

       

        mstcnt++;

        //用电统计

        if(bit_KWH_cnt)    KWH_cnt++;

       

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

          

        if(mstcnt>=9)      //每计数9次(定时器是3600HZ,9次是400HZ 2.5ms,数码管闪烁频率=3600/9次/4位=100HZ。以免与计时的一起执行,占用执行时间),改变一次显示位

        {

           mstcnt=0;

           P3=dispcode[ dispbuf[dispbitcnt] ];   

                       //  输出笔划。dispbuf中保存数据,dispcode中保存笔划码,本电路是共阴极

                                 //  dispbuf[0]为最右边的数码管

       

           SELECT_LED(dispbitcnt);  //选择数码管, 3是选中最左LED  0是选中最右LED          //P2=dispbitcode[dispbitcnt];         //选择哪个数码管,例如dispbitcnt为0是最右那个数码管选中

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

           if(dispbitcnt>=4)  dispbitcnt=0; 

        }

       

        //设置温度时,显示时间延时

        if(bit_displaysettemp_cnt)      displaysettemp_cnt++;        

       

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

        if(g_uc_setting_mode)    g_ui_settingmode_timeout_cnt++;

          

    }

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

    DELAY_STC15_WXL.H文件:

    //  STC15F 单片机  1T  11.0592MHZ

    //  STC15F2K60S2 单片机 1T  11.0592MHZ

    void Delay2us()      //@11.0592MHz

    {

        unsigned char i;

        i = 3;

        while (--i);

    }

    //

    void Delay8us()      //@11.0592MHz

    {

        unsigned char i;

        _nop_();

        _nop_();

        i = 19;

        while (--i);

    }

    //

    void Delay10us()     //@11.0592MHz

    {

        unsigned char i;

        _nop_();

        i = 25;

        while (--i);

    }

    //

    void Delay80us()     //@11.0592MHz

    {

        unsigned char i;

        _nop_();

        _nop_();

        _nop_();

        i = 218;

        while (--i);

    }

    //

    void Delay600us()    //@11.0592MHz

    {

        unsigned char i, j;

        i = 7;

        j = 113;

        do

        {

           while (--j);

        } while (--i);

    }

    //

    void Delay1ms()      //@11.0592MHz

    {

        unsigned char i, j;

        _nop_();

        _nop_();

        _nop_();

        i = 11;

        j = 190;

        do

        {

           while (--j);

        } while (--i);

    }

    ///

    void Delay20ms()     //@11.0592MHz

    {

        unsigned char i, j;

        i = 216;

        j = 37;

        do

        {

           while (--j);

        } while (--i);

    }

    void Delay50ms()     //@11.0592MHz

    {

        unsigned char i, j, k;

        _nop_();

        _nop_();

        i = 3;

        j = 26;

        k = 223;

        do

        {

           do

           {

               while (--k);

           } while (--j);

        } while (--i);

    }

    void Delay500ms()    //@11.0592MHz

    {

        unsigned char i, j, k;

        _nop_();

        _nop_();

        i = 22;

        j = 3;

        k = 227;

        do

        {

           do

           {

               while (--k);

           } 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为高电平

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

    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;

    }

  • 相关阅读:
    神经网络控制基本原理,神经网络理论及其应用
    solidity使用create2预测合约地址|create2用法|智能合约create2
    Hive概念及Hive,MySQL安装、配置
    最新堆叠查询注入攻击和注入代码分析技术
    网上电子元器件采购商城:打破采购环节信息不对称难题,赋能企业高效协同管理
    java毕业设计电商项目mybatis+源码+调试部署+系统+数据库+lw
    用yum命令安装kvm的主要组件及工具出现问题(操作系统linux)
    【无标题】
    【华为OD机试真题 python】 靠谱的车【2022 Q4 | 100分】
    Java编程题(完数)
  • 原文地址:https://blog.csdn.net/wxlfreewind/article/details/126846312