蓝桥杯
小蜜蜂
#include "stc15f2k60s2.h"
// 定义LED打开
#define LED(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0x80; \
P2 = P2 & 0x1f; \
}
// 以位数来定义第1、2至6个灯,注意,后面点亮要取反,因为是低电平有效
#define L1 0x01
#define L2 0x02
#define L3 0x04
#define L4 0x08
#define L5 0x10
#define L6 0x20
// 独立按键定义
sbit k7 = P3 ^ 0;
sbit k6 = P3 ^ 1;
sbit k5 = P3 ^ 2;
sbit k4 = P3 ^ 3;
typedef unsigned char uint8_t; // 自定义类型
void Timer0_Init(void) // 1毫秒@12.000MHz
{
AUXR |= 0x80; // 定时器时钟1T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0x20; // 设置定时初始值
TH0 = 0xD1; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
EA = 1;
}
uint8_t selectedValue; // S6、S7选择键值
uint8_t controlValue; // S5、S4控制键值
uint8_t selectEnableS6 = 0; // S6选中
uint8_t selectEnableS7 = 0; // S7选中
uint8_t ledEnableS6 = 0; // 因为S7按一下点亮L1,再按一次熄灭L1,所以用一个变量来操作状态
uint8_t ledEnableS7 = 0; // 因为S6按一下点亮L2,再按一次熄灭L2,所以用一个变量来操作状态
uint8_t ledEnableS4 = 0; // S4控制L4,一般理解为按一下亮,再按下灭
uint8_t ledEnableS5 = 0; // S5控制L3,一般理解为按一下亮,再按下灭
uint8_t timerCounter;
uint8_t timerCounterEnable;
uint8_t keyStatus; // 获取当前按键状态
uint8_t ledInfo = 0xff; // LED初始状态
uint8_t Falling = 0; // 按键按下的下降沿表示,用于防止长按状态下,前面的变量不停变化。
void KeyScan()
{
switch (keyStatus)
{
case 0: // 初始化,全部按键没按下,为高电平
k7 = 1;
k6 = 1;
k5 = 1;
k4 = 1;
keyStatus = 1;
break;
case 1:
if ((k7 == 0) || (k6 == 0)) // 如果S6、S7按下,产生 下降沿,
{
keyStatus = 2;
Falling = 1;
}
if (((k5 == 0) || (k4 == 0)) && (selectEnableS6 || selectEnableS7)) // 如果S5、S4按下,同时是在S7或S6已经按下的情况,产生 下降沿,
{
keyStatus = 3;
Falling = 1;
}
selectEnableS6 = 1; // 初始化S6启用状态
selectEnableS7 = 1; // 初始化S7启用状态
break;
case 2:
if ((k7 == 0) && selectEnableS7) // S7按下低电平,同时满足启用状态(用于对应题目中"S6、S7不可响应"操作的要求)
{
selectedValue = 7; // 根据按键值对应返回值
if (Falling) // 第1次按下时,即下降沿时
{
ledEnableS7 = ~ledEnableS7; // 用于控制点亮LED的标记,长按不翻转
}
}
else if ((k6 == 0) && selectEnableS6) // 同S7
{
selectedValue = 6;
if (Falling)
{
ledEnableS6 = ~ledEnableS6;
}
}
if ((k7 == 0) || (k6 == 0)) // 按键不松开,停在当前状态,长按状态,在测试时,如果不区别长按短按,LED灯会闪烁,因为程序中有翻转情况
{
keyStatus = 2; // 停留在当前状态
Falling = 0; // 没有下降沿,
}
else
{
keyStatus = 3;
Falling = 1;
}
break;
case 3:
if (k5 == 0) // 同S7、S6
{
controlValue = 5;
if (Falling)
{
ledEnableS5 = ~ledEnableS5;
}
}
else if (k4 == 0) // 同S7、S6
{
controlValue = 4;
if (Falling)
{
ledEnableS4 = ~ledEnableS4;
}
}
if ((k5 == 0) || (k4 == 0)) // 按键不松开,停在当前状态
{
keyStatus = 3;
Falling = 0;
}
else
keyStatus = 4;
break;
case 4:
if ((k7 == 1) && (k6 == 1) && (k5 == 1) && (k4 == 1))
keyStatus = 0;
default:
keyStatus = 0;
break;
}
}
void KeyProc()
{
if (timerCounterEnable)
{
timerCounterEnable = 0;
KeyScan();
if ((selectedValue == 7) && selectEnableS7) // S7按下,同时处于"可响应"状态
{
if (ledEnableS7) // 对应按下亮,再按下灭的情况中“按下亮”
{
ledInfo = ~L1; // 用于点亮L1,要取反
LED(ledInfo);
selectEnableS6 = 0; // 关闭S6,使其无法响应按键操作
}
else
{
ledInfo = 0xff; // 对应按下亮,再按下灭的情况中“再按下灭”
LED(ledInfo);
selectEnableS6 = 1; // 启用S6可响应操作
}
if (controlValue == 5 && ledEnableS7) // 在S5按下,同时处于S7已经点亮L1的情况下
{
if (ledEnableS5)
{
ledInfo = ~(L3 | L1); // 因为题目中说明再次按下S7,L1熄灭,所以无论是按S5还是S4,L1不能熄灭。
}
else
ledInfo = ~L1; // S5控制的L3灭的情况下,L1 还是亮的。
LED(ledInfo);
}
if (controlValue == 4 && ledEnableS7) // 同上S5分析
{
if (ledEnableS4)
{
ledInfo = ~(L4 | L1);
}
else
ledInfo = ~L1;
LED(ledInfo);
}
}
}
else if ((selectedValue == 6) && selectEnableS6) // 同上S7分析
{
if (ledEnableS6)
{
ledInfo = ~L2;
LED(ledInfo);
selectEnableS7 = 0;
}
else
{
ledInfo = 0xff;
LED(ledInfo);
selectEnableS7 = 1;
}
if (controlValue == 5 && ledEnableS6)
{
if (ledEnableS5)
{
ledInfo = ~(L5 | L2);
}
else
ledInfo = ~L2;
LED(ledInfo);
}
if (controlValue == 4 && ledEnableS6)
{
if (ledEnableS4)
{
ledInfo = ~(L6 | L2);
}
else
ledInfo = ~L2;
LED(ledInfo);
}
}
}
void main()
{
Timer0_Init();
timerCounter = 0;
timerCounterEnable = 0;
ledInfo = 0xff;
LED(ledInfo);
controlValue = 0;
selectedValue = 0;
selectEnableS6 = 1;
selectEnableS7 = 1;
while (1)
{
KeyProc();
}
}
void Timer0_Isr(void) interrupt 1 // 1ms
{
if (timerCounter++ == 20)
{
timerCounterEnable = 1;
timerCounter = 0;
}
}
代码还存在瑕疵,在S5、S4控制切换时,有亮灯延迟的情况,应该是ledinfo赋值的问题。
目前没做一下步检查。