按键及简易跑马灯设计框架

遇到一个面试题

题目:设计一个按键+跑马灯的DEMO

假设现在有A、B、C三个按键(均按下低电平),五个LED灯LED1~LED5高电平点亮

核心功能位跑马灯,每次只点亮相邻的三盏灯,以一定时间频率向左或向右移动一盏灯。

例如:

11100 -> 01110 -> 00111 -> 10011

程序刚上电或复位状态:灯显11100,移动方向向右,移动间隔时间1S,跑马灯静止状态

按键A单击为修改移动方向向左,双击为移动间隔时间-100ms

按键B单击为修改移动方向向右,双击为移动间隔时间+100ms

按键C单击为反转跑马灯运行或静止状态,长按(2S)为复位程序到初始状态

备注:

默认在32bitMCU裸机下实现,按键在硬件上无消抖处理。

只考察编程思维和代码风格,不检查语法,不需要完整的工程通过编译。

表述有不清晰不完整的地方,按照自己理解完成就行,无标准答案,无需在意真实硬件环境。

实现方法不一,我的程序框架如下:

//按键:KEY_A、KEY_B、KEY_C
//假设开一个100ms定时器 Time1_Callback()
//假设延时函数 delay_ms()
//LED灯:11100(0x1C)->01110(0x0E)->00111(0x07)->10011(0x13)->11001(0x19)->11100(0x1C)

#define RIGHT = 0;//跑马灯移动方向向右
#define LEFT = 1;//跑马灯移动方向向左
#define ON = 0;//按键按下
#define RUN = 1;//跑马灯运行
#define STOP = 0;//跑马灯停止

uint8 LED = 0x1C;//低五位表示跑马灯的亮灭
uint8 DIR = RIGHT;//跑马灯移动方向
uint8 time_count = 10;//定时器回调函数中计时值
uint8 time_inital_value = 10;//设定跑马灯移动频率值
uint8 run_stop = STOP;//跑马灯运行或停止标志

struct KEY_STATUS//定义按键的三个状态
{
	uint8 one_click;//按键单击标志
	uint8 double_click;//按键双击标志
	uint8 long_click;//按键长按标志
};

/* 100ms调用1次回调函数 */
void Time1_Callback()
{
	if(time_count > 0 )
	{
		time_count--;
	}

}

/* LED跑马灯运行接口 */
void LED_Run()
{
	if(run_stop == RUN)
	{
		if((time_count == 0) && (DIR = RIGHT))
		{
			time_count = time_inital_value;
			switch(LED)
				case 0x1C:
					//点亮对应位LED 11100
					LED = 0x0E;
					break;
				
				case 0x0E:
					//点亮对应位LED 01110
					LED = 0x07;
					break;

				case 0x07:
					//点亮对应位LED 00111
					LED = 0x13;
					break;

				case 0x13:
					//点亮对应位LED 10011
					LED = 0x19;
					break;

				case 0x19:
					//点亮对应位LED 11001
					LED = 0x1C;
					break;
		}
		else if((time_count == 0) && (DIR = LEFT))
		{
			switch(LED)
				case 0x1C:
					//点亮对应位LED 11100
					LED = 0x19;
					break;
				
				case 0x19:
					//点亮对应位LED 11001
					LED = 0x13;
					break;

				case 0x13:
					//点亮对应位LED 10011
					LED = 0x07;
					break;

				case 0x07:
					//点亮对应位LED 00111
					LED = 0x0E;
					break;

				case 0x0E:
					//点亮对应位LED 01110
					LED = 0x1C;
					break;

		}
	}
}


void main ()
{
	struct KEY_STATUS KEYA = {0,0,0};//按键A
	struct KEY_STATUS KEYB = {0,0,0};//按键B
	struct KEY_STATUS KEYC = {0,0,0};//按键C

	uint8 dec = 0;//用于循环计时判断按键长按
	uint8 i =0;//用于循环计时判断按键双击
	
	while(1)
	{
		/* KEY_A */
		if(KEY_A == ON)
		{
			delay_ms(20);//消抖
			if(KEY_A == ON)
			{
				while(KEY_A == ON && dec<20)
				{ 
					dec++;delay_ms(100); //长按判断的计时2S
				}
				if(dec >= 20)//长按
				{
					dec = 0;
					KEYA.one_click = 0;
					KEYA.double_click = 0;
					KEYA.long_click = 1;

					/* 按键A长按处理 */
				}
				else
				{
					for(i=0;i<10;i++)
					{
						delay_ms(20);
						if(KEY_A == ON)//双击
						{
							KEYA.one_click = 0;
							KEYA.double_click = 1;
							KEYA.long_click = 0;

							/* 按键A双击处理 */
							if(time_inital_value > 0)
							{
								time_inital_value--;//周期减小100ms
							}
						}
					}
					if(KEYA.double_click == 0)//单击
					{
						KEYA.one_click = 1;
						KEYA.double_click = 0;
						KEYA.long_click = 0;

						/* 按键A单击处理 */
						DIR = LEFT;//切换方向
					}

				}
			}


			/* KEY_B */
			if(KEY_B == ON)
			{
				delay_ms(20);//消抖
				if(KEY_B == ON)
				{
					while(KEY_B == ON && dec<20)
					{ 
						dec++;delay_ms(100); //长按判断的计时2S
					}
					if(dec >= 20)//长按
					{
						dec = 0;
						KEYB.one_click = 0;
						KEYB.double_click = 0;
						KEYB.long_click = 1;

						/* 按键B长按处理 */
					}
					else
					{
						for(i=0;i<10;i++)
						{
							delay_ms(20);
							if(KEY_B == ON)//双击
							{
								KEYB.one_click = 0;
								KEYB.double_click = 1;
								KEYB.long_click = 0;

								/* 按键B双击处理 */
								time_inital_value++;//周期增加100ms
							}
						}
						if(KEYB.double_click == 0)//单击
						{
							KEYB.one_click = 1;
							KEYB.double_click = 0;
							KEYB.long_click = 0;

							/* 按键B单击处理 */
							DIR = RIGHT;//切换方向
						}
					}
				}
			}


			/* KEY_C */
			if(KEY_C == ON)
			{
				delay_ms(20);//消抖
				if(KEY_C == ON)
				{
					while(KEY_C == ON && dec<20)
					{ 
						dec++;delay_ms(100); //长按判断的计时2S
					}
					if(dec >= 20)//长按
					{
						dec = 0;
						KEYC.one_click = 0;
						KEYC.double_click = 0;
						KEYC.long_click = 1;

						/* 按键C长按处理 */
						/* 复位 */
						run_stop = RUN;
						DIR = RIGHT;
						LED = 0x16;
						time_inital_value = 10;
					}
					else
					{
						for(i=0;i<10;i++)
						{
							delay_ms(20);
							if(KEY_C == ON)//双击
							{
								KEYC.one_click = 0;
								KEYC.double_click = 1;
								KEYC.long_click = 0;

								/* 按键C双击处理 */
							}
						}
						if(KEYC.double_click == 0)//单击
						{
							KEYC.one_click = 1;
							KEYC.double_click = 0;
							KEYC.long_click = 0;

							/*按键C单击处理*/
							if(run_stop == RUN)//停止、运行
							{
								run_stop = STOP;
							}
							else
							{
								run_stop = RUN;
							}
						}
					}
				}
			}
		}
		/* 循环执行 */
		LED_Run();
	}
}

——作为以后需要用到类似功能时的参考程序

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

studyingdda

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值