【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

时间:2021-02-22 01:19:55

????【蓝桥杯嵌入式】专题正在持续更新中,原理图解析✨,各模块分析✨以及历年真题讲解✨都在这儿哦,欢迎大家前往订阅本专题,获取更多详细信息哦????????????

????本系列专栏 -   蓝桥杯嵌入式_勾栏听曲_0的博客

????欢迎大家  ????  点赞????  评论????  收藏⭐️

????个人主页 -  勾栏听曲_0的博客????

????希望本文能对你有所帮助,如有不足请指正,共同进步吧????

????君子欲讷于言而敏于行。????

题目

硬件框图

【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解 

功能要求

功能概述

        1)设计一个停车计费系统,能够完成费率设置、费用计算等功能。

        2)使用串口获取车辆进、出停车场信息和时间,并能够输出计费信息。

        3)使用按键完成费率设置、调整功能。

        4)按照显示要求,通过LCD显示停车状态、费率参数。

        5)通过PA7输出固定频率和占空比的脉冲信号或持续低电平。

        6)使用LED指示灯完成相关指示功能。

性能要求

        1)计费信息输出响应时间:≤0.1秒;

        2)按键响应时间:≤0.2秒;

        3)车位数量:8个。

LED显示界面

        1)车位显示界面
        在车位显示界面下,通过LCD显示界面名称(Data)、停车场内目前的停车数量和空闲车位,CNBR和VNBR代表两类不同的停车类型。

【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

        上图所示停车数量共6辆,CNBR类2辆,VNBR类4辆,空闲车位2个。

        2)费率设置界面
        在费率设置界面下,通过LCD显示界面名称(Para)、CNBR类型和VNBR类型停车的费率,单位为元/小时,保留小数点后2位有效数字。

【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

        上图所示CNBR类停车费率位3.50元/小时,VNBR类型停车费率位2.00元/小时。

        3)LCD通用显示要求
        显示背景色(BackColor):黑色显示前景色(TextColor):白色
        请严格按照图示2、3要求设计各个信息项的名称(区分字母大小写)和行列位置。

按键功能

        1)B1:定义为“界面切换”按键,切换LCD显示“车位显示界面”和“费率设置界面”。

        2)B2:定义为“加”按键,每次按下B2按键,CNBR、VNBR费率增加0.5元。

        3)B3:定义为“减”按键,每次按下B3按键,CNBR、VNBR费率减少0.5元。

        4)B4:定义为“控制”按键,按下后,切换PA7端口输出状态(2KHz,20%占空比的脉冲信号或持续低电平),切换要求如下图所示。

【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

        5)通用按键设计要求
        按键应进行有效的防抖处理,避免出现一次按下、多次触发等情形。
        按键B2、B3仅在费率设置界面有效。

串口功能
        1)使用竞赛平台上的USB转串口完成相关功能设计。

        2)串口通信波特率设置为9600bps。

        3)使用4个任意ASCII字符组成的字符串标识车辆,作为车辆编号。

        4)串口接收车辆出入信息
        入停车场
                停车类型:车辆编号:进入时间(YYMMDDHHmmSs)举例:
                        CNBR:A392:200202120000
                        表示停车类型CNBR,编号为A392的车辆,进入停车场时间为2020年2月2日12时整。
        出停车场
                停车类型:车辆编号:退出时间举例:
                        VNBR: D583:200202132500
                        表示停车类型 VNBR,编号为D583的车辆,退出停车场时间为2020年2月2日13时25分。

        5)串口输出计费信息
        停车类型:车辆编号:停车时长:费用举例:
        串口接收车辆入停车场信息        VNBR: D583:200202120000

        串口接收车辆出停车场信息        VNBR: D583:200202213205

        串口输出计费信息                      VNBR:D583:10:20.00
        表示停车类型VNBR,编号为D583的车辆,停车时长为10小时,停车费用为20.00元。

        6)说明
        车辆出入信息通过“资源数据包”中提供的串口助手向竞赛平台发送字符串,格式需要严格按照示例要求。

        停车时长:整数,单位为小时,不足1小时,按1小时统计。

        停车费用:以元为单位,按小时计费,保留小数点后2位有效数字。

        系统收到入停车场信息后,不需要回复;接收到出停车场信息后,解析、计算并通过串口回复计费信息。
        当接收到的字符串格式不正确或存在逻辑错误,系统通过串口输出固定提示信息字符串 Error 。
