蓝桥杯单片机第十四届国赛真题解析(西风版本模板)

    驻波今天将14届国赛写完了,踩了贼多坑。幸好最后还是满分了。接下来驻波把14国的一些注意点和我犯的一些错误说一下,希望能对看到这篇文章的各位有一定的启发。

(驻波先在这叠个夹,驻波本人大一,专业知识不是很强,第一次写博客,若有错误欢迎指出,也希望各位多多包涵!!!)

    一.首先我们先来看数码管部分的题目

 

   这个数码管显示界面有一个比较新的点——负数的显示。

   那么对于这个问题我们应该怎么解决呢?

   我使用的是分类讨论的方法。我们知道数码管显示负数的较为复杂的地方是"-"号应该在哪,正数没没有负号,而负数时,一位负数和2位负数负号显示的地方并不一样。那么我们能不能把负数分类成两种情况,再将正数分类成一种情况来讨论呢,这样子每个情况下负号的情况都是确定的,这样子负号的问题是不是就解决了。那么负数显示是不是就迎刃而解了。下面是代码

    if(Distance_Cail >=0)//Distance_Cail为校准值
		{
			Seg_Buf[5] = 10;
			Seg_Buf[6] = (Distance_Cail>=10)?Distance_Cail/10:10;
			Seg_Buf[7] = Distance_Cail%10;
		}
		else if (Distance_Cail < 0 && Distance_Cail > -10)
		{
			Seg_Buf[6] = 11;//-
			Seg_Buf[7] = -Distance_Cail;
		}
		else if (Distance_Cail <= -10)
		{
			Seg_Buf[5] = 11;//-
		    Seg_Buf[6] = -Distance_Cail/10;
			Seg_Buf[7] = -Distance_Cail%10;
		}

      这个负号问题解决了,整个数码管显示的就没有什么难点了。接下来我就说几点小细节。

       1.数据类型的优化:我们明显可以发现温度和DAC下限虽然都为小数,但是我们只保留了一位有效数字。那么我是不是可以把温度和DAC下限的值乘以10倍来记录。这样子我是不是就可以把原本两个float的变量变成了一个unsigned int 的变量和一个unsigned char的变量,不仅大幅减少了字节数,还避免了浮点数的精度问题造成的误差。

      2.小数点集中处理:为了数码管小数点的显示的逻辑集中化,我们选择在要加小数点的数码管位置上加上一个','号,在后续动态数码管显示时,集中判断我们Seg_Buf数组里面的值是否大于','即可。

   数码管部分具体代码如下:

/* 数码管显示变量 */
idata unsigned char Seg_Disp_Mode = 0;//数码管显示模式 0测距界面 1参数界面 2工厂界面
idata unsigned char ;//数码管显示缓冲区
idata bit Distance_Show_Mode = 0;//数码管显示模式 0cm 1m
idata bit Param_Mode = 0; //数码管参数设置模式 0为距离 1为温度
idata unsigned char Factory_Mode = 0;//工厂模式 0为校准值模式 1为速度设置模式 2为DAC输出模式 

/* 数据变量 */
idata unsigned int Tem_10x;//温度的十倍
idata unsigned int Distance;//测距变量
idata char Distance_Cail = 0;//校准值 -90——90
idata unsigned char Parameter[2] = {40,30};//参数变量 0为距离参数 10-90cm 1为温度参数 0-80°
idata unsigned int Speed = 340;//速度 10-9990m/s
idata uc DAC_Iimit_10x = 10;//DAC下限 1-20

