第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第一套)

一.题目分析

(1).题目

(2).题目分析

1.串口功能分析

a.串口接收车辆出入信息:通过查询车库的车判断车辆是进入/出去

b.串口输出计费信息:输出编号,时长和费用

c.计算停车时长是难点,需要将年月日时分秒全部都转换成秒

d.当接收到的字符串格式不正确或者逻辑错误就输出Error

e.数据库

22个字节构成一组,最多有八组,然后定义结构体变量,该结构体的数据结构为车类型+车编号+时间的数据格式,用该结构体变量,创造一个数组

(3).逻辑导图

二.CubeMX配置

由于蓝桥杯使用的板子都是STM32G431RBT6,配置都是相同的,模板已经在第六届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)-CSDN博客配置完成,大家可以前往学习

三.相关代码实现

(1)MAIN

1.全局变量声明

cpp 复制代码
#include "main.h"
#include "RCC\bsp_rcc.h"
#include "KEY_LED\bsp_key_led.h"
#include "LCD\bsp_lcd.h"
#include "UART\bsp_uart.h"
#include "TIM\bsp_tim.h"
#include <string.h>

//***全局变量声明区
//*减速变量
__IO uint32_t uwTick_Key_Set_Point = 0;//控制Key_Proc的执行速度
__IO uint32_t uwTick_Led_Set_Point = 0;//控制Led_Proc的执行速度
__IO uint32_t uwTick_Lcd_Set_Point = 0;//控制Lcd_Proc的执行速度
__IO uint32_t uwTick_Usart_Set_Point = 0;//控制Usart_Proc的执行速度

//*按键扫描专用变量
uint8_t ucKey_Val, unKey_Down, ucKey_Up, ucKey_Old;

//*LED专用变量
uint8_t ucLed;

//*LCD显示专用变量
uint8_t Lcd_Disp_String[21];//最多显示20个字符

//*串口专用变量
uint16_t counter = 0;
uint8_t str_str[40];
uint8_t rx_buffer;


//***子函数声明区
void Key_Proc(void);
void Led_Proc(void);
void Lcd_Proc(void);
void Usart_Proc(void);

//全局变量区
_Bool Disp_Num;//0-数据显示,1-费率设置
_Bool PWM_Output_Num;//0-低电平,1-PWM

float VNBR_Price = 2.00;
float CNBR_Price = 3.50;

uint8_t VNBR_Use_Num;
uint8_t CNBR_Use_Num;
uint8_t No_Use_Num = 8;

uint8_t RX_BUF[200];//用于缓冲接收200个字节的数量
uint8_t Rx_Counter;//用于记录接收了多少个数据,同时可以索引RX_BUF中的数据位置


typedef struct
{
	uint8_t type[5];
	uint8_t id[5];
	uint8_t year_in;
	uint8_t month_in;
	uint8_t day_in;
	uint8_t hour_in;
	uint8_t min_in;
	uint8_t sec_in;
	_Bool notEmpty;
} Car_Data_Storage_Type;

Car_Data_Storage_Type Car_Data_Storage[8];//数据库构建完毕,用于存储8个进来的车的信息

2.系统主函数

cpp 复制代码
int main(void)
{

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

	/*bsp资源的初始化*/
	KEY_LED_Init();
	
	LCD_Init();
	LCD_Clear(Black);
    LCD_SetBackColor(Black);
    LCD_SetTextColor(White);	
	
	UART1_Init();
	PWM_OUTPUT_TIM17_Init();

	
	//*串口接收中断打开
	HAL_UART_Receive_IT(&huart1, (uint8_t *)(&rx_buffer), 1);	

	__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 0);//强制配置成低电平
  HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);		//PA7		
		
  while (1)
  {
		Key_Proc();
		Led_Proc();
		Lcd_Proc();
		Usart_Proc();
  	
  }

}

3.按键扫描子函数

a. 逻辑框图

b. 程序源码

