蓝桥杯嵌入式STM32G431——第十二届第一场省赛停车计费系统真题及代码

1、第十二届第一场省赛真题

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

2、解题思路整理

  1. 本届赛题相较于往届赛题在串口部分有较为复杂的逻辑,想要解决还是需要下一番功夫的。首先除了串口模块,LED、LCD、按键、PA7输出频率固定、占空比固定的PWM这些模块都是基本的套路比较简单,先把这些逐个完成。
  2. 串口模块:串口接收车辆进入停车场的信息(车辆类型:车辆ID:进入时间年月日时分秒-
    YYMMDDHHMMSS);车辆出停车场时的信息(车辆类型:车辆ID:出去时间年月日时分秒-YYMMDDHHMMSS)。
    串口发送车辆出停车场时车辆信息、停车时间、停车费用(车辆类型:车辆ID:停车时间(小时-未满一小时按一小时计算):停车费用)。
  3. 初步判断串口接收在RX_BUF中的数据是否满足正确格式,例如:接收到的字符串长度为22、对应固定位置不改变的字符(CNBR、VNBR、:)正确、时间为0~9的数字。不满足格式则串口输出error。
  4. 如果初步判断字符符合正确格式,则开始对车辆信息进行处理。
  5. 当串口接收到一辆符合要求的车辆信息,通过车辆ID判断停车场中是否存在该车。
    如果存在则该车为出停车场;串口输出车辆信息、停车时间、停车费用。(停车时间、停车费用应当大于0,否则串口输出error);清空对应车位的车辆信息、将车位非空标志置0表示该车位空闲。由于一年中有12个月,每个月的天数不同,故可将月份转化为天数计算用来处理不同月份天数跨度下对应的停车时间。
    如果不存在则该车为入停车场,找空闲车位,将该车的车辆信息、进入时间存入对应车位的车辆信息、将车位非空标志置1表示该车位有车(当无空闲车位时,再有车辆进入则输出error)。

3、主函数部分代码

#include "main.h"
#include "rcc.h"
#include "led_key.h"
#include "lcd.h"
#include "tim.h"
#include "uart.h"

//***子函数扫描速度控制变量区
__IO uint32_t uwTick_LED_Speed_Ctrl;
__IO uint32_t uwTick_KEY_Speed_Ctrl;
__IO uint32_t uwTick_LCD_Speed_Ctrl;
__IO uint32_t uwTick_UART_Speed_Ctrl;

//***全局变量区
uint8_t ucLED;
uint8_t key_value,key_up,key_down;
static uint8_t key_old;

uint8_t LCD_String_Disp[21];

uint8_t RX_Buffer;
uint8_t RX_BUF[200];
uint8_t RX_Count;

_Bool Interface_Ctrl = 0; //0-车位显示界面 1-费率设置界面
uint8_t CNBR_cost = 35; //上电默认3.50 此处对数据/10得所需要的费率
uint8_t VNBR_cost = 20; //上电默认2.00
_Bool B4_Ctrl = 0; //0-PA7处于低电平 1-PA7输出2KHz,20%占空比的PWM信号
uint8_t CNBR_count = 0; 
uint8_t VNBR_count = 0;
uint8_t IDLE_count = 8; //上电默认空闲车位8个
float stop_car_cost; //停车费用
uint8_t year_365[12]={31,28,31,30,31,30,31,31,30,31,30,31}; 
uint8_t year_366[12]={31,29,31,30,31,30,31,31,30,31,30,31}; //闰年366天
uint16_t month_trans_day_temp; //各月份之间的天数

//***子函数声明区***//
void SystemClock_Config(void);
void LED_Proc(void);
void KEY_Proc(void);
void LCD_Proc(void);
void UART_Proc(void);
_Bool CheckCmd(uint8_t *RX_str);
void sub_str(uint8_t *d_str,uint8_t *s_str,uint8_t locate,uint8_t length);
uint8_t find_locate(void);
uint8_t id_isExist(uint8_t *id_str);
uint16_t month_trans_day(uint8_t d_m,uint8_t s_m,uint8_t *month_a);


