第八届蓝桥杯单片机省赛代码分析
按照之前的模式,我列一下我完成这个省赛的时间表,希望能对你起到帮助!
分析题目,是否需要数码管和矩阵按键 | 约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。