cpp 复制代码
//***按键扫描子函数
void Key_Proc(void)
{
	if((uwTick -  uwTick_Key_Set_Point)<50)	return;//减速函数
		uwTick_Key_Set_Point = uwTick;

	ucKey_Val = Key_Scan();
	unKey_Down = ucKey_Val & (ucKey_Old ^ ucKey_Val); 
	ucKey_Up = ~ucKey_Val & (ucKey_Old ^ ucKey_Val);	
	ucKey_Old = ucKey_Val;
	switch(unKey_Down)
	{
		case 1://B1
			Disp_Num ^= 0x1;
			LCD_Clear(Black);
		break;
		
		case 2://B2
			if(Disp_Num == 1)//费率设置界面
			{
			 VNBR_Price += 0.5;
       CNBR_Price += 0.5;	
			}			
		break;	
	
		case 3://B3
			if(Disp_Num == 1)//费率设置界面
			{
				if((VNBR_Price - 0.5)> 0)
				{
					VNBR_Price -= 0.5;
					CNBR_Price -= 0.5;	
				}
			}				
		break;	
	
		case 4://B4
			PWM_Output_Num ^= 0x1;
			if(PWM_Output_Num == 0)//低电平
			{
					__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 0);//强制配置成低电平
			}
			else//高电平 
			{
					__HAL_TIM_SET_COMPARE(&htim17,TIM_CHANNEL_1, 100);//强制配置成PWM电平					
			}
		break;	
	
	}
}

4.LED扫描子函数

a. 逻辑框图

b. 程序源码

cpp 复制代码
void Led_Proc(void)
{
	if((uwTick -  uwTick_Led_Set_Point)<200)	return;//减速函数
		uwTick_Led_Set_Point = uwTick;

	if(No_Use_Num > 0)//表示还有车位
	{
		ucLed |= 0x1;	
	}
	else//如果空闲
	{
		ucLed &= (~0x1);		
	}
	
	if(PWM_Output_Num == 0)//低电平
	{
		ucLed &= (~0x2);	
	}
	else//PWM
	{		
    ucLed |= 0x2;		
	}
	
	LED_Disp(ucLed);
}

5. LCD扫描

a. 程序框图

b. 程序源码

cpp 复制代码
void Lcd_Proc(void)
{
	if((uwTick -  uwTick_Lcd_Set_Point)<100)	return;//减速函数
		uwTick_Lcd_Set_Point = uwTick;

	
	//用户代码
	if(Disp_Num == 0)//数据界面
	{
		sprintf((char *)Lcd_Disp_String, "       Data");
		LCD_DisplayStringLine(Line1, Lcd_Disp_String);		
	
		sprintf((char *)Lcd_Disp_String, "   CNBR:%1d",(unsigned int)CNBR_Use_Num);
		LCD_DisplayStringLine(Line3, Lcd_Disp_String);		
	
		sprintf((char *)Lcd_Disp_String, "   VNBR:%1d",(unsigned int)VNBR_Use_Num);
		LCD_DisplayStringLine(Line5, Lcd_Disp_String);			

		sprintf((char *)Lcd_Disp_String, "   IDLE:%1d",(unsigned int)No_Use_Num);
		LCD_DisplayStringLine(Line7, Lcd_Disp_String);		


	}
	else//参数界面
	{
		sprintf((char *)Lcd_Disp_String, "       Para");
		LCD_DisplayStringLine(Line1, Lcd_Disp_String);			
	
		sprintf((char *)Lcd_Disp_String, "   CNBR:%4.2f",CNBR_Price);
		LCD_DisplayStringLine(Line3, Lcd_Disp_String);		
	
		sprintf((char *)Lcd_Disp_String, "   VNBR:%4.2f",VNBR_Price);
		LCD_DisplayStringLine(Line5, Lcd_Disp_String);		

	}
}

6. 判别接收到22个字符是否合法函数

a. 逻辑框图

b. 程序源码

cpp 复制代码
_Bool CheckCmd(uint8_t* str)//用于判别接受的22个字符是否合法
{
	if(Rx_Counter != 22)
		return 0;//表示还不够22个数据
	if(((str[0]=='C')||(str[0]=='V'))&&(str[1]=='N')&&(str[2]=='B')&&(str[3]=='R')&&(str[4]==':')&&(str[9]==':'))
	{
		uint8_t i;
		for(i = 10; i< 22;i++)
		{
			if((str[i]>'9')||(str[i]<'0'))
				return 0;
		}
		return 1;//表示接收到的数据没问题
	}
}

7. 从长字符串提取一段给短字符串函数

a. 逻辑分析

传入参数为(数据的类型,提取的位置,从第几位开始提取,提取的个数)

b. 程序源码

