蓝桥杯单片机常见题型模型

一般LED、按键输入、数码管必考,其他外设选两个或以上考,题目都会涉及到数码管多个界面、参数闪烁等常见要求,下面进行做题套路及常见小要求的梳理。

目录

一、变量改变的标准写法

1、0、1互换

2、0->1->2...->a->0

3、自增自减考虑上下限

二、数码管

1、数码管多个模式界面的显示与切换

显示

切换

2、数码管选中参数的闪烁处理

3、参数的设置与保存

 4、数码管高位熄灭

三、按键长按

四、LED点亮要求

1.模式指示灯(互斥点亮)

2.关联灯

3.根据标志位全灭

4.单个LED闪烁

五、定时器计时

1.每隔1s标志位取反

2.xx秒的计数


一、变量改变的标准写法

1、0、1互换

Seg_Disp_Mode ^= 1;
//异或操作,每执行一次0变为1,1变为0

2、0->1->2...->a->0

if(++Seg_Disp_Mode == (a+1)) 
	Seg_Disp_Mode = 0;//	Seg_Disp_Mode在0-a之间循环切换

3、自增自减考虑上下限

//255和100是参数实际取不到的值

    case 8://参数自减按键
		if(Seg_Disp_Mode == 1) //当前处于温度参数设置界面
		{
			if(--Temperature_Params == 255) //限制温度下限为0,
				Temperature_Params = 0;
		}
	break;
	case 9://参数自加按键
		if(Seg_Disp_Mode == 1) //当前处于温度参数设置界面
		{
			if(++Temperature_Params == 100) //限制温度上限为99
				Temperature_Params = 99;
		}

二、数码管

1、数码管多个模式界面的显示与切换

显示

定义一个数码管显示模式标志位Seg_Disp_Mode,然后在void Seg_Proc()函数根据Seg_Disp_Mode值不同显示不同的值。

下面以第十二届省赛显示温度和DA输出为例:

unsigned char Seg_Disp_Mode;//数码管显示模式标志位
...
...
...
void Seg_Proc()
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;//数码管减速程序

	/* 信息采集区域 */
	Temperature = rd_temperature();//实时采集温度数据
	
	/* 数据显示区域 */
	switch(Seg_Disp_Mode)
	{
		case 0://温度显示界面
			Seg_Buf[0] = 11;//标识符C
			Seg_Buf[4] = (unsigned char)Temperature / 10 % 10;
			Seg_Buf[5] = (unsigned char)Temperature % 10;
			Seg_Buf[6] = (unsigned int)(Temperature * 100) / 10 % 10;
			Seg_Buf[7] = (unsigned int)(Temperature * 100) % 10;
			Seg_Point[5] = 1;//使能小数点
		break;
		case 1://参数设置界面
			Seg_Buf[0] = 12;//标识符P
			Seg_Buf[4] = Seg_Buf[5] = 10;//熄灭第五、第六个数码管
			Seg_Buf[6] = Temperature_Params / 10 % 10;
			Seg_Buf[7] = Temperature_Params % 10;
			Seg_Point[5] = 0;//关闭小数点
		break;
		case 2://DAC输出界面
			Seg_Buf[0] = 13;//标识符A
			Seg_Buf[5] = (unsigned char)Voltage_Output;
			Seg_Buf[6] = (unsigned int)(Voltage_Output * 100) / 10 % 10;
			Seg_Buf[7] = (unsigned int)(Voltage_Output * 100) % 10;
			Seg_Point[5] = 1;//使能小数点
		break;
	}
}

切换

在void Key_Proc()函数中的    switch(Key_Down) case x:进行Seg_Disp_Mode值的改变

如果Seg_Disp_Mode取值仅有0和1,即仅有两种模式采用异或的方式

switch(Key_Down)
	{
		case 4://界面切换
			Seg_Disp_Mode ^= 1;//循环切换数据界面和参数界面
    }

如果Seg_Disp_Mode取值有两种以上,只能采用循环的方式。

switch(Key_Down)
	{
		case 4://界面切换按键
			if(++Seg_Disp_Mode == 3) 
				Seg_Disp_Mode = 0;//数码管显示模式在0-2之间循环切换
    }

2、数码管选中参数的闪烁处理

例:温度设置界面要求显示最大温度30最小温度20,进入该模式选中的值要进行0.5s/次的闪烁

(1)定义数组P[2] = {30,20}(最大温度和最小温度有一定联系所以以数组形式存储)以及P_Index变量(用来索引数组)

(2)定义一个Seg_Star_Flag变量,使用定时器中断每隔500ms取反一次

(3)在数码管显示函数中,根据Seg_Star_Flag周期性0或1,使用选择运算符“?:”周期性显示与熄灭


