蓝桥杯国赛第九届 题目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

做题思路

首先写上要做的模块,界面,按键细节
一页纸就能让你不用看pdf即可写出整套题
简单示范:
在这里插入图片描述
突然就觉得很简单了对不对,笼统看就写几个模块,外加几个界面~

下面,我就参照我是如何思考以及我的写题策略来讲解第九届的国赛题啦

首先smg测试

void smg()
{
	P2 = (P2&0x1f)|0xc0; P0 = T_COM[smg_i]; P2 = (P2&0x1f);
	P2 = (P2&0x1f)|0xe0; P0 = ~table[smg_i]; P2 = (P2&0x1f);
  smg_i++;
	smg_i &= 0x07;
}

独立按键的长短按键

参照了第十届国赛题中的矩阵长短按键:第十届国赛题的做法
首先写这儿时候,我先写了三行独立按键,但是发现不好长按S6不好操作(对于我来说不太熟悉),就放弃了。
改写为以前写过的状态机的思路:

状态1:检测按键是否按下;
状态2:赋值;
状态3:检测按键是否松开;

状态机消抖的原理我就不讲啦,这里主要假设按键正常按下的思路。
在状态三中,我们就可以记录从状态2中到状态三中按键弹起的时间。

  • 长按: 如果时间大于780ms,视为长按,此时按键6就可以一直输出值(将keyreturn2 = key)key为检测按键的值。
  • 短按: 如果时间小于780ms就弹起了,就将状态2中间变量最后赋值给keyreturn2一次,然后输出。再次进入keyscan()函数中,keyreturn2又被赋初值99;
void keyscan()
{
	u8 key = 99,keyreturn2 = 99;
	h1 = 1; h2 = 1; h3 = 1; h4 = 1;
	if(h1 != 1) key = 7;
	if(h2 != 1) key = 6;
	if(h3 != 1) key = 5;
	if(h4 != 1) key = 4;
	switch(state)
	{
		case 0: if(key!=99) state = 1;keyreturn = 99; time=0; break;
		case 1: if(key==99) state = 0; else{state = 2; keyreturn = key;} break;
		case 2: if(key==99) {state = 0;
                         time_flag = 0;
			                   if(time>780) keyreturn2 = keyreturn*10;
			                   else keyreturn2 = keyreturn;
		                    }

只要把以上的思路理解啦,以后长短按键都不怕咯。

按键我同时写了led,检测按键模块是否写对。模块如下:

void led(u8 dat)
{
	P2 = (P2&0x1f)|0x80; P0 = ~dat; P2 = (P2&0x1f);
}
	switch(keyreturn2)
	{
		case 4:  led(0x10); break;
		case 5:  led(0x08); break;
		case 6:  led(0x04); break;
		case 7:  led(0x02); break;
		case 60: led(0x01); break;
	}

测电压(PCF)

测量光敏的通道是0x01,测量电压的通道是0x03,输出电压的通道是0x40。
关于这部分代码可以看我之前的博客。
链接:依据PCF芯片手册写代码

存储数据(AT24C02)

关于这部分代码可以参考我前面的文章,看芯片手册就行写代码,比赛完全不用背模块代码,只需要注意一些读写数据延时的细节即可:
链接:教你依据AT24C02英文手册写代码

   w_at24(0x55,40);
	 Delay5ms();   //刚刚没写这个,读出的数据re_v是255
	 re_v = r_at24(0x55);
//   Delay5ms();  //写不写这个无所谓不影响
w_at24(0x55,v);Delay5ms();  w_at24(0x56,pinlv/100);Delay5ms(); w_at24(0x57,pinlv%100);Delay5ms(); w_at24(0x58,temp/100);Delay5ms(); w_at24(0x59,temp%100);Delay5ms(); //写的后面要delay5没事,才能写进去

DS18B20

这个就是简单的测温模块,同样我是记得中文步骤,有好几个写法,我都放上来啦
在这里插入图片描述
扩大100倍的温度,保留了两位小数。

u16 r_t()
{
	u8 l,h;
	u16 temp;
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	Delay_OneWire(20);
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	l = Read_DS18B20();
	h = Read_DS18B20();
	temp = ((u16)(h<<8)|l)*6.25;
	return temp;
}


下面是测得整数温度

u8 r_t()
{
	u8 l,h,temp;
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	Delay_OneWire(20);
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	l = Read_DS18B20();
	h = Read_DS18B20();
	temp = h<<4|l>>4;//高位往高处移动,低位往低处移动
	return temp;
}

怎么样,我的模版是不是很简单~

NE555

题目要求是P34管脚进行外部脉冲计数
在这里插入图片描述
而在比赛时你可以查看STC手册
在这里插入图片描述
P34进行计数时,我们用的事定时器0,所以一开始看了题目,我就用的定时器1进行单片机整体程序的定时,中断,控制程序的运行。

测频原理和目的就是:测得单位时间内的外部脉冲数
那这时候T0作为计数器,进行计数;T1作为定时器,定时。
两者合作即可测得频率啦
代码如下:

void pinlv_count()
{
	if(flag1s)
	{ 
		TR0 = 0;   //如果这里不停止计数,pinlv的值就会很奇怪,写了这个,pinlv的值就会比较稳定
		flag1s = 0;
		pinlv = TH0*256+TL0;
	    TF0 = 0;
		TH0 = 0;
		TL0 = 0;
		TR0 = 1;
	}
}

要注意的是TR0一定要在pinlv = TH0*256+TL0;计算频率值 赋值为0,使T0停止计数。否则会测出错误的值(值会几k到几十k变动)。另外要退出计时时记得打开TR0,使得T0计数器工作。

哦对,还有一点,T0的工作模式bit3置1,让T0变为计数模式 直接利用位与TMOD |= 0x04;

在这里插入图片描述
好啦,那么第九届国赛题的模块就讲解完啦
剩下的就是逻辑咯

逻辑部分

先写S4控制的1)的三个界面

在这里插入图片描述
无误后进行下一步

S7按键

在这里插入图片描述

S6按键

虽然题目只说了按下 非电压阈值界面3) 按下S6,进入界面2)数据回显。但我为了控制方便,就设置为再次按下S6返回数据测试界面1)

