第八届省赛之电子钟,冲鸭!!!

路漫漫其修远兮,吾将上下而求索,,,啊!


博客停写了有两三天了,这几天一直在弄第九届省赛的电子钟的题目,emmm,有点儿心累叭,不过最后还是弄完了,嘻嘻,开熏,学到不少东西,今天来个总结叭~~~


先上题目:
一. 初始化

1)关闭蜂鸣器、继电器等无关外设;
2)设备初始化时钟为 23 时 59 分 50 秒,闹钟提醒时间 0 时 0 分 0 秒。

二. 显示功能
第八届省赛之电子钟,冲鸭!!!
三. 按键功能
1)按键 S7 定义为“时钟设置”按键,通过该按键可切换选择待调整的时、分、秒,当前选择的显示单元以 1 秒为间隔亮灭,时、分、秒的调整需注意数据边界属性。
第八届省赛之电子钟,冲鸭!!!
2)按键 S6 定义为“闹钟设置”按键,通过该按键可进入闹钟时间设置功能,数码管显示当前设定的闹钟时间。
第八届省赛之电子钟,冲鸭!!!
3)按键 S5 定义为“加”按键,在“时钟设置”或“闹钟设置”状态下,每次按下该按键当前选择的单元(时、分或秒)增加 1 个单位。
4)按键 S4 定义为“减”按键,在“时钟设置”或“闹钟设置”状态下,每次按下该按键当前选择的单元(时、分或秒)减少 1 个单位。
5)按键功能说明:
按键 S4、S5 的“加”、“减”功能只在“时钟设置”或“闹钟设置”状态下有效;在 “时钟显示”状态下,按下 S4 按键,显示温度数据,松开按键,返回“时钟显示”界面。
四. 闹钟提示功能
1)指示灯 L1 以 0.2 秒为间隔闪烁,持续 5 秒钟;
2)闹钟提示状态下,按下任意按键,关闭闪烁提示功能。


几点体会:

  1. 拿到题目先分析功能,划分功能模块,学会使用第三方标志量!!!!!(就是flag),用不同的标志量来区分不同的模式!!!很有用!!!
  2. 没有思路的时候不要着急,一步一步地实现功能,思路自然就清晰了;
  3. 按键部分,如何实现按一下改变上一次的标志量呢?异或符!!!(如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。)同样的,数码管的闪烁led的闪烁都可以用这个方法来实现,当然,这个异或一定是标志量的异或(比如说,定一个标志量,0代表led灭,1代表led灭)
  4. 结构体指针很有用!!!! 可以给它不同的实参,传进去不同的数值,比如增加时间,减少时间这两个函数,用了结构体指针之后,就能实现既可以改变闹钟时间,又可以改变时钟时间的功能了,只需要使用时传进去它们各自的 **地址!!!**可以看一下代码:
void AddTime(struct sTime *time)
{
	if(index == 7)//小时
	{
		(time->hour) += 0x01;
		if(((time->hour) & 0x0F) == 0x0A)//如果低四位是A的情况
		{
			(time->hour) = ((time->hour) & 0xF0) + 0x10; //显示正常值
		}
		if((time->hour) > 0x23)//小时部分不能超过24,超过24就置0
		{
			(time->hour) = 0x00;
		}
	}
	else if(index == 4)//分钟
	{
		time->min += 0x01;
		if(((time->min) & 0x0F) == 0x0A)
		{
			time->min = ((time->min) & 0xF0) + 0x10;
		}
		if((time->min) > 0x59)//分钟和秒不能超过59
		{
			time->min = 0x00;
		}
	}
	else//秒
	{
		(time->sec) += 0x01;
		if(((time->sec) & 0x0F) == 0x0A)
		{
			time->sec = ((time->sec) & 0xF0) + 0x10;
		}
		if((time->sec) > 0x59)
		{
			time->sec = 0x00;
		}
	}
}
  1. 注意结构体指针结构体变量在使用时的区别!!!结构体指针(time -> hour ),结构体变量(setClockTime .hour),指针用箭头(->),变量用(.);
  2. 关于 & 符号和 | 符号的运用:还拿上面的AddTime的函数来看,想单独保留(或者单独判断)哪一位,就只把那一位与1相与(&),其它位与0相与,如上面程序中,我们想单独判断一下低四位,就让它与0x0F相与
if(((time->hour) & 0x0F) == 0x0A)//如果低四位是A的情况

至于|)符号,一般在赋值的时候使用,例如:

P2 = (P2 & 0x1F) | 0xA0;
  1. 特别注意!!!中断函数中的时间标志变量,例如**tmr****这样的标志量,一旦计时到相应的时间,要立马清零,例如:
