第八届蓝桥杯单片机省赛代码分析

按照之前的模式,我列一下我完成这个省赛的时间表,希望能对你起到帮助!

分析题目,是否需要数码管和矩阵按键 约1分钟
让数码管显示1234.5678 约10分钟
通过矩阵按键改变数码管的显示内容 约15分钟
阅读题目,理解系统需求 约5分钟
让DS1302时钟芯片运作,初值为题目要求的23时59分50秒(显示数据出错,如何修复我会在代码区注释说明) 约15分钟
针对S5按键,时钟设置,增加1s间隔闪烁功能。(此时数码管出现“影子”,如何修复及其原理文末解释) 约20分钟(15分钟修理BUG)
针对S8,S9按键,完善时钟设置。(不含闹钟)(代码中的写法要学习,因为熟悉这种使用,所以这部分我很快解决) 约15分钟
增加闹钟闪烁(与第七届错误相同,可参考我的上一篇文章,这次出现这个BUG我修理的非常快蓝桥杯单片机省赛 第七届(代码+分析)_tuygre的博客-CSDN博客 约10分钟(2分钟修理BUG)
增加闹钟设置(复制时钟内容,修改参数) 约10分钟
增加温度模块(出现数据无法显示BUG,发现第二次模块忘记初始化,这种错误就不解释了,可以看蓝桥杯单片机模块代码(DS18B20温度测量)(代码+注释)_tuygre的博客-CSDN博客 约25分钟(15分钟修理BUG)
非设置模式按下S8,显示温度(BUG是温度显示后无法返回,是代码段位置错误,我会在代码区注释说明) 约15分钟(10分钟修理BUG)
再次审题,检测修饰功能(比如count=0这样的小细节,使得系统更加精准) 约10分钟

这次的试题很简单,也很难。简单在模块简单,单个功能逻辑简单。难在需要的参量很多,如果各个功能逻辑没有明白,出错了就很难修改,因为完全不知道修改哪里。所以还是那句话,一步步来,做一步烧录一次,如果出错,修改成功的概率非常大。就比如本题的LED闪烁,我也是先让他亮的准,再做到闪烁。也是LED,我意识到完全可以用一个参数,不用让lighten函数到处都是,但是那时已经进入了别的功能的书写,完全不知道从何处开始修改(为此道歉,但完全不影响功能)。希望我的LED经历给你启发。

本题对于逻辑清晰,书写习惯好的同学是福利。对于逻辑混乱或接触单片机时间非常短的同学,可以说是个难题了,5-10小时的书写完全有可能,但不要灰心。

既然参量多,我就直接先列表。方便大家理解。【此处依旧默认你是跟着我写的文章一步步学的,模块博客里出现的参数不解释】

time1[3] set flag time0 tishi jishu se
闹钟时间 时钟模式设置 闪烁控制,为1对应单元不亮 时钟时间 为1表示闹钟触发,LED允许闪烁 控制闹钟闪烁时长 时钟模式设置
tiao wendu count shan count1 time0[3]
为1灯亮 为1显示温度 控制时钟设置界面闪烁 控制闹钟闪烁间隔 控制闹钟设置界面闪烁 时钟时间

下面我们进入代码编程,整体看完后,我在讲讲我BUG的修改思路(函数后有“/*相同*/”代表与之前模块的内容几乎相同【此处默认你是跟着我写的文章一步步学的】。因为自己在写系统时不会看之前的模块,所以参数名字会有区别,其他几乎相同。):

#include "STC15F2K60S2.H"
#include "stdio.h"
#include "ds1302.h"
#include "onewire.h"

typedef unsigned char u8;
typedef unsigned int u16;

u8 org[10],tran[10],wei,old,new,time1[3]={0,0,0},delay,set,flag;
u8 time0[3]={23,59,50},tishi,se,tiao,low,high,*zhen1,*zhen2,wendu;
u16 count,jishu,count1,shan,tem;

void lighten(u8 led)/*相同*/
{
  P0=~led;
  P2=P2&0X1F|0X80;
  P2=P2&0X1F;
}
  
void close()/*相同*/
{
  P0=0;
  P2=P2&0X1F|0XA0;
  P2=P2&0X1F;
  P0=0XFF;
  P2=P2&0X1F|0X80;
  P2=P2&0X1F;
}

void write()/*相同*/
{
  u8 mid1;
  Write_Ds1302_Byte(0x8e,0);
  mid1=((time0[0]/10)<<4)+(time0[0]%10);
  Write_Ds1302_Byte(0x84,mid1);
  mid1=((time0[1]/10)<<4)+(time0[1]%10);;
  Write_Ds1302_Byte(0x82,mid1);
  mid1=((time0[2]/10)<<4)+(time0[2]%10);;
  Write_Ds1302_Byte(0x80,mid1);
  Write_Ds1302_Byte(0x8e,0x80);
}

void open()/*相同*/
{
  EA=1;
  ET1=1;
}

void Timer1Init(void)	/*相同*/	//1毫秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
}

void translate(u8 org[],u8 tran[])/*相同*/
{
  u8 k,i,mid;
  for(k=0,i=0;i<8;i++,k++)
  {
    switch(org[k])
	{
	  case '0':mid=0xc0;break;
	  case '1':mid=0xf9;break;
	  case '2':mid=0xa4;break;
	  case '3':mid=0xb0;break;
	  case '4':mid=0x99;break;
	  case '5':mid=0x92;break;
	  case '6':mid=0x82;break;
	  case '7':mid=0xf8;break;
	  case '8':mid=0x80;break;
	  case '9':mid=0x90;break;
	  case '-':mid=0xbf;break;
	  case 'C':mid=0xc6*;break;
	  default:mid=0xff;
	}
	if(org[k+1]=='.')
	{
	  mid&=0x7f;
	  k++;
	}
	tran[i]=mid;
  }
}

void display(u8 tran[],u8 wei)/*相同*/
{
  P0=0XFF;
  P2=P2&0X1F|0XE0;
  P2=P2&0X1F;
  P0=1<<wei;
  P2=P2&0X1F|0XC0;
  P2=P2&0X1F;
  P0=tran[wei];
  P2=P2&0X1F|0XE0;
  P2=P2&0X1F;
}

u8 keytran()/*相同*/
{
  u16 key;
  u8 retu;
  P44=0;P42=1;P35=1;P34=1;
  key=P3&0X0F;
  P44=1;P42=0;P35=1;P34=1;
  key=(key<<4)|(P3&0X0F);
  P44=1;P42=1;P35=0;P34=1;
  key=(key<<4)|(P3&0X0F);
  P44=1;P42=1;P35=1;P34=0;
  key=(key<<4)|(P3&0X0F);
  switch(~key)
  {
    case 0X8000:retu=4;break;
	case 0X4000:retu=5;break;
	case 0X2000:retu=6;break;
	case 0X1000:retu=7;break;
	case 0X0800:retu=8;break;
	case 0X0400:retu=9;break;
	case 0X0200:retu=10;break;
	case 0X0100:retu=11;break;
	case 0X0080:retu=12;break;
	case 0X0040:retu=13;break;
	case 0X0020:retu=14;break;
	case 0X0010:retu=15;break;
	case 0X0008:retu=16;break;
	case 0X0004:retu=17;break;
	case 0X0002:retu=18;break;
	case 0X0001:retu=19;break;
	default:retu=0;
  }
  return retu;
}

void keydo()/*全系统最美的函数*/
{
  new=keytran();
  if(new!=old&&new!=0)
  {
    switch(new)
	{
       case 5:if(se==0&&(++set==4)) set=0;count=0;flag=0;tishi=0;lighten(0);shan=0;break;
/*一定要学会这种写法!if(se==0&&(++set==4)) set=0;保证闹钟设置情况下不会进入时钟设置,并且若
se!=0,则&&从左至右,不会对++set==4判定!set值就不会改变,就不会对时钟设置有任何影响!其余等于0的参数本质上都是优化,会让系统功能很漂亮,读者可以删除他们体会*/
	   case 4:if(set==0&&(++se==4)) se=0;count1=0;flag=0;tishi=0;lighten(0);shan=0;break;
/*与上类似,但是count1与count不可以是一个参数,否则在S4,S5一功能作用时,另一个按钮会影响闪烁,可以自己修改试试!*/
	   case 8:if(set==1) {if(!time0[0]) time0[0]=23;else time0[0]--;}
	          if(set==2) {if(!time0[1]) time0[1]=59;else time0[1]--;}
			  if(set==3) {if(!time0[2]) time0[2]=59;else time0[2]--;}
			  if(se==1) {if(!time1[0]) time1[0]=23;else time1[0]--;}
	          if(se==2) {if(!time1[1]) time1[1]=59;else time1[1]--;}
			  if(se==3) {if(!time1[2]) time1[2]=59;else time1[2]--;}
			  tishi=0;lighten(0);shan=0;
			  break;/*修改参数*/
	   case 9:if(set==1) if(++time0[0]==24) time0[0]=0;
              if(set==2) if(++time0[1]==60) time0[1]=0;
			  if(set==3) if(++time0[2]==60) time0[2]=0;
			  if(se==1) if(++time1[0]==24) time1[0]=0;
              if(se==2) if(++time1[1]==60) time1[1]=0;
			  if(se==3) if(++time1[2]==60) time1[2]=0;
			  tishi=0;lighten(0);shan=0;break;/*修改参数*/
	}
  }
   if(new==8&&se==0&set==0) wendu=1;
   else wendu=0;/*之前将它放在了if(new!=old&&new!=0)内,那样进入S8就无法通过S8退出温度显示了,可以试试,注意题目哦,松手就要恢复时间显示*/
   old=new;
}

void read()/*相同*/
{
  u8 mid2;
  mid2=Read_Ds1302_Byte(0x81);
  time0[2]=(mid2>>4)*10+(mid2&0x0f);/*+的运算优先级高于&,所以()不能省哦*/
  mid2=Read_Ds1302_Byte(0x83);
  time0[1]=(mid2>>4)*10+(mid2&0x0f);
  mid2=Read_Ds1302_Byte(0x85);
  time0[0]=(mid2>>4)*10+(mid2&0x0f);
}

void main()
{
  open();
  close();
  zhen1=&low;zhen2=&high;
  rd_temperature(zhen1,zhen2);/*读取温度,读取温度时间长,没有这一步第一次读温度就是初始值*/
  write();/*先将23时59分50秒写入DS1302*/
  Timer1Init();
  while(1)
  {
    if(!delay)/*降速,读取温度函数降速是应为速度慢,不降速会使得其他部分降速。 keydo降速为了防止多次识别按下*/
	{
	  delay=1;
	  translate(org,tran);
	  keydo();
	  if(set==0) read();
	  if(wendu==1)  {rd_temperature(zhen1,zhen2); tem=((high*256+low)/16.0);}
	}
	if(time0[0]==time1[0]&&time0[1]==time1[1]&&time0[2]==time1[2]) tishi=1;
	if(wendu==1)  sprintf(org,"     %2uC",(u16)tem);
	else if(flag==0&&se==0)  sprintf(org,"%02u-%02u-%02u",(u16)time0[0],(u16)time0[1],(u16)time0[2]);
	else if(flag==0&&se!=0)	 sprintf(org,"%02u-%02u-%02u",(u16)time1[0],(u16)time1[1],(u16)time1[2]);
	else if(set==1&&flag==1) {sprintf(org,"  -%02u-%02u",(u16)time0[1],(u16)time0[2]); write();}
	else if(set==2&&flag==1) {sprintf(org,"%02u-  -%02u",(u16)time0[0],(u16)time0[2]); write();}
	else if(set==3&&flag==1) {sprintf(org,"%02u-%02u-  ",(u16)time0[0],(u16)time0[1]); write();}
	else if(se==1&&flag==1) {sprintf(org,"  -%02u-%02u",(u16)time1[1],(u16)time1[2]); }
	else if(se==2&&flag==1) {sprintf(org,"%02u-  -%02u",(u16)time1[0],(u16)time1[2]); }
	else if(se==3&&flag==1) {sprintf(org,"%02u-%02u-  ",(u16)time1[0],(u16)time1[1]); }
  /*依据参数决定数码管显示内容,ELSE IF很好地减少了判断次数,减少单片机负担*/
    }
}

void stop1() interrupt 3
{ 
  if(set) count++;
  if(se) count1++;
  if(tishi) 
  { 
    shan++;
    jishu++;
    if(jishu==5000) {tishi=0;lighten(0);shan=0;}
  }
  else if(!tishi) jishu=0;
  if(shan==200) {shan=0;if(++tiao==2) tiao=0;}
  if(tiao==1) lighten(1);
  if(tiao==0) lighten(0);
  if(count==1000) {count=0;if(++flag==2) flag=0;}
  if(count1==1000) {count1=0;if(++flag==2) flag=0;}
/*因为闹钟设置时无法进入时钟设置,时钟设置时无法进入闹钟设置,flag可以共用*/
  if(++delay==10) delay=0;
  display(tran,wei);
  if(++wei==8) wei=0;
}

我解释“让DS1302时钟芯片运作,初值为题目要求的23时59分50秒(显示数据出错,如何修复我会在代码区注释说明)”BUG的修改过程:

数码管出现影子,一定是没有消影成功,但如蓝桥杯单片机模块代码(数码管)(代码+注释)_tuygre的博客-CSDN博客所说,我写了消隐部分,为何仍然有影子。很奇怪。错误的部分给那看看:

void display(u8 tran[],u8 wei)/*错误*/
{
  P0=0XFF;
  P2=P2&0X1F|0XE0;
  P2=P2&0X1F;
  P0=tran[wei];
  P2=P2&0X1F|0XE0;
  P2=P2&0X1F;
  P0=1<<wei;
  P2=P2&0X1F|0XC0;
  P2=P2&0X1F;
}

感觉几乎一样吧?再看看?位选段选位置反了,位选就在消影后,数字电路高低电平的那种一个时刻就转换好完全不实际,他其实在很短的时间内仍然有一定电平,为了给足时间,段选在位选后。

好了,第八届告一段落,读者可以复制代码,修改删除参数部分代码,感受我一些看似无用,实际让系统更加稳定的设置。如果你真正理解了,也可以帮我简化闹钟的LED闪烁。

时钟模块:蓝桥杯单片机模块代码(DS1302时钟芯片)(代码+注释)_tuygre的博客-CSDN博客_芯片代码 

头文件onewire有修改,具体看蓝桥杯单片机模块代码(DS18B20温度测量)(代码+注释)_tuygre的博客-CSDN博客

官方提供的原理图,用户手册等下载地址如下:

链接:https://pan.baidu.com/s/1y8lRYHxLKojL4_r0PZPYRw 
提取码:19so

注释无法插入图片,提到的相关信息读者自己在文件夹中寻找。

南京信息工程大学本科学生学习笔记,供大家参考。

如有错误,联系QQ3182097183。

物联沃分享整理
物联沃-IOTWORD物联网 » 第八届蓝桥杯单片机省赛代码分析

发表评论