//*具有温度日历 闹钟功能的项目实例源程序
/* 程序用MCC18编译通过,晶振为4M,看门狗定时器关,上电复位和低电压编程关 */
//此程序一在仿真软件PROTEUS中通过
#include
//头文件引用
//大LED数码管显示代码表
const unsigned char LEDCODE[]=
{
//0,1,2,3,4,5,6,7,8,9定义
0xc0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0xf8,0x80,0x98 //共阳极接法
//0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F //共阴极接法
};
//宏定义
#define TRUE 1
#define FALsE 0
//*************************************************************
//初始化端口函数
void ChushiIO()
{
TRISA = 0x01; //RA0定义为输入端
TRISC = 0Xe0; //定义C口RC5,RC6,RC7为输入端,RC0-RC4为输出端
TRISB = 0x1f; //B端口用于按键输入RB0-RB4
PORTAbits.RA0 = 0; //A端口初始数据为低电平
PORTB = 0x00; //键未按下状态
PORTC = 0x00; //c口初始化输出为低电平
INTCON2bits.RBPU = 0; //使能B口的内部上拉
ADCON1 = 0x07; //RA为模拟输入口
}
//***********************************************************
//***********************************************************
//---定义软件定时器定时1s和0.5秒结构体变量
struct TIMER_sTRUCT
{
unsigned int Interval; //时间计数器
unsigned char Enable; //时间计数器的标志
};
struct TIMER_sTRUCT second_1s,second_Halfs;
//-------------------------------------------------------------
//-------------------------------------------------------------
//---定义实时带温度与闹铃功能的日历/时钟结构体变量
struct REALCLOCK_sTRUCT
{
unsigned char Year; //年份
unsigned char Month; //月份
unsigned char Day; //日
unsigned char Week; //星期
unsigned char Hour; //时
unsigned char Minute; //分
unsigned char second; //秒
unsigned char Temp; //温度
};
struct REALCLOCK_sTRUCT RealClock;
//-------------------------------------------------------------
//-------------------------------------------------------------
//---定义定时闹钟结构体变量
struct ALARMCLOCK_sTRUCT
{
unsigned char Day; //日
unsigned char Week; //星期
unsigned char Hour; //时
unsigned char Minute; //分
unsigned char second; //秒
unsigned char status; //状态
};
struct ALARMCLOCK_sTRUCT AlarmClock;
//-------------------------------------------------------------
/*74HC595芯片 QA--QH: 八位并行输出端,可以直接控制数码管的8个段。
QH': 级联输出端。我将它接下一个595的SI端。SI: 串行数据输入端
引脚介绍:
SCLR(10脚): 低点平时将移位寄存器的数据清零,这里接RC3
SCK(11脚):上升沿时数据寄存器的数据移位,下降沿移位寄存器数据不变 这里接RC1(
RCK(12脚):上升沿时移位寄存器的数据进入数据存储寄存器,下降沿时存储寄存器数据不变 这里接RC2
G(13脚): 高电平时禁止输出(高阻态),低电平有效 这里接RC4
SI(14脚): 串行数据输入端 ,这里接RC0
*/
//-------------------------------------------------------------
//---定义PIC单片机与74HC595芯片接口连接关系
#define HC595_SI LATCbits.LATC0 //串行数据输入端
#define HC595_SCK LATCbits.LATC1 //上升沿时数据寄存器的数据移位,下降沿移位寄存器数据不变
#define HC595_RCK LATCbits.LATC2 //上升沿时移位寄存器的数据进入数据存储寄存器,下降沿时存储寄存器数据不变
#define HC595_SCLR LATCbits.LATC3 //低点平时将移位寄存器的数据清零
#define HC595_G LATCbits.LATC4 //高电平时禁止输出(高阻态),低电平有效
//---定义LED数码管显示缓冲区
unsigned char LEDBuffer[16]; //保存将要送入LED数码管显示的数据
//---延时函数的定义
void Delay(unsigned int t)
{
while(t--);
}
//---定义大LED数码管初始化函数
void InitizeLED(void)
{
HC595_G=1; //并行输出为高阻状态,74HC595不能输出 接RC4
HC595_SCLR=0; //清空移位寄存器,接RC3
HC595_SI=0; //串行数据输入端清零 接RC0
HC595_SCK=0; //移位时钟初始为低 接RC1
HC595_RCK=0; //存储器时钟初始为低 接RC2
HC595_G=0; //允许并行输出,可输出状态
HC595_SCLR=1; //结束复位状态
}
//************74HC595不用修改,测试已通过****************************************
//---定义大LED显示驱动函数送数据给数码管显示函数
/*原理过程:
// 当串行数据输入端SI(14脚)引脚上的数据在SCK(11脚(上升沿时数据寄存器的数据移位,
//下降沿移位寄存器数据不变)为上升1时,SI数据输入移位寄存器,当RCK(12脚上升沿时移位寄存
//器的数据进入数据存储寄存器,下降沿时存储寄存器数据不变 这里接RC2,当使能 G(13脚)时,数据从*/
/* 数据寄存器送入到QA-AH上,然后显示 流程如下:*/
//*LedData指针,DataLength数据长度
void sendDataToLED(unsigned char *LedData,unsigned char DataLength)
{
unsigned char i,j,temp;
TRISC &= 0xe0;
HC595_SCLR=0; //清空移位寄存器
_asm//简短延时
NOP
NOP
_endasm
HC595_SCLR=1; //结束复位状态
HC595_G=1; //禁止允许并行输出,
for(j=0;j {
temp=LEDCODE[*LedData];//取出LED显示的代码表按位(8位)送入变量中
for(i=0;i<8;i++)
{
HC595_SCK=0;
_asm//简短延时
NOP
NOP
_endasm
if(0x80==(temp & 0x80))
{
HC595_SI=1; //将字节按位移出
} else
{
HC595_SI=0;
}
HC595_SCK=1; //SI的数据送入移位寄存器
_asm//简短延时
NOP
NOP
_endasm
HC595_SCK=0;
_asm//简短延时
NOP
NOP
_endasm
temp<<=1; //当前指针的内容左移一位
_asm//简短延时
NOP
NOP
_endasm
}
LedData++;//指针指向下一个数据
}
HC595_RCK = 0;
_asm//简短延时
NOP
NOP
_endasm
HC595_RCK = 1; //移位寄存器的内容送到进入锁存器
_asm//简短延时
NOP
NOP
_endasm
HC595_G=0; //允许并行输出,数据寄存器数据送数码管显示 16个数一起送出
_asm//简短延时
NOP
NOP
_endasm
}
//-------------------------------------------------------------
/*DS1302读写原理:
// DS1302工作时为了对任何数据传送进行初始化,需要将复位脚(RST)
// 置为高电平且将8位地址和命令信息装入移位寄存器。数据在时钟(SCLK)
// 的上升沿串行输入,前8位指定访问地址,命令字装入移位寄存器后,
// 在之后的时钟周期,读操作时输出数据,写操作时输出数据 */
//-------------------------------------------------------------
//---定义PIC单片机与Ds1302引脚之间的连接关系
#define Ds1302CE_1 LATCbits.LATC5=1 //RST(引脚5)使能脚电路初始时为高电平
#define Ds1302CE_0 LATCbits.LATC5=0
#define Ds1302CE_o TRISC &=0xDF //c5为输出
#define Ds1302IO_o TRISC &=0xBF //c6为输出(引脚6数据输出输入)
#define Ds1302IO_I TRISC |=0x40 //c6为输入
#define Ds1302IO_1 LATCbits.LATC6=1
#define Ds1302IO_0 LATCbits.LATC6=0
#define Ds1302IO_D PORTC & 0x40 //RC6数据取值
#define Ds1302CK_1 LATCbits.LATC7=1 //串行时钟引脚7
#define Ds1302CK_0 LATCbits.LATC7=0
#define Ds1302CK_o TRISC &=0x7F //定义输出脚
//---Ds1302初始化函数
void Init_Ds1302_RTC(void)
{
Ds1302CK_o; //置CK为输出
Ds1302CE_o; //置CE为输出
Ds1302CE_0; //输出为0
Ds1302CK_0; //输出为0
Ds1302IO_1;
Ds1302IO_I; //置IO为输入
}
//********************************************************************************************
//单字节写:在进行操作之前先得将CE(也可说是RST)置高电平,然后单片机将控制字的位0放到I/O上
//,当I/O的数据稳定后,将SCLK置高电平,DS1302检测到SCLK的上升沿后就将I/O上的数据读取,
//然后单片机将SCLK置为低电平,再将控制字的位1放到I/O上,如此反复,将一个字节控制字的8个位
//传给DS1302。接下来就是传一个字节的数据给DS1302,当传完数据后,单片机将CE置为低电平,操作结束。
//********************************************************************************************
// DS1302写入函数
void Write_Ds1302_RTC(unsigned char RTC_Address,unsigned char RTC_Data)
{
unsigned char i;
Ds1302CE_o;//CE为输出
Ds1302CK_o;//CK为输出
Ds1302IO_o;//IO为输出*/
_asm//简短延时
NOP
NOP
_endasm
Ds1302CE_0;
Ds1302CK_0;
Ds1302CE_1; //CE=1 DS1302开始启动操作
_asm //简短延时 保证I/O稳定
NOP
NOP
_endasm
for(i=8;i>0;i--)
{
//送地址给Ds1302
if(RTC_Address & 0x01)
{
Ds1302IO_1; //取数据值1
} else
{
Ds1302IO_0;
}
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_1; //sclk为高电平,为DS1302读取I/O闪的控制命令
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_0; //CK=0 控制位送出
_asm //简短延时
NOP
NOP
_endasm
RTC_Address>>=1; //右移一位
Delay(20);
}
//送数据 上升源有效
for(i=8;i>0;i--)
{
Ds1302CK_0;
_asm //简短延时
NOP
NOP
_endasm
if(RTC_Data & 0x01)
{
Ds1302IO_1;
}
else
{
Ds1302IO_0;
}
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_1; //CK=1
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_0; //CK=0
_asm //简短延时
NOP
NOP
_endasm
RTC_Data>>=1; //右移一位
Delay(20);
}
Ds1302CK_0; //CK=0
Ds1302CE_0; //CE=0
Delay(10);
}
//*******************************************************
/*单字节读操作的一开始写控制字的过程和上面的单字节写操作是一样,
但是单字节读操作在写控制字的最后一个位,SCLK还在高电平时,DS1302就
将数据放到I/O上,单片机将SCLK置为低电平后数据锁存,单机机就可以
读取I/O上的数据。如此反复,将一个字节的数据读入单片机。*/
/*读与写操作的不同就在于,写操作是在SCLK低电平时单片机将数据放到IO上
,当SCLK上升沿时,DS1302读取。而读操作是在SCLK高电平时DS1302放
数据到IO上,将SCLK置为低电平后,单片机就可从IO上读取数据。*/
//*******************************************************
//---读取Ds1302日历函数
unsigned char Read_Ds1302_RTC(unsigned char RTC_Address)
{
unsigned char i;
unsigned int temp;
TRISC &=0x1f; //要这一条 ,RC5-RC7输出
Ds1302CE_o; //CE为输出
Ds1302CK_o; //CK为输出
Ds1302IO_o; //IO为输出
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_0; //CK=0
Ds1302CE_0; //CE=0
Ds1302CE_1; //CE=1
_asm //简短延时
NOP
NOP
_endasm
for(i=8;i>0;i--) //CPU送出访问Ds1302的地址
{
if(RTC_Address & 0x01)
{
Ds1302IO_1; //I/O取值为1
} else
{
Ds1302IO_0;
}
_asm//简短延时
NOP
NOP
_endasm
Ds1302CK_1; //CK=1
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_0; //CK=0
_asm //简短延时
NOP
NOP
_endasm
RTC_Address>>=1;//右移一位
Delay(10);
}
//读DS1302数据时要产生下降沿
TRISC = 0x40;
Ds1302IO_I; //IO为输入
for(i=8;i>0;i--)
{
temp=(temp>>1); //右移一位
if(Ds1302IO_D) //数据为1
{
temp|=0x80; //取值1
}
else
{
temp&=0x7f; //取值0
}
Ds1302CK_1; //CK=1
_asm //简短延时
NOP
NOP
_endasm
Ds1302CK_0; //CK=0 单片机读取数据
_asm //简短延时
NOP
NOP
_endasm
}
Ds1302CK_0;
Ds1302CE_0; //CE=0 读操作结束
_asm //简短延时
NOP
NOP
_endasm
return(temp); //返回读取到的内容
}
//-------------------------------------------------------------
//---定义ADC缓冲区,和指针变量
unsigned char ADCBuffer[5]; //ADC采集缓冲区
unsigned char ADCBufferPointer; //ADC采集缓冲区指针
unsigned char TemperatureDisplayFlag; //当缓冲区满时,处理完后标志
//---ADC初始化函数定义
void ADC_Initize(void)
{
unsigned char i;
ADCON1=0x07; //初始化ADC的参考电压取VCC,GND
ADCON2=0x00; //左对齐,0TAD,FOsC/2
ADCON0=0x03; //选择通道RA0,启动A/D转换开始
//初始化ADCBuffer变量指针和缓冲区
ADCBufferPointer=0;
for(i=0;i {
ADCBuffer[i]=0;
}
}
//-------------------------------------------------------------
//-------------------------------------------------------------
//---初始化PIC18F单片机工作于内部时钟4MHz状态下
void INTOsC_Initize(void)
{
OSCCON=0x60;//选择内部4MHz的主振荡器
}
//-------------------------------------------------------------
//---TMR0定时器初始化函数 1s=1000ms 1ms=1000μs
// 1μs=1000ns
void TMR0_Initize(void)
{
//不带分频器,4MHz的芯片,时钟周期为1/4= 0.25,指令周期为0.25*4 =1US(微妙),其最大延时为1*256 us(8位机)
//带1:256分频器,则最大延时256*256 =65535us(微妙)
INTCONbits.GIE = 0; //全局中断关闭
RCONbits.IPEN = 1; // 使能中断优先级 RCON
T0CONbits.TMR0ON=FALsE; //TMR0停止运行
INTCON=0Xc0; //允许所有高优先级中断和所有外设中断
T0CON= 0x47; //0100 0111(0x12TMR0初始化为8位定时,预分频比为1:256)
TMR0L= 256-196; //定时50ms= 50000us初值
//当延时到时就产生中断
INTCONbits.TMR0IF = 0; //清中断标志位,未发声中断
INTCON2bits.TMR0IP = 0; //低优先级使能 INTCON2 高等于1
INTCONbits.TMR0IE = 1; //允许timer0中断溢出
second_Halfs.Interval=0; //计算T0延时0.5s的次数初始化
second_1s.Interval=0; //计算T0延时01s的次数初始化
INTCONbits.GIE = 1; //开全局中断
}
//-------------------------------------------------------------
//-------------------------------------------------------------
//定时器低优先级中断入口函数声明
void TMR0_TMR1_IsR(void); //中断服务程序函数声明
#pragma code low_vector=0x18 //低级中断服务程序的地址
void low_interrupt(void)
{
_asm goto TMR0_TMR1_IsR _endasm //跳到中断服务程序
}
#pragma code //返回当前地址,而不是低级中断地址
//-------------------------------------------------------------
//-------------------------------------------------------------
//---低优先中断服务程序
#pragma interruptlow TMR0_TMR1_IsR
void TMR0_TMR1_IsR(void)
{
unsigned char i,j,temp;
if(TRUE==INTCONbits.TMR0IF) //定时100ms溢出
{
TMR0L= 256-196; //装入初值
second_Halfs.Interval++; //计算T0延时100ms的次数
if(5==second_Halfs.Interval) //每0.5秒读Ds1302和闪烁,事实此处为250ms
{
second_Halfs.Interval=0; //计算T0延时0.5s的次数初始化
second_Halfs.Enable=TRUE;
}
second_1s.Interval++;
if(10==second_1s.Interval) //事实此处为500ms
{
second_1s.Interval=0;
second_1s.Enable=TRUE;
}
if(0==ADCON0bits.DONE) //判断A/D转换是否完毕 AD转换完毕,此时处于空闲状态
{
ADCBuffer[ADCBufferPointer]=ADRESH; //装入当前缓冲区
ADCON0bits.GO=1; //重新启动A/D开始转换
ADCBufferPointer++;//指针加1
if(ADCBufferPointer==sizeof(ADCBuffer)) //缓冲区装满否 此为装满
{
ADCBufferPointer=0; //指针初始化
//将缓冲区的内容按从大到小排序
for(i=0;i for(j=i;j {
if(ADCBuffer[i] {
temp=ADCBuffer[i];
ADCBuffer[i]=ADCBuffer[j];
ADCBuffer[j]=temp;
}
}
//取缓冲区中间的数值作为当前测量到的温度值数据
RealClock.Temp=ADCBuffer[sizeof(ADCBuffer)/2];
TemperatureDisplayFlag=TRUE;
}
}
INTCONbits.TMR0IF=FALsE; //清TMR0溢出中断标志
}
}
//-------------------------------------------------------------
//---按键引脚的定义
#define DATEKEY PORTBbits.RB0 //日历日期功能键
#define TIMEKEY PORTBbits.RB1 //日历时间功能键代表
#define ALARMKEY PORTBbits.RB2 // 定时闹铃功能键
#define NUMBERADDKEY PORTBbits.RB3 //加调功能键
#define NUMBERsUBKEY PORTBbits.RB4 //减调功能键
//---功能按键状态变量定义
unsigned char DateKeyID;
unsigned char TimeKeyID;
unsigned char AlarmKeyID;
//-------------------------------------------------------------
void LED_Show() //DS1302显示初始化及温度初始化函数
{
RealClock.second =15; RealClock. Day=16;
RealClock.Minute=11; RealClock.Month=11;
RealClock.Hour = 12; RealClock.Year=99;
RealClock.Week=3; RealClock.Temp=25;
LEDBuffer[0] = RealClock.second%10;
LEDBuffer[1] = RealClock.second/10;
LEDBuffer[2] = RealClock.Minute%10;
LEDBuffer[3] = RealClock.Minute/10;
LEDBuffer[4] = RealClock.Hour%10;
LEDBuffer[5] = RealClock.Hour/10;
LEDBuffer[6] = RealClock.Week;
LEDBuffer[7] = RealClock. Day%10;
LEDBuffer[8] = RealClock. Day/10;
LEDBuffer[9] = RealClock.Month%10;
LEDBuffer[10] = RealClock.Month/10;
LEDBuffer[11] = RealClock.Year%10;
LEDBuffer[12] = RealClock.Year/10;
LEDBuffer[13] = RealClock.Temp%10;
LEDBuffer[14] = RealClock.Temp/10;
LEDBuffer[15] = 0; //显示0
}
//-------------------------------------------------------------
//主函数
//************************************************************
//---主程序 main函数
void main(void)
{
unsigned char i,j;
unsigned long temp;
//键值功能初始化
DateKeyID = 0; // 日历日期功能键代表
TimeKeyID = 0; // 日历时间功能键代表
AlarmKeyID = 0; // 定时闹铃功能键代表
ChushiIO(); //初始化端口函数
INTOsC_Initize(); //初始化时钟源
TMR0_Initize(); //初始化TMR0定时器
ADC_Initize(); //初始化A/D模块
InitizeLED(); //初始化LED数码管显示
LED_Show(); //数码管显示初始化函数
sendDataToLED(LEDBuffer,16); //送值到数码管显示
T0CONbits.TMR0ON = 1; //启动t0定时器
//*********************按键模块开始*******************
while(1)
{
//-------------------------------------------------------
//---定时半秒时间到,读取Ds1302日历和闪烁现象产生
if(TRUE==second_Halfs.Enable)
{
T0CONbits.TMR0ON = 0;
second_Halfs.Enable=FALsE;
if((0==DateKeyID)&&(0==TimeKeyID)&&(0==AlarmKeyID)) //显示处于正常状态
{
i=Read_Ds1302_RTC(0x81); //读取Ds1302的秒
RealClock.second=(i/16)*10+(i%16);
i=Read_Ds1302_RTC(0x83); //读取Ds1302的分
RealClock.Minute=(i/16)*10+(i%16);
i=Read_Ds1302_RTC(0x85); //读取Ds1302的时
RealClock.Hour=(i/16)*10+(i%16);
i=Read_Ds1302_RTC(0x87); //读Ds1302的日
RealClock.Day=(i/16)*10+(i%16);
i=Read_Ds1302_RTC(0x89); //读Ds1302的月
RealClock.Month=(i/16)*10+(i%16);
i=Read_Ds1302_RTC(0x8b); //读Ds1302的星期
RealClock.Week=i%16;
i=Read_Ds1302_RTC(0x8d); //读Ds1302的年
RealClock.Year=(i/16)*10+(i%16);
LEDBuffer[12]=RealClock.Year/10;//装入年份内容
LEDBuffer[11]=RealClock.Year%10;
LEDBuffer[10]=RealClock.Month/10;//装入月份内容
LEDBuffer[9]=RealClock.Month%10;
LEDBuffer[8]=RealClock.Day/10;//装入年份内容
LEDBuffer[7]=RealClock.Day%10;
LEDBuffer[6]=RealClock.Week;
LEDBuffer[4]=RealClock.Hour%10;//装入时钟内容
LEDBuffer[5]=RealClock.Hour/10;
LEDBuffer[2]=RealClock.Minute%10;//装入分钟内容
LEDBuffer[3]=RealClock.Minute/10;
LEDBuffer[0]=RealClock.second%10;//装入秒钟内容
LEDBuffer[1]=RealClock.second/10;
}
//---------------------如果闹铃处于设置状态------------------------------
if(AlarmKeyID>0)
{
switch(AlarmKeyID)
{
case 1:
if(0==AlarmClock.status) //闹铃处于关闭状态 显示关闭
{
LEDBuffer[12]=0xc0; // 年的十位显示"O"
LEDBuffer[11]=0x8e; // 年的个位显示"F"
LEDBuffer[10]=0x8e; //月的十位显示"F"
LEDBuffer[9] =0xc0; //月的个位显示"O"
LEDBuffer[8] =0x8e; //日的十位显示"F"
LEDBuffer[7] =0x8e; //日的个位显示"F"
}
else
{
LEDBuffer[12]=0xc0; //"O"
LEDBuffer[11]=0x8c; //"P"
LEDBuffer[10]=0x86; //"E"
LEDBuffer[9] =0xc8; //"n "
LEDBuffer[8] =0xc0; //"O"
LEDBuffer[7] =0xc8; //"n"
}
break;
case 2:
if((0xff==LEDBuffer[4])&&(0xff==LEDBuffer[5])) //灭状态
{
LEDBuffer[4]=AlarmClock.Hour%10; //装入闹铃时钟内容
LEDBuffer[5]=AlarmClock.Hour/10;
}
else
{
LEDBuffer[4]=0xff; //装入LED灭的内容
LEDBuffer[5]=0xff;
}
break;
case 3:
if((0xff==LEDBuffer[2])&&(0xff==LEDBuffer[3])) //灭状态
{
LEDBuffer[2]=AlarmClock.Minute%10; //装入闹铃分钟内容
LEDBuffer[3]=AlarmClock.Minute/10;
}
else
{
LEDBuffer[2]=0xff; //装入LED灭的内容
LEDBuffer[3]=0xff;
}
break;
case 4:
if((0xff==LEDBuffer[0])&&(0xff==LEDBuffer[1])) //灭状态
{
LEDBuffer[0]=AlarmClock.Day%10; //装入闹铃秒钟内容
LEDBuffer[1]=AlarmClock.Day/10;
}
else
{
LEDBuffer[0]=0xff; //装入LED灭的内容
LEDBuffer[1]=0xff;
}
break;
}
}
//---------------------------------------------------
//---------------------------------------------------
//---校准日期和星期时,以闪烁现象提示
if(DateKeyID>0)
{
switch(DateKeyID)
{
case 1: //年设置
if((0xff==LEDBuffer[11])&&(0xff==LEDBuffer[12])) //灭状态
{
LEDBuffer[11]=RealClock.Year%10; //装入年份内容
LEDBuffer[12]=RealClock.Year/10;
}
else
{
LEDBuffer[11]=0xff; //装入LED灭的内容
LEDBuffer[12]=0xff;
}
break;
case 2: //月设置
if((0xff==LEDBuffer[9])&&(0xff==LEDBuffer[10])) //灭状态
{
LEDBuffer[9]=RealClock.Month%10; //装入月份内容
LEDBuffer[10]=RealClock.Month/10;
}
else
{
LEDBuffer[9]=0xff; //装入LED灭的内容
LEDBuffer[10]=0xff;
}
break;
case 3: //日设置
if((0xff==LEDBuffer[7])&&(0xff==LEDBuffer[8])) //灭状态
{
LEDBuffer[7]=RealClock.Day%10; //装入年份内容
LEDBuffer[8]=RealClock.Day/10;
}
else
{
LEDBuffer[7]=0xff; //装入LED灭的内容
LEDBuffer[8]=0xff;
}
break;
case 4: //星期设置
if(0xff==LEDBuffer[6]) //灭状态
{
LEDBuffer[6]=RealClock.Week; //装入星期内容
}
else
{
LEDBuffer[6]=0xff;
}
break;
}
}
//---------------------------------------------------
//---------------------------------------------------
//---校准时间时,以闪烁现象提示
if(TimeKeyID>0)
{
switch(TimeKeyID)
{
case 1: //时设置
if((0xff==LEDBuffer[4])&&(0xff==LEDBuffer[5])) //灭状态
{
LEDBuffer[4]=RealClock.Hour%10; //装入时钟内容
LEDBuffer[5]=RealClock.Hour/10;
}
else
{
LEDBuffer[4]=0xff; //装入LED灭的内容
LEDBuffer[5]=0xff;
}
break;
case 2: //分设置
if((16==LEDBuffer[2])&&(16==LEDBuffer[3]))//灭状态
{
LEDBuffer[2]=RealClock.Minute%10;//装入分钟内容
LEDBuffer[3]=RealClock.Minute/10;
}
else
{
LEDBuffer[2]=0xff;//装入LED灭的内容
LEDBuffer[3]=0xff;
}
break;
case 3: //秒设置
if((0xff==LEDBuffer[0])&&(0xff==LEDBuffer[1])) //灭状态
{
LEDBuffer[0]=RealClock.Day%10; //装入秒钟内容
LEDBuffer[1]=RealClock.Day/10;
}
else
{
LEDBuffer[0]=0xff; //装入LED灭的内容
LEDBuffer[1]=0xff;
}
break;
}
}
//---------------------------------------------------
//---------------------------------------------------
//---将闪烁现象送到LED数码管显示
if((DateKeyID>0)||(TimeKeyID>0)||(AlarmKeyID>0)) //如闹铃,日期 时间都处于设置状态
{
sendDataToLED(LEDBuffer,sizeof(LEDBuffer));
}
//---------------------------------------------------
T0CONbits.TMR0ON=TRUE;
}
//-------------------------------------------------------
//---定时1s时间到,送出显示
if(TRUE==second_1s.Enable)
{
T0CONbits.TMR0ON = 0;
second_1s.Enable=FALsE;
if((0==DateKeyID)&&(0==TimeKeyID)&&(0==AlarmKeyID)) //如闹铃,日期 时间都处于正常设置状态
{
sendDataToLED(LEDBuffer,sizeof(LEDBuffer));
}
T0CONbits.TMR0ON=TRUE;
}
//-------------------------------------------------------
//-------------------------------------------------------
//---功能键及数字调节键识别与处理过程
//---
//-------------------------------------------------------
//---闹铃设置功能键
if(0==ALARMKEY) //闹铃设置键是否按下
{
Delay(1000); //去抖动
if(0==ALARMKEY) //是真得按下了吗
{
DateKeyID=0;
TimeKeyID = 0; //其它校准或设置变量状态为正常状态
AlarmKeyID++; //闹铃设置状态变量加1
if(1==AlarmKeyID) //在闹铃设置状态为1的情况下,复制当前日历的内容到闹铃变量中
{
AlarmClock.second=RealClock.second; //复制秒
AlarmClock.Minute=RealClock.Minute; //复制分
AlarmClock.Hour=RealClock.Hour; //复制时
AlarmClock.Week=RealClock.Week; //复制日
AlarmClock.Day=RealClock.Day; //复制星期
}
if(0==AlarmClock.status)//如果闹铃是关闭的,则
{
if(2==AlarmKeyID)
{
AlarmKeyID=0;//直接回到正常状态
}
}
else//如果是闹铃是打开的,则
{
if(5==AlarmKeyID)
{
AlarmKeyID=0;//可以设置时分秒闹铃时间
}
}
}
while(0==ALARMKEY); //等待按键释放
}
//-------------------------------------------------------
//---校准日期的功能键
if(0==DATEKEY)//日期校准按键是否按下
{
Delay(1000);//去按键抖动
if(0==DATEKEY)//再判断是否真得按下
{
TimeKeyID=0;//其它校准或设置变量状态为正常状态
AlarmKeyID=0;
DateKeyID++;//校准日期状态变量加1
if(5==DateKeyID)
{
DateKeyID=0;//所有校准完毕,回到正常状态
}
}
while(0==DATEKEY);//等待按键释放
}
//-------------------------------------------------------
//-------------------------------------------------------
//---校准时间的功能键
if(0==TIMEKEY)
{
Delay(1000);
if(0==TIMEKEY)
{
DateKeyID=0;
AlarmKeyID=0;//其它校准或设置变量状态为正常状态
TimeKeyID++;
if(4==TimeKeyID)
{
TimeKeyID=0;
}
}
while(0==TIMEKEY);
}
//-------------------------------------------------------
//-------------------------------------------------------
//---数字加调节键
if(0==NUMBERADDKEY) //数字增调节键按下了
{
Delay(1000); //去抖动
if(0==NUMBERADDKEY) //再判断是否真得按下
{
if(AlarmKeyID>0) //如果闹铃设置状态是处于设置条件下,则
{
switch(AlarmKeyID) //根据闹铃状态变量来分析调节哪个功能
{
case 1: //闹铃打开/关闭设置状态
//如果闹铃关闭则打开,否则关闭
if(0==AlarmClock.status) //关闭
{
AlarmClock.status=1; //打开
} else
{
AlarmClock.status=0;
}
break;
case 2: //设置闹铃的时状态
if(0!=AlarmClock.status) //如果闹铃是打开的,则
{
AlarmClock.Hour++; //闹铃的时变量加1
if(24==AlarmClock.Hour)
{
AlarmClock.Hour=0; //到24归0
}
}
break;
case 3: //设置闹铃的分状态
if(0!=AlarmClock.status) //如果闹铃是打开的,则
{
AlarmClock.Minute++; //闹铃的分变量加1
if(60==AlarmClock.Minute)
{
AlarmClock.Minute=0; //到60归0
}
}
break;
case 4: //设置闹铃的秒状态
if(0!=AlarmClock.status) //如果闹铃是打开的,则
{
AlarmClock.second++; //闹铃的秒变量加1
if(60==AlarmClock.second)
{
AlarmClock.second=0;//到60归0
}
}
break;
}
if((AlarmKeyID>1)&&((0==DateKeyID)||(0==TimeKeyID))) //如闹铃在设置状态,且日期和时间有一个处于正常状态,则
{
//将调整的结果送到LED显示缓冲区
LEDBuffer[0]=AlarmClock.second%10; //秒的个位
LEDBuffer[1]=AlarmClock.second/10; //秒的十位
LEDBuffer[2]=AlarmClock.Minute%10;
LEDBuffer[3]=AlarmClock.Minute/10;
LEDBuffer[4]=AlarmClock.Hour%10;
LEDBuffer[5]=AlarmClock.Hour/10;
}
}
//******************************************************************
if(TimeKeyID>0) //如果时间设置状态是处于设置状态条件下,则
{
switch(TimeKeyID) //根据时间设置状态变量的数值来决定
{
case 1: //时间设置的时状态
RealClock.Hour++; //时间设置时加1
if(24==RealClock.Hour)
{
RealClock.Hour=0; //到24归0
}
//将其转换成16进制,以方便写入Ds1302的时寄存器单元(十和个位和并为一个字节)
//RealClock.Hour/10)<<4 把小时的十位放于字节的高4位,
i=((RealClock.Hour/10)<<4)|(RealClock.Hour%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x84,i); //写入Ds1302的时寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 2: //时间设置的分状态
RealClock.Minute++; //时间设置的分加1
if(60==RealClock.Minute)
{
RealClock.Minute=0; //到60归0
}
//将其转换成16进制,以方便写入Ds1302的分寄存器单元 (十和个位和并为一个字节)
i=((RealClock.Minute/10)<<4)|(RealClock.Minute%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x82,i); //写入Ds1302的分寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 3: //时间设置的秒状态
RealClock.second++; //时间设置的秒变量加1
if(60==RealClock.second)
{
RealClock.second=0; //到60归0
}
//将其转换成16进制,以方便写入Ds1302的秒寄存器单元 (十和个位和并为一个字节)
i=((RealClock.second/10)<<4)|(RealClock.second%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x80,i); //写入Ds1302的秒寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
}
}
//***********************************************************
if(DateKeyID>0) //如果日期设置状态变量处于设置状态条件下,则
{
switch(DateKeyID) //根据日期设置状态变量的内容来决定
{
case 1: //年份校准状态
RealClock.Year++; //设置的年变量加1
if(99 {
RealClock.Year=0; //到100归0
}
//将其转换成16进制,以方便写入Ds1302的年寄存器单元 (十和个位和并为一个字节)
i=((RealClock.Year/10)<<4)|(RealClock.Year%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x8c,i); //写入Ds1302的年寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 2: //月份校准状态
RealClock.Month++; //设置的月变量加1
if(12 {
RealClock.Month=1; //到12归1
}
//将其转换成16进制,以方便写入Ds1302的月寄存器单元(十和个位和并为一个字节)
i=((RealClock.Month/10)<<4)|(RealClock.Month%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x88,i); //设置Ds1302的月寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 3: //日期校准状态
RealClock.Day++; //设置日的变量加1
switch(RealClock.Month) //根据月份来调整日期最大值
{
case 1: //属于一个月中有31天的调整
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if(31 {
RealClock.Day=1; //到32归1
}
break;
case 4: //属于一个月中有30天的调整
case 6:
case 9:
case 11:
if(30 {
RealClock.Day=1;//到31归1
}
break;
case 2: //属于2月份根据是平的还是闰年来调整
if(0==(RealClock.Year%4)) //如果是闰年.则
{
if(29 {
RealClock.Day=1; //到30归1
}
}
else//是平年,则
{
if(28 }
break;
}
case 4: //星期校准状态
RealClock.Week++; //设置星期变量加1
if(7 {
RealClock.Week=1;//到8归1
}
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x8a,RealClock.Week); //写入Ds1302的星期
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
//将其转换成16进制,以方便写入Ds1302的日寄存器单元(十和个位和并为一个字节)
i=((RealClock.Day/10)<<4)|(RealClock.Day%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x86,i); //写入Ds1302的日寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
}
}
//在设置状态下将所调节的内容装入显示缓冲区 如日期在设置状态,且闹钟和时间有一个处于正常状态,则
if(((DateKeyID>0)||(TimeKeyID>0))&&(0==AlarmKeyID))
{
//将调整的结果送到LED显示缓冲区
LEDBuffer[0]=RealClock.second%10; //秒的个位
LEDBuffer[1]=RealClock.second/10; //秒的十位
LEDBuffer[2]=RealClock.Minute%10; //分的个位
LEDBuffer[3]=RealClock.Minute/10; //分的十位
LEDBuffer[4]=RealClock.Hour%10; //时的个位
LEDBuffer[5]=RealClock.Hour/10; //时的十位
LEDBuffer[6]=RealClock.Week; //星期
LEDBuffer[7]=RealClock.Day%10; //日的个位
LEDBuffer[8]=RealClock.Day/10; //日的十位
LEDBuffer[9]=RealClock.Month%10; //月的个位
LEDBuffer[10]=RealClock.Month/10; //月的十位
LEDBuffer[11]=RealClock.Year%10; //年的个位
LEDBuffer[12]=RealClock.Year/10; //年的十位
}
}
while(0==NUMBERADDKEY);//等待数字增调节键释放
}
//-------------------------------------------------------
//-------------------------------------------------------
//---数字减调节键程序段
if(0==NUMBERsUBKEY) //数字减调节键按下
{
Delay(1000); //去抖动
if(0==NUMBERsUBKEY) //再判断是否真得按下
{
if(TimeKeyID>0) //如果时间设置状态变量是处于设置状态条件下,则
{
switch(TimeKeyID) //根据时间设置状态变量来决定
{
case 1: //时间设置的时状态
RealClock.Hour--; //时减1
if(0xFF==RealClock.Hour)
{
RealClock.Hour=23; //到-1则归23
}
//将其转换成16进制,以方便写入Ds1302的时寄存器单元 (十和个位和并为一个字节)
i=((RealClock.Hour/10)<<4)|(RealClock.Hour%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x84,i); //写入Ds1302的时寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 2: //时间设置的分状态
RealClock.Minute--; //分减1
if(0xFF==RealClock.Minute)
{
RealClock.Minute=59; //到-1归59
}
//将其转换成16进制,以方便写入Ds1302的分寄存器单元(十和个位和并为一个字节)
i=((RealClock.Minute/10)<<4)|(RealClock.Minute%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x82,i); //写入Ds1302的分寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 3: //时间设置的秒状态
RealClock.second--; //秒减1
if(0xFF==RealClock.second)
{
RealClock.second=59; //到-1归59
}
//将其转换成16进制,以方便写入Ds1302的秒寄存器单元(十和个位和并为一个字节)
i=((RealClock.second/10)<<4)|(RealClock.second%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x80,i); //写入Ds1302的秒寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
}
}
//********************************日期设置****************************
if(DateKeyID>0) //日期设置状态变量是处于设置条件下,则
{
switch(DateKeyID) //根据日期设置状态变量来决定
{
case 1: //日期设置的年份
RealClock.Year--; //年变量减1
if(0xFF==RealClock.Year)
{
RealClock.Year=99;//到-1归99
}
//将其转换成16进制,以方便写入Ds1302的年寄存器单元(十和个位和并为一个字节
i=((RealClock.Year/10)<<4)|(RealClock.Year%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x8c,i); //写入Ds1302的年寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 2: //日期设置的月份
RealClock.Month--; //月变量减1
if(0==RealClock.Month)
{
RealClock.Month=1; //到0归1
}
//将其转换成16进制,以方便写入Ds1302的月寄存器单元 (十和个位和并为一个字节
i=((RealClock.Month/10)<<4)|(RealClock.Month%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x88,i); //写入Ds1302的月寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 3: //日期设置的日
RealClock.Day--; //日变量减1
if(0==RealClock.Day)
{
RealClock.Day=1; //到0归1
}
//将其转换成16进制,以方便写入Ds1302的日寄存器单元 (十和个位和并为一个字节
i=((RealClock.Day/10)<<4)|(RealClock.Day%10);
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x86,i); //写入Ds1302的天寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
case 4: //日期设置的星期
RealClock.Week--; //星期减1
if(0==RealClock.Week)
{
RealClock.Week=1; //到0归1
}
//将其转换成16进制,以方便写入Ds1302的星期寄存器单元
i=RealClock.Week%10;
Write_Ds1302_RTC(0x8e,0x00); //Ds1302的允许写打开
Write_Ds1302_RTC(0x8a,i); //写入Ds1302的星期寄存器单元
Write_Ds1302_RTC(0x8e,0x80); //Ds1302的禁止写禁止
break;
}
}
//在DateKeyID 或TimeKeyID设置状态下将所调节的内容装入显示缓冲区
if(((DateKeyID>0)||(TimeKeyID>0))&&(0==AlarmKeyID))
{
//将调整的结果送到LED显示缓冲区
LEDBuffer[0]=RealClock.second%10; //秒的个位
LEDBuffer[1]=RealClock.second/10; //秒的十位
LEDBuffer[2]=RealClock.Minute%10; //分的个位
LEDBuffer[3]=RealClock.Minute/10; //分的十位
LEDBuffer[4]=RealClock.Hour%10; //时的个位
LEDBuffer[5]=RealClock.Hour/10; //时的十位
LEDBuffer[6]=RealClock.Week; //星期
LEDBuffer[7]=RealClock.Day%10; //日的个位
LEDBuffer[8]=RealClock.Day/10; //日的十位
LEDBuffer[9]=RealClock.Month%10; //月的个位
LEDBuffer[10]=RealClock.Month/10; //月的十位
LEDBuffer[11]=RealClock.Year%10; //年的个位
LEDBuffer[12]=RealClock.Year/10; //年的十位
}
}
while(0==NUMBERsUBKEY); //等待数字减调节释放
}
//-------------------------------------------------------
//-------------------------------------------------------
//---温度数据采集和滤波完毕,将结果送到显示缓冲区
if(TRUE==TemperatureDisplayFlag) //如果AD缓冲区已满数据
{
TemperatureDisplayFlag=FALsE;
temp=RealClock.Temp; //实时时钟赋值temp
temp *= 500;
temp /= 256;
temp -= 273; //将A/D转换的数据根据AD590计算公式转换成温度值
LEDBuffer[15]=0xff; //数码管灭
if(0x80000000==(temp & 0x80000000))//测量温度为负
{
temp = ~temp;//取反
temp++;//加1
}
LEDBuffer[15] = 0x3f; //数码管显示负号 PORTD = 0011 1111 低电平有效
LEDBuffer[14] = temp/10; //将温度值存储到缓冲区
LEDBuffer[13] = temp%10;
}
//-------------------------------------------------------
}
}