void Seg_Proc()
{
	switch (Seg_Disp_Mode)
	{
	case 0://测距界面
        Seg_Buf[0] = (Tem_10x>=100)?Tem_10x/100%10:10;
		Seg_Buf[1] = Tem_10x/10%10+',';
		Seg_Buf[2] = Tem_10x%10;
		Seg_Buf[3] = 11;
		if(Distance_Show_Mode == 0)//如果在cm模式
		{
			Seg_Buf[4] = (Distance>1000)?Distance/1000%10:10;
			Seg_Buf[5] = (Distance>=100)?Distance/100%10:10;
			Seg_Buf[6] = (Distance>=10)?Distance/10%10:10;
			Seg_Buf[7] = Distance%10;
		}
		else if(Distance_Show_Mode == 1)//如果在m模式
		{
			Seg_Buf[4] = (Distance>1000)?Distance/1000%10:10;
			Seg_Buf[5] = Distance/100%10+',';
			Seg_Buf[6] = Distance/10%10;
			Seg_Buf[7] = Distance%10;
		}
		break;
	   case 1://参数界面
	   Seg_Buf[0] = 12;//P
       Seg_Buf[1] = Param_Mode;
	   Seg_Buf[1] += 1;
	   Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] =10;
	   Seg_Buf[6] = (Parameter[Param_Mode]>=10)?Parameter[Param_Mode]/10:10;
	   Seg_Buf[7] = Parameter[Param_Mode]%10;
	   break;
	   case 2://工厂界面
	   Seg_Buf[0] = 13;//F
	   Seg_Buf[1] = Factory_Mode+1;
	   switch (Factory_Mode)
	   {
	   case 0:
		Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = 10;
		if(Distance_Cail >=0)
		{
			Seg_Buf[5] = 10;
			Seg_Buf[6] = (Distance_Cail>=10)?Distance_Cail/10:10;
			Seg_Buf[7] = Distance_Cail%10;
		}
		else if (Distance_Cail < 0 && Distance_Cail > -10)
		{
			Seg_Buf[6] = 11;
			Seg_Buf[7] = -Distance_Cail;
		}
		else if (Distance_Cail <= -10)
		{
			Seg_Buf[5] = 11;
		    Seg_Buf[6] = -Distance_Cail/10;
			Seg_Buf[7] = -Distance_Cail%10;
		}
		break;
		case 1:
	    Seg_Buf[2] = Seg_Buf[3] = 10;
		Seg_Buf[4] = (Speed>=1000)?Speed/1000:10;
		Seg_Buf[5] = (Speed>=100)?Speed/100%10:10;
		Seg_Buf[6] = (Speed>=10)?Speed/10%10:10;
		Seg_Buf[7] = Speed%10;
		break;
		case 2:
        Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] = 10;
		Seg_Buf[6] = DAC_Iimit_10x/10+',';
		Seg_Buf[7] = DAC_Iimit_10x%10;
		break;
	   }
	   break;
	}
}
void Time1_Server() interrupt 3
{
  uwTick++;
  /*数码管动态扫描*/
	Seg_Pos = (++Seg_Pos)%8;
	if(Seg_Buf[Seg_Pos]>=',')//一定有=号
	  Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]-',',1);
	else
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],0);
}

   二.然后我们来看按键部分 

 

     按键这部分的比较新颖的考点为双按键,这个考点在今年的16届省赛也有所涉及。

     关于双按键问题我们如果用之前的按键底层是不是就不那么合适了。毕竟我们也不能通过Key_Old == 8 && Key_Old == 9来判断是否8,9同时按下吧。那么我的想法是改变按键底层。具体的代码如下:

    unsigned char temp = 0;
    P44 = 0; P42 = 1;P35 = 1;P34 = 1;
	if(P33 == 0)Temp = 4;
	if(P32 == 0)Temp = 5;
	if(P31 == 0)Temp = 6;
	if(P30 == 0)Temp = 7;
	
	P44 = 1; P42 = 0;P35 = 1;P34 = 1;
	if(P33 == 0)Temp = 8;
	if(P32 == 0)Temp = 9;
	if(P31 == 0)Temp = 10;
	if(P30 == 0)Temp = 11;
	if(P33 == 0 && P32 == 0) Temp = 89;
	
	P44 = 1; P42 = 1;P35 = 0;P34 = 1;
	if(P33 == 0)Temp = 12;
	if(P32 == 0)Temp = 13;
	if(P31 == 0)Temp = 14;
	if(P30 == 0)Temp = 15;
	
	P44 = 1; P42 = 1;P35 = 1;P34 = 0;
	if(P33 == 0)Temp = 16;
	if(P32 == 0)Temp = 17;
	if(P31 == 0)Temp = 18;
	if(P30 == 0)Temp = 19;
	
	return Temp;

   如上面的底层,我们判断P33 == 0并且P32 == 0是不是就能判断两个按键同时按下了,如何我再给这种情况的temp赋值,那么我在主函数判断89是否就可以判断是否按下双按键了。

   但是双按键还有一个细节需要注意,如果我们用Key_Down来判断的话,由于人手不太好在极短时间内同时按下S8和S9。那么如果比如单片机检测你是先按下的S8再按下的S9。那么这个情况下Key_Val=89,Key_Old=8,Key_Down =  Key_Val & (Key_Val ^ Key_Old)  = 89 & (89 ^ 8) = 81。并不等于89,那么这种情况下我就不太能通过Key_Down来判断是否按下了。同理Key_Up再这种情况下也一样会有误差(人手不太可能同时松开2个按键)。(4T评分好像用Key_Up和Key_Down不会扣分,因为4t用的是虚拟机)

  那么我应该用什么来判断呢。这里我通过Key_Old来进行判断,通过Key_Old == 89来判断按下,Key_Old != 89 来判断松开。

  具体代码如下:

	if(Key_Old == 89)
	{
	    LongDown_Judge = 1;//长按
	}
	if(Key_Old != 89)
	{
	    LongDown_Judge = 0;//长按结束
		if(LongDown_Time > 2000)
		{
			Init();//长按复位
		}
		LongDown_Time = 0;
	}

     然后就是按键的1个小细节:

    1.记录模式下,按键不进行操作。这个我们可以建立一个变量来判断是否在记录模式,然后在整个按键的switch-case结构的外层加上一个if判断即可。但是也可以在按键switch-case结构前,放一个if判断,如果为记录模式就返回空值就可以了。

   具体按键部分代码如下:

