蓝桥杯第八届赛题电子钟及代码(小蜜蜂老师风格)

       本代码完全按照小蜜蜂老师的风格来书写,因为本人确实不太喜欢官方例程的风格。

        由于时间比较仓促,部分代码可能存在一些冗余。但是均给出注释。测试尚未发现明显的问题,如有问题欢迎留言指出。

题目

代码

        main.c

#include "ds1302.h"
#include "onewire.h"unsigned char Set[8] = {0x50&0x7f,0x59,0x23,0x04,0x04,0x01,0x22,0x00}; //表示当前的时间
const unsigned char ReadAddr[8] ={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d,0x8f}; //读地址
const unsigned char WriteAddr[8] ={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e}; //写地址
const unsigned char NumTable[18] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0xff};
unsigned char SetAlarm[3] = {0x00,0x00,0x00}; //设定闹钟的时间
unsigned char Temp[3] = {0x50,0x59,0x23};
unsigned char TempAlarm[3] = {0x00,0x00,0x00};
unsigned char count02 = 0, count5 = 0,count1 = 0;
unsigned char SetPointer=0, AlarmPointer = 0; // 指示当前正在设置的位,时/分/秒
unsigned char temperature=0;sbit L1 = P0 ^0 ;
sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;
bit ShrinkFlag = 0;
bit AlarmFlag = 0;
bit SMGFlag = 0;
//********************初始化时钟,记得要打开写保护*********************************
void InitClock(){unsigned char i=0;Write_Ds1302_Byte(0x8e,0x00);for(i=0;i<8;i++){Write_Ds1302_Byte(WriteAddr[i],Set[i]);}Write_Ds1302_Byte(0x8e,0x80);
}//******************读取时钟的函数*************************************************
void ReadDs1302Timer(){unsigned char j;for(j=0;j<7;j++){Set[j] = Read_Ds1302_Byte(ReadAddr[j]);}
}void delay(unsigned int t){
while(t--);
}void Timer0Init(void)		//50毫秒@11.0592MHz
{AUXR &= 0x7F;		//定时器时钟12T模式TMOD &= 0xF0;		//设置定时器模式TL0 = 0x00;		//设置定时初始值TH0 = 0x4C;		//设置定时初始值TF0 = 0;		//清除TF0标志TR0 = 1;		//定时器0开始计时EA = 1;ET0 = 1;
}//*********************中断服务函数*************************************
void Timer0Service() interrupt 1{count02++; //计时0.2sif(count02 == 4){ShrinkFlag ^= 1; //0.2s灯闪烁一次count02 =0;}count5 ++;//计时5sif(count5 == 100){AlarmFlag = 0;  //最多5s后灯就不闪了count5 = 0;}count1 ++;if(count1== 20){SMGFlag ^= 1;  //数码管设置的位闪烁count1 = 0;}
}//*****************译码器*******************
void HC138(unsigned char num){P2 &= 0X1F;switch(num){case 4:P2 |= 0x80;break;case 5:P2 |= 0XA0;break;case 6:P2 |= 0XC0;break;case 7:P2 |= 0XE0;break;default:;}
}//*********************显示数码管的一位************************
void DisplayByte(unsigned char pos, unsigned char dat){HC138(7);P0 = 0XFF;HC138(6);P0 = (0x01)<>4]);delay(500);DisplayByte(1,NumTable[Temp[2]%16]);delay(500);}else {DisplayByte(0,0xff);DisplayByte(1,0xff);}}else {//没有选中小时则让小时的位常亮DisplayByte(0,NumTable[Temp[2]>>4]);delay(500);DisplayByte(1,NumTable[Temp[2]%16]);delay(500);}DisplayByte(2,NumTable[16]);delay(500);if(SetPointer ==2 ){//选中分钟则让小时的位进行闪烁if(SMGFlag){DisplayByte(3,NumTable[Temp[1]>>4]);delay(500);DisplayByte(4,NumTable[Temp[1]%16]);delay(500);}else{DisplayByte(3,0xff);DisplayByte(4,0xff);}}else{//没有选中分钟则让分钟的位常亮DisplayByte(3,NumTable[Temp[1]>>4]);delay(500);DisplayByte(4,NumTable[Temp[1]%16]);delay(500);}DisplayByte(5,NumTable[16]);delay(500);if(SetPointer==3){ //选中秒种if(SMGFlag){DisplayByte(6,NumTable[Temp[0]>>4]);delay(500);DisplayByte(7,NumTable[Temp[0]%16]);delay(500);}else{DisplayByte(6,0xff);DisplayByte(7,0xff);}}else {DisplayByte(6,NumTable[Temp[0]>>4]);delay(500);DisplayByte(7,NumTable[Temp[0]%16]);delay(500);}DisplayALL();break;//*************************设置闹钟设置界面************************************//与上述一样,只不过是设置的是闹钟的TempAlarm,这里代码可以简化的,多传个参数就行//这里和Set的存法不一样,Set的数据我用的是BCD码,这里直接是十进制的顺序码,所以比较混乱,之后会需要用BCD转HEX//如果都用BCD码存其实更加方便一些,只需要加/减按键的时候换算BCD就行。读者自行修改case 2:if(AlarmPointer == 1){//选中小时if(SMGFlag){DisplayByte(0,NumTable[TempAlarm[2]/10]);delay(500);DisplayByte(1,NumTable[TempAlarm[2]%10]);delay(500);}else {DisplayByte(0,0xff);DisplayByte(1,0xff);}}else {DisplayByte(0,NumTable[TempAlarm[2]/10]);delay(500);DisplayByte(1,NumTable[TempAlarm[2]%10]);delay(500);}DisplayByte(2,NumTable[16]);delay(500);if(AlarmPointer ==2 ){//选中分钟if(SMGFlag){DisplayByte(3,NumTable[TempAlarm[1]/10]);delay(500);DisplayByte(4,NumTable[TempAlarm[1]%10]);delay(500);}else{DisplayByte(3,0xff);DisplayByte(4,0xff);}}else{DisplayByte(3,NumTable[TempAlarm[1]/10]);delay(500);DisplayByte(4,NumTable[TempAlarm[1]%10]);delay(500);}DisplayByte(5,NumTable[16]);delay(500);if(AlarmPointer==3){ //选中秒种if(SMGFlag){DisplayByte(6,NumTable[TempAlarm[0]/10]);delay(500);DisplayByte(7,NumTable[TempAlarm[0]%10]);delay(500);}else{DisplayByte(6,0xff);DisplayByte(7,0xff);}}else {DisplayByte(6,NumTable[TempAlarm[0]/10]);delay(500);DisplayByte(7,NumTable[TempAlarm[0]%10]);delay(500);}DisplayALL();break;//**************************平常的计时界面***********************************case 3:ReadDs1302Timer();	DisplayByte(0,NumTable[Set[2]>>4]);delay(500);DisplayByte(1,NumTable[Set[2]%16]);delay(500);DisplayByte(2,NumTable[16]);delay(500);DisplayByte(3,NumTable[Set[1]>>4]);delay(500);DisplayByte(4,NumTable[Set[1]%16]);delay(500);DisplayByte(5,NumTable[16]);delay(500);DisplayByte(6,NumTable[Set[0]>>4]);delay(500);DisplayByte(7,NumTable[Set[0]%16]);delay(500);DisplayALL();break;//**************************温度显示界面***********************************/case 4:EA = 0; //关中断,防止单总线的时序被中断破坏temperature = rd_temperature()>>4;EA = 1;DisplayByte(5,NumTable[temperature/10]);delay(500);DisplayByte(6,NumTable[temperature%10]);delay(500);DisplayByte(7,NumTable[12]);             delay(500);DisplayALL();break;}}void LEDProcess(){HC138(4);P0 = 0xff;if(AlarmFlag){L1 = ShrinkFlag;}
}//*********************10进制转BCD码****************************
unsigned char Conv2BCD(unsigned char x){if (x> 9){return x+0x06;}else return x;//*******************闹钟,如果设定的闹钟和当前的一样就让LED闪烁,注意由于我的SetAlarm是10进制的顺序码,所以比较的时候先要转成BCD码再比较
}
void Alarm(){if(Conv2BCD(SetAlarm[0]) == Set[0] &&Conv2BCD(SetAlarm[1]) == Set[1] &&Conv2BCD(SetAlarm[2]) == Set[2]){ //我是傻逼这俩不是一个码一个是BCD,一个是十进制AlarmFlag = 1;count1=0;}LEDProcess();
}//********************执行加法操作*****************
void AddProcess(){if(AlarmPointer){  // 如果AlarmPointer不为0,说明在设置闹钟界面switch(AlarmPointer){case 1:TempAlarm[2]++; if(TempAlarm[2]>23) TempAlarm[2] = 0; //按键的边界处理break;case 2:TempAlarm[1]++;if(TempAlarm[1]>59) TempAlarm[1] = 0;break;case 3:TempAlarm[0]++;if(TempAlarm[0]>59) TempAlarm[0] = 0;break;}}else if(SetPointer) {//如果SetPointer不为0,说明在设置时钟界面switch(SetPointer){case 1:if((Temp[2]&0x0f) >= 9){Temp[2] = Temp[2] +1 +6; // BCD码 记得换成BCD码的时候不是直接++,比如0x19要变到0x20,其实加了7,这就是BCD码}else Temp[2]++;//如果低位不大于9,就直接+1就行if(Temp[2]>0x23) Temp[2] = 0; // 边界处理break;case 2:if((Temp[1]&0x0f) >= 9){Temp[1] = Temp[1] +1 +6; // BCD码}else Temp[1]++;if(Temp[1]>0x59) Temp[1] = 0;break;case 3:if((Temp[0]&0x0f) >= 9){Temp[0] = Temp[0] +1 +6; // BCD码}else Temp[0]++;if(Temp[0]>0x59) Temp[0] = 0;break;}}else{ // 此时不处在设置的状态,此状态下啥也不干。}
}//*********************处理减法****************************
void MinusProcess(){if(AlarmPointer){
//	TempAlarm[0] = SetAlarm[0];
//	TempAlarm[1] = SetAlarm[1];
//	TempAlarm[2] = SetAlarm[2];switch(AlarmPointer){case 1:if(TempAlarm[2]==0) TempAlarm[2] = 23; //如果是0的话对于无符号数进行特判,处理边界条件else TempAlarm[2]--;break;case 2:if(TempAlarm[1]==0) TempAlarm[1] = 59;else TempAlarm[1]--;break;case 3:if(TempAlarm[0]==0) TempAlarm[0] = 59;else TempAlarm[0]--;break;}}else if(SetPointer) {
//	Temp[0] = Set[0];
//	Temp[1] = Set[1];
//	Temp[2] = Set[2];switch(SetPointer){case 1:if(Temp[2]==0) Temp[2] = 0x23; //特判else if((Temp[2]&0x0f)==0){Temp[2] = Temp[2] -1-6; // 如果低位是0,根据BCD码的性质应该多减6}else Temp[2]--;//低位不是0 减1就行break;case 2:if(Temp[1]==0) Temp[1] = 0x59;else if((Temp[1]&0x0f)==0){Temp[1] = Temp[1] -1-6; // BCD码}else Temp[1]--;break;case 3:if(Temp[0]==0) Temp[0] = 0x59;else if((Temp[0]&0x0f)==0){Temp[0] = Temp[0] -1-6; // BCD码}else Temp[0]--;break;}}else{ // 此时不处在设置的状态,显示温度}
}void ScanKeys(){unsigned char i=0;if(S4 == 0){delay(100);if(S4==0){AlarmFlag = 0; //按下任意按键,LED停止闪动SetPointer = 0;DisplaySMG(2); //S4显示设置闹钟界面AlarmPointer++; if(AlarmPointer==4){ //当为4时相当于按下了确定键,此时把SetAlarm更新为TempAlarm的值AlarmPointer =0;SetAlarm[0] = TempAlarm[0];SetAlarm[1] = TempAlarm[1];SetAlarm[2] = TempAlarm[2];}}}while(S4==0){if(SetPointer)DisplaySMG(1);else if(AlarmPointer)DisplaySMG(2);else DisplaySMG(3);}if(S5 == 0){delay(100);if(S5==0){AlarmFlag = 0;AlarmPointer =0;DisplaySMG(1);	//S4显示设置时钟界面SetPointer++;if(SetPointer==4) {SetPointer =0; Write_Ds1302_Byte(0x8e,0x00);for(i=0;i<3;i++){Write_Ds1302_Byte(WriteAddr[i],Temp[i]); //更新Set,传给电子钟芯片}Write_Ds1302_Byte(0x8e,0x10);}}}while(S5==0){if(SetPointer)DisplaySMG(1);else if(AlarmPointer)DisplaySMG(2);else DisplaySMG(3);}if(S6 == 0){delay(100);if(S6==0){AlarmFlag = 0;AddProcess();}}while(S6==0){if(SetPointer)DisplaySMG(1);else if(AlarmPointer)DisplaySMG(2);else DisplaySMG(3);}if(S7 == 0){delay(100);if(S7==0){AlarmFlag = 0;MinusProcess();}}while(S7==0){if(SetPointer)DisplaySMG(1);else if(AlarmPointer)DisplaySMG(2);else DisplaySMG(4); //显示温度界面}
}int main(){InitClock();Timer0Init();while(1){ReadDs1302Timer(); //更新时间if(SetPointer==0){Temp[0] = Set[0];  //把Temp更新为现在的时间,为的是使用者调时间的时候和目前的时间比较接近方便调Temp[1] = Set[1];Temp[2] = Set[2];}if(SetPointer) //显示DisplaySMG(1);else if(AlarmPointer)DisplaySMG(2);else DisplaySMG(3);ScanKeys();//扫描键盘Alarm(); //LED到点闪烁}
}

底层驱动

ds1302.c

#include "ds1302.h"  									//写字节
void Write_Ds1302(unsigned  char temp) 
{unsigned char i;for (i=0;i<8;i++)     	{ SCK = 0;SDA = temp&0x01;temp>>=1; SCK=1;}
}   //向DS1302寄存器写入数据
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{RST=0;	_nop_();SCK=0;	_nop_();RST=1; 	_nop_();  Write_Ds1302(address);	Write_Ds1302(dat);		RST=0; 
}//从DS1302寄存器读出数据
unsigned char Read_Ds1302_Byte ( unsigned char address )
{unsigned char i,temp=0x00;RST=0;	_nop_();SCK=0;	_nop_();RST=1;	_nop_();Write_Ds1302(address);for (i=0;i<8;i++) 	{		SCK=0;temp>>=1;	if(SDA)temp|=0x80;	SCK=1;} RST=0;	_nop_();SCK=0;	_nop_();SCK=1;	_nop_();SDA=0;	_nop_();SDA=1;	_nop_();return (temp);			
}

ds1302.h

#ifndef __DS1302_H
#define __DS1302_H#include 
#include sbit SCK = P1^7;		
sbit SDA = P2^3;		
sbit RST = P1^3; void Write_Ds1302(unsigned char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte( unsigned char address );#endif

onewire.c(一定记得修改Delay_OneWire)

#include "onewire.h"
#include "STC15F2K60S2.H"sbit DQ = P1^4;  //单总线内部延时函数
void Delay_OneWire(unsigned int t)  
{unsigned char i=0;while(t--){for(i=0;i<12;i++) ;}
}//单总线写操作
void Write_DS18B20(unsigned char dat)
{unsigned char i;for(i=0;i<8;i++){DQ = 0;DQ = dat&0x01;Delay_OneWire(5);DQ = 1;dat >>= 1;}Delay_OneWire(5);
}//单总线读操作
unsigned char Read_DS18B20(void)
{unsigned char i;unsigned char dat;for(i=0;i<8;i++){DQ = 0;dat >>= 1;DQ = 1;if(DQ){dat |= 0x80;}	    Delay_OneWire(5);}return dat;
}//DS18B20初始化
bit init_ds18b20(void)
{bit initflag = 0;DQ = 1;Delay_OneWire(12);DQ = 0;Delay_OneWire(80);DQ = 1;Delay_OneWire(10); initflag = DQ;     Delay_OneWire(5);return initflag;
}unsigned int rd_temperature(){unsigned char low, high;init_ds18b20();Write_DS18B20(0xcc);Write_DS18B20(0x44);init_ds18b20();Write_DS18B20(0xcc);Write_DS18B20(0xbe);low = Read_DS18B20();high= Read_DS18B20();return (high<<8)+low;
}

onewire.h

#ifndef __ONEWIRE_H
#define __ONEWIRE_Hunsigned int rd_temperature(void);  #endif

一些Debug心得

1.为什么出现电子钟不走的情况,多半是因为没有写读函数,或者是CH位被置为1了
2.比较语句>=优先级高于赋值语句==的优先级比& | && ||高 这很重要啊(A&0x1f)>= 0x11
3.BCD码与10进制的转换
4.对于无符号数小于0时的边界最好要特判
5.记得上来就把onewire的东西改了,这里

void Delay_OneWire(unsigned int t)  
{unsigned char i=0;while(t--){for(i=0;i<12;i++) ;}
}


中间for和while的位置不可调换。如果调换,看上去好像是对的,但是while当t减到0才出来,此时t等于0,下一次进while直接出了但是t--变成65535了,然后就开始巨大的延迟。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部