一般LED、按键输入、数码管必考,其他外设选两个或以上考,题目都会涉及到数码管多个界面、参数闪烁等常见要求,下面进行做题套路及常见小要求的梳理。
目录
一、变量改变的标准写法
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++;
}
}