【聚沙成塔 | 4T网单片机练习】检测按键3s内是否按下+定时闪烁的LED

一、题目

二、难点分析 

以前没有做过这种逻辑的题目,对我来说,难点有两处:

①  如何判断按键是否3s内有被按过?

我的想法:有些复杂,实现起来bug很多,说出来当反面教材。为每一个按键都设置一个状态变量和一个计时变量。状态变量来表示某个按键是否被按下,计时变量在中断服务程序中负责记录过了多少ms。如果按下某按键就修改状态变量为1,代表可以开始计数,然后在定时中断中,如果状态变量为1就对计时变量+1,如果计时变量到3000,就让状态变量归0。但随后我就发现思路有bug,就是3s内我再次按下按键,那计时变量按理说应该重新计时才对,于是我就修改,当按键按下后,不仅要修改状态变量为1,还要设置计数初值为0,这样确保无论何时按下按键,都能让计时从头开始。

我这个思路非常的冗杂,配合LED实现起来特别难。

大佬想法:参观了4T测评大佬分享出来的代码,给我呈现出了一个全新的思路。

我称之为“倒计时算法”

即无需使用状态变量,仅需为每个按键分别配一个计时变量(初始值0)。

当按键按下后,启动倒计时,将计时变量设置为3000(3s)。

然后写一个用于放在中断服务程序中的函数LED_DIS(),这个函数负责执行led逻辑。

led 逻辑受按键按下3s的控制,由于有倒计时,所以这个实现起来非常便利。

只需要在这个函数中判断计时变量是否为0,就可以选择执行接下来的led灯亮灭的功能。

//L1
if(time[0])		//如果倒计时不为0,则进入倒计时-1和led亮起程序
{
	time[0]--;	// 此函数会放在中断服务程序,所以此倒计时 1ms 减 1	
	led &= ~1;	// ~是按位取反,1是0000 0001,~1就是1111 1110;&=就是led和~1按位与运算,也就是将第0位置0,点亮 L1
}
else
	led |= 1;		// |是按位或,也就是将最低位置1,熄灭L1

代码解释:
如果计时变量不为0

        就将计时变量-1 

        点亮L1

如果计时变量是0

        熄灭L1

 太妙了!!!!!!

②  如何让指示灯 0.1s 间隔闪烁?

a. 如何控制 LED的某一位亮灭? 

一句话:位运算在指示灯这块非常实用。

我的思路:还是先说一下我的反面教材,我先开始直接对P0口赋值,这就非常容易出错呀,因为假如两个按键同时在3s内按下,你说人家该听按键1对应的P0逻辑,还是按键2对应的P0逻辑呢???所以肯定不能直接对P0赋值。 

大佬思路:看了大佬led这块用的全是按位运算符

总的来说,就是要先定义一个名为 led 的变量,用于存储最终要传给P0口的值,然后对led变量根据不同的情况进行对应的按位运算,实现对不同位的准确处理,最后再把led变量给P0。

这种做法非常稳健,形象点说,就是先考虑好所有的情况再出手。led变量相当于一个盘子,先把所有人要点的菜都放上去,再端到餐厅分发给大家,有条不紊。

而不是像我的做法那样,我只有一个服务员,顾客A说要牛奶,服务员送过去了,顾客B说要酸奶,服务员送过去了。假如顾客AB前后相差大于3s来点餐,服务员是处理得过来的。但假如顾客AB在三秒内同时点餐,就来不及给他们同时处理了。

符号意义效果代码备注
&按位与按位置 0led &= ~1;0 对应的位置0有效
|按位或按位置 1led |= 1;1 对应的位置1有效
^按位 异或按位 取反led ^= 8;1 对应的位取反有效

总结:

想要亮,就用与&;

想要灭,就用或|;

想要闪烁(反转),就用异或^;

b.如何控制LED等间隔闪烁?

前面讲倒计时算法的时候,最重要的部分就是倒计时变量 ,所以此处也充分利用了这个变量来让LED每隔0.1秒闪烁一次。

	if(time[3])
	{
		time[3]--;
		if(time[3]%100==0)	//小巧思:如果倒计时是100的整数倍,说明应该闪烁了
			led ^= 16;					// ^是按位异或,16就是0001 0000,第4位是1
	}											//效果:把第4位取反
	else
		led |= 16;		// 熄灭L5