if(tmr200ms >= 100)
	{
		tmr200ms = 0;
		flag200ms = 1;
		sta ^= 1;
	}
  1. .c 文件中定义的变量,如果在别的函数中调用,需要在.h文件中加extern声明,子函数调用声明即可,不用加extern。

题目分析:
用到哪些底层?
1. 时钟 ——> DS1302
2. 温度 ——>DS18B20
3. 按键 ——>KEY(独立按键)
4. 数码管显示 ——>LED(数码管)

有几个模式?
时钟正常运行模式时钟设置模式闹钟设置模式
特殊功能?
闹钟提示(L1闪烁,五秒关闭,且闹钟提示情况下按下任意按键可关闭L1),设置时间时,数码管要以1s为间隔闪烁(注意:在设置模式下,数码管是不实时刷新时间的),按键功能
针对题目分析,我设置了以下几个标志量

u8 flag18b20 = 0;//0-时钟显示,1-温度显示
u8 flagstop = 1;//0-led开始闪烁,1-led不闪烁
u8 sta = 0;//1-L1亮,0-L1灭
u8 flagmode = 0;//0-运行模式,1-时钟设置模式,2-闹钟设置模式
u8 flagenter = 0;//0-得到实时时间,1-不刷新时间
u8 numBlinkSta = 0;//0-数码管亮,1-数码管灭

下面来说明每个功能的具体实现:
一. 时钟显示:之前写DS1302时就已经写过,照搬过来即可,这里只注意一点!!!!由于我们是支持改变时钟初始时间功能的,那么我们在刷新时间时,从DS1302内部得到的实时时间,就必须是在我们设置后的时钟时间的基础上再自动加一的,也就是说我们改变时钟时间后,要将改变后的值写入DS1302刷新显示时,得到的实时时间也必须是改变后的时钟时间

void RefreshTime()
{
	GetRealTime(&setClockTime);//改变后写入DS1302的时钟时间
	ShowLedNumber(7, setClockTime.hour>>4);
	ShowLedNumber(6, setClockTime.hour&0x0F);
	ShowLedNumber(5, 0xBF);
	ShowLedNumber(4, setClockTime.min>>4);
	ShowLedNumber(3, setClockTime.min&0x0F);
	ShowLedNumber(2, 0xBF);
	ShowLedNumber(1, setClockTime.sec>>4);
	ShowLedNumber(0, setClockTime.sec&0x0F);
}

那么我们是什么时候把时钟时间写入 DS1302呢?肯定是要在改变之后再写入鸭,那什么时候改变呢?题目中有加减时间的功能要求,按下S5加一按下S4减一,那么我们就在按下按键之后写入,于是就有了下面这段代码:

else if(keycode == '3')//按下S5
	{
		if(flagmode == 0)//如果是正常运行模式
		{
			if(flagstop == 0)//L1处于闪烁状态
			{
				flagstop = 1;//那么就让L1停止闪烁
			}
		}
		if(flagmode == 1)//如果是时钟设置模式
		{
				AddTime(&setClockTime);
				SetRealTime(&setClockTime);//将改变后的时钟时间写入DS1302
			    RefreshSetTime(&setClockTime);//为了在我们设置时间时可以清楚地看到设置后的数字
		}
		if(flagmode == 2)//如果是闹钟设置模式
		{
				AddTime(&setAlarmTime);
				RefreshSetTime(&setAlarmTime);
		}
	}

二. 温度显示:这个也很简单,主要就是用到DS18B20的底层,我会再写一篇关于DS18B20的博客,然后再细讲。
三. 数码管闪烁: 这个地方就用到了我们上面说的异或啦,主要思想就是,先定义一个标志位,也就是下面这个,因为是间隔一秒闪烁的,那么我们就隔一秒让我们的标志量与1异或,至于这个1秒的间隔当然是要在我们的定时器中断里判断啦。

u8 numBlinkSta = 0;//0-数码管亮,1-数码管灭

主要代码(以时钟设置为例,闹钟设置类似):

void SetClockTimer()//时钟设置
{
	if(flagenter == 0)//允许得到实时时间
	{
		GetRealTime(&setClockTime);//得到实时时间
		flagenter = 1;//马上置1,表示不再获取实时时间了,确保设置模式下,数码管不再刷新时间
	}
	if(numBlinkSta == 1)
	{
		LedBuff[index] = 0xFF;
		LedBuff[index - 1] = 0xFF; 
	}
	else
	{
		RefreshSetTime(&setClockTime);//显示时间
	}	
}

判断部分:

static u16 tmr1s = 0;

if(tmr1s >= 500)//每2ms进入一次中断,故500次表示定时1s
	{
		tmr1s = 0;
		numBlinkSta ^= 1;
	}

四. LED闪烁,这里思想与数码管一样,代码如下:

