【第十二届蓝桥杯嵌入式省赛真题】

第十二届蓝桥杯嵌入式-停车计费

文章目录

  • 第十二届蓝桥杯嵌入式-停车计费
  • 1.题目分析
  • 2.项目结构
  • 2.1停车部分整体流程
  • 2.2串口数据解析流程
  • 2.3细节部分
  • 3.代码结构
  • 3.1停车部分
  • 3.2主函数部分
  • 1.题目分析

    刚看到题目时 大致看了一眼后,第一眼发现是串口部分比较不容易,其他的模块都很简单。串口的逻辑是主要难点,所以我首先先构想了一下串口部分的项目组成逻辑。然后才开始一步步做题。
    源码去资源或者文章末尾里找哈!下载不收钱!!!




    2.项目结构

    基本的逻辑页面切换等这里就不多赘述主要是停车计费部分逻辑

    2.1停车部分整体流程

    我的整体思路如下:

    1. 每过来一辆车 就先判断能不能 “出库”。如果不能出库的车,那么才有可能停的进车库
    2. 如果这辆车能够出库,也就是当前车库已经存在这辆车,那么直接出库即可
    3. 如果当前车辆不能出库,那么执行停车函数,判断有没有可以停车的车位进行停车即可
    

    2.2串口数据解析流程

    自己写了一个根据索引和长度截取字符串的函数

    后期配合strcpy,atoi可以轻松的截取解析字符串和转化数字

    2.3细节部分

    1. 出库的车的时间,不能比入库时早
    2. 车辆类型只有CNBR和VNBR
    3. 时间的格式必须要正确,比如小时必须小于24,分钟必须小于等于60这种的
    4. 不满一小时按照一小时算
    

    3.代码结构

    3.1停车部分

    结构如下 我采取结构体数组的方法模拟停车场,并分出时间结构体和停车结构体

    整理出其中的关键代码进行讲解

    停车 park.c

    //停车
    int park(Parking* parking, Car car, Time time)
    {
    	int i = 0;
    	int typeFlag;
    	//没位置了
    	if(remainParkSize<=0)
    	{
    			return 0;
    	}
    	//判断车辆类型
    	if (strcmp(car.type, "CNBR")==0)
    	{
    	  	typeFlag=1;
    	}
    		if (strcmp(car.type, "VNBR")==0) 
    	{
    			typeFlag=2;
    	}
    	for (i = 0; i < 8; i++)
    	{
    		//判断能否停车
    		if (canPark(parking[i]))
    		{
    			//CNBR
    			if(typeFlag==1)
    			{
    				remainParkSize--;
    				cnbrSize++;
    			}
    			//VNBR
    			if(typeFlag==2)
    			{
    				
    				remainParkSize--;
    				vnbrSize++;
    			}
    			//车位不够的时候关灯
    			if(remainParkSize<=0)
    			{
    					LED_Control(LEDALL,0);
    			}
    			//到这就是成功停车了 结构体内赋值
    			strcpy((parking[i].car).id, car.id);
    			strcpy((parking[i].car).type, car.type);
    			parking[i].time.year = time.year;
    			parking[i].time.month = time.month;
    			parking[i].time.day = time.day;
    			parking[i].time.hour = time.hour;
    			parking[i].time.minute = time.minute;
    			parking[i].time.second = time.second;
    			//该车位置为不能停车
    			parking[i].canPark = 0;
    			return 1;
    		}
    	}
    	return 0;
    }
    

    出库 goOut.c

    double goOut(Parking* parking, Car car, Time time, float cnbrPrice, float vnbrPrice)
    {
    	int i = 0;
    	int typeFlag = 0;
    	double price = 0;
    	Car oldCar;
    	Time oldTime;
    	//判断车辆类型
    	if (strcmp(car.type, "CNBR")==0)
    	{
    		price = cnbrPrice;
    	  	typeFlag=1;
    	}
    	if (strcmp(car.type, "VNBR")==0)
    	{
    		price = vnbrPrice;
    		typeFlag=2;
    	}
    	for (i = 0; i < 8; i++)
    	{
    		oldCar = parking[i].car;
    		oldTime = parking[i].time;
    		//如果该位置可以停车,那肯定和出库没有关系 直接不进行下一步判断
    		if (parking[i].canPark == 1)
    		{
    			continue;
    		}
    		//如果存在相同的车辆 那么可以出库
    		if (isExist(oldCar, car))
    		{
    			//CNBR
    			if(typeFlag==1)
    			{
    				remainParkSize++;
    				cnbrSize--;
    			}
    			//VNBR
    			if(typeFlag==2)
    			{
    				remainParkSize++;
    				vnbrSize--;
    			}
    			//计算停车的小时数
                //这里已经能够判断 年月日小时,分和秒要继续判断
    			parkTime = (time.year - oldTime.year) * 365 * 24 +
    								 (time.month - oldTime.month) * 30 * 24 +
    								 (time.day - oldTime.day) * 24 +
    								 (time.hour - oldTime.hour);
    			//时间非法
    			if(parkTime<0)
    			{
    				parkTime=0;
    				return -2;
    			}
    			//年月日小时相等
    			else if(parkTime==0)
    			{
    				//时间非法
    				if((time.minute*60+time.second)-(oldTime.minute*60+time.second) < 0)
    				{
    					return -2;
    				}
    				//未满一小时
    				else
    				{
    					successGoOut(parking,i);
    					return price;
    				}
    			}
    			else
    			{
    				sum = price * (float)parkTime;
    			}	
                //成功出库
    			successGoOut(parking,i);
    			return sum;
    		}
    	}
    	return -1;
    }
    

    综合以上两个函数,即可进行总的停车逻辑控制,里面的小函数,像判断车的存在啊,成功出库后的车位清0操作,开灯,以及判断同名车辆什么的都很简单,这里就不发啦,直接看源码就好。

    这里我们来看看逻辑组合部分

    parkControl.c

    //停车控制
    void parkControl(Parking *parking,Car car,Time time)
    {
    	sum = goOut(parking,car,time,cnbrPrice,vnbrPrice);
    	//车库中没找到这辆车 尝试停车
    	if(sum==-1.0)
    	{
    		if(park(parking,car,time))
    		{
    				USART_SendString("Success parking car!\n");
    		}
    		else
    		{
    				USART_SendString("Error parking car!\n");
    		}
    	}
    	//停车时长错误
    	if(sum==-2)
    	{
    		USART_SendString("Error ParkTime\n");
    	}
    	//找到该车,出库
    	if(sum>=0)
    	{
    			sprintf(sendComputer,"%s:%s:%dhour:%.2f$\n",type,id,parkTime,sum);
    			USART_SendString(sendComputer);
    	}
    	return;
    }
    

    到这我们已经实现了停车逻辑的控制也就是逻辑部分已经完成

    3.2主函数部分

    接下来大家来看看主函数的控制和串口解析,函数结构如下

    //刷新显示区
    void fflushData();
    void fflushPara();
    void updateAll();
    void show();
    
    //串口解析
    int analysis(char* data);
    //接收数据操作
    void receiveData();
    //切割字符串
    char* substring(char* ch,int pos,int length);
    

    首先最重要的先来看切割字符串,其实就是利用指针的操作赋值。

    这样后续我们切割串口接收的数据时就非常的轻松并且明了。

    参数1是待切割的串,参数2是切割起始位置,参数3是切割长度
    切割字符串

    char* substring(char* ch,int pos,int length)  
    {  
        char* des=ch;  
        char* subch;  
        int i;  
        des=des+pos;  
        for(i=0;i<length;i++)  
        {  
            subch[i]=*(des++);  
        }  
        subch[length]='\0';
        return subch; 
    } 
    

    数据解析

    int analysis(char* data)
    {
        	//配合切割函数简单完成
    		strcpy(type,substring(data,0,4));
    		strcpy(id,substring(data,5,4));
    		year = atoi(substring(data,10,2));
    		month = atoi(substring(data,12,2));
    		day = atoi(substring(data,14,2));
    		hour = atoi(substring(data,16,2));
    		minute = atoi(substring(data,18,2));
    		second = atoi(substring(data,20,2));
    
    		//清空接收区
    		memset(USART_RXBUF,0,sizeof(USART_RXBUF));
    		
        	//车辆类型限定
    		if(strcmp(type,"VNBR")!=0 && strcmp(type,"CNBR")!=0)
    		{
    			USART_SendString("Error Type Format\n");
    			return 0;
    		}
        	//时间bug处理
    		else if(month>12 || day>31 || hour>24 || minute>60 || second>60 )
    		{
    			USART_SendString("Error Time Format\n");
    			return 0;
    		}
    }
    

    利用这两个函数十分轻易的就做到了数据的解析和保存

    这里最后我们只需要配合接收数据时的操作,就可以基本完成停车部分了

    接收数据的操作如下

    //接收数据开始逻辑
    void receiveData()
    {
    		if(RXOVER == 1)
    		{
    			RXOVER = 0;
    			//调用解析数据
    			if(analysis(USART_RXBUF))
    			{
    				//初始化车和时间
    				car = initCar(id,type);
    				time = initTime(year,month,day,hour,minute,second);
    				//停车流程控制
    				parkControl(parking,car,time);
    			}
    			//这里是为了保证我们在显示车位界面时的实时刷新(小处理 问题不大)
                //因为接收数据是无论在哪个界面时都要接收的,为了保证数据实时的刷新显示
                //利用这个标志位解决
    			if(showFlag==0)
    			{
    				fflushData();
    			}
    			USART_ITConfig(USART2	,USART_IT_RXNE,ENABLE);
    			return;
    		}
    		return;
    }
    
    

    界面刷新函数那些的,没什么的,到时候大家自取源码就可以啦

    界面切换函数

    void show()
    {
    	LCD_Clear(White);
    	fflushPara();
    	while(1)
    	{
    		keyTemp=Key_Scan();
    		switch(keyTemp)
    		{
    				//车位显示和费率设置的切换
    				case '1':
    					showFlag=!showFlag;
    					LCD_Clear(White);
    					return;
    				//进到函数里就保证了题目要求的按键2和按键3只在设置界面有效
    				case '2':
    					vnbrPrice+=0.5;
    					cnbrPrice+=0.5;
    					fflushPara();
    					break;	
    				case '3':
    					vnbrPrice-=0.5;
    					cnbrPrice-=0.5;
    					fflushPara();
    					break;
    		}
    		//切换界面后也要照样接收数据进行处理,否则破坏了数据一致性
    		receiveData();
    	}
    }
    

    最后主函数如下

    while(1)
    	{		
    		keyTemp=Key_Scan();
    		switch(keyTemp)
    		{
    			case '1':
    				showFlag=!showFlag;
    				show();
    				break;
    			case '4':
    				pwmFlag = !pwmFlag;
      				TIM_Cmd(TIM3, pwmFlag);
    				LED_Control(LED2,pwmFlag);
    				break;
    		}
    		fflushData();
    		//接收数据开始处理
    		receiveData();
    	}
    

    下载链接:链接:源码

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【第十二届蓝桥杯嵌入式省赛真题】

    发表评论