bit Seg_Star_Flag;//数码管闪烁标志位
unsigned char P_Dat[2] = {30,20};//参数数据储存数组
unsigned char P_Dat_Index;//参数数据数组指针 0-上限 1-下限
...
...
...
void Seg_Proc()
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	
	switch(Seg_Disp_Mode)
	{
		...
		case 1://参数设置界面
			Seg_Buf[0] = 12;//P
			Seg_Buf[3] = P_Dat[0] / 10 % 10;
			Seg_Buf[4] = P_Dat[0] % 10;
			Seg_Buf[5] = 13;//-
			Seg_Buf[6] = P_Dat[1] / 10 % 10;
			Seg_Buf[7] = P_Dat[1] % 10;	

			if(P_Dat_Index == 0)
/*正常写法,Seg_Star_Flag每500ms0、1切换。导致每500msSeg_Buf数组相应的值为正常值和10(熄灭)
			{
				Seg_Buf[3] = Seg_Star_Flag?P_Dat[0] / 10 % 10:10;
				Seg_Buf[4] = Seg_Star_Flag?P_Dat[0] % 10:10;
			}
			else
			{
				Seg_Buf[6] = Seg_Star_Flag?P_Dat[1] / 10 % 10:10;
				Seg_Buf[7] = Seg_Star_Flag?P_Dat[1] % 10:10;				
			}
*/


/*根据上述规律得到的简便写法
			Seg_Buf[3*(P_Dat_Index+1)] = Seg_Star_Flag?P_Dat[P_Dat_Index] / 10 % 10:10;
			Seg_Buf[3*(P_Dat_Index+1)+1] = Seg_Star_Flag?P_Dat[P_Dat_Index] % 10:10;	
*/				


			Seg_Point[5] = 0;		
		break;
	}
	
}

...
...
...
void Timer0Server() interrupt 1
{
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
	if(++Seg_Pos == 8) Seg_Pos = 0;
	Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	
	if(++Timer_500MS == 500)
	{
		Timer_500MS = 0;
		Seg_Star_Flag ^= 1;
	}
	
}

也可以如下,seg_buf[]的值为10或11,即为熄灭(10)或分隔符(11)

seg_buf[2] = seg_buf[5] = 10 + (unsigned char)seg_star_flag;

3、参数的设置与保存

一般涉及到参数设置模式,定义参数时定义一对(一个用于在参数设置界面的显示与修改,另一个用于控制其他外设参数变量),保证设置的参数仅在退出设置界面后生效。

以温度参数控制DA输出为例:

(1)定义两个数组

(2)在界面切换按键被按下后,进行值的传递保存