//***定义结构体用于存储车辆的信息
typedef struct car_information
{
	uint8_t type_in[5]; //注意数组的大小加上结束符‘/0’
	uint8_t id_in[5]; 
	uint8_t year;
	uint8_t month;
	uint8_t day;
	uint8_t hour;
	uint8_t min;
	uint8_t sec;
	_Bool notEmpty;

}car_information_storage;

car_information_storage car_num[9]; //分别对应8辆车的信息 定义9个空间防止数组越界 如果只有8个位置 当第九辆车进入会克卡死



//***主函数区***//
int main(void)
{

  HAL_Init();
	
  SystemClock_Config();
	
	LED_KEY_Init();
	
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	
	USART1_UART_Init();
	HAL_UART_Receive_IT(&huart1, (uint8_t *)(&RX_Buffer) ,1);
	
	TIM17_Init();
	
  while (1)
  {
		LED_Proc();
		KEY_Proc();
		LCD_Proc();
		UART_Proc();
  }

}



//***LED控制子函数
void LED_Proc(void)
{
	if((uwTick - uwTick_LED_Speed_Ctrl)<200) return;
		uwTick_LED_Speed_Ctrl = uwTick;	//每200ms执行一次
		
	if(IDLE_count)
		ucLED |= 0x01;
	else
		ucLED &= 0x02;
	
	LED_Disp(ucLED);
}


//**按键控制子函数
void KEY_Proc(void)
{
	if((uwTick - uwTick_KEY_Speed_Ctrl)<100) return;
		uwTick_KEY_Speed_Ctrl = uwTick;	//每100ms执行一次
	
	key_value = KEY_Scan();
	key_down = key_value & (key_value ^ key_old);
	key_up = ~key_value & (key_value ^ key_old);
	key_old = key_value;
	
	switch(key_down)
	{
		case 1: //B1实现车位显示界面与费率设置界面的切换
			Interface_Ctrl ^= 1; //取异或 实现0-1来回切换
//			if(Interface_Ctrl == 0)
//				Interface_Ctrl = 1; //费率设置界面
//			else
//				Interface_Ctrl = 0; //车位显示界面
			LCD_Clear(Black); //清屏
			break;
	
		case 2: //B2-按下CNBR VNBR费率增加0.5
			if(Interface_Ctrl == 1) //在费率设置界面有效
			{
				CNBR_cost += 5;
				VNBR_cost += 5;
			}
			break;
			
		case 3: //B2-按下CNBR VNBR费率减少0.5
			if(Interface_Ctrl == 1) //在费率设置界面有效
			{
				if(VNBR_cost == 0)
				{
					CNBR_cost = 15;
					VNBR_cost = 0;
				}
				else
				{
					CNBR_cost -= 5;
					VNBR_cost -= 5;
				}
			}
			break;
			
		case 4:
			B4_Ctrl ^= 1; //取异或 实现0-1来回切换
			break;
	}
	
}


