关于第八届蓝桥杯单片机初赛----电子钟设计的题目分析和编程思路分享

       关于第八届蓝桥杯单片机初赛----电子钟设计的题目分析和编程思路分享

1.前言
蓝桥杯单片机比赛,对于初试者,比如那些没有接触51系列单片机,或者很少编程训练的人而言,确实很有挑战力。而即使对于有基础的人而言,也有逻辑思维的挑战,因为大部分的人能够编译,但是时间上的话,是个很大的问题。这里的话,我将会把自己的编程思路,和快速做题的技巧分享给大家,掌握我这种编程思路的话,对于大家拿过省二是非常容易的事情,但是要拿省一,还需要将技巧和方法更加熟练。如果有更为有效的方法的话,希望大佬能够指正。下面将开始自己的看题分析。

题目分析步骤

1.大致浏览+迅速布局
首先,我估计很多人或许在做完客观题的时候,就已经创建好工程,将Main.c作为自己接下来,要奋战的战区。在翻看程序设计题的时候,我们不该盲目着敲代码,我们应该先花两分钟左右的时间,将整个题目设计的模块,涉及的按键看下。当然,我所谓的看,不是全看透,毕竟我们大多数人是常人,没有天才一样的过目不忘的记忆力。我们需要了解我们这个程序设计题,用到什么模块,模块中具体使用的是什么部分。以本届试题为例:
(1)数码管:知道用了数码管,接下来啊需要快速的在程序中写下能实现数码管亮的程序(注意,只要搭建好模块就好,并不一定要能够成功编译,毕竟你连数码管要显示的内容都不知道)
(2)DS18B20:看到有这个,直接将底层驱动文件单线(onewire)的.c源文件和.h头文件复制粘贴到你创建的文件工程下。然后在你的Main.c中快速的搭建读取温度的一个函数模块。
(3) 实时时钟:和DS18B20的做法一样,将SPI通信方式,也就是底层文件下的ds1302.c和ds1302.h复制粘贴到你的文件工程下。然后在Main.c中搭建大致的一个写时钟,一个读时钟的函数。(放心,如果你忘记了具体的指令,可以先别写,你只是搭建一个模块,好让自己知道自己已经写了不少代码)。
(4) LED指示灯:这部分和数码管有点类似,可以迅速搭建一个led灯的控制模块。
(5) 按键:按键需要知道自己是独立按键还是矩阵键盘。同样独立按键,需要通过浏览题目的过程,知道自己是作存在延时去抖的独立按键,还是所谓的三行扫描的按键。一般遇到“按下按键显示什么,松开返回什么”,这样的话延时去抖的按键处理可以更好的实现此效果。就这道题目而言,延时去抖的独立按键,比较容易。所以明确这些,可以快速的写下独立按键模块。
这部分完成时间,建议控制在40分钟之内。
硬件框图
2.寻找突破点,逐步完善
(1)接下来,我们需要开始认真读题,发现初始化,将初始化的要求完成。
(2)查看题目出现几钟数码管显示,好确定数码管有几种工作方式转换。通过看题,我们发现,有闹钟模式显示,时钟模式显示,还有温度显示。我们可以定义三个数码管数组显示内容,定义为smg_1[],smg_2[],smg_3[],这些数组的值怎样安排,我在代码中会有备注。这样定义方便,对于不同的情况,数组直接放。
(3)接着是按键功能了,对于按键功能,我们需要理清思维图:
S7是时钟按键设置,每按一次,时分秒会切换,这里很明显,需要定义一个时分秒切换标志位。当然还要注意设置一个工作状态位,区分闹钟模式,和温度显示。来区分按键按下后,显示哪种状态。显示单元以1s间隔亮灭,(这里如果觉得比较难处理,可以先放放放)对于这个处理,我一般采用关闭位选和打开位选的方法来实现。按键S6的处理比起S7要容易很多,按下S6,需要设定一个工作状态的标志位和其他工作模式区分。
S5按键是在时钟模式和闹钟模式的基础上进行加1操作,这里需要注意边界属性,也就是所加值的上限情况。S4和S5相似。Led灯的控制稍加处理。
这部分处理,建议控制在2个小时内。在这里插入代码片
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.全局已毕,逐步细化
当所有的模块都做好的时候,我们有些小细节,还没有处理,这个时候需要花个30分钟的时间迅速处理完。最后需要从主函数入手,一个一个去实现每个模块该有的功能。这个时候,有些需要放在定中断的就放入中断进行处理。
这里我会先把第一次完稿的实验代码发出,简化修正后的代码,我将会以资源的形式免费上传。第一次的代码有些冗长,但是容易看懂比较入门。

