一、比赛题目
本届蓝桥杯单片机难度相较于第 15 届有所提升,题目在细节方面较多。下面对相关难点及易错细节进行分析。
二、难点剖析
(一)超声波模块考察
超声波以及串口部分,是许多首次参加蓝桥杯的选手常常选择忽略的知识点。这就导致在比赛时,面对超声波模块不知如何使用。
(二)距离数据采集与运动状态判定
- 赛题要求设备间隔 1 秒采集距离数据,并依据连续两次采集的距离差值(ΔL)来判定运动状态。虽然连续两次采集对于大部分选手而言并非难事,但采集时间间隔的把控以及状态锁定机制成为难点。
- 运动状态更新与保持机制:当运动状态发生变化时,该状态会立即更新并锁定 3 秒。在这锁定的 3 秒期间,正常的距离采集功能依旧保持,但不会对运动状态变化进行判定。需要注意的是,运动状态改变后锁定 3 秒,并非 3 秒后就能立刻改变运动状态。实际上,3 秒后才开始采集距离差值,即在解锁后的第一秒初和末,根据这一秒采集时间内的距离差值来判断运动状态是否变化。所以,状态锁定的实际时间为 3 + 1 = 4 秒。从题目所给的运动状态更新图也能清晰看出,上电后并不立即采集,1 秒后开始采集,第二秒开始时判断距离差值,然后改变状态并锁定三秒,到第五秒结束,第六秒采集这一秒的距离差值并进行判断。由此可知,总共的锁定时间至少是 4 秒。
(三)光敏电阻获取环境光强数据
通过光敏电阻获取环境光强数据。需要注意的是,亮度等级越高表示环境越暗,在编写代码时无需进行反向逻辑处理。
(四)双按键长按考点
双按键长按这一考点在十四届国赛中曾出现过。对于没有做过国赛题的选手而言,往往难以写出双按键的相关代码。实现方法是在矩阵按键底层,通过行列扫描判断 P33 和 P32 这两个 IO 口是否同时为低电平,若同时为低电平,则表示 S8 和 S9 两个按键同时按下。
三、其他易错细节
- 超声波数码管显示时,采用高位补 0 的方式,而非高位熄灭。
- 设备间隔 1 秒采集距离数据,刚上电时不采集,1 秒后才开始进行采集操作。
- 当设备触发 “接近” 判定时,L1 - L4 指示灯需按照题目要求显示;在未触发 “接近” 判定的情况下,L1 - L2 - L3 - L4 指示灯全部熄灭。
- 进入参数界面时,LED、继电器状态会被锁定(不可变化),退出参数界面后取消状态锁定,这里的锁定意味着运动状态不改变。
- 数码管显示内容刷新需满足刷新≤0.1 秒的性能要求。
- 每次从运动检测界面切换到参数设置界面时,默认处于温度参数子界面。
- 在统计界面下,当 S8、S9 按键均按下且保持 2 秒以上时,可清零继电器吸合次数数值。需要注意的是,双按键操作仅在统计界面下有效。
四、赛题源码
以下是博主写的本届赛题的源码,代码仅供参考,不一定全对。
main.c
#include <STC15F2K60S2.H>
#include <smg.h>
#include <init.h>
#include <key.h>
#include <wave.h>
#include <led.h>
#include <onewire.h>
#include <iic.h>
#include <intrins.h>
#include <math.h>
unsigned char key_up,key_down,key_val,key_old,key_slow_down;
unsigned char smg_pos,smg_pont[8] = {0,0,0,0,0,0,0,0},smg_buf[8] = {10,10,10,10,10,10,10,10};
unsigned char smg_Slow_down;
unsigned char ledbuf[8] = {0,0,0,0,0,0,0,0};
unsigned char temperature;//温度变量
unsigned char cs_temperature = 30;//温度参数
unsigned char cs_jl = 30;//距离参数
unsigned char smg_mode;//0环境状态 1运动检测 2参数设置 3统计数据
unsigned char sport_flag = 0;//0静止 1徘徊 2跑动
unsigned char sport_flag_gb = 0;//判断状态是否改变
unsigned char light_level;//光强等级
unsigned char rd_wave;//超声波
unsigned char cj_wave;//采集距离超声波
unsigned char relay_date;//继电器吸合次数
unsigned int time_3s;
unsigned int time_1s;//
unsigned int change_rd_wave;//距离改变值
unsigned int time_2s;
unsigned char time_100ms;
bit relay_flag;
bit led_flag;//led闪烁标志位
bit jiejin;//1接近判断
bit high_temp;//高温判定
bit merue_flag;//采集标志位
bit start_cj_flag;//避免第一次采集
float ani1_Ad;//光敏
bit cs_flag;//0温度 1距离
bit sd_flag;//状态锁定标志位
bit qk_flag;
void Delay750ms(void) //@12.000MHz
{
unsigned char data i, j, k;
_nop_();
_nop_();
i = 35;
j = 51;
k = 182;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void key_proc()
{
if(key_slow_down<10) return;
key_slow_down = 0;
key_val = key_read();
key_down = key_val & (key_val ^ key_old);
key_up = ~key_val& (key_val ^ key_old);
key_old = key_val;
if(smg_mode==3)
{
if(key_old == 89)
{
qk_flag = 1;
if(time_2s>=2000)
{
relay_date = 0;
}
}
else
{
time_2s = 0;
qk_flag = 0;
}
}
switch(key_down)
{
case 4://界面切换
if(++smg_mode==4) smg_mode = 0;
cs_flag = 0;
break;
case 5://参数界面
if(smg_mode==2)
cs_flag ^= 1;
break;
case 8://+
if(smg_mode==2)
{
if(cs_flag==0) //温度参数界面
{
cs_temperature++;
if(cs_temperature==81) cs_temperature = 80;
}
else
{
cs_jl+=5;
if(cs_jl==85) cs_jl = 80;
}
}
break;
case 9://-
if(smg_mode==2)
{
if(cs_flag==0) //温度参数界面
{
cs_temperature--;
if(cs_temperature==19) cs_temperature = 20;
}
else
{
cs_jl-=5;
if(cs_jl==15) cs_jl = 20;
}
}
break;
}
}
void smg_proc()
{
if(smg_Slow_down<90) return;
smg_Slow_down = 0;
ani1_Ad = ad_Read(0x01)/51;
rd_wave = wave_Read();
temperature =read_temperature();
if(ani1_Ad>=3)
light_level = 0;
else if(ani1_Ad<3 && ani1_Ad>=2)
light_level = 1;
else if(ani1_Ad<2&&ani1_Ad>=0.5)
light_level = 2;
else if(ani1_Ad<0.5)
light_level = 3;
if(merue_flag==1)//运动状态采集
{
if(start_cj_flag==1)
change_rd_wave = abs(cj_wave - rd_wave);//变化距离
start_cj_flag = 1;
cj_wave = rd_wave;
merue_flag = 0;
}
if(change_rd_wave<cs_jl)//触发接近判定
jiejin = 1;
else
jiejin = 0;
if(cs_temperature<temperature)//触发高温判定
high_temp = 1;
else
high_temp = 0;
if(sd_flag==0)//可以改变状态
{
if(change_rd_wave<5) //静止
sport_flag = 0;
else if(change_rd_wave>=5&&change_rd_wave<10) //徘徊
sport_flag = 1;
else if(change_rd_wave>=10) //跑动
sport_flag = 2;
}
if(sport_flag != sport_flag_gb)
{
sd_flag = 1;//状态锁定
sport_flag_gb = sport_flag;
}
switch(smg_mode)
{
case 0://环境状态
smg_buf[0] = 11;//C
smg_buf[1] = temperature/10%10;
smg_buf[2] = temperature%10;
smg_buf[4] = 10;
smg_buf[5] = 10;
smg_buf[6] = 12;//n
smg_buf[7] = light_level+1;
break;
case 1://运动检测状态
smg_buf[0] = 13;//L
smg_buf[1] =sport_flag+1;
smg_buf[2] =10;
smg_buf[4] = 10;
smg_buf[5] = rd_wave/100%10;
smg_buf[6] = rd_wave/10%10;//n
smg_buf[7] = rd_wave%10;
break;
case 2://参数界面
smg_buf[0] = 14;//P
smg_buf[5] = smg_buf[2] = 10;
smg_buf[4] = 10;
if(cs_flag==0)
{
smg_buf[1] =11;//C
smg_buf[6] = cs_temperature/10%10;//
smg_buf[7] = cs_temperature%10;
}
else
{
smg_buf[1] =13;//L
smg_buf[6] = cs_jl/10%10;//
smg_buf[7] = cs_jl%10;
}
break;
case 3://数据统计界面
smg_buf[0] = 12;//n
smg_buf[1] = 11;//c
smg_buf[2] = 10;
smg_buf[3] = 10;
smg_buf[4] =relay_date/1000? relay_date/1000%10:10;
smg_buf[5] =relay_date/100? relay_date/100%10:10;
smg_buf[6] = relay_date/10?relay_date/10%10:10;//n
smg_buf[7] = relay_date%10;
break;
}
}
void led_proc()
{
unsigned char i;
if(smg_mode!=2)
{
if(jiejin==1)//触发接近判定 灯亮
{
ledbuf[0] = 1;
ledbuf[1] = (light_level==0)?0:1;
ledbuf[2] = (light_level>=2)?1:0;
ledbuf[3] = (light_level==3)?1:0;
}
else
{
for(i=0;i<5;i++)
ledbuf[i] = 0;
}
switch(sport_flag)
{
case 0: //静止
ledbuf[7] = 0;
break;
case 1: //徘徊
ledbuf[7] = 1;
break;
case 2://跑动
ledbuf[7] = led_flag;
break;
}
if(high_temp==1&&jiejin==1)
{
relay(1);
if(relay_flag==0)
relay_date++;
relay_flag = 1;
}
else
{
relay_flag = 0;
relay(0);
}
}
}
void Timer0_Init(void) //1??@12.000MHz
{
AUXR &= 0x7F; //?????12T??
TMOD &= 0xF0; //???????
TL0 = 0x18; //???????
TH0 = 0xFC; //???????
TF0 = 0; //??TF0??
TR0 = 1; //???0????
ET0 = 1;
EA = 1;
}
void timer0server() interrupt 1
{
key_slow_down++;
smg_Slow_down++;
if(++smg_pos==8) smg_pos = 0;
smg_disp(smg_pos,smg_buf[smg_pos],smg_pont[smg_pos]);
disp_led(ledbuf);
if(++time_1s == 1000)
{
time_1s = 0;
merue_flag = 1;//开始采集
}
if(sd_flag==1)//锁定 计时
{
if(++time_3s==4000)
{
sd_flag = 0;//解锁
time_3s = 0;
}
if(qk_flag==1)
{
time_2s++;
if(time_2s>2000) time_2s = 2001;
}
}
if(sport_flag==2)
{
if(++time_100ms==100)
{
time_100ms = 0;
led_flag ^= 1;
}
}
}
void main()
{
read_temperature();
Delay750ms();
Timer0_Init() ;
system_init();
while(1)
{
led_proc();
key_proc();
smg_proc();
}
}
led.c
#include <led.h>
unsigned char temp = 0x00;
unsigned char temp_old = 0xff;
void disp_led(unsigned char *ledbuf)
{
temp = 0x00;
temp = (ledbuf[0]<<0) | (ledbuf[1]<<1) |(ledbuf[2]<<2) |(ledbuf[3]<<3) |(ledbuf[4]<<4) |
(ledbuf[5]<<5) |(ledbuf[6]<<6) |(ledbuf[7]<<7) ;
if(temp != temp_old)
{
P0 = ~temp;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
temp_old = temp;
}
}
void relay(bit flag)
{
temp = 0x00;
if(flag)
temp |= 0x10;
else
temp &= 0x10;
if(temp != temp_old)
{
P0 = temp;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
temp_old = temp;
}
}
smg.c
#include <smg.h>
unsigned char code smg_dula [] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xc6,0xc8,0xc7,0x8c};
unsigned char code smg_wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
void smg_disp(unsigned char wela,dula,point)
{
P0 = 0xff;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
P0 = smg_wela[wela];
P2 = P2 & 0x1f | 0xc0;
P2 &= 0x1f;
P0 = smg_dula[dula];
if(point)
P0 &= 0x7f;
P2 = P2 & 0x1f | 0xe0;
P2 &= 0x1f;
}
init.c
#include <init.h>
void system_init()
{
P0 = 0xff;
P2 = P2 & 0x1f | 0x80;
P2 &= 0x1f;
P0 = 0x00;
P2 = P2 & 0x1f | 0xa0;
P2 &= 0x1f;
}
key.c
#include <key.h>
unsigned char key_read()
{
unsigned char temp = 0;
P44 = 0; P42 = 1; P35 =1; P34 = 1;
if(P33==0) temp = 4;
if(P32==0) temp = 5;
if(P31==0) temp = 6;
if(P30==0) temp = 7;
P44 = 1; P42 = 0; P35 =1; P34 = 1;
if(P33==0) temp = 8;
if(P32==0) temp = 9;
if(P33==0&&P32==0) temp = 89;
return temp;
}
超声波 wave.c
#include <wave.h>
#include <intrins.h>
sbit Tx = P1^0;
sbit Rx = P1^1;
void Delay12us(void) //@12.000MHz
{
unsigned char data i;
_nop_();
_nop_();
i = 37;
while (--i);
}
void wave_init()
{
unsigned char i;
EA = 0;
for(i=0;i<8;i++)
{
Tx = 1;
Delay12us();
Tx = 0;
Delay12us();
}
EA = 1;
}
unsigned char wave_Read()
{
unsigned int temp;
CMOD = 0x00;
CH = CL = 0;
wave_init();
CR = 1;
while((Rx==1)&&(CF == 0));
CR = 0;
if(CF == 0)
{
temp = CH << 8 | CL;
return temp * 0.017;
}
else
{
CF = 0;
return 0;
}
}