【提分必看!】蓝桥杯单片机提分技巧(国一经验分享)_蓝桥杯单片机组-爱代码爱编程
前言
本人在(2024年)第十五届蓝桥杯竞赛单片机赛道中取得了全国一等奖的成绩。本文我将会和读者们分享蓝桥杯单片机竞赛可能会用到的提分技巧,帮读者们叩开国一的大门!
官方资料获取(已整合):
蓝桥杯单片机资源包(含指导手册和考点大纲)
优质教程推荐:
蚂蚁工厂科技蓝桥杯单片机教程
小蜜蜂蓝桥杯单片机基础技能与进阶强化教程
其他相关文章:
【国一超全代码分享!】蓝桥杯单片机各模块代码整合
PCA可编程计数器阵列
IAP15F2K61S2单片机只有定时器0、定时器1和定时器2三个定时器,有时我们会遇到定时器资源不够用的情况:NE555频率测量占用了定时器0,超声波测距占用了定时器1,定时器2用作了串口波特率发生器。这时我们会发现没有定时器进行周期计数为我们的业务逻辑提供(周期)中断服务。为了解决这个问题,我们要启动单片机中的PCA模块。
这里简单介绍一下PCA:
PCA(可编程计数器阵列Programmable Counter Array)有5个16位的捕获/比较模块与之相连,由高字节(PCA0H)和低字节(PCA0L)组成。
在这里我们只需要知道:PCA可以充当16位重装载的“定时器”使用。
PCA_Timer.c
// PCA初始化
void PCA_TimerInit(void)
{
// 这些只能背!
CMOD |= 0x01;//初始化为系统时钟12分频 允许CCON中的CF中断
CCON &= 0x00;// CF=0;CR=0;
CL=0x18;
CH=0xFC; // 这里的值根据需要的定时时间手动设置
CR=1;// 开启计数
}
//中断回调函数
void PCA_isr() interrupt 7
{
if(CF)
{
CF=0;// 需要手动(软件)清零溢出标志位
CL=0x18;// 需要手动重装载,定时器是自动重装载
CH=0xFC;// 需要手动重装载,定时器是自动重装载
// 下面添加自己的代码
}
}
PCA_Timer.h
#include "STC15F2K60S2.H"
void PCA_TimerInit(void);
按照以上操作,我们就可以把PCA当做定时器来用,这样就可以很大程度上解决编程过程中“定时器不够用”的问题。
按键状态检测
按键的底层代码参考:【国一超全代码分享!】蓝桥杯单片机各模块代码整合
// 声明四种状态
unsigned char Key_Value; // 按键当前状态,0为低电平,1为高电平
unsigned char Key_Down; // 下降沿
unsigned char Key_Up; // 上升沿
unsigned char Key_Old; // 按键的上一状态,0为低电平,1为高电平
//
Key_Value = Read_KBD(); // 读取按键当前状态(这里KBD对应矩阵键盘)
Key_Down = Key_Value&(Key_Old^Key_Value);
Key_Up = ~Key_Value&(Key_Old^Key_Value);
Key_Old = Key_Value;
两种计时按键
近两年赛题都会涉及到按键长短按的考点,下面提供一个写法给大家。
// 注意tick和flag,最好都创建独立的,避免冲突
// 按下按键超过时间,LED直接亮
// Key_Value:检测按键保持高电平或低电平
// Key_Up:检测按键上升沿(即按下到松开的那一瞬间)
if(Key_Value==4 && led1_flag==0)
{
if(led1_tick++>2000)
{
led1_tick=0;
led1_flag=1;
ucLed^=0xF0;
}
}
else
{
if(led1_flag==0) led1_tick=0;
else if(led1_flag==1 && Key_Up==4) led1_flag=0;
}
// 按下按键超过时间,松开了按键,LED才亮
if(Key_Value==5 && led2_flag==0)
{
if(led2_tick++>2000)
{
led2_tick=0;
led2_flag=1;
}
}
else
{
if(led2_flag==0) led2_tick=0;
else if(led2_flag==1 && Key_Up==5)
{
led2_flag=0;
ucLed^=0xF0;
}
}
PWM输出逻辑
十三届国赛真题(如果没有记错的话)出了一次PWM输出的考点,要求某个引脚输出频率为1kHz,占空比为20%的PWM信号。之后大家在做到这道题的时候会发现,输出PWM信号并不难,但是输出PWM的代码一旦没写好,就很容易影响到其他模块(比如影响到温度读取或者时钟显示等)。以下是我的处理方法,通过这种处理能正常输出PWM信号,也可以不影响其他模块的正常使用。
if(pwm_flag==1)
{
pp2_flag=0;
if(pwm_tick<8)
{
if(pp1_flag==0)
{
pp1_flag=1;
P0|=0x20;
P2=P2&0x1F|0xA0;
P2=P2&0x1F;
}
}
else
{
if(pp1_flag==1)
{
pp1_flag=0;
P0|=0x00;
P2=P2&0x1F|0xA0;
P2=P2&0x1F;
}
}
if(++pwm_tick==10) pwm_tick=0;
}
else if(pwm_flag==0)
{
pp1_flag=0;
if(pwm_tick<2)
{
if(pp2_flag==0)
{
pp2_flag=1;
P0|=0x20;
P2=P2&0x1F|0xA0;
P2=P2&0x1F;
}
}
else
{
if(pp2_flag==1)
{
pp2_flag=0;
P0|=0x00;
P2=P2&0x1F|0xA0;
P2=P2&0x1F;
}
}
if(++pwm_tick==10) pwm_tick=0;
}
外部中断(以外中断0为例)
// 外部中断(这两句要写在main中)
IT0 = 0; //设置INT0的中断类型 (1:仅下降沿 0:上升沿和下降沿)
EX0 = 1; //使能INT0中断
//-----------------------------------------
//中断服务程序
void exint0() interrupt 0 //INT0中断入口 INT0对应的应该是按键S5
{
// 下面写要执行的任务
ucLed ^= 0xff;
}
数码管闪烁
sprintf(seg_string,"P1 %2d"(unsigned int)dist);
if(shan_tick<2) // 200ms亮灭闪烁
{
seg_string[6]=' ';
seg_string[7]=' ';
}
if(++shan_tick==4) shan_tick=0;