#include <STC15F2K60S2.h> //此头文件定义单片机中的特殊寄存器
#include <ds1302.h>
#include <onewire.h>
#define uchar unsigned char
#define uint  unsigned  int
#define S5    8   //定时设置按键
#define S4    12  //闹钟设置按键
#define S9    9   //加按键
#define S8    13  //减按键
#define button 16 //无效按键值
uchar wugao[8]={0,0,10,0,0,10,0,0};//数码管数据
uchar naozhong[8]={0,0,10,0,0,10,0,0};//闹钟时间存放
uchar ds_nz[]={0,0,0};
uint temphl;
uchar ds1302[3]={23,59,50};//定义初始时间23时,59分,50秒
uchar weixuan[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管位选
code uchar smg_duan[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff,0xc6};
uchar shine;  //数码管闪烁标志
uchar nz=0;    //闹钟标志
uchar nz_flag;
uchar key_value; //按键值
uchar key_flag;//键盘扫描标志
uchar nz_qf=0;
uchar DS18B20=0;
void init_system()//初始化系统
{
 P0=0x00;
 P2=(P2&0x1f)|0xa0;//关闭蜂鸣器,继电器等外设
 P2&=0x1f;
 P0=0xff;
 P2=(P2&0x1f)|0x80;//关闭led灯
 P2&=0x1f;
}
void delayms(uchar i)  //毫秒级别的延时程序
{
	 uchar k;
  	 while(i--)
	 {
		
	  for(k=707;k>0;k--);
	 }
}
void key_down() //矩阵按键检测,这一模块需要特别注意
{ 
	uchar i;    //用来检测按键释放延时
 P3=0xf0;P44=1;P42=1;
	if((P3!=0xf0)||(P44!=1)||(P42!=1))
	{
	  delayms(20);
		if((P3!=0xf0)||(P44!=1)||(P42!=1))
		{
		  if(P44==0)          key_value=0;
      if(P42==0)          key_value=1;
      if((P3&0x20)==0)    key_value=2;
      if((P3&0x10)==0)    key_value=3;			
	  P3=0x0f;P44=0;P42=0;
		if((P3&0x01)==0)     key_value=key_value+0;
		if((P3&0x02)==0)     key_value=key_value+4;
		if((P3&0x04)==0)     key_value=key_value+8;
		if((P3&0x08)==0)     key_value=key_value+12;
	}
    while((i<40)&&(P3!=0x0f))
		{
		  i++;
			delayms(20);
		}
  }
}
void RTC_set()  //设置时钟,时/分/秒
{
	 uchar temp;
 Write_Ds1302_Byte( 0x8e, 0);     // 允许写操作
 temp=((ds1302[0]/10)<<4)+ds1302[0]%10;
 Write_Ds1302_Byte( 0x84, temp);  // 允许写时
 temp=((ds1302[1]/10)<<4)+ds1302[1]%10;
 Write_Ds1302_Byte( 0x82, temp);  // 允许写分
 temp=((ds1302[2]/10)<<4)+ds1302[2]%10;
 Write_Ds1302_Byte( 0x80, temp);  // 允许写秒
 Write_Ds1302_Byte( 0x8e, 0x80);  // 禁止写操作
}
void RTC_read()//时钟读取
 {
	 uchar temp;
   temp=Read_Ds1302_Byte ( 0x85 );
   ds1302[0]=((temp/16)*10)+temp%16;
	 temp=Read_Ds1302_Byte ( 0x83 );
   ds1302[1]=((temp/16)*10)+temp%16;
	 temp=Read_Ds1302_Byte ( 0x81 );
   ds1302[2]=((temp/16)*10)+temp%16;
 }
 
 void DS18_set()  //温度传感器
 {
	 uchar temh,teml;
	 uint temparature;
   init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0x44);
	 delayms(10);
	 init_ds18b20();
	 Write_DS18B20(0xcc);
	 Write_DS18B20(0xbe);
   teml=Read_DS18B20();
	 temh=Read_DS18B20();
	 temparature=temh<<8;
	 temparature|=teml;
	 temphl=temparature*0.0625;
 
 }
