单片机型号:IAP15F2K60S2.
蓝桥杯第十届国赛有串口,本文着手进行一番练习,
文末会附上整个工程下载地址。
这一届赛题的练习对我来说还是有些难度
我看题后做了以下几个设计方面的点总结列举:
0.界面的切换,这个点非常简单。
1.按键长按与短按不可相互影响,即,我想短按就必须小于1s松手,但怎么判断我未来状态会是一直按着超过1s,还是不到1s就松开的?
2.参数设置后,发现参数发生变化,参数改动次数加一,这个点不难,但是烦。
3.串口接收和发送设计中,如何拼接字符串与变量来发送?
4.不同位置LED指示灯切换互不影响,这个点很简单。
5.定时器1究竟分给超声波还是串口?
以下将会围绕这些点,进行问题的解决。
目录
我的编程习惯在这篇文章有描述:
关于界面的切换:
第十届国赛一共五个界面:三个数据界面,俩个参数界面,
界面量算是比较少了,还是很好做切换的:
1.定义nr1~nr8,八个变量,是用来指示数码管打印内容的,
之后改变这八个变量即可改变打印内容了:
unsigned char nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8;
2.弄明白界面之间关系,明显是俩层金字塔类型的关系:
顶层: 数据界面 参数界面
底层: 温度显示 距离显示 变更次数 温度参数 距离参数
因为只有俩层,所以我定义了俩个变量来描述这个关系:
jm_2 只在1和0之间变换,分别对应 数据界面 和参数界面
jm_1 根据jm_2的情况变换
当jm_2==1时,jm_1 可以有 1、2、3 三种情况,对应 温度显示 距离显示 变更次数
当jm_2==0时,jm_1 只有1、2 俩种情况了,对应 温度参数 距离参数
unsigned char jm_1; //翻页界面
unsigned char jm_2; //参数、数据大界面切换
void give_nr()
{
if(jm_2==0)
{
switch(jm_1)
{
case 1:
//对应情况填充nr1~nr8的值
break;
case 2:
//对应情况填充nr1~nr8的值
break;
case 3:
//对应情况填充nr1~nr8的值
break;
}
else if(jm_2==1)
{
switch(jm_2)
{
case 1:
//对应情况填充nr1~nr8的值
break;
case 2:
//对应情况填充nr1~nr8的值
break;
case 3:
//对应情况填充nr1~nr8的值
break;
}
}
}
最后莫忘记,jm_1和jm_2在按键操 作和 初始化时 的数值要符合逻辑
key_value=key_return();
switch(key_value)
{
case 4:
jm_2=!jm_2; jm_1=1;
break;
case 5:
jm_1++;
if(jm_2==0 && jm_1>=4 ) jm_1=1; //数据界面有三个
if(jm_2==1 && jm_1>=3) jm_1=1; //参数界面有俩个
break;
case 8:break;
case 9:break;
}
关于按键长按、短按识别互不影响:
我们可以发现,正常的按键函数不加按键长按识别时,
按下状态都有一个对应状态的while()循环来等待松手。
所以短按是松手就算一次,
而我按键长按的实现是:
采用将对应按键标志位置于循环中,用定时器中断
对这个循环持续的时间 进行计数计时 的办法 实现 长按效果
这很容易导致长按松手一瞬间也触发一次短按,
这里贴出按键长按不影响写法的实现原理:
此代码存在#include "key_4589.h"
#include "key_4589.h"
u8 key8_flag; //K8长按标志
u8 key9_flag; //K9长按标志
u8 key_long_state; //长按松手禁短触
void Delay12ms() //@12.000MHz
{
unsigned char i, j;
i = 141;
j = 16;
do
{
while (--j);
} while (--i);
}
void key_scan_inint(u8 n)
{
switch(n)
{
case 1:X1=0;X2=1;Y1=1;Y2=1;break;
case 2:X1=1;X2=0;Y1=1;Y2=1;break;
}
}
u8 key_return()
{
u8 key_value;
key_value=0;
Delay12ms(); //消抖
key_scan_inint(1);
if(Y1==0){while(Y1==0);key_value=4;}
if(Y2==0){
while(Y2==0){key8_flag=1;} //当S8按住的状态时key8_flag=1
key8_flag=0;key_value=8;} //松手key8_flag=0;返回键值
key_scan_inint(2);
if(Y1==0){while(Y1==0);key_value=5;}
if(Y2==0){while(Y2==0){key9_flag=1;}
key9_flag=0;key_value=9;}
if(key_long_state==1) //如果上次是进行了长按
{
key_long_state=0; //清零长按标志
key_value=0; //清除长按松手误发的键值
}
return key_value;
}
此代码存在#include "Timer.h"
void Timer0_server() interrupt 1
{
u8 i,key;
u16 K8,K9;
TL1=0X18;
TH1=0XFC;
i++;key++;
if(i==8) //8ms打印一次数码管
{
i=0;
give_nr();
smg_display(nr1,nr2,nr3,nr4,nr5,nr6,nr7,nr8);
count_10ms++;
}
if(key==15) //15ms扫描一次按键
{
key=0;
key_flag=1;
}
if(key8_flag==1) //对K8长按进行计数
{
K8++;
if(K8==1000) //此处不将K8清0,长按将不会连续被触发
{
count_canshu=0; //清零变更次数
key_long_state=1;
}
}
if(key8_flag==0){K8=0;} //按键函数中,松手后对应标志会被清0,计数也应清零
if( key9_flag==1) //对K9长按进行计数
{
K9++;
if(K9==1000) //此处不将K9清0,长按将不会连续被触发
{
DAC_flag=1; //开启DAC输出
key_long_state=1;
}
}
if(key9_flag==0){K9=0;} //按键函数中,松手后对应标志会被清0,计数也应清零
}
这样,让按键函数与定时器服务函数进行了一个反馈调节,
就能解决这个问题了!
关于参数发生变化变量才加一:
参数是否发生变化,我们可采用,与原数据进行比较的方式
在进入参数页面就记载一次俩个参数的原数据
此行代码在主函数,按键选择判断 S4的情况中:
定义的全局变量有:
u16 count_canshu; //参数变动计数
u16 temp_cun; //温度参数原始数据
u8 distance_cun; //距离参数原始数据
u16 wendu_can; //温度参数
u8 distance_can; //距离参数
case 4:
if(jm_2==0)
{
jm_2=1;
jm_1=1;
//切换到参数界面就记录一下,为之后作比较用
temp_cun=wendu_can;
distance_cun=distance_can;
}
if(jm_2==1)
{
jm_2=0;
jm_1=1;
//切换回数据界面判断是否需要给参数变动计数
if(temp_cun!=wendu_can || distance_cun!=distance_can)
{
count_canshu++;
}
}
break;
关于串口拼接字符串与变量:
用print函数与格式控制符%02bu即可,在此文有详细介绍:
关于LED的点亮互不影响:
用LED点亮数组的思想:
//与之进行相与运算,以打开对应led
//1_L1 2_L2 3_L3 4_L4 5_L5 6_L6 7_L7 8_L8
u8 code LED_codeON[10]={0xff,
0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
//与之进行相或运算,以关闭对应led
//1_L1 2_L2 3_L3 4_L4 5_L5 6_L6 7_L7 8_L8
u8 code LED_codeOFF[10]={0xff,
0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
使用步骤如下:
1.在题目中,我们先定义一个unsigned char led_tmp; 可以给它 初始化赋值为0xff。
2.在定时器内不断刷新点亮LED时只需要这俩句:
if(i==15) //15ms刷新LED
{
inint_74HC573(4);
P0=led_tmp;
}
这样P0 就会每15ms 给LED的端口输出 led_tmp 的值。
3.我们在需要改变LED输出亮灭情况的 if语句下 对led_tmp进行赋值
就可以改变LED亮灭情况。
比如,我想让板子最左边的LED1亮或灭,我可以这样:
led_temp&=LED_codeON[1]; //让LED1亮
led_temp|=LED_codeOFF[1];//让LED1灭
这样进行与或运算得到的值,不会抹去上一位置的LED的亮灭状态,
而是让俩个位置LED状态叠加出现,
比直接进行让 P0=0xfe 之类的覆盖式赋值要实用;
三个定时器0、1、2分配问题:
定时器0用于通常模块的调用,数据处理大宗
定时器1用于给超声波计时
定时器2给串口