[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解

这篇具有很好参考价值的文章主要介绍了[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

最近,我报名了今年的蓝桥杯嵌入式比赛,为此刷了一下以往的真题。以下是我对十二届蓝桥杯省赛真题的一些思路和心得,还有一些具体代码的实现。

一、题目介绍

[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解,蓝桥杯,stm32,嵌入式硬件
[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解,蓝桥杯,stm32,嵌入式硬件
[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解,蓝桥杯,stm32,嵌入式硬件

[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解,蓝桥杯,stm32,嵌入式硬件
[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解,蓝桥杯,stm32,嵌入式硬件

二、相关模块及重难点分析

1、相关模块

第十二届比赛主要用到的模块包括:LED、KEY、LCD、TIM、USART

2、重难点分析

这道题主要目的是做一个停车管理收费系统,初始化两个界面来显示CNBR和VNBR两种类型的停车情况,以及剩余的空闲车位和两种类型的停车费用。通过上面的信息来判断LED的亮灭,以及PA7输出的状态。最后通过串口接受车辆信息,来分析是入库停车,还是出库离去。若是入库停车,则寻找车位,并保存信息。反之,则计算停车费用并通过串口打印,最后删除车辆信息。
**难点1:**串口处理接收的车辆信息,然后再判断是出库还是入库。
**难点2:**时间处理函数,将时间统一转换成秒数再计算出停车的时间,转换成小时,再通过单价得出时间的费用。(最麻烦的是其中还要考虑年份是否为闰年)。
**难点3:**整体代码的一个思路和逻辑,这可能是本届题目最大的一个考点和难点吧!

三、题解

1、变量定义

//gocar判断是否来车,serial判断是否车辆信息是否合格
uchar Gocar,serial=0;
uint16_t Compare;    /*设置比较值,来确保输出为20%的占空比*/
uint8_t led;         /*LED值*/
uint8_t key_val;     /*按键值*/
uint8_t view=0;      /*界面值*/
uint8_t CNBR=2,VNBR=4; /*两种类型车辆的初始值*/
uint8_t IDLE=8;		   /*初始化空余车位*/
uint8_t arr[23];       /*接受车辆信息的数组*/
uint8_t ass[20];	   /*打印错误信息数组*/
char temp[30];         /*展示具体信息数组*/
float CNBR_fee=3.50,VNBR_fee=2.50;/*不同类型车辆的停车单价*/
extern struct Scinfo now_car; /*构造当前车辆信息的结构体*/
extern struct Scinfo car[9];  /*已有车辆信息结构体*/

2、各类函数声明

void key_proc(void); 	/*声明按键处理函数*/
void dis_proc(void);	/*声明界面切换函数*/
uint8_t uart_proc(void);/*声明车辆信息判断函数*/

3、主函数以及while中的代码

在这里面一定要记得先去处理LED1、2的亮灭,因为可能在后面写一些其他的要求时从而忘记不同情况下的LED的亮灭状态。

	//初始化LCD
	LCD_Init();      
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	
	//开启TIM3通道捕获2,捕获PWM脉冲
	HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_2);

	  while (1)
  {  
	  LED_Disp(led);
	  
	  //开始接受数据
	  HAL_UART_Receive_IT(&huart1,arr,22);
	  //判断是否有还有车位剩余
	  IDLE=8-CNBR-VNBR;
	  key_proc();
	  dis_proc(); 
	  //判断是否来车
	  if(Gocar)
	   {
		 Gocar=0;	
		 //判断车辆信息是否有误
		 serial=uart_proc();
		 if(serial==0)
		 {
			 //通过串口 打印ERROR
			sprintf((char *)ass,"ERROR\r\n");
			HAL_UART_Transmit(&huart1,ass,sizeof(ass),50);
			//清除ass数组数据
			memset(ass,0,sizeof(ass));
		 }
		}
	   
	//判断是否还有剩余车位来控制LED1的亮灭
	  if(IDLE>0)
	  {
		led |=1;
	  }
	  else
	  {
	 //因为控制LED灯是八位,~是安位取反操作,这里操作将最低位置为零。
	  led&=~1;
	  }	
}

4、扫描按键及按键处理函数

这里按键的小细节可能就是避免单次按下,按键多次响应。至于PA7输出低电平脉冲,直接设为零,肯定是低电平脉冲了。不要被迷惑了,我一开始就是被迷惑了,然后想的太复杂了。

uint8_t keynum=0;
uint8_t key_read(void)
{
	//按键锁
	static unsigned char keyLock = 1;
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET
      || HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET || HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET) 
      && keyLock == 1){
		 //给按键上锁 避免多次触发
        keyLock = 0;
        //这个是HAL库自带函数不会影响实时性
		//if(HAL_GetTick()%15 < 10) return 0;
		//这里消抖延时函数,会影响实时性(最好不用)
       HAL_Delay(10);

        //B1
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == RESET){
            return 1;
        }
        //B2
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == RESET){
            return 2;
        }
        //B3
        if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == RESET){
            return 3;
        }
		//B4
        if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == RESET){
            return 4;
        }
    }
	//按键松开
    if((HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0) == SET && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1) == SET
      && HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2) == SET && HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == SET) 
      && keyLock == 0){
	//开锁
        keyLock = 1;
    }
    return 0;

}

