蓝桥杯单片机学习记录6——第12届省赛真题练习
前言
上一篇我们练习了第10届的省赛题,主要是为了练习NE555的使用方法及调试。
这次我们接着来练习第12届的省赛题,这一届的题目也不难,同样用了NE555,主要是复习NE555的使用方法。同时还用到了按键长短触发,在13届的题目里有遇到过,当时并没有解决,但这次我们来实现它。
赛题要求
硬件框图
从这张图我们可以知道这个题目要用到哪些外设,同时也是我们写程序的第一步。
先将硬件程序写好并调试无误,为我们后面写功能要求提供保障,减少debug的时间。
功能要求
- 显示功能
- 按键功能
- LED 指示灯功能
- 初始状态说明
处于频率显示界面。
LED 指示灯功能开。
设计思路
虽然省赛题不难,但是要完成所有要求也不是那么容易的,主要就是要理清楚功能之间的联系。
题目已经把个个外设的功能要求都罗列出来了,在写的过程中,我们也是按照顺序将这些要求分模块完成,再找到之间的联系拼接起来。
框图在实际的练习过程中我并没有制作,是在写文章的时候临时做的。但我们在做的时候,脑子里应该要有一个大概的框图。
实现代码
这里就不再放框图了,直接看代码吧。重点是主要的功能实现部分,底层的驱动代码就不会放太多了。
main.c
- 包含头文件及变量
#include <STC15F2K60S2.H>
#include "hardware.h" //外设底层实现文件
#include "iic.h"
typedef unsigned char u8;
typedef char i8;
typedef unsigned int u32;
typedef int i32;
u32 adc,KeyTick;
u32 NE_f=0,count_f=0,pre_adc=0,pre_f=0;
u8 KeyNum,led,keylong,KeyLock;
u8 Page=0,channel=0x01;
bit VoltFlag=0,LEDFlag=0;
- main函数
void main()
{
InitInterrupt(); //定时器中断初始化
P_Init(); //P0控制外设初始化
while(1)
{
Data_Task();
Key_Task();
Logic_Task();
Display();
}
}
-定时器中断任务函数
void InitInterrupt(void)
{
TMOD = 0x16; //定时器0用于计数器
TH0 = 0xFF;
TL0 = 0xFF;
TH1 = (65535 - 49999) / 256; //定时器1用于定时
TL1 = (65535 - 49999) % 256;
AUXR |= 0X04; //定时器2用于定时
T2L = 0X20;
T2H = 0XD1;
AUXR |= 0X10;
IE2 |=0X04;
ET0 = 1;
ET1 = 1;
TR0 = 1;
TR1 = 1;
EA = 1;
}
//NE555功能实现
void Timer0() interrupt 1
{
++NE_f;
}
void Timer1() interrupt 3
{
static u8 cnt;
TH1 = (65535 - 49999) / 256;
TL1 = (65535 - 49999) % 256;
++cnt;
if(cnt >= 20)
{
count_f = NE_f;
NE_f = 0;
cnt = 0;
}
}
- 数据更新函数
void Data_Task(void)
{
adc = PFC_ADC(channel); //adc获取数字信号
adc = adc*100/51; //将0~255配置为0~500
KeyNum = Key(); //获取按键值
Key_Long(); //长按操作
}
- 按键触发任务函数
void Key_Task(void)
{
//S4按键功能切换显示页面
if(KeyNum == 1)
{
++Page;
if(Page>=3) Page=0;
}
//S5按键功能切换电压输出通道
if(KeyNum == 2)
{
VoltFlag = ~VoltFlag;
}
//S6按键功能记录当前通道3输出的电压值
if(KeyNum == 3)
{
if(channel == 0x03)
{
pre_adc = PFC_ADC(0x03)*100/51;
}
}
//S7按键功能记录当前频率输出值
if(KeyNum == 4)
{
pre_f = count_f;
}
}
- 逻辑功能函数
void Logic_Task(void)
{
//选择电压通道
if(VoltFlag) channel = 0x03;
else channel = 0x01;
//每次进入电压页面都显示通道1的电压值
if(Page==1) {VoltFlag=0;channel = 0x01;}
//长按时间超过1s,标志位翻转,计数复位
if(KeyTick>=1000)
{
LEDFlag = ~LEDFlag;
KeyTick=0;
}
if(LEDFlag) //标志位为1时,LED显示关闭,为0时,显示打开
{
led &= ~0xff;
LED_Task(led);
}
else
{
LED_Logic();
}
}
//LED显示逻辑函数
void LED_Logic(void)
{
if(channel == 0x03)
{
if(adc>pre_adc)
led |= 0x01;
else
led &= ~0x01;
}
if(count_f>pre_f)
led |= 0x02;
else
led &= ~0x02;
if(Page == 0) led |= 0x04;
else led &= ~0x04;
if(Page == 1) led |= 0x08;
else led &= ~0x08;
if(Page == 2) led |= 0x10;
else led &= ~0x10;
LED_Task(led);
}
- 数码管显示函数
void Display(void)
{
switch(Page)
{
case 0: //频率显示页面
Nixie(1,20);
if(count_f>9999)
Nixie(4,count_f/10000%10);
else
Nixie(4,24);
if(count_f>999)
Nixie(5,count_f/1000%10);
else
Nixie(5,24);
if(count_f>99)
Nixie(6,count_f/100%10);
else
Nixie(6,24);
Nixie(7,count_f/10%10);
Nixie(8,count_f%10);
break;
case 1: //周期显示页面
Nixie(1,21);
Nixie(7,5);
Nixie(8,0);
break;
case 2: //电压显示页面
Nixie(1,22);
Nixie(2,23);
if(channel == 0x01)
Nixie(3,1);
else if(channel == 0x03)
Nixie(3,3);
Nixie(6,adc/100%10+10);
Nixie(7,adc/10%10);
Nixie(8,adc%10);
break;
}
}
按键长短按触发
按键长按功能的实现我用了定时器2来计时,可以防止与NE555功能冲突,提高长按和频率测量的精度。
void Key_Long(void)
{
u8 keynumber=0;
if(!P30) keynumber = 4;
if(keynumber == 4)
{
KeyLock = 1;
if(KeyTick>10)
{
keylong = keynumber;
}
else
keylong = 0;
}
else
{
KeyLock = 0;
keylong = 0;
KeyTick = 0;
}
}
void Timer2() interrupt 12
{
if(KeyLock == 1)
{
KeyTick++;
}
else if(KeyLock == 0)
{
KeyTick = 0;
}
}