//***LCD控制子函数
void LCD_Proc(void)
{
	if((uwTick - uwTick_LCD_Speed_Ctrl)<100) return;
		uwTick_LCD_Speed_Ctrl = uwTick;	//每100ms执行一次
	
	if(!B4_Ctrl) //PA7处于低电平
	{
		HAL_TIM_PWM_Stop(&htim17 , TIM_CHANNEL_1); 
		
		__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 0);//强制配置成低电平
		HAL_TIM_PWM_Start(&htim17 , TIM_CHANNEL_1);	
		ucLED &= 0x01;			
	}
	else //PA7输出2KHz,20%占空比的PWM信号
	{
		HAL_TIM_PWM_Stop(&htim17 , TIM_CHANNEL_1); 
	
		//设置占空比为20%
		__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 100);//强制配置成PWM
		HAL_TIM_PWM_Start(&htim17 , TIM_CHANNEL_1);
		ucLED |= 0x02;
	}
	
	if(!Interface_Ctrl) //车位显示界面
	{
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "       Data");
		LCD_DisplayStringLine(Line1 , LCD_String_Disp);		
		
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "   CNBR:%1d",CNBR_count);
		LCD_DisplayStringLine(Line3 , LCD_String_Disp);		
		
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "   VNBR:%1d",VNBR_count);
		LCD_DisplayStringLine(Line5 , LCD_String_Disp);	

		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "   IDLE:%1d",IDLE_count);
		LCD_DisplayStringLine(Line7 , LCD_String_Disp);		
	}
	else //费率设置界面
	{
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "       Para");
		LCD_DisplayStringLine(Line1 , LCD_String_Disp);		
		
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "   CNBR:%4.2f" , (float)CNBR_cost/10);
		LCD_DisplayStringLine(Line3 , LCD_String_Disp);		
		
		memset(LCD_String_Disp , 0 ,sizeof(LCD_String_Disp));
		sprintf((char*)LCD_String_Disp , "   VNBR:%4.2f" , (float)VNBR_cost/10);
		LCD_DisplayStringLine(Line5 , LCD_String_Disp);	

	}
	
}