//按键处理函数
void key_proc()
{
	key_val=key_read();
	//判断按下哪个按键,进行不同的操作
	switch(key_val)
	{
		case 1:
			//切换界面
			view=!view;
			//界面刷新
			LCD_Clear(Black);
			key_val=0;
		break;
		case 2:
			if(view==1)
			{
			CNBR_fee+=0.5f;
			VNBR_fee+=0.5f;
			key_val=0;
			}
		break;
		case 3:
			if(view==1)
			{
			CNBR_fee-=0.5f;
			VNBR_fee-=0.5f;
			key_val=0;
			}
			break;
		case 4:
			//按下B4,然后卡其LED2来设置比较值确保输出为20%
			m^=1;
			led^=2;
			if(m==1)
			{
				LED_Disp(led);
				Compare=400;
				__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,Compare);			
			}
			else 
			{
				LED_Disp(led);
				Compare=0;
				__HAL_TIM_SetCompare(&htim3,TIM_CHANNEL_2,Compare);	
			}
			
	}
}

5、LED函数

void LED_Disp(uint8_t dsLED)
{
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);//先让所有灯熄灭
	HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);//开启锁存器
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

6、界面处理函数

void dis_proc()
{
	if(view==0)
	{
	sprintf(temp,"       Data");
	LCD_DisplayStringLine(Line0,(uint8_t*)temp);
	sprintf(temp,"   CNBR=%d",CNBR);
	LCD_DisplayStringLine(Line2,(uint8_t*)temp);
	sprintf(temp,"   VNBR=%d",VNBR);
	LCD_DisplayStringLine(Line4,(uint8_t*)temp);
	sprintf(temp,"   IDLE=%d",IDLE);
	LCD_DisplayStringLine(Line6,(uint8_t*)temp);
	}
	if(view==1)
	{
	sprintf(temp,"       Para");
	LCD_DisplayStringLine(Line0,(uint8_t*)temp);
	sprintf(temp,"   CNBR=%f",CNBR_fee);
	LCD_DisplayStringLine(Line2,(uint8_t*)temp);
	sprintf(temp,"   VNBR=%f",VNBR_fee);
	LCD_DisplayStringLine(Line4,(uint8_t*)temp);		
	}
}

7、串口回调函数

这里就是难点1:车辆详细信息的处理,解决方法是通过构造结构体来简化这个信息处理的难度。还有一个不容忽视的问题,就是回调函数中千万别写太多,题目中有一定要求。

extern uchar Gocar;
extern uint8_t arr[23];
struct Scinfo
{
	uint8_t NBR;
	uint8_t Name[5];
	uint8_t Year;
	uint8_t Month;
	uint8_t Day;
	uint8_t Hour;
	uint8_t Minute;
	uint8_t Second;
};
struct Scinfo now_car;