在这里插入图片描述
然后在界面3)S6就是阈值的++

S5

在这里插入图片描述

整个按键&smg逻辑代码

检测按键:

	switch(keyreturn2)
	{
		case 4: mode++;if(mode == 3) mode = 0; break;
		case 5: flag_cunchu = 1;break;
		case 6: if(flag_interface==1){index_v++; if(index_v == 51) index_v = 1; } else {re_view = ~re_view; mode = 0; flag_onece = 1;} break;
		case 7: flag_interface = ~flag_interface; mode = 0; break;
		case 60: index_v++; if(index_v == 51) index_v = 1; break;//长按
	}

数码管界面:

void smg_dis()
{
if(flag_interface == 0 && re_view == 0) //dat_dis
{
	if(mode == 0)
	{led1 = 0; led2 = 0; led3 = 1;
	//数据显示-电压
	table[0] = 0x3e;
	table[1] = 0x00;
	table[2] = 0x00;
	table[3] = 0x00;
	table[4] = 0x00;
	table[5] = 0x00;
	table[6] = T_duan[v/10%10]|0x80;
	table[7] = T_duan[v%10];
	}
	else if(mode == 1)
	{led1 = 0; led2 = 1; led3 = 0;
	//频率显示
	table[0] = 0x71;
	table[1] = 0x00;
	table[2] = 0x00;
	if(pinlv/10000>0) 
	table[3] = T_duan[pinlv/10000%10]; 
	else table[3] = 0x00;
	if(pinlv/1000>0)  table[4] = T_duan[pinlv/1000%10]; else table[4] = 0x00;
	if(pinlv/100>0)   table[5] = T_duan[pinlv/100%10]; else table[5] = 0x00;
	if(pinlv/10>0)    table[6] = T_duan[pinlv/10%10]; else table[6] = 0x00;
	table[7] = T_duan[pinlv%10];	
   }
	else
	{led1 = 1; led2 = 0; led3 = 0;
	//温度显示
	table[0] = 0x39;
	table[1] = 0x00;
	table[2] = 0x00;
	table[3] = 0x00;
	table[4] = T_duan[temp/1000%10];
	table[5] = T_duan[temp/100%10]|0x80;
	table[6] = T_duan[temp/10%10];
	table[7] = T_duan[temp%10];		
	}
}
else if(flag_interface == 1)//index_dis
{
	led1 = 0; led2 = 0; led3 = 0;
	table[0] = 0x3e;
	table[1] = 0x00;
	table[2] = 0x00;
	table[3] = 0x00;
	table[4] = 0x00;
	table[5] = 0x00;
	table[6] = T_duan[index_v/10%10]|0x80;
	table[7] = T_duan[index_v%10];	
}	
else if(re_view == 1)
{
	led1 = 0; led2 = 0; led3 = 0;
	if(flag_onece)
	{
		flag_onece = 0;
		Delay5ms();   //刚刚没写这个,读出的数据re_v是255
	  re_v = r_at24(0x55);
		Delay5ms();   
	  re_pinlv = r_at24(0x56);	
    re_pinlv = re_pinlv*100;		
		Delay5ms();   //刚刚没写这个,读出的数据re_v是255
	  re_temp = r_at24(0x58)*100+r_at24(0x59);	
	}
	if(mode == 0)
	{
	//数据显示-电压
	table[0] = 0x3e;
	table[1] = 0x00;
	table[2] = 0x00;
	table[3] = 0x00;
	table[4] = 0x00;
	table[5] = 0x00;
	table[6] = T_duan[re_v/10%10]|0x80;
	table[7] = T_duan[re_v%10];
	}
	else if(mode == 1)
	{
	//频率显示
	table[0] = 0x71;
	table[1] = 0x00;
	table[2] = 0x00;
	if(re_pinlv/10000>0) 
	table[3] = T_duan[re_pinlv/10000%10]; 
	else table[3] = 0x00;
	if(re_pinlv/1000>0)  table[4] = T_duan[re_pinlv/1000%10]; else table[4] = 0x00;
	if(re_pinlv/100>0)   table[5] = T_duan[re_pinlv/100%10]; else table[5] = 0x00;
	if(re_pinlv/10>0)    table[6] = T_duan[re_pinlv/10%10]; else table[6] = 0x00;
	table[7] = T_duan[re_pinlv%10];	
   }
	else
	{
	//温度显示
	table[0] = 0x39;
	table[1] = 0x00;
	table[2] = 0x00;
	table[3] = 0x00;
	if(re_temp/1000>0) table[4] = T_duan[re_temp/1000%10]; else table[4] = 0x00;
	table[5] = T_duan[re_temp/100%10]|0x80;
	table[6] = T_duan[re_temp/10%10];
	table[7] = T_duan[re_temp%10];		
	}	
}

我每写一部分就会测试自己的逻辑是否正确。
就像是把一张乱网里的线慢慢摊开
学电子的同学,一定要学会分解,一步一步来,就会有水到渠成的顺利。

如果想要整个工程的代码,可以点赞收藏,评论区留下你的邮箱~

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