欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 美景 > 第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第一套)

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

2024/10/24 13:23:53 来源:https://blog.csdn.net/weixin_51089092/article/details/142688740  浏览:    关键词:第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第一套)

一.题目分析

(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-PWMfloat 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://B1Disp_Num ^= 0x1;LCD_Clear(Black);break;case 2://B2if(Disp_Num == 1)//费率设置界面{VNBR_Price += 0.5;CNBR_Price += 0.5;	}			break;	case 3://B3if(Disp_Num == 1)//费率设置界面{if((VNBR_Price - 0.5)> 0){VNBR_Price -= 0.5;CNBR_Price -= 0.5;	}}				break;	case 4://B4PWM_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);//提取车的idsubstr(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  3Second_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));//清空该位置所有内容,为0Rx_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博客里面有详细的讲解,大家可前往此链接学习

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com