首先,先讲一下我编写代码的流程(后以此为例,不再重复):
无论是省赛还是国赛,都会发现数码管显示及矩阵按键是必不可少的,所以我的第一步是还原动态数码管的内容,通过给org数组赋值,让数码管显示我想要显示的内容。确保这部分无误后,还原矩阵按键模块,通过按键改变org数组内的内容,改变数码管显示,如与预期相同,在进行下一步。我对我完成这道省赛的时间做一个展示,希望能对你起到帮助。
分析题目,是否需要数码管和矩阵按键 | 约2分钟 |
让数码管显示1234.5678 | 约20分钟 |
通过矩阵按键改变数码管的显示内容 | 约20分钟 |
阅读题目,理解系统需求 | 约10分钟 |
针对S4按键,修改模式标志 | 约2分钟 |
针对S5按键,修改时间,开启倒计时(完成编写后出现两个BUG:1.逻辑错误,时间直接从0变为2分钟,从未出现1分钟的显示 2.修改完1问题后矩阵按键按下一次识别多次,加入delay降低识别速度) | 约30分钟(大部分为修改BUG) |
针对S9按键,完成清除时间功能 | 约2分钟 |
完成不同模式下的LED灯亮灭(完成编写后出现一个BUG:LED等有闪烁现象,原因经分析为中断抢占P2的使用。这个问题我从底层分析,说起来容易,实际上很多人根本找不到原因,必须要很理解原理,不断临时废除部分代码才能找出,具体内容我们下面再说) | 约40分钟(绝大部分为修改BUG) |
完成PWM输出(此处还增加了LED,因为周围没有示波器,又要现象可被观察) | 约15分钟 |
针对S8,增加温度模块(完成编写后出现一个BUG:按键有失灵的情况,经过分析,读取温度在delay参数的控制之外,又放慢了矩阵按键的识别速度,我选择让读取温度在delay参数的控制之内) | 约30分钟(大部分为修改BUG) |
再次审题,检测修饰功能(比如count=0这样的小细节,使得系统更加精准) | 约10分钟 |
这样的时间还是挺可以的,剩余时间超过1小时,客观题时间再长,我们时间也充足。一个一个功能的实现,立刻烧录检测是否存在问题,这样的习惯可以更好地解决一些问题。但与同学们交流以后发现很多同学交流后发现,他们习惯于将整体代码全部书写完毕以后直接烧录,然后进行代码的改错、修饰,但是我想说很多BUG是你预测不到的,如果一起写之后再修改,除非你对很多问题已经非常熟悉,又或者你的分析能力超群,否则一定会陷入迷茫。个人感觉自己的水平在中上吧,点对点的修改BUG时间都占了编程的约一半时间。初学者,个人认为,一步步脚踏实地,最好。
下面我们进入代码编程,整体看完后,我在讲讲我BUG的修改思路(函数后有“/*相同*/”代表与之前模块的内容几乎相同【此处默认你是跟着我写的文章一步步学的】。因为自己在写系统时不会看之前的模块,所以参数名字会有区别,其他几乎相同。):
#include "STC15F2K60S2.H"
#include "stdio.h"
#include "onewire.h"/*与我写的模块相同,与官方不同*/
typedef unsigned int u16;
typedef unsigned char u8;
u8 org[10],tran[9],wei,old,new,mode=1,time,dis,led,delay,flag,count1,low,high,*low1,*high1;
u16 count,te;/*需要16位的数字用无符号整型!!!*/
sfr clock = 0x8f;/*为打开PWM输出做准备*/
void lighten(u8 led)/*相同*/
{
P0=0XFF;
P2=P2&0X1F|0X80;
P2=P2&0X1F;
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 open()/*多打开了一个定时器1*/
{
EA=1;
ET0=1;
ET1=1;
}
void Timer1Init(void)/*相同*/ //1毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x20; //设置定时初值
TH1 = 0xD1; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
}
void translate(u8 org[],u8 tran[])/*相同*/
{
u8 j,k,mid0;
for(j=0,k=0;j<8;j++,k++)
{
switch(org[k])
{
case'0':mid0=0xc0;break;
case'1':mid0=0xf9;break;
case'2':mid0=0xa4;break;
case'3':mid0=0xb0;break;
case'4':mid0=0x99;break;
case'5':mid0=0x92;break;
case'6':mid0=0x82;break;
case'7':mid0=0xf8;break;
case'8':mid0=0x80;break;
case'9':mid0=0x90;break;
case'-':mid0=0xbf;break;
case'c':mid0=0xc6;break;
default:mid0=0xff;
}
if(org[k+1]=='.')
{
mid0&=0x7f;
k++;
}
tran[j]=mid0;
}
}
u8 key_translate()/*相同*/
{
u16 key;
u8 key_return;
P34=0;P35=1;P42=1;P44=1;
key=(key<<4)+(P3&0x0f);
P34=1;P35=0;P42=1;P44=1;
key=(key<<4)+(P3&0x0f);
P34=1;P35=1;P42=0;P44=1;
key=(key<<4)+(P3&0x0f);
P34=1;P35=1;P42=1;P44=0;
key=(key<<4)+(P3&0x0f);
switch(~key)
{
case 0x1000:key_return=19;break;
case 0x2000:key_return=18;break;
case 0x4000:key_return=17;break;
case 0x8000:key_return=16;break;
case 0x0100:key_return=15;break;
case 0x0200:key_return=14;break;
case 0x0400:key_return=13;break;
case 0x0800:key_return=12;break;
case 0x0010:key_return=11;break;
case 0x0020:key_return=10;break;
case 0x0040:key_return=9;break;
case 0x0080:key_return=8;break;
case 0x0001:key_return=7;break;
case 0x0002:key_return=6;break;
case 0x0004:key_return=5;break;
case 0x0008:key_return=4;break;
default:key_return=0;
}
return key_return;
}
void Timer0Init(void)/*相同*/ //100微秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x50; //设置定时初值
TH0 = 0xFB; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
void keydo()
{
new=key_translate();
if(new!=old&&new!=0)
{
if(new==4)
{
if(++mode==4) mode=1;/*三种模式来回切换*/
switch(mode)
{
case 1:flag=2;count1=0;break;/*flag用来控制占空比*/
case 2:flag=3;count1=0;break;
case 3:flag=7;count1=0;break;
}
}
if(new==5)
{
time+=60;/*调整时间*/
if(time>=180) time=0;
else if(time>=120) time=120;
else time=60;
count=0;
}
if(new==9) time=0;/*时间归0*/
if(new==8) if(++dis==2) dis=0;/*dis区分显示内容*/
}
old=new;
}
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;
}
void disled()/*LED显示*/
{
if(time==0) led=0;/*时间为0,关闭灯*/
else
switch(mode)/*根据模式,点亮相应灯。注意同时要熄灭其他灯*/
{
case 1:led=led&0xf8;led=led|0x01;break;
case 2:led=led&0xf8;led=led|0x02;break;
case 3:led=led&0xf8;led=led|0x04;break;
}
}
void main()
{
low1=&low;high1=&high;
clock=clock|0x01;/*打开PWM输出,本系统通过P34,也就是定时器0进行输出,《用户手册》488页*/
close();
rd_temperature(low1,high1);
Timer1Init();
Timer0Init();
open();
while(1)
{
disled();
translate(org,tran);
if(time) lighten(led);
if(!delay)
{
keydo();
rd_temperature(low1,high1);
delay=1;
}
if(dis==0) sprintf(org,"-%1u- %4u",(u16)mode,(u16)time);
if(dis==1)
{
te=(low+high*256);
sprintf(org,"-4- %2uc",(u16)(te/16.0));
}
}
}
void time0() interrupt 1
{
if(++wei==8) wei=0;
display(tran,wei);
if(time!=0) lighten(led);
else lighten(0);
if(++count1==10) count1=0;
if(time!=0&&count1<=flag)/*1KHZ信号,周期就是1毫秒,自然风为例,就需要300微秒高电平,700微秒低电平,分析完所有模式之后,决定以100微秒为基本单位,进行编程*/
{
P34=1;
led=led|0x80;
}
else
{
P34=0;
led=led&0x7f;
}
}
void time1() interrupt 3
{
if(++delay==10) delay=0;
count++;
if(count==1000)
{
count=0;
if(time>0) time--;/*倒计时*/
}
}
为了方便代码的理解,我将参数的意义再次做成表格:
mode | 初值为1,记录的是系统工作模式 |
time | 系统剩余工作时间。为0时代表系统停止工作,PWM输出停止,灯不亮,倒计时停止。所以在多处限制函数运行。 |
dis | 显示内容,决定是显示温度还是工作状态。 |
delay | 降速,判断时间间隔延长可有效防止矩阵按键多次识别(有时例如竞争冒险等还是会影响输出的结果)。 |
flag | 限制PWM(本系统选择P34,定时器0)高低电平每个周期占空比。 |
count1 | 依据flag,判断高低电平是否需要改变。 |
count | 倒计时用。 |
最后我解释BUG的修改过程:
简单的BUG就不做解释了,跟着自己的代码走就能发现的错误(也就是逻辑错误),完全不足为惧!我选择两个讲解。
文章开头表格中提到:“针对S8,增加温度模块(完成编写后出现一个BUG:按键有失灵的情况,经过分析,读取温度在delay参数的控制之外,又放慢了矩阵按键的识别速度,我选择让读取温度在delay参数的控制之内)。”怎么发现的呢?当我显示温度后,矩阵按键识别很慢,但显示系统工作状态时矩阵按键又正常,所以一定是按下S8后的问题,在先前的S8中,我有识别温度的函数,也就是每次main函数while(1)执行一次,因为S8按下,都要执行读取温度函数,而温度模块又是单线,相比速度低,导致keydo执行的周期瞬间提高。我选择降低执行读取温度函数频率,受delay控制。当然有很多种方法啦,我只选我最先想到的。
文章开头表格中提到:“完成不同模式下的LED灯亮灭(完成编写后出现一个BUG:LED等有闪烁现象,原因经分析为中断抢占P2的使用。这个问题我从底层分析,说起来容易,实际上很多人根本找不到原因,必须要很理解原理,不断临时废除部分代码才能找出,具体内容我们下面再说)”
数码管和led的锁存器最终都是通过P2选择的,所以如果一个位于主函数(lighten函数),一个位于中断(display函数)就会相互干扰。原理可以看:
蓝桥杯单片机模块代码(LED)(代码+注释)_tuygre的博客-CSDN博客
代码里有完整的解释。
头文件onewire有修改,具体看蓝桥杯单片机模块代码(DS18B20温度测量)(代码+注释)_tuygre的博客-CSDN博客
其实修BUG才是比赛的难度所在,这需要独立分析的能力,有时还需要基础知识,理解原理。不行就多练吧!之后每周至少更新一期省赛,希望关注!
官方提供的原理图,用户手册等下载地址如下:
链接:https://pan.baidu.com/s/1y8lRYHxLKojL4_r0PZPYRw
提取码:19so
注释无法插入图片,提到的相关信息读者自己在文件夹中寻找。
南京信息工程大学本科学生学习笔记,供大家参考。
如有错误,联系QQ3182097183。