//回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//记录车辆信息
	now_car.NBR=arr[0];
	now_car.Name[0]=arr[5];
	now_car.Name[1]=arr[6];
	now_car.Name[2]=arr[7];
	now_car.Name[3]=arr[8];
	now_car.Year=(arr[10]-'0')*10+(arr[11]-'0');
	now_car.Month=(arr[12]-'0')*10+(arr[13]-'0');
	now_car.Day=(arr[14]-'0')*10+(arr[15]-'0');
	now_car.Hour=(arr[16]-'0')*10+(arr[17]-'0');
	now_car.Minute=(arr[18]-'0')*10+(arr[19]-'0');
	now_car.Second=(arr[20]-'0')*10+(arr[21]-'0');
	
	Gocar =1;
	//清楚arr数据,为下次接收做准备
	memset(arr,0,sizeof(arr));
}

8、车辆信息判断处理函数

判断车辆信息来确保输入格式正确,以及闰年的判断来确保时间以及年、月、日时间格式的正确。然后再通过判断车库是否有这台车,从而来判定调用哪个函数。

uint8_t uart_proc(void)
{
	uint16_t Year;
	uint8_t i;
	if((now_car.NBR=='C'||now_car.NBR=='V')&&now_car.Hour<24&&now_car.Minute<60&&now_car.Second<60)
	{
	Year=2000+Year;
	if(now_car.Month==1||now_car.Month==3||now_car.Month==5||now_car.Month==7||now_car.Month==8||now_car.Month==10||now_car.Month==12)
	{
		if(now_car.Day>31) return 0;
	}
	else if(now_car.Month==4||now_car.Month==6||now_car.Month==9||now_car.Month==11)
	{
		if(now_car.Day>30) return 0;
	}
	else
	{
		if((Year%4==0&&Year%100!=0)||Year%400==0)//判断是否为闰年
		{
			if(now_car.Day>28) return 0;
		}	
		else
		{
			if(now_car.Day>29) return 0;
		}
	}
	//判断是否存在这辆车 若存在则调用出库离开函数
	for(i=0;i<8;i++)
	{
		if(car[i].NBR==now_car.NBR && strcmp((char *)car[i].Name,(char *)now_car.Name)==0)
		{
			if(output_car(i))
			{
				return 1;
			}
			else 
			{
				return 0;
			}
				
		}
	}
	
	//寻找剩余车位 找到车位调用入库停车函数
	for(i=0;i<8;i++)
	{
		if(car[i].NBR!='C'||car[i].NBR!='V')
		{
			input_car(i);
			return 1;
		}
	}
	}
	return 0;

}

9、入库停车和出库离开的处理函数

入库函数:
判断是哪种车辆类型,并保存车辆详细信息

extern struct Scinfo now_car;
extern uint8_t IDLE,CNBR,VNBR;
struct Scinfo car[9]={0};//具体枚举在回调函数那里定义
uint8_t tip[30]={0};

void input_car(uint8_t location)
{
	if(IDLE>0)
	{
	if(now_car.NBR=='C')
	{
		CNBR++;
	}
	else
	{
		VNBR++;
	}
	car[location].NBR=now_car.NBR;
	car[location].Name[0]=now_car.Name[0];
	car[location].Name[1]=now_car.Name[1];
	car[location].Name[2]=now_car.Name[2];
	car[location].Name[3]=now_car.Name[3];
	car[location].Year=now_car.Year;
	car[location].Month=now_car.Month;
	car[location].Day=now_car.Day;
	car[location].Hour=now_car.Hour;
	car[location].Minute=now_car.Minute;
	car[location].Second=now_car.Second;
	}
}

出库函数:
判断哪种车辆离开 并计算停车费用

uint8_t output_car(uint8_t location)
{
	float fee=0;
	uint32_t second=0,second_last;
	uint32_t time;
	
	//把时间换算成秒数 在算出停车时间
second=Time_to_Seconds(car[location].Year,car[location].Month,car[location].Day,car[location].Hour,car[location].Minute,car[location].Second);
	second_last=Time_to_Seconds(now_car.Year,now_car.Month,now_car.Day,now_car.Hour,now_car.Minute,now_car.Second);
	if(second_last==second)
	{
		return 0;
	}
	
	//计算出停车时间 在转换成小时
	time=(second_last-second)/60/60;
	
	//未满一个小时 按一个小时计算
	if((second_last-second)>time*60*60)
	{
		time++;  //余数补偿
	}
	if(now_car.NBR=='C')
	{
		fee=time*3.50;
		CNBR--;
	}
	else
	{
		fee=time*2.50;
		VNBR--;
	}
	
	//输出车辆具体费用 并删除它的新息
	sprintf((char *)tip,"%cNBR:%4s:%.2d:%3.2f\r\n",car[location].NBR,car[location].Name,time,fee);
	HAL_UART_Transmit(&huart1,tip,sizeof(tip),0xfff);
	memset(tip,0,sizeof(tip));
	car[location].NBR='\0';
	car[location].Name[0]=0;
	car[location].Name[1]=0;
	car[location].Name[2]=0;
	car[location].Name[3]=0;
	car[location].Year=0;
	car[location].Month=0;
	car[location].Day=0;
	car[location].Hour=0;
	car[location].Minute=0;
	car[location].Second=0;
	return 1;
}