void Key_Proc()
{
  //读取按键值
  Key_Val = Key_Read();
  //判断按键是否按下
	Key_Down = Key_Val & (Key_Val ^ Key_Old);
  //判断按键是否释放
	Key_Up = ~Key_Val & (Key_Val ^ Key_Old);
  //更新按键旧值
	Key_Old = Key_Val;
	if(Key_Enable == 0)
	return;
	switch (Key_Down)
	{
      case 4:
	  Seg_Disp_Mode = (++Seg_Disp_Mode)%3;
	  switch(Seg_Disp_Mode)
	  {
	  case 0:
	  Distance_Show_Mode = 0;
	  break;
	  case 1:
	  Param_Mode = 0;
	  break;
	  case 2:
	  Factory_Mode = 0;
	  break;
	  }
	  break;	
	  case 5:
	  switch(Seg_Disp_Mode)
	  {
	  case 0:
	  Distance_Show_Mode = !Distance_Show_Mode;
	  break;
	  case 1:
	  Param_Mode = !Param_Mode;
	  break;
	  case 2:
	  Factory_Mode = (++Factory_Mode)%3;
	  break;
	  }
	  break;
	  case 8://加按钮
	  if(Seg_Disp_Mode == 1)
	  {
	    if(Param_Mode == 0)
		{
		  if(Parameter[0] < 90)
		  Parameter[0] += 10;
		}
		else if(Param_Mode == 1)
		{
		  if(Parameter[1] < 80)
		  Parameter[1] += 1;
		}
	  }
	  else if(Seg_Disp_Mode == 2)
	  {
	    if(Factory_Mode == 0)
		{
		  if(Distance_Cail < 90)
		  Distance_Cail += 5;
		}
		else if(Factory_Mode == 1)
		{
		  if(Speed < 9990)
		  Speed += 10;
		}
		else if(Factory_Mode == 2)
		{
		  if(DAC_Iimit_10x < 20)
		  DAC_Iimit_10x += 1;
		}
	  }
	  else if(Seg_Disp_Mode == 0)
	  {
	      Key_Enable = 0;
		  Distance_Record_Index = 0;//重置索引
	  }
	  break;
	  case 9://减按钮
	  if(Seg_Disp_Mode == 1)
	  {
	    if(Param_Mode == 0)
		{
		  if(Parameter[0] > 10)
		  Parameter[0] -= 10;
		}
		else if(Param_Mode == 1)
		{
		  if(Parameter[1] > 0)
		  Parameter[1] -= 1;
		}
	  }
	  else if(Seg_Disp_Mode == 2)
	  {
	    if(Factory_Mode == 0)
		{
		  if(Distance_Cail > -90)
		  Distance_Cail -= 5;
		}
		else if(Factory_Mode == 1)
		{
		  if(Speed > 10)
		  Speed -= 10;
		}
		else if(Factory_Mode == 2)
		{
		  if(DAC_Iimit_10x > 1)
		  DAC_Iimit_10x -= 1;
		}
	  }
	  else if(Seg_Disp_Mode == 0)
	  {
	      Volagte_Output_Judge = 1;
	  }
	}
	if(Key_Old == 89)
	{
	    LongDown_Judge = 1;//长按
	}
	if(Key_Old != 89)
	{
	    LongDown_Judge = 0;//长按结束
		if(LongDown_Time > 2000)
		{
			Init();//长按复位
		}
		LongDown_Time = 0;
	}
}
/*定时器部分*/
void Time1_Server() interrupt 3
{
  uwTick++;
  if(LongDown_Judge)
  {
    if(++LongDown_Time > 2000)
    {
      LongDown_Time = 2001;
    }
  }

  if(Key_Enable == 0)//如果在记录模式
  {
	if(++Record_Time >= 6000)
	{
	  Key_Enable = 1;
	  Record_Time = 0;
	}
  }
}

   接下来我们来看外设。本题中外设用了超声波,温度和DAC输出。 其中温度只有一个简单的读取操作,还比较简单我们在这就不再赘述。

  三.我们先来看超声波:

  超声波模块有三方面的主要问题:

  1.超声波的速度可变:
     在以前的题目当中我们一般超声波的速度为声速即位340m/s但是这道题声速不一定为340m/s。那这怎么办呢?初中物理知识告诉我们超声波测距公式为。那么我们知道超声波测距的时间单位为us,速度单位为m/s,距离单位为cm。那么

这就说明统一单位后的公式为。这样子我们是不是就可以将超声波的底层修改一下扩展到所有速度了。具体代码如下

unsigned int U_Wave_Data(unsigned int speed)
{
	unsigned long int U_Wave_Time;
	unsigned int temp;
  CMOD  = 0x00;
	CH = CL = 0;
	U_Wave_Init();
	CR = 1;
	while((RX == 1)&&(CF == 0));
	CR = 0;
	if(CF == 0)
	{
	  U_Wave_Time = (CH<<8)|CL;
	  temp = speed*U_Wave_Time/20000;// 10^-4 cm/us * us == cm 
	  return temp;
	}
	else
	{
	  CF = 0;
		return 0;
	}
}

     这里还有一个要注意的点是我把返回值的类型设置为了unsigned int 因为如果速度增大的话,超声波测距的最大结果也一定会大于255的。

   2. 记录模式

    这个我一开始的理解错误了,这个题目说的也不太具体,注意题目中说的是一组数据,记录连续变化。那么就相当于我需要每隔相同时间记录一次距离·,然后放进一个数组当中。这里我选择500ms采集一次数据(这个时间),那么6s就有12个数据要记录,那我就设立一个12位的数组来记录就可以了。但是单片机不太可能一直卡500ms进去一次,所以我在调度器中给超声波设置了490ms读取。

   3.加上校准值的距离可能为负数

   这个在14届国赛的题目中未提及,所以4t也不会设计相应的测评点。对于这个问题你可以选择置之不理,也可以将这个值不予记录,也可以直接将负数情况下的距离置零。

   超声波模块的代码具体如下:

