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