蓝桥杯单片机第十届国赛 部分功能解析

@蓝桥杯第十届国赛部分功能解析TOC

蓝桥杯单片机第十届国赛 部分功能解析

备注: 这是本人第一次发表的文章,内容有不足、有问题、有改进的地方请在评论区留言 (主要供给个人复习,随手一写,对大佬无用请转走)

按键部分:

下降沿代码

	key_number = key_trigger();
	key_change = (key_number ^ Key_old) & key_number;
	Key_old = key_number;
(这三行代码是从蓝桥杯官方指导书上学到的,非常非常实用。)
	备注:
	key_trigger() 负责对按下的按键进行采集并返回。
	key_number    负责读取key_trigger()的返回值。
	key_change    负责记录下降沿触发的按键。
	key_old       负责记录按键的上一个状态

拿例子说话
当有按键’4’按下时:
key_number = 4;
key_old = 0;
key_change = (4 ^ 0) & 4;
key_old = 4;
那么key_change的值 就为:4
(key_change 就得到了一次按下的按键值)

这是单片机在有按键按下时的第一次扫描,第二次扫描的变化及以后
key_number = 4;
key_old = 4;
key_change = (4 ^ 4) & 4;
key_old = 4;
得到的key_change结果就是:0
(这时候如果我们一直按着不松手得到的key_change结果也就一直是 0。呢就会有人怀疑为什么 & key_number,这一步到底有什么用?答案就在下面。)

当我们松手时
key_number = 0;
key_old = 4;
key_change = (0 ^ 4) & 0;
key_old = 0;
我们仔细观察这个 & key_number 这步操作,发现 key_change 仍然为 0 试想一下如果没有这步操作将会是什么?

理解了以后也就解决了上面的问题。我们发现如果触发一个按键以后 key_change仅仅会得到一次按下的键值,其余都是0,也就得到了下降沿的检测;

扩展 上升沿

	key_change = (~key_number) & (key_number ^ Key_old);

这里没有使用到,所以不再赘述,可以按照如上方法进行验证

长按按键(1s)+ 按键汇总

代码如下:

void timer0() interrupt 1 //12mhz 1ms
{
     
	if(key_delay) key_delay--;  
}
void key_pricedure()
{
     
	if(key_slow_down) return; //减速
	key_slow_down = 1; //此行代码是为了提高效率 在规定时间内仅进入一次,防止多次进入 
	
	key_number = key_trigger();
	key_change = (key_number ^ Key_old) & key_number;
	Key_old = key_number; //上面对此部分进行了解析

	switch(key_change)
	{
     
		case 8:
			key_delay = 1000;  
			//......下面部分加入按键按下时操作的代码,且长按不会影响下面的内容
			
	}
	if((key_number == 8) && (key_delay == 0)) //按键8触发 同时保持了1s
	{
     	
		key_delay = 1000;  //1s进入一次长按代码 且短按代码只进入一次
		//......下面部分加入按键长按的代码
		//DAC_output_flag ^= 1; 例子
	}
}

(如果我们按下S8按键,使switch语句中的case 8执行一次。)
“按下时间” < 1s:
在一秒以内 “key_number = 0” ,此时 key_delay 还在不断的进行"-- --"操作,等减到0的时候,key_number早就变成了0 逃之夭夭。
那么程序也就不会执行到长按的代码 。
“按下时间” >= 1s:
当按下S8以后1S内不松手,也就代表着key_delay = 0 的时候此时 key_number = 8;也就符合长按要求。

但应注意根据需求加 key_delay = 1000;
例如:上面的 DAC_output_flag ^= 1 ; 如果没有加key_delay = 1000; 进入长按程序后就会使这个标志位一直在翻转,你根本不知道你松手以后会变成0\1;加了以后就是1S进入一次长按,翻转一次。
如果我们需要长按后仅仅触发一次,那么最笨的方法就是进入长按函数后加大key_delay 的值,使下一次进入增大到很长时间。( 当然也可以增加标志位来操作,效果都一样。)