/*超声波底层*/
unsigned int U_Wave_Data(unsigned int speed)
{
	unsigned long int U_Wave_Time;
	unsigned int temp;
  CMOD  = 0x00;
	CH = CL = 0;
	U_Wave_Init();
	CR = 1;
	while((RX == 1)&&(CF == 0));
	CR = 0;
	if(CF == 0)
	{
	  U_Wave_Time = (CH<<8)|CL;
	  temp = speed*U_Wave_Time/20000;// 10^-4 cm/us * us == cm 
	  return temp;
	}
	else
	{
	  CF = 0;
		return 0;
	}
}

/*main.c部分*/
void Get_Distance()
{
    unsigned int temp = 0;
    temp = U_Wave_Data(Speed);//读取距离
    Distance = (temp + Distance_Cail)>=0?(temp + Distance_Cail):0;
	if(Key_Enable == 0)
	{
		Record_Judge = 1;
		if(Distance_Record_Index < 12)
		{
	    Distance_Record[Distance_Record_Index] = Distance;
		Distance_Record_Index++;
		}
	}
}

   注意一下,由于本题要一个个记录,所以我们并不使用中值滤波来过滤距离值。 

   四.我们再来看DAC输出模块:

    DAC输出模块要注意的点只有一个,我们是用DAC来输出已记录的距离变化。既然我们是500ms记录的一次 距离数据,那么我们也要500ms一次输出转化的DAC电压。

   具体DAC模块代码如下:

void AD_DA()
{
  float temp = 0;
  if(Volagte_Output_Judge == 1)
  {
	if(Record_Judge == 1)
	{
	  if(Distance_Record[DAC_Output_Index] < 10)
		temp = DAC_Iimit_10x/10.0;
	  else if(Distance_Record[DAC_Output_Index] >= 10 && Distance_Record[DAC_Output_Index] <= 90)
	     temp = (5-DAC_Iimit_10x/10.0)/80.0*(Distance_Record[DAC_Output_Index]-10)+DAC_Iimit_10x/10.0;
	  else if(Distance_Record[DAC_Output_Index] > 90)
		temp = 5;
	  if (DAC_Output_Index == 11)
	  {
	    DAC_Output_Index = 0;
		Volagte_Output_Judge = 0;//停止输出
	  }
	  Da_Write(temp*51);
	}
  }
}
/*中断函数部分*/
void Time1_Server() interrupt 3
{
if(Volagte_Output_Judge == 1)
  {
    if(++DAC_SlowDown >= 500)
	{
	  DAC_SlowDown = 0;
	  DAC_Output_Index++;
	}
  }
}

    五.最后我们来看Led模块的代码:

   

    这个模块唯一的难点在与测距界面下如何用Led表示二进制。这个我们换一个角度思考,就是我们能不能把距离全转换成一个unsigned char 的变量,然后通过取出单位数据的方式,来判断单位数据是否为0,从而判断Led灯是否亮起,若单位为0则熄灭,单位为1则点亮。

    关于转换还算比较简单的,我们设一个临时变量temp,如果距离值小于等于255我们令temp等于距离值,如果大于255,题目告诉我全亮,那么全亮表示的二进制的值也恰好是255,那么此时我令temp为255就可以了。

   那么接下来我们只需要把temp用Led表示出来就可以了。这个问题怎么解决呢。用Led表示二进制的本质是每一个Led表示单位数据,其中亮为1灭为0。那么反过来我们把temp的单位提出来,判断是否为0,然后赋值给Led,是不是就可以了。那么如何取出呢?这里我们用到或的方法来实现。

temp & (0x01<<i)

   若我们提出第一位,那么我们或上0x01,除了第一位之外的所有位都变成了0,对于第一位则不变,这样我们是不是就提出了第一位。同理若我们要提出第i位是不是就只需将0x01左移i位就可以了。那么取出8位是不是只需一个for循环即可搞定,我们再判断单位是否为0(用三目运算符)来给Led赋值就可以了。

    具体的Led模块的代码如下 :