LED指示灯功能
        1)若停车场内存在空闲车位,指示灯LD1点亮,否则熄灭。

        2)PA7输出2KHz,20%占空比脉冲信号期间,指示灯LD2点亮,否则熄灭。

初始状态说明
        1)上电默认PA7处于低电平状态。

        2)上电默认处于车位显示界面。

        3)上电默认参数,CNBR费率3.50元/小时,VNBR费率2.00元/小时。

        4)每次重新上电后,默认空闲车位为8个。


真题讲解系列文章重点关注顶层逻辑代码编写,各模块的代码编写大家可点击蓝桥杯嵌入式专题,里面有各个模块的详细解析与代码编写

 赛题分析

        拿到赛题,第一件事就是看硬件框图,因为可以看出本届赛题的重点考点。除了老三样(LED,按键,LCD)以外,就是我们的重点考点了。很显然,本次赛题的考点是串口通信与脉冲频率的设置。

        然后我们再来理解具体要实现的功能。看到功能概述,可以知道串口通信对应着停车计费系统,而脉冲频率对应着改变PA7的占空比。其他细节方面我们通过代码来进一步了解。

代码实现

串口模块

        最重要的功能就是完整接收串口传来的出入车库的车辆信息。然后判断是接收的信息车牌是否已经存在,已经存在就代表是出库 ,不存在就代表入库,出库就要进行计费计算,并通过串口将结果按赛题要求打印出来。期间出入库是,改变相应的变量值,如空闲车位等。

void uart_rx_proc()
{
	if(rx_pointer>0)
	{
		if(rx_pointer==22 && i < 8)	//如果接收到的是22个字符
		{
			sscanf(rxdata,"%4s:%4s:%12s",car_type,car_data,car_time);	//将字符串拆分
			int j = 0;
			for( j=0;j < 8;j++)
			{
				if ((strcmp(car_data, Car[j].car_data) == 0) && (strcmp(car_type, Car[j].car_type) == 0))	//如果与之前数据的DATA相同,则代表这次是出库,计算费用,并通过串口发送
				{
					char tyear[4],tm[2],td[2],th[2],tf[2],ts[2], tyear1[4],tm1[2],td1[2],th1[2],tf1[2],ts1[2];
					int year,m,d,h,f,s,year1,m1,d1,h1,f1,s1;
					int time = 0;
					double fee;
					sscanf(car_time,"%2s%2s%2s%2s%2s%2s",tyear,tm,td,th,tf,ts);		//将表示时间的字符串分割
					sscanf(Car[j].car_time,"%2s%2s%2s%2s%2s%2s",tyear1,tm1,td1,th1,tf1,ts1);
					year=atoi(tyear);			//将字符串转为整形变量
					m = atoi(tm);
					d = atoi(td);
					h = atoi(th);
					f = atoi(tf);
					s = atoi(ts);
					
					year1=atoi(tyear1);	
					m1 = atoi(tm1);
					d1 = atoi(td1);
					h1 = atoi(th1);
					f1 = atoi(tf1);
					s1 = atoi(ts1);
					
					if(year1 == year)		//计算时间
					{
						if(m == m1)
						{
							time = ceil( ( ((d*24+h)*60+f)*60+s - ((d1*24+h1)*60+f1)*60+s1 ) / 3600);		//ceil向下取整,计算出停车多少小时
						}
					}
					if(strcmp(car_type,"CNBR") == 0)	//判断车型
					{
						fee = time * CNBR_fee;
						CNBR_sum--;
					}
					else
					{
						fee = time * VNBR_fee;
						VNBR_sum--;
					}
					
					char temp[20];
					sprintf(temp,"%s:%s:%d:%.2f",car_type,car_data,time,fee);
					HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);	//串口发送计费信息
					
					if(i != j)		//如果匹配的不是已入库的最后一个,就将最后一个入库的信息复制到这个位置上
					{
						sprintf(Car[j].car_type,"%s",Car[i].car_type);		//将最后入库的信息复制到当前出库的位置
						sprintf(Car[j].car_data,"%s",Car[i].car_data);
						sprintf(Car[j].car_time,"%s",Car[i].car_time);
					}
					
					i--;		//i--后,下次有新入库的车辆就直接覆盖这次最后入库的信息
					
					break;
				}
				else
				{
					if(j >= 7)		//说明未匹配成功
					{
						sscanf(rxdata,"%4s:%4s:%12s",Car[i].car_type,Car[i].car_data,Car[i].car_time);	//将字符串拆分
						char temp[20];
						sprintf(temp,"%s:%s:%s",Car[i].car_type,Car[i].car_data,Car[i].car_time);
						HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);	//串口发送入库信息
						i++;
						if(strcmp(Car[i].car_type,"VNBR") == 0)
						{
							VNBR_sum++;
						}
						else
						{
							CNBR_sum++;
						}
					}
				}
			}
		}
		else
		{
			char temp[20];
			sprintf(temp,"Error");
			HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);	//串口发送一个错误提示
		}
		rx_pointer=0;
		memset(rxdata,0,30);
		
		
		LDLE_sum = 8-i;
		if(i == 8)
		{
			TurnOff_LED(1);
		}
		else
		{
			TurnOn_LED(1);
		}
	}
}

        在串口调试工具中的实现效果 (第一次黑色信息为串口发送给开发板的让车库信息,第二次为出车库信息)