//***串口控制子函数
void UART_Proc(void)
{
	if((uwTick - uwTick_UART_Speed_Ctrl)<100) return;
		uwTick_UART_Speed_Ctrl = uwTick;	//每100ms执行一次	

	 //***在LCD上显示CheckCmd是否有效
//	memset(LCD_String_Disp,0,sizeof(LCD_String_Disp));
//	sprintf((char*)LCD_String_Disp, "%1d",CheckCmd(RX_BUF));
//	LCD_DisplayStringLine(Line8, LCD_String_Disp);	
	
	if(CheckCmd(RX_BUF)) //满足长度 类型 时间格式正确
	{
		uint8_t car_type[5]; //车的类型
		uint8_t car_id[5];	//车的ID
		uint8_t stop_locate = 0xff; //停车位置 默认无车辆
		uint8_t spare_locate; //空位
		uint8_t year_temp,month_temp,day_temp,hour_temp,min_temp,sec_temp;
		float time_count; //总秒数
		
		//***车辆出入停车场的对应的年月日时分秒字符->整型的转换
		year_temp = (RX_BUF[10]-0x30)*10+(RX_BUF[11]-0x30);
		month_temp = (RX_BUF[12]-0x30)*10+(RX_BUF[13]-0x30);
		day_temp = (RX_BUF[14]-0x30)*10+(RX_BUF[15]-0x30);
		hour_temp = (RX_BUF[16]-0x30)*10+(RX_BUF[17]-0x30);
		min_temp = (RX_BUF[18]-0x30)*10+(RX_BUF[19]-0x30);
		sec_temp = (RX_BUF[20]-0x30)*10+(RX_BUF[21]-0x30);
		
		//**判断时间格式是否正确
		if((month_temp>12)||(day_temp>30)||(hour_temp>23)||(min_temp>59)||(sec_temp>59))
		{
			printf("error\n"); //串口输出error
		}
		else //串口接收到一辆车的信息且全部正确后执行
		{
//			printf("accpect\r\n");
			//将该车的类型与ID存入定义的数组中
			sub_str(car_type , RX_BUF , 0 ,4);
			sub_str(car_id , RX_BUF , 5 ,4);
//			printf("%s\n",car_id);			
			
			stop_locate = id_isExist(car_id); //判断对应的停车信息的有无 返回停车位置
//			printf("%d\n",stop_locate);
			
			if(stop_locate!=0xff) //停车位置不为0xff 有停车信息 该车辆已经进入停车场
			{
				//***对时间进行处理***//
				//一年中12个月每个月的天数不一样 只需将对月份的时间处理转化为对天数的处理
				if(year_temp % 4 == 0) //闰年
				{
					month_trans_day_temp = month_trans_day(month_temp,car_num[stop_locate].month,year_366); //闰年2月份有29天
				}
				else
				{
					month_trans_day_temp = month_trans_day(month_temp,car_num[stop_locate].month,year_365);
				}
				
				time_count = (float)((year_temp-car_num[stop_locate].year)*365*24*3600)+(month_trans_day_temp*24*3600)+
				((day_temp-car_num[stop_locate].day)*24*3600)+((hour_temp-car_num[stop_locate].hour)*3600+(min_temp-car_num[stop_locate].min)*60+
				(sec_temp-car_num[stop_locate].sec)); //停车时间的总秒数
				
				time_count = (time_count + 3599)/3600; //转换为小时 未满1小时按一小时计费
				
				if(time_count < 0) //时间错误输出error
				{
					printf("error\n"); //串口输出error
				}
				else 
				{
					if(car_type[0] == 'C')
					{
						CNBR_count--; //CNBR类型车辆-1
						stop_car_cost = (uint16_t)time_count*((float)CNBR_cost/10);
					}
					else if(car_type[0] == 'V')
					{
						VNBR_count--; //VNBR类型车辆-1
						stop_car_cost = (uint16_t)time_count*((float)VNBR_cost/10);
					}
					IDLE_count++; //空闲车位+1
					printf("%s:%s:%d:%.2f\n",car_type,car_id,(uint16_t)time_count,stop_car_cost); //串口输出车辆出停车场时的车辆信息、停车时间、停车费用
				}
				
				memset(&car_num[stop_locate] , 0 ,sizeof(car_num[stop_locate])); //表示车辆离开 该位置为车辆信息清空
			}
			else if(stop_locate == 0xff)//停车场中无该车辆
			{
				spare_locate = find_locate();  //判断是否有空闲车位
//				printf("%d\n",spare_locate);
				
				if(spare_locate!=0xff) //停车场有空位 将车辆信息存入对应的空位
				{
					sub_str(car_num[spare_locate].type_in , car_type , 0 ,4);
//					printf("%s\n",car_num[spare_locate].type_in);
					sub_str(car_num[spare_locate].id_in , car_id , 0 ,4);	
//					printf("%s\n",car_num[spare_locate].id_in);					
					car_num[spare_locate].year = year_temp;
					car_num[spare_locate].month = month_temp;
					car_num[spare_locate].day = day_temp;
					car_num[spare_locate].hour = hour_temp;
					car_num[spare_locate].min = min_temp;
					car_num[spare_locate].sec = sec_temp;
					car_num[spare_locate].notEmpty = 1; //置1 对应位置有车不为空
					
					if(car_type[0] == 'C')
					{
						CNBR_count++; //CNBR类型车辆+1
					}
					else if(car_type[0] == 'V')
					{
						VNBR_count++; //VNBR类型车辆+1
					}
					IDLE_count--; //空闲车位-1
				}
				else //停车场无空位串口输出error
				{
					printf("error\n");
				}
			}
		}
	}
	
	//完成一次串口数据接收
	RX_Count = 0; //串口接收字符个数清空
	memset((char*)RX_BUF , 0 ,sizeof(RX_BUF)); //中断接收数组清空 为下一次接收做1准备
}


