蓝桥杯单片机第十届国赛练习

单片机型号:IAP15F2K60S2.

蓝桥杯第十届国赛有串口,本文着手进行一番练习,

文末会附上整个工程下载地址。

这一届赛题的练习对我来说还是有些难度

我看题后做了以下几个设计方面的点总结列举:

0.界面的切换,这个点非常简单。

1.按键长按与短按不可相互影响,即,我想短按就必须小于1s松手,但怎么判断我未来状态会是一直按着超过1s,还是不到1s就松开的?

2.参数设置后,发现参数发生变化,参数改动次数加一,这个点不难,但是烦。

3.串口接收和发送设计中,如何拼接字符串与变量来发送

4.不同位置LED指示灯切换互不影响,这个点很简单。

5.定时器1究竟分给超声波还是串口?

以下将会围绕这些点,进行问题的解决。

目录

我的编程习惯在这篇文章有描述:

关于界面的切换:

关于按键长按、短按识别互不影响:

关于参数发生变化变量才加一:

关于串口拼接字符串与变量:

关于LED的点亮互不影响:

三个定时器0、1、2分配问题:


我的编程习惯在这篇文章有描述:

蓝桥杯单片机第十一届省赛练习回顾_NULL指向我的博客-CSDN博客

关于界面的切换:

第十届国赛一共五个界面:三个数据界面,俩个参数界面,

界面量算是比较少了,还是很好做切换的:

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即可,在此文有详细介绍:

蓝桥杯单片机串口通信学习提升笔记——部分2_NULL指向我的博客-CSDN博客

关于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给串口

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NULL指向我

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

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

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

打赏作者

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

抵扣说明:

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

余额充值