【蓝桥杯嵌入式】蓝桥杯第十二届省赛程序真题,真题分析与代码讲解

按键模块

        按键模块就是简单的界面切换,费率的加减。要注意的是第四个按键被设置为改变PA7频率的按键。

void key_proc()
{
	if(key[0].key_flag == 1)
	{
		view++;
		if(view==2) view=0;
		LCD_Clear(Black);		//清屏
		key[0].key_flag = 0;
	}
	if(view==1 && key[1].key_flag == 1)
	{
		CNBR_fee += 0.5;
		VNBR_fee += 0.5;
		key[1].key_flag = 0;
		LCD_Clear(Black);
	}
	else if(view==1 && key[2].key_flag == 1)
	{
		CNBR_fee -= 0.5;
		VNBR_fee -= 0.5;
		key[2].key_flag = 0;
		LCD_Clear(Black);
	}
	
	if(key[3].key_flag == 1)
	{
		if(pa7_duty == 0)
		{
			pa7_duty = 20;
			TurnOn_LED(2);
		}
		else
		{
			pa7_duty = 0;
			TurnOff_LED(2);
		}
		__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,pa7_duty);		//设置占空比pa7_duty
		key[2].key_flag = 0;
	}
}

LCD模块

        LCD就是按赛题要求显示相应内容即可。注意:从第11届开始,已经要求LCD显示必须完全按照赛题上的格式来显示,每一行每一列显示什么一定要注意。

void disp_proc()
{
	if(view==0)
	{
		char text[30];
		sprintf(text,"      Data   ");
		LCD_DisplayStringLine(Line1, (uint8_t *)text);
		sprintf(text,"   CNBR:%d",CNBR_sum);
		LCD_DisplayStringLine(Line3, (uint8_t *)text);
		sprintf(text,"   VNBR:%d",VNBR_sum);
		LCD_DisplayStringLine(Line5, (uint8_t *)text);
		sprintf(text,"   IDLE:%d",LDLE_sum);
		LCD_DisplayStringLine(Line7, (uint8_t *)text);
	}
	else if(view==1)
	{
		char text[30];
		sprintf(text,"    Para    ");
		LCD_DisplayStringLine(Line2, (uint8_t *)text);
		sprintf(text,"   CNBR:%.2f",CNBR_fee);
		LCD_DisplayStringLine(Line4, (uint8_t *)text);
		sprintf(text,"   VNBR:%.2f",VNBR_fee);
		LCD_DisplayStringLine(Line6, (uint8_t *)text);
	}
}