“小巧思”代码解释:
如果倒计时除以100的余数是0(倒计时是100的整数倍),说明距离按键按下已经100ms(0.1s),那么就让led变量的第4位(0001 0000=16),进行反转,从而实现定时闪烁。

三、main.c完整代码

#include <STC15F2K60S2.H>

#define Y0C P2=P2&0X1F|0X00
#define Y4C P2=P2&0X1F|0X80
#define Y5C P2=P2&0X1F|0XA0
#define Y6C P2=P2&0X1F|0XC0
#define Y7C P2=P2&0X1F|0XE0
typedef unsigned char u8;
typedef unsigned int u16;

//define variate

u16 time[4]={0};	//用于存放四个按键的倒计时
u8 led=0xff;			//用于记录led的数据口,记得传递给P0


void init()
{
	Y4C;P0=0XFF;Y0C;
	Y5C;P0=0X00;Y0C;
	Y6C;P0=0XFF;Y0C;
	Y7C;P0=0XFF;Y0C;
}


void Timer1Init()		//1毫秒@12.000MHz
{
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x20;		//设置定时初值
	TH1 = 0xD1;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1 = 1;
	EA  = 1;
}

void Delay10ms()		
{
	unsigned char i, j;

	i = 117;
	j = 184;
	do
	{
		while (--j);
	} while (--i);
}

void key()
{
	//S4	L1
	P44=0; P42=1; P35=1; P34=1;
	if(P33==0)
	{
		Delay10ms();
		if(P33==0)
			time[0]=3000;			//按键按下,次按键对应的倒计时置为3000(3s)
		while(!P33);
	}
	//S9	L2
	P44=1; P42=0; P35=1; P34=1;
	if(P32==0)
	{
		Delay10ms();
		if(P32==0)
			time[1]=3000;
		while(!P32);
	}
	//S14	L4	0.1
	P44=1; P42=1; P35=0; P34=1;
	if(P31==0)
	{
		Delay10ms();
		if(P31==0)
			time[2]=3000;
		while(!P31);
	}
	//S19	L5	0.1
	P44=1; P42=1; P35=1; P34=0;
	if(P30==0)
	{
		Delay10ms();
		if(P30==0)
			time[3]=3000;
		while(!P30);
	}
}

void led_dis()
{
	//L1
	if(time[0])		//如果倒计时不为0,则进入倒计时-1和led亮起程序
	{
		time[0]--;	// 此函数会放在中断服务程序,所以此倒计时 1ms 减 1	
		led &= ~1;	// ~是按位取反,1是0000 0001,~1就是1111 1110;&=就是led和~1按位与运算,也就是将第0位置0,点亮 L1
	}
	else
		led |= 1;		// |是按位或,也就是将最低位置1,熄灭L1
	
	//L2
	if(time[1])
	{
		time[1]--;
		led &= ~2;	// ~是按位取反,2是0000 0010,~2就是1111 1101;&=就是led和~2按位与运算,也就是将第1位置0,点亮 L2
	}
	else
		led |= 2;		// 熄灭L2
	
	//L4 0.1s
	if(time[2])
	{
		time[2]--;
		if(time[2]%100==0)	//小巧思:如果倒计时是100的整数倍,说明应该闪烁了
			led ^= 8;					// ^是按位异或,8就是0000 1000,led假如是1111 1101,那结果就是1111 0101;
	}											//led假如是1111 0101,那结果就是1111 1101
												//按位异或,0无效,原来是什么就还是什么;1有效,把原来是0的改为1,原1改为0。
												//效果:把第3位取反
	else
		led |= 8;		  // 熄灭L4
	
	//L5 0.1s
	if(time[3])
	{
		time[3]--;
		if(time[3]%100==0)	//小巧思:如果倒计时是100的整数倍,说明应该闪烁了
			led ^= 16;					// ^是按位异或,16就是0001 0000,第4位是1
	}											//效果:把第4位取反
	else
		led |= 16;		// 熄灭L5
	
	//LED显示
	Y4C;P0=led;Y0C;
	
}

void tm1_isr() interrupt 3
{
	led_dis();		// 倒计时控制的led控制逻辑函数
}

main()
{
	init();
	Timer1Init();
	
	while(1)
	{
		key();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值