void Led_Proc()
{
	unsigned char temp,i;
	bit Relay_Enable;
    switch (Seg_Disp_Mode)
	{
	case 0:
        if(Distance <= 255)
		temp = Distance;
		else
		temp = 255;
		for(i=0;i<8;i++)
		{
		  ucLed[i] = (temp & (0x01<<i))?1:0;
		}
		break;
	case 1:
	    ucLed[0] = ucLed[1] = ucLed[2] = ucLed[3] = ucLed[4] = ucLed[5]	= ucLed[6] = 0;
	    ucLed[7] = 1;
		break;
	case 2:
	ucLed[7] = ucLed[1] = ucLed[2] = ucLed[3] = ucLed[4] = ucLed[5]	= ucLed[6] = 0;
	ucLed[0] = Flash_Judge;    
	break;
	}
	if(Distance <= (Parameter[0] +5) && Distance >= (Parameter[0] -5) && Tem_10x/10.0 <= Parameter[1])
	Relay_Enable = 1;
	else
	Relay_Enable = 0;
	Relay(Relay_Enable);
	Led_Disp(ucLed);
}

/* 中断部分 */
void Time1_Server() interrupt 3
{
  if(Seg_Disp_Mode == 2)//如果在工厂模式
  {
    if(++Led_SlowDown >= 100)
	{
	  Led_SlowDown = 0;
	  Flash_Judge = !Flash_Judge;
	}
  }
  else
  {
    Flash_Judge = 0;
	Led_SlowDown = 0;
  }
}

  那么到这这道题就已经完成了。 下面是全部的代码:

/* Key.c */
unsigned char Key_Read()
{
   unsigned char Temp = 0;
	P44 = 0; P42 = 1;P35 = 1;P34 = 1;
	if(P33 == 0)Temp = 4;
	if(P32 == 0)Temp = 5;
	if(P31 == 0)Temp = 6;
	if(P30 == 0)Temp = 7;
	
	P44 = 1; P42 = 0;P35 = 1;P34 = 1;
	if(P33 == 0)Temp = 8;
	if(P32 == 0)Temp = 9;
	if(P31 == 0)Temp = 10;
	if(P30 == 0)Temp = 11;
	if(P33 == 0 && P32 == 0) Temp = 89;
	
	P44 = 1; P42 = 1;P35 = 0;P34 = 1;
	if(P33 == 0)Temp = 12;
	if(P32 == 0)Temp = 13;
	if(P31 == 0)Temp = 14;
	if(P30 == 0)Temp = 15;
	
	P44 = 1; P42 = 1;P35 = 1;P34 = 0;
	if(P33 == 0)Temp = 16;
	if(P32 == 0)Temp = 17;
	if(P31 == 0)Temp = 18;
	if(P30 == 0)Temp = 19;
	
	return Temp;
}

/* 超声波部分 */
unsigned int U_Wave_Data(unsigned int speed)
{
	unsigned long int U_Wave_Time;
	unsigned int temp;
    CMOD  = 0x00;
	CH = CL = 0;
	U_Wave_Init();
	CR = 1;
	while((RX == 1)&&(CF == 0));
	CR = 0;//ֹͣ��ʱ
	if(CF == 0)
	{
	  U_Wave_Time = (CH<<8)|CL;
	  temp = speed*U_Wave_Time/20000;// 10^-4 cm/us * us == cm 
	  return temp;
	}
	else
	{
	  CF = 0;
		return 0;
	}
}

/*main.c*/
/*头文件声明区*/
//器件底层
#include <Led.H>
#include <Init.H>
#include <Seg.H>
#include <Key.H>
#include <U_Wave.H>
#include <iic.H>
#include <ds1302.H>
#include <onewire.H>
#include <Uart.H>
#include <filtering.h>

//库函数底层
#include <STC15F2K60S2.H>
#include <intrins.h>
#include <math.h>
#include <String.H>

/*变量声明区*/
typedef unsigned char uc;
typedef unsigned int ui;
idata uc ucLed[8] = {0,0,0,0,0,0,0,0};//Led数据存储数组
idata uc Key_Val,Key_Old,Key_Down,Key_Up;//按键处理哈四年专用变量
idata uc Seg_Pos,Seg_Buf[8] = {10,10,10,10,10,10,10,10};//数码管动态扫描专用变量
idata unsigned long int uwTick;//系统计时变量

/* 数码管显示变量 */
idata unsigned char Seg_Disp_Mode = 0;//数码管显示模式 0测距界面 1参数界面 2工厂界面
idata unsigned char ;//数码管显示缓冲区
idata bit Distance_Show_Mode = 0;//数码管显示模式 0cm 1m
idata bit Param_Mode = 0; //数码管参数设置模式 0为距离 1为温度
idata unsigned char Factory_Mode = 0;//工厂模式 0为校准值模式 1为速度设置模式 2为DAC输出模式 

/* 数据变量 */
idata unsigned int Tem_10x;//温度的十倍
idata unsigned int Distance;//测距变量
idata char Distance_Cail = 0;//校准值 -90——90
idata unsigned char Parameter[2] = {40,30};//参数变量 0为距离参数 10-90cm 1为温度参数 0-80°
idata unsigned int Speed = 340;//速度 10-9990m/s
idata uc DAC_Iimit_10x = 10;//DAC下限 1-20
idata ui Distance_Record[12] = {0};//测距记录
idata unsigned char Distance_Record_Index = 0;//测距记录索引
idata bit Record_Judge;//测距记录判断
idata bit LongDown_Judge;//长按判断
idata unsigned int LongDown_Time = 0;//长按时间 2000ms
idata bit Key_Enable = 1;//按键使能 0为未使能 1为使能
idata ui Record_Time;//测距记录时间 6000ms
idata bit Volagte_Output_Judge;//电压输出判断
idata bit Flash_Judge;
idata unsigned char Led_SlowDown; //100ms
idata ui DAC_Output_Index = 0;//DAC输出索引
idata ui DAC_SlowDown = 0;//DAC输出延时 500ms