void ds1302_data()  //数据处理部分(接受来自DS1302处理后的数据)
{
 if(DS18B20==0)
 {wugao[0]=ds1302[0]/10;
 wugao[1]=ds1302[0]%10;
 wugao[2]=10;
 wugao[3]=ds1302[1]/10;
 wugao[4]=ds1302[1]%10;
 wugao[5]=10;
 wugao[6]=ds1302[2]/10;
 wugao[7]=ds1302[2]%10;
 naozhong[0]=ds_nz[0]/10;
 naozhong[1]=ds_nz[0]%10;
 naozhong[3]=ds_nz[1]/10;
 naozhong[4]=ds_nz[1]%10;
 naozhong[6]=ds_nz[2]/10;
 naozhong[7]=ds_nz[2]%10;
 }
 else
 {
	   wugao[0]=11;
     wugao[1]=11;
     wugao[2]=11;
     wugao[3]=11;
     wugao[4]=11;
     wugao[5]=temphl/10;
			 wugao[6]=temphl%10;
			 wugao[7]=12;
 }
}
 void smg_disp()    //数码管显示部分
 {
	static uchar n=0; //控制位选
	static uchar m=0; //控制段选
  P0=0xff;
	P2=(P2&0x1f)|0xe0;
	P2&=0x1f;
	P0=weixuan[n];
	P2=(P2&0x1f)|0xc0;
	P2&=0x1f;
	if(nz==0) P0=smg_duan[wugao[m]];    //时钟显示
	   else  P0=smg_duan[naozhong[m]]; //闹钟显示
	P2=(P2&0x1f)|0xe0;
	P2&=0x1f;
	 if(++m==8) m=0;
	 if(++n==8) n=0;
 }
 void time0_init()
  {
    TMOD=0x00;  //12T模式下,方式0,16位自动重装
		TH0=(65536-1000)/256;
		TL0=(65536-1000)%256;
		ET0=1;
		EA=1;
		TR0=1;
  }
	void time1_init()		//定时器1初始化,12模式
	{
	  TMOD|=0x00;  //12T模式下,方式0,16位自动重装
		TH1=(65536-2000)/256;
		TL1=(65536-2000)%256;
		ET1=1;
		TR1=1;
	
	
	}
	void nz_voice() //闹钟响函数
	{
	 uchar *p=ds_nz,*q=ds1302;
		if(*(p+0)==*(q+0))
			    if(*(p+1)==*(q+1))
						 if(*(p+2)==*(q+2)) nz_flag=1;
	}
	void led_shine() //led灯闪烁函数
	{
		if(nz_flag==1)
	  { 
			P2=(P2&0x1f)|0x80;
		  P0=(nz_qf)?0xfe:0xff;
		  P2&=0x1f;
	  }
		else 
		{
		  P2=(P2&0x1f)|0x80;
		  P0=0xff;
		  P2&=0x1f;
		}
	}
 void main()
 {
	 static uchar m=0; //用于显示S5按键变化的变量
	 static uchar n=0; //用于显示S4按键变化的变量
   init_system();//初始化
   time0_init();
   time1_init();
   RTC_set();	 //定时器初始化
   while(1)
	{ if(key_flag==1)
		{ key_down();
			key_flag=0;
			if(nz_flag==0)
			{ if(key_value==S5)   //S5按键被按下
		{ 
			key_value=button;  //清零
			if(++m==4)   m=0;
			switch(m)
		  {
				case 0:   shine=0;RTC_set();break; //未设置定时闹钟
				case 1:   shine=1;break; //设置时
				case 2:   shine=2;break; //设置分
				case 3:   shine=3;break; //设置秒
			 
			}
		}
	 if(key_value==S4)   //闹钟设置
	 {
		 key_value=button;
		 nz=1;
		 if(++n==4)   n=0;
			switch(n)
		  {
				case 0:   shine=0;nz=0;break; //未设置定时闹钟
				case 1:   shine=1;break; //设置时
				case 2:   shine=2;break; //设置分
				case 3:   shine=3;break; //设置秒
			
			}
			 
	 }
	 
	if(key_value==S9)  //时钟按键加设置
	{
		key_value=button;
	 if((shine!=0)&&(nz==0))
	  {
			if(shine==1) {ds1302[0]+=1;if(ds1302[0]==24){ds1302[0]=0;}}
			if(shine==2) {ds1302[1]+=1;if(ds1302[1]==60){ds1302[1]=0;}}
			if(shine==3) {ds1302[2]+=1;if(ds1302[2]==60){ds1302[2]=0;}}
	  }
		if((shine!=0)&&(nz==1))
		{
      if(shine==1) {ds_nz[0]+=1;if(ds_nz[0]==24){ds_nz[0]=0;}}
			if(shine==2) {ds_nz[1]+=1;if(ds_nz[1]==60){ds_nz[1]=0;}}
			if(shine==3) {ds_nz[2]+=1;if(ds_nz[2]==60){ds_nz[2]=0;}}
		}
		
	}
	 if(key_value==S8)  //时钟按键减设置
	{
		key_value=button;
	 if((shine!=0)&&(nz==0))
	  {
			if(shine==1) {ds1302[0]-=1;if(ds1302[0]==-1){ds1302[0]=23;}}
			if(shine==2) {ds1302[1]-=1;if(ds1302[1]==-1){ds1302[1]=59;}}
			if(shine==3) {ds1302[2]-=1;if(ds1302[2]==-1){ds1302[2]=59;}}
	  }
		if((shine!=0)&&(nz==1))
	  {
			if(shine==1) {ds_nz[0]-=1;if(ds_nz[0]==-1){ds_nz[0]=23;}}
			if(shine==2) {ds_nz[1]-=1;if(ds_nz[1]==-1){ds_nz[1]=59;}}
			if(shine==3) {ds_nz[2]-=1;if(ds_nz[2]==-1){ds_nz[2]=59;}}
	  }
		if(shine==0)
		{
			DS18B20=1;
		 
		}
	}
}
	if(nz_flag==1)
	{
	  if(key_value!=button) {key_value=button; nz_flag=0;}
	}
}
 nz_voice();}
	}
	void tiem0()   interrupt 1  //定时中断0入口
	{
	 static uint t=0;
	 static uint d=0;
	 static uint k=0;
	 static uint s=0;
		static uint n=0;
		 smg_disp();
		ds1302_data();
		if(++k==200)  
			{ 
		key_flag=1;k=0;
			if(DS18B20==1)
			{DS18_set();if(++n==2) {DS18B20=0;n=0;}
		}
		 if(nz_flag==1)
		  {	nz_qf=~nz_qf;
				led_shine();
		if(++s==25) { nz_flag=0;s=0;}
		}
	  }
		 if(++t==1000)
		 {
		    t=0;
			  if(d==0)
			 { switch(shine)   //通过位选关闭来控制间隔亮灭
				  {
				 case 1:  weixuan[0]=0x00;weixuan[1]=0x00;break;
				 case 2:  weixuan[3]=0x00;weixuan[4]=0x00;break;
				 case 3:  weixuan[6]=0x00;weixuan[7]=0x00;break;
					}
		 }
			 else 
			 {
			    switch(shine)
					{ 
					case 1:  weixuan[0]=0x01;weixuan[1]=0x02;break;
				  case 2:  weixuan[3]=0x08;weixuan[4]=0x10;break;
				  case 3:  weixuan[6]=0x40;weixuan[7]=0x80;break;
					}
			 }
			 if(++d==2) d=0;
			 if(shine!=1)  {weixuan[0]=0x01;weixuan[1]=0x02;}
			 if(shine!=2)  {weixuan[3]=0x08;weixuan[4]=0x10;}
			 if(shine!=3)  {weixuan[6]=0x40;weixuan[7]=0x80;}
	 }
	}
	void time1()  interrupt 3//定时器1中断入口
	{
	  static uint k=0;
		if(++k==250)    //0.5s,DS1302扫描一次
		{
		 k=0;
			if((shine==0)&&(DS18B20==0)) RTC_read();
		}
	
	}
  • 8
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值