蓝桥杯单片机学习记录4——第11届省赛真题练习2
前言
上一篇我们练习了第11届的省赛题1,这里我们接着来讲题2。
赛题要求
硬件框图
这一题的难度明显比上一题要难,虽然外设没用多少,但要求是挺多的,更加考验逻辑分析能力。
区别于上一篇的按键,这里用的是JBD矩阵按键。
还有EEPROM和ADC功能。
这里有个有意思的点,虽然省赛会用BTN和JBD的其中一种,但是都只用了4个按键,也算是简单化了吧。
- 显示功能
- 按键功能
- LED 指示灯功能
- 初始状态说明
初始状态上电默认处于数据显示界面,计数值为 0,指示灯 L2 熄灭。
设备上电后,应自动从 E2PROM 内部地址 0 读出数据,并将该数据处理为电压参数 VP。
设计思路
功能逻辑框图
代码实现框图
这里的框图我就不放了,可以参考上一篇的,因为都差不多,都是四线一区的框架。当然,你也可以试着自己去做一个框图,那我也就不多赘述了。
代码分析
main.c
#include <STC15F2K60S2.H>
#include "hardware.h"
#include "iic.h"
bit StateFlag=0,VcntFlag=0,addFlag=0,minusFlag=0;
bit cntFlag=0,romFlag=0,overFlag=0;
u8 KeyNum,led,mode=0,Page=0,Vcnt=0;
u32 adc; //adc的值是0~500,这样可以方便计算,所以用无符号整型的变量
i32 Vp=300;
void main()
{
InitIniterrupt(); //定时器初始化
Initsystem(); //初始化外设,关闭继电器和蜂鸣器
Vp = AT24C02_Read(0)*10; //每次上电后都读取EEPROM地址0处的值赋给Vp
while(1)
{
Date_Task(); //数据更新函数
Key_Task(); //按键任务函数
Logic_Task(); //逻辑任务函数
Display(); //数码管显示任务函数
}
}
- 定时器中断函数
void InitIniterrupt(void)
{
TMOD = 0x01;
TH0 = (65535 - 10000) / 256;
TL0 = (65535 - 10000) % 256;
TR0 = 1;
ET0 = 1;
EA = 1;
}
void Timer0() interrupt 1 //10ms中断进入
{
static u32 cnt;
TH0 = (65535 - 10000) / 256;
TL0 = (65535 - 10000) % 256;
if(overFlag) //当参数电压过大标志位打开时,开始计时
{
cnt++;
if(cnt >= 500) //5s计时,L1打开,计时复位
{
led |= 0x01; //用或操作可以保留上一个LED状态,使不同的LED逻辑不受干扰
LED_Task(led);
cnt = 0;
}
}
}
- 数据更新函数
void Date_Task(void)
{
KeyNum = MatrixKey();
adc = ADC_Read(0x03)*5; //将0~255的电压值转换为0~500的电压值,方便显示
if(cntFlag) //当计数标志位打开,计数自加,复位标志位,计数最大值为99
{
++Vcnt;
cntFlag=0;
if(Vcnt > 99) Vcnt = 99;
}
}
- 按键任务函数
void Key_Task(void)
{
if(KeyNum == 9) //S12
{
StateFlag = 1; //状态标志位打开
}
if(KeyNum == 10) //S13
{
if(mode==2) //为计数页面时
VcntFlag = 1; //计数复位标志位打开
}
if(KeyNum == 13) //S16
{
if(mode==1) //为参数页面时
addFlag = 1; //加1标志位打开
}
if(KeyNum == 14) //S17
{
if(mode==1)
minusFlag = 1;
}
}
- 数码管显示函数
void Display(void)
{
switch(mode) //0为数据页面,1为参数页面,2为计数页面
{
case 0:
Nixie(1,16);
Nixie(6,adc/100%10+10);
Nixie(7,adc/10%10);
Nixie(8,adc%10);
break;
case 1:
Nixie(1,17);
Nixie(6,Vp/100%10+10);
Nixie(7,Vp/10%10);
Nixie(8,Vp%10);
break;
case 2:
Nixie(1,18); //这里题目应该有问题,要求显示N,但是数码管无法显示,这里我显示了H
Nixie(7,Vcnt/10);
Nixie(8,Vcnt%10);
break;
}
}
- 逻辑任务函数
//页面状态函数
void Page_State(void)
{
if(StateFlag) //状态标志位打开,选择模式
{
switch(mode)
{
case 0:mode=1;StateFlag=0;break;
case 1:mode=2;StateFlag=0;break;
case 2:romFlag=1;mode=0;StateFlag=0;break; //在退出参数页面时rom标志位打开
}
}
}
//LED逻辑函数
void LED_Logic(void)
{
if(adc < Vp) overFlag = 1; //数字电压量小于参数电压时,参数电压过大标志位打开
else if(adc >= Vp) {overFlag = 0;led &=~0x01;LED_Task(led);} //与操作使当前状态的LED复位,不影响其他状态的LED
if(Vcnt % 2) {led |= 0x02;LED_Task(led);} //计数为奇数时L2点亮
else if(Vcnt % 2==0) {led &= ~0x02;LED_Task(led);}
}
void Logic_Task(void)
{
Page_State();
if(adc == Vp) cntFlag = 1; //当数字电压量等于参数电压时,计数标志位打开,不等于则关闭
if(adc != Vp) cntFlag = 0;
if(VcntFlag) //计数清零
{
Vcnt = 0;
VcntFlag = 0;
}
if(addFlag) //Vp+0.5,超过5回0
{
Vp = Vp + 50;
addFlag = 0;
if(Vp > 500) Vp = 0;
}
if(minusFlag) //Vp-0.5,低于0回5
{
Vp = Vp - 50;
minusFlag = 0;
if(Vp < 0) Vp = 500;
}
if(romFlag) //当rom标志位打开时,将参数电压缩小10倍写入EEPROM地址0,复位标志位,因为Vp范围超过255,无符号字符型存不下,所以要缩小
{
AT24C02_Wirte(0,Vp/10);
romFlag = 0;
}
LED_Logic();
}
这一篇也是只讲main.c的内容,其余部分不过多介绍。当然,如果有疑问也可以在评论区或私信留言。
小结
这次的真题我也是完成了大部分的要求,其实还有一个计数按错按键的任务要求,我尝试了一下,有点麻烦。若你有不错的想法和思路,欢迎讨论。
好了!到这里我就已经完成了3届的省赛真题了,那么离蓝桥杯比赛也挺近了,真题我还是会接着写,但也不会写太多,主要挑一些没用过的外设写。原因就是省赛题难度真的不是很高,要求使用的外设也就那几个,功能要求的也就那一种框架,只要会了这种框架的模板,再把外设加进去就可以了,至少完成80%的功能要求是轻轻松松的。
最后,如果我的代码和思路对你有帮助,别忘了点赞关注哦 >_< !