cpp 复制代码
void substr(uint8_t* d_str, uint8_t* s_str, uint8_t locate, uint8_t length)//从长字符串里边提取出一段给短字符串
{ 
	uint8_t i = 0;
	for(i=0; i<length; i++)
	{
		d_str[i] = s_str[locate + i];
	}
	d_str[length] = '\0';
}

8.判别车是否在车库里面

a.逻辑分析

使用到了strcmp函数比较字符串是否相同,相同就返回0

【函数原型】 int strcmp(const char *s1, const char *s2);

【参数】s1, s2 为需要比较的两个字符串。

【返回值】若参数s1 和s2 字符串相同则返回0,s1 若大于s2 则返回大于0 的值,s1 若小于s2 则返回小于0 的值

b.程序源码

cpp 复制代码
//判别车的id是否在库里边
uint8_t isExist(uint8_t* str)
{
	uint8_t i = 0;	
	for(i=0; i<8; i++)
	{
		if((strcmp((const char*)str,(const char*)Car_Data_Storage[i].id)) == 0)//表示字符串匹配,有这个字符串		
		{
			return i;//如果该id在数据库存着,返回这个id在数据库当中的位置
		}
	}	
	return 0xFF;//如果没有,返回oxff
}

9.判断0-7号,哪个位置有空挡

a.逻辑分析

轮询数据库里面的空档标志位,当标志位为0说明没有被使用,则返回第i号位置

b.程序源码

cpp 复制代码
uint8_t findLocate(void)
{
	uint8_t i = 0;
	for(i = 0;i <= 7; i++ )
	{
		if(Car_Data_Storage[i].notEmpty == 0)
			return i;//0-7号位
	}
	return 0XFF;
}

10.串口扫描子函数

a.逻辑框图

b.程序源码