10、时间转换函数

这是本题的难点2:将时间统一转换成秒数,然后用还要判断年份是否为闰年。注意这里处理闰年的月份时,也只有二月份天数变化,所以这里还要判定月份时间是否大于2月份,大于二月份才对月份进行多加一天时间的处理。(这时间处理函数,也可以网上找,但是毕竟是练习,可以多练练)

//这里将12个月的天数用数组处理,可以变的更加方便。
uint8_t mon[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};

uint32_t Time_to_Seconds(uint8_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second)
{
	uint16_t i;
	uint32_t Second;
	Second = 0;
	uint16_t Y;
	Y = 2000+year;
	for(i = 2000; i < Y ; i++)
	{
		if(i % 400 == 0 || (i % 4 == 0 && i % 100 != 0))
		{
			Second += 366*24*60*60;
		}
		else
			Second += 365*24*60*60;
	}
	
	//判断是否为闰年 若月份大于2月份则多加一天时间
	if ((Y % 400 == 0 || (Y % 4 == 0 && Y % 100 != 0)) && month > 2)
	{
		Second += 24*60*60;
	}
	for(i = 1; i < month ; i++)
	{
		Second += mon[i]*24*60*60;
	}
	for(i = 1; i < day; i++)
	{
		Second += 24*60*60;
	}
	for(i = 1; i < hour; i++)
	{
		Second += 60*60;
	}
	for(i = 1; i < minute; i++)
	{
		Second += 60;
	}
	Second += second;
	return Second;
}

四、总结

总的来说,这届试题最大的难点还是车辆信息的处理和计费部分的逻辑,可能一时间有些处理不清。但是我从官方的解法发现,他们在解题之前都画了流程图,对我这个手残来说实属为难,不过当我画个草图来分析时,确实头脑变得清晰了很多,所以这个难点还是很好解决的。当然啦,除了本题难点外,题目还是有很多细节部分像我在函数开头或者注释的时候所说的那样,容易忽视,所以比赛做题时还是要格外注意。
最后想说,这是我第一次在CSDN上写文章,如果有什么不足之处,请多多见谅。毕竟,我写这篇文章,只是想记录和总结一下我的做题心得和步骤。

五、源码

源码文章来源地址https://www.toymoban.com/news/detail-799528.html