当我们在松手的时候虽然key_delay 可能还在一直“减减”但也无伤大雅,因为我们按下S8按键以后仍然会对其重新赋值!

超声波部分:

代码如下:

sbit csb_TI = P1^0;
sbit csb_RI = P1^1;
void timer1_init()
{
     
	AUXR &= 0XBF;
	TMOD &= 0X0F;
	TCON |= 0X40;
	
	TL1 = 0xF4;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
}

unsigned int csb_distance_fun() //最大距离99
{
     
	unsigned char number = 10; //发送5个周期的超声波
	unsigned int temp;
	TL1 = 0xF4;		//设置定时初值 
	TH1 = 0xFF;		//设置定时初值 
	csb_TI = 0; 
	while(number--) //控制翻转次数 10次 也就是五个周期,发送5个周期的超声波
	{
     
		while(!TF1); //12ms会使csb_TI 进行一次翻转 也就制造了 4000hz的频率
		TF1 = 0;
		csb_TI ^= 1;
	}	
	TR1 = 0; //如果不关掉定时器,由于12ms很快 可能会使定时器1出发标志为 ‘1’ 那么下面就影响了(这段解释有瑕疵)
	TH1 = 0x00; 
	TL1 = 0x00;
	TR1 = 1;
	while(!TF1 && csb_RI); //检测是否接收到超声波信号 如果有那么csb_RI会被硬件拉低 或者就是长时间未接收到超声波信号都会打破死循环。
	if(TF1) //是否是时间长未接收到超声波信号,这个时间长度是 65536ms 因为上面使TH1和TL1 都为0了
	{
     
		TF1 = 0;
		temp = 99; 
	}
	else //如果接收到超声波信号
	{
     
		temp = (TH1 << 8) + TL1;
		temp = (unsigned int)(temp * 0.017); //距离计算 = 声速 * 时间 / 2
		if(temp > 99) temp = 99;
	}
	return temp;	
}

超声波测距原理上面的备注已经比较详细了,这里不再赘述。

  • 但是在超声波部分我遇到了一个问题:
    temp = (TH1 << 8) + TL1;
    temp = (unsigned int)(temp * 0.017); //距离计算 = 声速 * 时间 / 2
    如果换成
    temp = (((TH1 << 8) + TL1)* 17)/ 1000;
    以后会出现问题
    大概就是如果超过65内部就会进位 比如66就成了0,67成了1…一直没有弄清楚原因,有知道的大佬请留言,万分感谢。

串口部分:

本部分主要也是按照官方思路走的,总之官方代码yyds!

void uart1() interrupt 4
{
     
	if(RI == 1)
	{
     
		uart_read_number[uart_i_read++] = SBUF;
		RI = 0;
	}
}

  if(uart_read_number[uart_i_read-1] == '\n')
  {
     
  		if((uart_read_number[0] == 'S') && (uart_read_number[1] == 'T')&& (uart_read_number[2] == '\r'))
  		{
     
  			//......
  		}
  		else if((uart_read_number[0] == 'P' && uart_read_number[1] == 'A' && uart_read_number[2] == 'R' && 
				 uart_read_number[3] == 'A' && uart_read_number[4] == '\r'))
		{
     
		   //.......
		}
		else
		{
     
			//.......
		}
		uart_i_read = 0;
  }
  else  if(uart_i_read == 6)
  {
     
  	uart_send_fun("ERROR\r\n");  //切记这里发送的是字符串
  	uart_i_read = 0;
  }

由于我们每次有效指令的结尾都是‘\n’这也就是个突破口,我们只要检测’\n’来了以后,再进行判断指令的内容是什么,而后进行对于操作即可。

这种操作简单,清晰!

蓝桥杯单片机第十届国赛 部分功能解析_第1张图片

你可能感兴趣的:(蓝桥杯国赛,单片机)