cpp 复制代码
void Usart_Proc(void)
{
	if((uwTick -  uwTick_Usart_Set_Point)<100)	return;//减速函数
	uwTick_Usart_Set_Point = uwTick;		
	if(CheckCmd(RX_BUF))//粗糙的判断,第一步,判别数据个数以及数据格式是否合法
	{
		
		uint8_t car_id[5];
		uint8_t car_type[5];	
		uint8_t year_temp,month_temp,day_temp,hour_temp,min_temp,sec_temp;
		
		
		year_temp = (((RX_BUF[10] - '0')*10) + (RX_BUF[11] - '0')); 
		month_temp = (((RX_BUF[12] - '0')*10) + (RX_BUF[13] - '0')); 	
		day_temp = (((RX_BUF[14] - '0')*10) + (RX_BUF[15] - '0')); 	
		hour_temp = (((RX_BUF[16] - '0')*10) + (RX_BUF[17] - '0')); 	
		min_temp = (((RX_BUF[18] - '0')*10) + (RX_BUF[19] - '0')); 	
		sec_temp = (((RX_BUF[20] - '0')*10) + (RX_BUF[21] - '0')); 		
		if((month_temp>12)||(day_temp>31)||(hour_temp>23)||(min_temp>59)||(sec_temp>59))//验证日期和时间是否合法
		{
			goto SEND_ERROR;
		}
		
		substr(car_id, RX_BUF, 5, 4);//提取车的id
		substr(car_type, RX_BUF, 0, 4);	//提取车的类型
		
		//**********************车还没有进入******
		if(isExist(car_id) == 0xFF)//表示库里边没有这辆车的ID,表示这个车还没进入
		{
			uint8_t locate = findLocate();//找到哪个地方是空的

			
			if(locate == 0xFF)//没有找到哪个地方是空的
			{
			  goto SEND_ERROR;
			}
			
			//准备存储
			substr(Car_Data_Storage[locate].type, car_type, 0, 4);//把当前车的类型存入			
			substr(Car_Data_Storage[locate].id, car_id, 0, 4);//把当前车的id存入
			Car_Data_Storage[locate].year_in = year_temp;
			Car_Data_Storage[locate].month_in = month_temp;			
			Car_Data_Storage[locate].day_in = day_temp;			
			Car_Data_Storage[locate].hour_in = hour_temp;			
			Car_Data_Storage[locate].min_in = min_temp;			
			Car_Data_Storage[locate].sec_in = sec_temp;		
			Car_Data_Storage[locate].notEmpty = 1;
			
			if(Car_Data_Storage[locate].type[0] == 'C')
				CNBR_Use_Num++;
			else if(Car_Data_Storage[locate].type[0] == 'V')
				VNBR_Use_Num++;

			No_Use_Num--;		
		}
		
		//**********************如果车已经进来了,现在是出去******		
		else if(isExist(car_id) != 0xFF)//表示数据库里有他的信息,返回他在数据库的位置
		{
			int64_t Second_derta;//用于核算小时的差值	
			
			uint8_t in_locate = isExist(car_id);//记住在数据库中的位置
		
			if(strcmp((const char*)car_type,(const char*)Car_Data_Storage[in_locate].type) != 0)//说明不匹配
			{
			  goto SEND_ERROR;			
			}
			
			//2000 2001 2002  //1   2  3
			Second_derta = (year_temp - Car_Data_Storage[in_locate].year_in)*365*24*60*60 + (month_temp - Car_Data_Storage[in_locate].month_in)*30*24*60*60+\
				(day_temp - Car_Data_Storage[in_locate].day_in)*24*60*60 + (hour_temp - Car_Data_Storage[in_locate].hour_in)*60*60 + \
				(min_temp - Car_Data_Storage[in_locate].min_in)*60 + (sec_temp - Car_Data_Storage[in_locate].sec_in);
			
			if(Second_derta < 0)//说明出去的时间超前进去的时间
			{
					goto SEND_ERROR;			
			}
			
			Second_derta = (Second_derta + 3599)/3600;  //小时数据已经核算出来
			sprintf(str_str, "%s:%s:%d:%.2f\r\n",Car_Data_Storage[in_locate].type,Car_Data_Storage[in_locate].id,(unsigned int)Second_derta,(Second_derta*(Car_Data_Storage[in_locate].id[0]=='C'?CNBR_Price:VNBR_Price)));
			HAL_UART_Transmit(&huart1,(unsigned char *)str_str, strlen(str_str), 50);		
			
			if(Car_Data_Storage[in_locate].type[0] == 'C')
				CNBR_Use_Num--;
			else if(Car_Data_Storage[in_locate].type[0] == 'V')
				VNBR_Use_Num--;

			No_Use_Num++;			
			
			memset(&Car_Data_Storage[in_locate],0,sizeof(Car_Data_Storage[in_locate]));//清空该位置所有内容,为0
			
		}
	
		goto CMD_YES;
		
		SEND_ERROR:	
			sprintf(str_str, "Error\r\n");
			HAL_UART_Transmit(&huart1,(unsigned char *)str_str, strlen(str_str), 50);
		
		CMD_YES:
				memset(&RX_BUF[0],0,sizeof(RX_BUF));//清空该位置所有内容,为0
				Rx_Counter = 0;
	}
}

11.串口接收中断回调函数

a.逻辑分析

实现将字符数据保存到环形缓冲区里面

b. 程序源码

cpp 复制代码
//串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	RX_BUF[Rx_Counter] = rx_buffer;
	Rx_Counter++;
	HAL_UART_Receive_IT(&huart1, (uint8_t *)(&rx_buffer), 1);
}

(2)BSP

第六届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)-CSDN博客里面有详细的讲解,大家可前往此链接学习

相关推荐
爱倒腾的老唐4 分钟前
01、如何学习单片机
单片机·嵌入式硬件·学习
点灯小铭6 分钟前
基于单片机的夹具压力控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计
雾削木6 小时前
stm32解锁芯片
javascript·stm32·单片机·嵌入式硬件·gitee
Swift社区6 小时前
LeetCode 394. 字符串解码(Decode String)
算法·leetcode·职场和发展
tt5555555555557 小时前
LeetCode进阶算法题解详解
算法·leetcode·职场和发展
让我们一起加油好吗7 小时前
【基础算法】DFS中的剪枝与优化
算法·深度优先·剪枝
热爱编程的小刘7 小时前
STM32学习路线开启篇:外部中断
stm32
Q741_1477 小时前
C++ 模拟题 力扣495. 提莫攻击 题解 每日一题
c++·算法·leetcode·模拟
我命由我123458 小时前
Excel - Excel 列出一列中所有不重复数据
经验分享·学习·职场和发展·word·powerpoint·excel·职场发展
三佛科技-134163842128 小时前
手持小风扇MCU方案,智能风扇方案设计开发
单片机·嵌入式硬件