/*  长按复位函数 */
void Init()
{
   unsigned char i;
   Distance_Show_Mode = 0;
   Parameter[0] = 40;
   Parameter[1] = 30;
   Distance_Cail = 0;
   Speed = 340;
   DAC_Iimit_10x = 10;
   Record_Time = 0;
   Key_Enable = 1;
   Record_Judge = 0;
   Distance_Record_Index = 0;
   Seg_Disp_Mode = 0;
   for(i = 0;i < 12;i++)
   {
      Distance_Record[i] = 0;
   }
}

/*按键处理函数*/
void Key_Proc()
{
  //读取按键值
  Key_Val = Key_Read();
  //判断按键是否按下
	Key_Down = Key_Val & (Key_Val ^ Key_Old);
  //判断按键是否释放
	Key_Up = ~Key_Val & (Key_Val ^ Key_Old);
  //更新按键旧值
	Key_Old = Key_Val;
	if(Key_Enable == 0)
	return;
	switch (Key_Down)
	{
      case 4:
	  Seg_Disp_Mode = (++Seg_Disp_Mode)%3;
	  switch(Seg_Disp_Mode)
	  {
	  case 0:
	  Distance_Show_Mode = 0;
	  break;
	  case 1:
	  Param_Mode = 0;
	  break;
	  case 2:
	  Factory_Mode = 0;
	  break;
	  }
	  break;	
	  case 5:
	  switch(Seg_Disp_Mode)
	  {
	  case 0:
	  Distance_Show_Mode = !Distance_Show_Mode;
	  break;
	  case 1:
	  Param_Mode = !Param_Mode;
	  break;
	  case 2:
	  Factory_Mode = (++Factory_Mode)%3;
	  break;
	  }
	  break;
	  case 8://加按钮
	  if(Seg_Disp_Mode == 1)
	  {
	    if(Param_Mode == 0)
		{
		  if(Parameter[0] < 90)
		  Parameter[0] += 10;
		}
		else if(Param_Mode == 1)
		{
		  if(Parameter[1] < 80)
		  Parameter[1] += 1;
		}
	  }
	  else if(Seg_Disp_Mode == 2)
	  {
	    if(Factory_Mode == 0)
		{
		  if(Distance_Cail < 90)
		  Distance_Cail += 5;
		}
		else if(Factory_Mode == 1)
		{
		  if(Speed < 9990)
		  Speed += 10;
		}
		else if(Factory_Mode == 2)
		{
		  if(DAC_Iimit_10x < 20)
		  DAC_Iimit_10x += 1;
		}
	  }
	  else if(Seg_Disp_Mode == 0)
	  {
	      Key_Enable = 0;
		  Distance_Record_Index = 0;//重置索引
	  }
	  break;
	  case 9://减按钮
	  if(Seg_Disp_Mode == 1)
	  {
	    if(Param_Mode == 0)
		{
		  if(Parameter[0] > 10)
		  Parameter[0] -= 10;
		}
		else if(Param_Mode == 1)
		{
		  if(Parameter[1] > 0)
		  Parameter[1] -= 1;
		}
	  }
	  else if(Seg_Disp_Mode == 2)
	  {
	    if(Factory_Mode == 0)
		{
		  if(Distance_Cail > -90)
		  Distance_Cail -= 5;
		}
		else if(Factory_Mode == 1)
		{
		  if(Speed > 10)
		  Speed -= 10;
		}
		else if(Factory_Mode == 2)
		{
		  if(DAC_Iimit_10x > 1)
		  DAC_Iimit_10x -= 1;
		}
	  }
	  else if(Seg_Disp_Mode == 0)
	  {
	      Volagte_Output_Judge = 1;
	  }
	}
	if(Key_Old == 89)
	{
	    LongDown_Judge = 1;//长按
	}
	if(Key_Old != 89)
	{
	    LongDown_Judge = 0;//长按结束
		if(LongDown_Time > 2000)
		{
			Init();//长按复位
		}
		LongDown_Time = 0;
	}
}

