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

news2025/2/21 11:58:07

一.题目分析

(1).题目

 (2).题目分析


1.串口功能分析

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

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

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

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

e.数据库

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

 (3).逻辑导图

二.CubeMX配置

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

三.相关代码实现

(1)MAIN

1.全局变量声明

#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.系统主函数

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. 程序源码

//***按键扫描子函数
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.  程序源码

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. 程序源码

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. 程序源码

_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. 程序源码

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.程序源码

//判别车的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.程序源码

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.程序源码

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. 程序源码

//串口接收中断回调函数
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博客里面有详细的讲解,大家可前往此链接学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2187774.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习-----------------机器翻译与数据集

目录 机器翻译与数据集下载和预处理数据集预处理步骤词元化词汇表该部分总代码 固定长度阶段或填充该部分总代码 转换成小批量数据集用于训练训练模型总代码 机器翻译与数据集 import os import torch from d2l import torch as d2l下载和预处理数据集 #save d2l.DATA_HUB[fr…

被字节恶心到了

字节 日常逛 xhs 看到一篇吐槽贴&#xff0c;表示被公司恶心到了&#xff1a; 这位网友表示&#xff0c;最近是公司举办了 Q2 和 H1 的优秀员工表彰&#xff0c;自己的 1&#xff08;直属领导&#xff09;评上了&#xff0c;但仔细一看&#xff0c;1 获奖的所有产出都是自己的&…

sql注入第7关(学习记录)

看到这里好像和前面的不一样了&#xff0c;多了个use outfile 先输入个符号&#xff0c;看报错&#xff0c;还是得看别人的教程&#xff0c;通过查找&#xff0c;好像要通过图片来进行注入&#xff0c;ok呀&#xff0c;又是新的方式&#xff0c; 首先我们需要知道他的闭合方式…

uniapp+Android智慧居家养老服务平台 0fjae微信小程序

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 老年人 登…

算法 | 鹈鹕算法POA-Transformer-LSTM多变量回归预测

&#x1f525; 内容介绍 近年来&#xff0c;随着大数据时代的到来和计算能力的飞速提升&#xff0c;对复杂系统进行精确预测的需求日益增长。多变量时间序列预测作为一项关键技术&#xff0c;广泛应用于金融、能源、交通等诸多领域。传统的预测方法&#xff0c;例如ARIMA和多元…

Prometheus Metrics和PromQL的使用

