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 <STC\STC12C5A60S2.H>
#include <INTRINS.h>
#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<num;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;
}