/*信息处理函数*/
void Seg_Proc()
{
	switch (Seg_Disp_Mode)
	{
	case 0://测距界面
        Seg_Buf[0] = (Tem_10x>=100)?Tem_10x/100%10:10;
		Seg_Buf[1] = Tem_10x/10%10+',';
		Seg_Buf[2] = Tem_10x%10;
		Seg_Buf[3] = 11;
		if(Distance_Show_Mode == 0)//如果在cm模式
		{
			Seg_Buf[4] = (Distance>1000)?Distance/1000%10:10;
			Seg_Buf[5] = (Distance>=100)?Distance/100%10:10;
			Seg_Buf[6] = (Distance>=10)?Distance/10%10:10;
			Seg_Buf[7] = Distance%10;
		}
		else if(Distance_Show_Mode == 1)//如果在m模式
		{
			Seg_Buf[4] = (Distance>1000)?Distance/1000%10:10;
			Seg_Buf[5] = Distance/100%10+',';
			Seg_Buf[6] = Distance/10%10;
			Seg_Buf[7] = Distance%10;
		}
		break;
	   case 1://参数界面
	   Seg_Buf[0] = 12;//P
       Seg_Buf[1] = Param_Mode;
	   Seg_Buf[1] += 1;
	   Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] =10;
	   Seg_Buf[6] = (Parameter[Param_Mode]>=10)?Parameter[Param_Mode]/10:10;
	   Seg_Buf[7] = Parameter[Param_Mode]%10;
	   break;
	   case 2://工厂界面
	   Seg_Buf[0] = 13;//F
	   Seg_Buf[1] = Factory_Mode+1;
	   switch (Factory_Mode)
	   {
	   case 0:
		Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = 10;
		if(Distance_Cail >=0)
		{
			Seg_Buf[5] = 10;
			Seg_Buf[6] = (Distance_Cail>=10)?Distance_Cail/10:10;
			Seg_Buf[7] = Distance_Cail%10;
		}
		else if (Distance_Cail < 0 && Distance_Cail > -10)
		{
			Seg_Buf[6] = 11;
			Seg_Buf[7] = -Distance_Cail;
		}
		else if (Distance_Cail <= -10)
		{
			Seg_Buf[5] = 11;
		    Seg_Buf[6] = -Distance_Cail/10;
			Seg_Buf[7] = -Distance_Cail%10;
		}
		break;
		case 1:
	    Seg_Buf[2] = Seg_Buf[3] = 10;
		Seg_Buf[4] = (Speed>=1000)?Speed/1000:10;
		Seg_Buf[5] = (Speed>=100)?Speed/100%10:10;
		Seg_Buf[6] = (Speed>=10)?Speed/10%10:10;
		Seg_Buf[7] = Speed%10;
		break;
		case 2:
        Seg_Buf[2] = Seg_Buf[3] = Seg_Buf[4] = Seg_Buf[5] = 10;
		Seg_Buf[6] = DAC_Iimit_10x/10+',';
		Seg_Buf[7] = DAC_Iimit_10x%10;
		break;
	   }
	   break;
	}
}

/*Led处理函数*/
void Led_Proc()
{
	unsigned char temp,i;
	bit Relay_Enable;
    switch (Seg_Disp_Mode)
	{
	case 0:
        if(Distance <= 255)
		temp = Distance;
		else
		temp = 255;
		for(i=0;i<8;i++)
		{
		  ucLed[i] = (temp & (0x01<<i))?1:0;
		}
		break;
	case 1:
	    ucLed[0] = ucLed[1] = ucLed[2] = ucLed[3] = ucLed[4] = ucLed[5]	= ucLed[6] = 0;
	    ucLed[7] = 1;
		break;
	case 2:
	ucLed[7] = ucLed[1] = ucLed[2] = ucLed[3] = ucLed[4] = ucLed[5]	= ucLed[6] = 0;
	ucLed[0] = Flash_Judge;    
	break;
	}
	if(Distance <= (Parameter[0] +5) && Distance >= (Parameter[0] -5) && Tem_10x/10.0 <= Parameter[1])
	Relay_Enable = 1;
	else
	Relay_Enable = 0;
	Relay(Relay_Enable);
	Led_Disp(ucLed);
}


/*AD_DA*/
void AD_DA()
{
  float temp = 0;
  if(Volagte_Output_Judge == 1)
  {
	if(Record_Judge == 1)
	{
	  if(Distance_Record[DAC_Output_Index] < 10)
		temp = DAC_Iimit_10x/10.0;
	  else if(Distance_Record[DAC_Output_Index] >= 10 && Distance_Record[DAC_Output_Index] <= 90)
	     temp = (5-DAC_Iimit_10x/10.0)/80.0*(Distance_Record[DAC_Output_Index]-10)+DAC_Iimit_10x/10.0;
	  else if(Distance_Record[DAC_Output_Index] > 90)
		temp = 5;
	  if (DAC_Output_Index == 11)
	  {
	    DAC_Output_Index = 0;
		Volagte_Output_Judge = 0;//停止输出
	  }
	  Da_Write(temp*51);
	}
  }
}

/*温度读取函数*/
void Read_Tem()
{
   unsigned int temp = 0;
   temp = (ui)(rd_Tem()*10);//读取温度
   Tem_10x = Median_Filter(temp);//中值滤波
}

void Get_Distance()
{
    unsigned int temp = 0;
    temp = U_Wave_Data(Speed);//读取距离
    Distance = (temp + Distance_Cail)>=0?(temp + Distance_Cail):0;//    Distance = Median_Filter(temp);//中值滤波
	if(Key_Enable == 0)
	{
		Record_Judge = 1;
		if(Distance_Record_Index < 12)
		{
	    Distance_Record[Distance_Record_Index] = Distance;
		Distance_Record_Index++;
		}
	}
}