_Bool CheckCmd(uint8_t *RX_str)	//判断接收中断数据的有效性 长度是否满足 类型是否满足 时间为数字是否满足
{
	uint8_t i;
	if(RX_Count != 22)  //如果串口接收到的数据不是22个字符则不满足
	{
		return 0; 
	}
	
	//***判断类型是否满足 对应位的固定符号是否满足
	if((RX_BUF[0]=='C'||RX_BUF[0]=='V')&&(RX_BUF[1]=='N')&&(RX_BUF[2]=='B')&&(RX_BUF[3]=='R')&&(RX_BUF[4]==':')&&(RX_BUF[9]==':'))
	{	
		for(i=10;i<22;i++)
		{
			//***判断第十位往后的是否为数字
			if((RX_BUF[i]>9) 
			{
				printf("error\n"); //串口输出error
				return 0;
			}
		}
		
	}
	else //不满足类型或符号返回0
	{
		printf("error\n"); //串口输出error
		return 0;
	}
	
	//格式全部满足返回1
	return 1;
}


//***将目标字符串的值替换为源字符串中的值
void sub_str(uint8_t *d_str,uint8_t *s_str,uint8_t locate,uint8_t length)
{
	uint8_t i;
	for(i=0;i<length;i++)
	{
		d_str[i] = s_str[locate+i];
	}
	d_str[length] = '\0'; //字符串的末尾需加入结束符 用于判别车辆id时使用的字符串比较函数

}


//***判断车辆id是否已进入停车场
uint8_t id_isExist(uint8_t *id_str) //如果已经存在于停车场则返回停车位置 否则返回0xff
{
	uint8_t i = 0;
	for(i=0;i<8;i++) 
	{
		if((strcmp((const char*)id_str,(const char*)car_num[i].id_in))== 0)
		{
			return i;
		}
	}
	return 0xff;
}


//***找空闲的车位
uint8_t find_locate(void) //如果有空位返回空位位置 否则返回0xff
{
	uint8_t i;
	for(i=0;i<8;i++) 
	{
		if(!car_num[i].notEmpty) //有空位返回对应空位
		{
			return i;
		}
	}
	return 0xff;
}

//***时间处理子函数月份转换为天数
uint16_t month_trans_day(uint8_t d_m,uint8_t s_m,uint8_t *month_a)
{
	uint8_t i;
	uint16_t day_sum = 0;
	for(i=s_m-1;i<d_m-1;i++)
	{
		day_sum = day_sum + month_a[i]; //将对应年数组中每个月对应天数累加
	}
	return day_sum;
	
}

//***串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	RX_BUF[RX_Count] = RX_Buffer;
	RX_Count++;
	HAL_UART_Receive_IT(&huart1, (uint8_t *)(&RX_Buffer) ,1);

}


//***printf重定向为串口输出
int fputc(int ch, FILE * f)
{
	char c = ch;
	HAL_UART_Transmit(&huart1 , (uint8_t *)&c , 1 ,50);
	return ch;
}

//***测试数据 CNBR:3.50 VNBR:2.00

//*VNBR:D583:220403121217 进
//*VNBR:D583:220503121217 出 30天 720小时 1440.00

//*VNBR:D583:220303121217 进
//*VNBR:D583:220403121217 出 31天 744小时 1488.00

//*VNBR:D583:220203121217 进
//*VNBR:D583:220303121217 出 28天 672小时 1344.00

//*VNBR:D583:220228121216 进 
//*VNBR:D583:220301121216 出 24小时 48.00元

//*VNBR:D583:220303121216 进 
//*VNBR:D583:220303121316 出 1小时 2.00元

//*VNBR:D584:220303121216
//*VNBR:D584:220303131256 2小时 4.00元

//*VNBR:D585:220303121216
//*VNBR:D585:220303141446 3小时 6.00元

//*CNBR:D586:220303121313
//*CNBR:D586:220303131416 2小时 7.00元

//*CNBR:D587:220303121513
//*CNBR:D587:220303141813 3小时 10.50元

//*CNBR:D588:220303121216
//*CNBR:D588:220304121216 1天 25*3.50=84.00元

//*CNBR:D589:220303121216
//*CNBR:D589:220303121219 1小时 3.50元

//*CNBR:D590:220303121216
//*CNBR:D590:220303211446 10小时 35.00元



评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lzya.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值