到了这里,关于[蓝桥杯嵌入式]STM32G431——第十二届第一场省赛停车计费系统真题及程序设计代码详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第十二届蓝桥杯嵌入式省赛第一场真题(基于HAL库的巨简代码+超级详解)

    相关说明: 开发板:CT117E-M4(STM32G431RBT6) 开发环境: CubeMX+Keil5 涉及题目:第十二届蓝桥杯嵌入式省赛第一场真题 技巧:字符串比较 、字符串数组转移提取、for和return搭配使用、goto语句、利用%c和%s打印 CubeMX配置、主要函数代码及说明: 1.使能外部高速时钟: 2.配置时钟树:

    2023年04月11日
    浏览(26)
  • STM32G431RB--基于HAL库(蓝桥杯嵌入式赛前梳理)

    明天就进行蓝桥杯的比赛了,最后一天再重新梳理一下各个模块的使用和代码的编写。 如果各个模块的MX配置是根据我之前发的来的,那么这篇文章中的代码完全适用;如不是,原理部分也是相同的,代码部分适用,可以自行判断,作为一个参考。 引脚: 1.控制LED灯亮灭时需

    2023年04月08日
    浏览(36)
  • 蓝桥杯嵌入式STM32 G431 hal库开发速成——ADC与DAC

    模数转换器(ADC):它将模拟信号转换为单片机能够处理的数字信号。在很多应用中,比如温度传感器、压力传感器等,信号最初都是模拟形式的。ADC 读取这些模拟信号,然后将它们转换为数字形式,以便单片机可以读取和处理。 数模转换器(DAC):它执行相反的操作,将

    2024年02月01日
    浏览(31)
  • 蓝桥杯电子类嵌入式(STM32G431)备赛学习记录(一)——LED

    2023年2月10日,距离第十四届蓝桥杯正式比赛还有不到两个月的样子,从今天开始正式备赛。之前博主有看过野火的教程,学的是STM32F103。蓝桥杯嵌入式组现在用的是STM32G431,板子在二手平台上买的,视频参考b站up主01Studio相关教程(后面提到简写为教程)。学习的模块按照教

    2024年01月25日
    浏览(29)
  • 蓝桥杯嵌入式STM32G431RBT6竞赛指南与模板——最后的绝唱

    谨以此文和我去年前的一篇蓝桥杯单片机的教程构成电子类的 青铜双壁. 国信长天单片机竞赛训练之原理图讲解及常用外设原理(遗失的章节-零)_昊月光华的博客-CSDN博客     目录 时钟树 串口重定向:printf输出 动态点灯(点灯大师) 按键(常用状态机) 同一时刻对多个按键按

    2024年02月07日
    浏览(23)
  • 蓝桥杯嵌入式STM32G431RBT6的学习(总大纲)(HAL库学习)板子介绍

    我写蓝桥杯嵌入式大概用到的外设,都是非常常用的。我在这里汇总一下。 蓝桥杯嵌入式基础模块——GPIO的使用(新板)STM32G431(HAL库开发)_薛定谔的猫咪死了的博客-CSDN博客 蓝桥杯嵌入式基础模块——串口的使用(新板)STM32G431(HAL库开发)_薛定谔的猫咪死了的博客-C

    2024年02月02日
    浏览(24)
  • 蓝桥杯嵌入式基础模块——LCD显示器的基本使用(新板)STM32G431(HAL库开发)

            在蓝桥杯嵌入式官方给我们提供好了,LCD显示的底层源码,我们只需要,记住里面的API函数,会用这些函数就行。         在官方给的资料中找到这个文件名字DK117_G4 Data Packet-开发板驱动文件里面就是所有的底层文件有两种类型,一种是基于HAL库的一种是标准库

    2024年02月09日
    浏览(24)
  • 蓝桥杯嵌入式CT117E-M4学习笔记02-STM32G431RBT6芯片学习

    首先学习了解一下蓝桥杯嵌入式CT117E-M4开发板的主控芯片STM32G431RBT6,本文仅为个人学习成果总结,如有错误,恳请指正。 上图为STM32CubeMX选型界面,如图可以看出STM32G431RBT6具有以下特点和硬件集成。 采用Cortex-M4 32位RISC核心架构,工作频率最高可达170Mhz。 128kBytes的FLASH,32

    2023年04月09日
    浏览(30)
  • 【蓝桥杯】一文解决蓝桥杯嵌入式开发板(STM32G431RBT6)LCD与LED显示冲突问题,并讲述LCD翻转显示

       不知道大家在开发过程中有没有碰到: LCD与LED同时(宏观上的同时)点亮时产生的LED状态紊乱问题 。 LCD与LED显示冲突现象图   为了解决上述问题,小编给出了以下三种解决方案: 方案一:    每次LCD显示完成数据后,关闭所有LED一次。   但是 使用该方法比较麻烦,

    2023年04月12日
    浏览(27)
  • 嵌入式常用术语与AHL-STM32L431运行示例

    1.1 与硬件相关的术语 1. 封装(Package) 2. 印制电路板(Printed Circuit Board,PCB) 3. 动态可读写随机存储器(Dynamic Random Access Memory,DRAM) 4. 静态可读写随机存储器(Static Random Access Memory,SRAM) 5. 只读存储器(Read Only Memory,ROM) 6. 闪存存储器(Flash Memory) 7. 模拟量(Analog Signal) 

    2024年04月14日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包