void LedBlink()
{
	if(sta)
	{
		P2 = (P2 & 0x1F) | 0x80;
		P0 = 0xFE;//点亮L1
		P2 &= 0x1F;
	}
	else
	{
		P2 = (P2 & 0x1F) | 0x80;
		P0 = 0xFF;//关闭L1
		P2 &= 0x1F;
	}
}

五. 时钟(闹钟)设置:程序上面已经放过了,这里主要说明几点:1. 关于index这个变量的作用,是用来判断我们正在设置的是小时还是分,秒,main.c文件中,我定义了index这个全局变量(注意是char类型的),初始化为7,当我们按下按键时,index会执行 -3操作 ,切换到分,再按就切换到秒,此时再判断一下index是否小于0(当然这时候肯定是小于0的),进入运行模式,代码如下:

if(keycode == '1')//按下S7按键
	{
		if(flagmode == 0)//如果当前为运行模式
		{
			if(flagstop == 0)
			{
				flagstop = 1;
			}
			else
			{
				flagmode = 1;//那么进入设置模式
				flagenter = 0;
				index = 7;
			}
		}
		else if(flagmode == 1)//如果按下按键当前为设置时钟模式
		{
			index -= 3;
			if(index < 0)
			{
				index = 7;
				flagmode = 0;//那么进入运行模式
			}
			RefreshSetTime(&setClockTime);//消除按下按键,时,分,秒,任意两个一起闪烁的情况
			
		}
	}

闹钟设置与时钟类似,但是要注意的是,闹钟是不需要从DS1302内部获取时间的,也就是说不需要判断flaginter这个标志量,只需要在定义闹钟时间的结构体里写好初始时间就好了;

struct sTime setAlarmTime={0,0,0,0,0,0,0};//闹钟时间结构体,初始化为0

六. 加减时间功能,话不多说,先上代码,一看便知

void AddTime(struct sTime *time)//结构体指针
{
	if(index == 7)//时
	{
		(time->hour) += 0x01;
		if(((time->hour) & 0x0F) == 0x0A)//如果低四位是A的情况
		{
			(time->hour) = ((time->hour) & 0xF0) + 0x10; //显示正常值
		}
		if((time->hour) > 0x23)//小时不能超过23
		{
			(time->hour) = 0x00;
		}
	}
	else if(index == 4)//分
	{
		time->min += 0x01;
		if(((time->min) & 0x0F) == 0x0A)
		{
			time->min = ((time->min) & 0xF0) + 0x10;
		}
		if((time->min) > 0x59)//分秒不能超过59
		{
			time->min = 0x00;
		}
	}
	else//秒
	{
		(time->sec) += 0x01;
		if(((time->sec) & 0x0F) == 0x0A)
		{
			time->sec = ((time->sec) & 0xF0) + 0x10;
		}
		if((time->sec) > 0x59)
		{
			time->sec = 0x00;
		}
	}
}

低四位为A要特殊判断!!!这里涉及到的就是16进制与10进制加法的不同之处了,16进制是满16进110进制满10 进1 ,举个例子:当我们加到9(也就是0x09)时,再加0x01,我们想让数码管显示10,但是在16进制下,10是用A来表示的,所以我们得到的是(0x0A),也就是说这时候如果我们不做处理,数码管显示的就是a。

那么我们是如何处理的呢?特殊情况就特殊处理呗,读者如果把所有的特殊情况都列出来会发现,所有的特殊情况都是在出现9+1,也就是某一位满10的情况下产生的,满10没有进1,而是变成了A,那么我们就判断一下这种情况,然后人为进1就好了鸭,这样再去看我们的程序是不是就好理解了呢?减法也是同样的思路哦:

void SubTime(struct sTime *time)
{
	if(index == 7)
	{
		(time->hour) -= 0x01;
		if(((time->hour) & 0x0F) == 0x0F)//如果低四位是A的情况
		{
			(time->hour) = ((time->hour) & 0xF0) + 0x09; //显示正常值
		}
		if((time->hour) == 0xF9)
		{
			(time->hour) = 0x23;
		}
	}
	else if(index == 4)
	{
		time->min -= 0x01;
		if(((time->min) & 0x0F) == 0x0F)
		{
			time->min = ((time->min) & 0xF0) + 0x09;
		}
		if((time->min) == 0xF9)
		{
			time->min = 0x59;
		}
	}
	else
	{
		(time->sec) -= 0x01;
		if(((time->sec) & 0x0F) == 0x0F)
		{
			time->sec = ((time->sec) & 0xF0) + 0x09;
		}
		if((time->sec) == 0xF9)
		{
			time->sec = 0x59;
		}
	}
}

好了,到这里所有的功能我们就都实现了,把这些功能总和一下,合理地放在我们的程序里就ok啦~

上一篇:"Hour of Code" was held in Hangu No. 9 Middle School, Binhai New Area, Tianjin


下一篇:[JS]-08