/*计时器1初始化函数*/
void Timer1Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0xBF;		//定时器时钟12T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0x18;		//设置定时初值
	TH1 = 0xFC;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	EA = 1;
	ET1 = 1;
}

void Time1_Server() interrupt 3
{
  uwTick++;
	
  if(LongDown_Judge)
  {
    if(++LongDown_Time > 2000)
    {
      LongDown_Time = 2001;
    }
  }

  if(Key_Enable == 0)//如果在记录模式
  {
	if(++Record_Time >= 6000)
	{
	  Key_Enable = 1;
	  Record_Time = 0;
	}
  }

  if(Seg_Disp_Mode == 2)//如果在工厂模式
  {
    if(++Led_SlowDown >= 100)
	{
	  Led_SlowDown = 0;
	  Flash_Judge = !Flash_Judge;
	}
  }
  else
  {
    Flash_Judge = 0;
	Led_SlowDown = 0;
  }

  if(Volagte_Output_Judge == 1)
  {
    if(++DAC_SlowDown >= 500)
	{
	  DAC_SlowDown = 0;
	  DAC_Output_Index++;
	}
  }
	/*数码管动态扫描*/
	Seg_Pos = (++Seg_Pos)%8;
	if(Seg_Buf[Seg_Pos]>=',')//一定有=号
	  Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]-',',1);
	else
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],0);
}

/*定义调度器结构体*/
typedef struct
{
  void (*task_func)(void);
	unsigned long int rate_ms;//运行周期
	unsigned long int last_run;//上一次运行的时间
}task_c;

/*配置调度器*/
idata task_c Scheduler_Task[] = 
{
  {Led_Proc,1,0},//Led运行函数 1ms执行一次
	{Key_Proc,10,0},//按键运行函数 1ms执行一次
	{Seg_Proc,100,0},//数码管运行函数 100ms执行一次
	{AD_DA,80,0},//数模电读取函数 90ms执行一次
	{Read_Tem,100,0},//温度读取函数 100ms执行一次
	{Get_Distance,480,0},//距离读取函数 480ms执行一次
};

idata uc Task_Num;//调度器任务数量

/*调度器初始化*/
void Scheduler_Init()
{
  Task_Num = sizeof(Scheduler_Task)/sizeof(task_c);
}

/*调取器运行函数*/
void Scheduler_Run()
{
  unsigned char i;
	for(i=0;i<Task_Num;i++)
	{
		unsigned long int Now_Time = uwTick;
	  if(Now_Time > (Scheduler_Task[i].last_run+Scheduler_Task[i].rate_ms))
		{
		  Scheduler_Task[i].last_run = Now_Time;//顺序不能反
			Scheduler_Task[i].task_func();
		}
	}
}


/*main*/
void main()
{
    System_Init();
	Scheduler_Init();//放在定时器初始化前面
	Timer1Init();
	while(1)
	{
    Scheduler_Run();
	}
}

  

 

### 关于蓝桥杯单片机第十五届中的西风模板蓝桥杯单片机中,尤其是像第十五届这样的事,题目通常会涉及实际工程应用背景下的硬件控制与软件编程能力测试。对于提到的“西风模板”,虽然具体细节未完全公开[^1],但从以往的经验来看,“西风模板”可能是某类特定的功能模块或者应用场景的设计代号。 #### 可能的方向分析 1. **功能需求解析** “西风模板”的命名方式可能暗示其具有某种特殊的应用场景或技术特点。例如,在嵌入式开发领域,它可能涉及到传感器数据采集、通信协议处理(如MQTT)、PWM波形生成等功能[^2]。这些功能可以通过STM32微控制器及其外设来实现。 2. **示例代码结构** 下面提供一段基于STM32平台的伪代码框架作为参考,假设该模板的核心任务是对一组输入信号进行处理并输出结果: ```c #include "stm32f10x.h" void SystemInit(void); float calculateSquareRoot(float value); int main() { float inputValue, result; // 初始化系统时钟及其他资源 SystemInit(); while (1) { // 假设通过串口接收字符串形式的数据 char buffer[50]; gets(buffer); // 获取用户输入 // 使用 sscanf 提取数值 if(sscanf(buffer, "%f", &inputValue) == 1){ // 计算平方根 result = calculateSquareRoot(inputValue); // 将结果显示到调试终端或其他设备上 printf("Result: %.2f\n", result); } // 添加延时防止CPU占用过高 Delay(100); } } // 开方计算函数定义 float calculateSquareRoot(float value){ return sqrt(value); // 利用标准库函数求解 } ``` 上述代码展示了如何利用 `sscanf` 函数从字符串中提取浮点数,并调用 `sqrt()` 完成简单的数学运算操作。需要注意的是,实际比中可能会加入更多复杂逻辑以及错误检测机制以提高鲁棒性。 3. **设计方案概述** 针对“西风模板”的整体解决方案可以分为以下几个方面考虑: - 数据获取阶段:确定采用何种方法读取外部环境参数; - 处理流程制定:明确内部算法模型构建思路; - 输出呈现形式:规划最终成果展示途径。 尽管目前无法确切获知官方发布的全部内容,但以上介绍能够帮助参者初步理解此类问题的大致方向。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值