Metrics 官方解释是 Metrics are numerical measurements in layperson terms. (通俗地讲&#xff0c;Metrics就是数字测量) Prometheus fundamentally stores all data as time series &#xff08;Prometheus把所有数据都存储为时间序列&#xff09; Every time series is u…

《PMI-PBA认证与商业分析实战精析》第6章 跟踪与监督

第6章 跟踪与监督 本章主要内容包括&#xff1a; 跟踪 关系与依赖性 批准需求 基线化已批准需求 使用跟踪矩阵来监督需求 需求生命周期 管理需求变更 本章涵盖的考试重点&#xff1a; 跟踪与监督的六项活动 跟踪与监督六项活动的可交付成果及活动间的关系 跟踪的定义…

指南:Linux常用的操作命令!!!

引言: 操作系统是软件的一类。 主要作用是协助用户调度硬件工作&#xff0c;充当用户和计算机硬件之间的桥梁。 尽管图形化是大多数人使用计算机的第一选择&#xff0c;但是在Linux操作系统上多数都是使用的&#xff1a;命令行在开发中&#xff0c;使用命令行形式&#xff0c…

【有啥问啥】联邦学习(Federated Learning, FL):保护隐私的分布式机器学习

联邦学习&#xff08;Federated Learning, FL&#xff09;&#xff1a;保护隐私的分布式机器学习 联邦学习&#xff08;Federated Learning, FL&#xff09;作为一种前沿的分布式机器学习技术&#xff0c;正逐步成为解决数据隐私保护与模型性能提升之间矛盾的关键方案。以下是…

HTTP Cookie与Session

目录 一. 引入Cookie 1.1 定义 1.2 工作原理 1.3 分类 二. 认识Cookie 三. 测试Cookie 五. 引入Session 六. 测试Session 这篇博客&#xff0c;我们来看看Cookie与Session&#xff0c;内容干货满满。 一. 引入Cookie 1.1 定义 HTTP Cookie&…

幂等性及技术解决方案

目录 定义幂等性 为什么需要幂等性幂等性设计注意事项幂等性的范围分布式锁解决幂等性 设计 延伸阅读 定义幂等性 简单地说&#xff0c;我们可以多次执行幂等运算而不改变结果或者使用相同的输入参数中被调用多次&#xff0c;则不具有额外效果的操作&#xff0c;也就是多次执…

使用pytdx获取历史股票行情

使用pytdx获取历史股票行情 先看效果pytdx基础获取历史股票行情将历史数据存入数据库 先看效果 获取从2010年01月01日-2024年09月30日的股票数据 pytdx基础 https://blog.csdn.net/firexiaHouse/article/details/142687052?spm1001.2014.3001.5501 获取历史股票行情 def …

C++11--智能指针

引入 为什么需要智能指针&#xff1f; 在介绍异常时&#xff0c;遇到以下场景&#xff0c;处理异常就会比较棘手&#xff1a; void Func() {int* arr1 new int[10];int* arr2 new int[20];int* arr3 new int[30];// ...delete[] arr1;delete[] arr2;delete[] arr3; }这里…

一文吃透 SpringBoot (从入门到精通)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

15分钟学 Python 第35天 :Python 爬虫入门(一)

Day 35 : Python 爬虫简介 1.1 什么是爬虫&#xff1f; 网页爬虫&#xff08;Web Crawler&#xff09;是自动访问互联网并提取所需信息的程序。爬虫的主要功能是模拟用户通过浏览器访问网页的操作&#xff0c;从而实现对网页内容的批量访问与信息提取。它们广泛应用于数据收集…

【IPv6】IPv6地址格式及地址分类(组播、单播、任播)整理

IPv6地址格式 IPv6 地址从 IPv4 地址的 32 bits 扩展到 128 bits&#xff0c;IPv6 地址的表示、书写方式也从 IPv4 的点分十进制&#xff0c;修改16进制的冒号分割 IPv4 点分格式(.) 192.168.11.11 IPv6 冒号分割(:) 2408:8459:3032:0000:0000:0000:0001:a9fd IPv6 的规范…

平面电磁波的电场能量磁场能量密度相等,注意电场能量公式也没有复数形式(和坡印廷类似)

1、电场能量密度和磁场能量密度相等(实数场算的) 下面是电场能量密度和磁场能量密度的公式&#xff0c;注意这可不是坡印廷定理。且电场能量密度没有复数表达式&#xff0c;即不是把E和D换成复数形式就行的。注意&#xff0c;一个矢量可以转化为复数形式&#xff0c;两个矢量做…

6.4 数据处理架构模式和实践

6.4 数据处理架构模式和实践 目录概述需求&#xff1a; 设计思路实现思路分析1.批处理架构2.实时处理架构3.流处理架构4.微服务架构&#xff08;重点&#xff09;5.数据湖架构6.数据仓库架构 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , s…

Java | Leetcode Java题解之第452题用最少数量的箭引爆气球

题目&#xff1a; 题解&#xff1a; class Solution {public int findMinArrowShots(int[][] points) {if (points.length 0) {return 0;}Arrays.sort(points, new Comparator<int[]>() {public int compare(int[] point1, int[] point2) {if (point1[1] > point2[1…

微软官网列出了 Windows 11 LTSC 2024 中的全部新功能

今天早些时候&#xff0c;微软发布了有关受托管PC的Windows 11 24H2 升级和兼容性的详细信息。 该帖子针对的是负责在各自办公室和组织中处理系统的 IT 系统管理员。与此同时&#xff0c;微软也发布了有关 Windows 11 LTSC 或长期服务渠道的信息。 该公司已于四月早些时候证实…