unsigned char Temperature_Params;//温度参数变量(用于设置显示)
unsigned char Temperature_Params_Ctrol = 25;//温度参数控制变量(用于实际控制) 初始值:25
...
...
...
switch(Key_Down)
	{
		case 4://界面切换按键
			if(++Seg_Disp_Mode == 3) 
				Seg_Disp_Mode = 0;//数码管显示模式在0-2之间循环切换
			if(Seg_Disp_Mode == 1) //从温度显示界面切换到温度参数设置界面
				Temperature_Params = Temperature_Params_Ctrol;//将实际控制数据赋值给设置变量 便于数据更改
			if(Seg_Disp_Mode == 2) //当界面从参数设置切换出去后
				Temperature_Params_Ctrol = Temperature_Params;//将设置数据保存到控制变量
		break;
...
...
...
case 1://参数设置界面
	Seg_Buf[0] = 12;//标识符P
	Seg_Buf[4] = Seg_Buf[5] = 10;//熄灭第五、第六个数码管
	Seg_Buf[6] = Temperature_Params / 10 % 10;
	Seg_Buf[7] = Temperature_Params % 10;
	Seg_Point[5] = 0;//关闭小数点
break;
switch(Key_Down)
	{
		case 4://界面切换
			Seg_Disp_Mode ^= 1;//循环切换数据界面和参数界面
			if(Seg_Disp_Mode == 1) //切换到参数界面
			{
				//将控制值赋值给显示数组 便于修改数据
				Temperature_Params_Disp[0] = Temperature_Params_Ctrol[0];
				Temperature_Params_Disp[1] = Temperature_Params_Ctrol[1];
				//上下限指针复位
				Temperature_Params_Index = 1;
			}
			else //切换到数据界面
			{
				if(Temperature_Params_Disp[0] > Temperature_Params_Disp[1]) //设置参数合理
				{
					//将显示值赋值给控制数组 用于保存数据
					Temperature_Params_Ctrol[0] = Temperature_Params_Disp[0];
					Temperature_Params_Ctrol[1] = Temperature_Params_Disp[1];
					Error_Flag = 0;//标志位拉低 表示设置正确
				}
				else
					Error_Flag = 1;//标志位拉高 表示设置错误
			}
		break;
		case 5://参数切换
			if(Seg_Disp_Mode == 1) //处于参数界面
				Temperature_Params_Index ^= 1;//切换参数选中上下限
		break;

 4、数码管高位熄灭

unsigned char i = 3;
while(Seg_Buf[i] == 0) //数码管高位熄灭
	{
		Seg_Buf[i] = 10;
		if(++i == 7) break; //保证最低位不熄灭 避免程序卡死
	}

从第四位数码管开始检验,如果第四位数码管的值为0, 那么让它熄灭。直到检测到倒数第一位(仅i刚变为7,未赋值)break跳出循环

三、按键长按

若按下S14自增、S15自减,超过500ms快速加减(加减速度此时取决于按键读取速度默认10ms):

(1)定义记录按键时间变量unsigned int Key_Time以及标志位bit Key_Flag

(2)如果产生下降沿,在定时器中断中将Key_Flag置1Key_Time开始计数,如果记到600那么无需再记,令Key_Time等于600即可(防止变量溢出)

(3)根据Key_Time的值大于还是小于500可进行对应操作,进行其他操作之前先使Key_Time、Key_Flag置0

unsigned int Key_Time;//记录按键时间变量
bit Key_Flag;//按键使能标志位
...
...
...
if(Seg_Disp_Mode == 1)
	{
		if(Key_Down == 14 || Key_Down == 15)//产生下降沿
			Key_Flag = 1;//拉高标志位 开始计时
	}
	if(Key_Time < 500) //在五百毫秒之内
	{
		if(Key_Up == 14) //短按
		{
			Key_Flag = Key_Time = 0;
			if(++P_Dat[P_Dat_Index] == 71)
				P_Dat[P_Dat_Index] = 10;
		}
		if(Key_Up == 15) //短按
		{
			Key_Flag = Key_Time = 0;
			if(--P_Dat[P_Dat_Index] == 9)
				P_Dat[P_Dat_Index] = 70;
		}
	}
	else //在五百毫秒之外
	{
		if(Key_Old == 14)
		{
			if(++P_Dat[P_Dat_Index] == 71)
				P_Dat[P_Dat_Index] = 10;
		}
		if(Key_Old == 15)
		{
			if(--P_Dat[P_Dat_Index] == 9)
				P_Dat[P_Dat_Index] = 70;
		}
		if(Key_Up == 14 || Key_Up == 15)
		{
			Key_Flag = Key_Time = 0;
		}
	}
...
...
...
void Timer0Server() interrupt 1
{
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
	if(++Seg_Pos == 8) Seg_Pos = 0;
	Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	
	if(Key_Flag == 1)
	{
		if(++Key_Time == 600)
			Key_Time = 600;
	}
}

 长按S7超过1秒,计数器清零,存储计数值

	if(Seg_Disp_Mode == 1) //处于湿度界面
	{
		if(Key_Down == 7) //产生下降沿
			Key_Flag = 1;//使能标志位
		if(Key_Up == 7) //产生上升沿
		{
			Key_Flag = 0;//拉低标志位
			if(Timer_Key > 1000) //执行长按操作
			{
				Relay_Count = 0;//计数值清零
				EEPROM_Write(&Relay_Count,0,1);//储存计数值
			}
			Timer_Key = 0;//复位计时变量
		}
	}

	if(Key_Flag == 1)
	{
		if(++Timer_Key > 1200) //防止一直长按计时变量溢出
			Timer_Key = 1200;
	}

四、LED点亮要求

1.模式指示灯(互斥点亮)

不同模式,点亮不同的LED灯。

例:模式一(Seg_Disp_Mode为0)点亮L1,模式二(Seg_Disp_Mode为1)点亮L2,模式三(Seg_Disp_Mode为2)点亮L3

for(i=0;i<3;i++)//模式指示灯
	 ucLed[1+i] = (i == Seg_Disp_Mode);

2.关联灯

根据某个其他外设的值对某个LED点亮或熄灭,例如:

处理方式类似与数电中已知真值表写逻辑表达式,仅写出1状态的表达式,不满足表达式即为0状态

ucLed[2] = ((Voltage >= 1.5 && Voltage < 2.5) || (Voltage >= 3.5));

3.根据标志位全灭

if(Seg_Enable_Flag == 0) //Led不使能
	{
		for(i=0;i<8;i++)//熄灭所有Led
			ucLed[i] = 0;
	}

4.单个LED闪烁

ucLed[0] = Led_Enable_Flag & Led_Star_Flag;
//如果使能位为1,闪烁标志位0、1交替变换,导致传到数组里面的值也0、1交替变换

五、定时器计时

1.每隔1s标志位取反

	if(++Timer_1000ms == 1000)//时钟提示符、LED每1s取反一次
	{
		Timer_1000ms = 0;
		seg_star_flag ^= 1;
		led_star_flag ^= 1;
	}

2.xx秒的计数

	if(seg_mode_disp == 1)
	{
		if(++sec_tick == 1000)
		{
			sec_tick = 0;
			sec_time++;
		}
	}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值