蓝桥杯嵌入式真题试水(一)——2021

STM32G431嵌入式

蓝桥杯嵌入式真题试水(一)——2021


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

最近在备赛蓝桥杯嵌入式,做了几套真题,现通过写博客来记录下自己踩过的一些坑和遇到的一些问题以及解决办法!


提示:以下是本篇文章正文内容,下面案例可供参考

一、2021年试题

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二、涉及到的模块

1.按键,LED,LCD

这些模块可以说是每年必考的,我们只需要在CUBEMX上配置好即可!

2.UART

串口!这场试题让我对串口有了一个比较深的了解!

3.PWM脉冲输出

也是对Cubemx进行配置即可!(较为简单)

我的配置:
在这里插入图片描述

三:题目分析


1.LCD显示(规范)

2.按键功能

key1:实现对LCD屏幕交互的操作
key2:对不同车型的停车费用进行修改,每次增加0.5
key3:每次减少0.5
key4:切换PA7端口的电平状态.即输出2KHZ,20%占空比的脉冲信号,或者持续低电平!
注意!
这里按键2,3只能在LCD修改界面才能起作用

3.串口功能

1.波特率9600
2.四个ASCII字符作为车辆编号——车牌
3.串口发送规范
在这里插入图片描述
4.串口输出规范
在这里插入图片描述
5.当输入信息不规范时,串口应当发出Error!

四:重点!

1.一些细节!比如LCD初始状态,计费标准的初始状态,PA7端口的初始状态,还有性能要求!
这里的性能要求我们给系统的主频要足够高!
我一般给的80Mhz,这其实是不够的,在学长点拨之后,我认为可以给到120Mhz!
2.根据题目需求,我们必须构建一个数据库,用来存储车辆入库的信息!
可以通过构建一个如下结构体(仅供参考)
在这里插入图片描述

五:贴上代码

typedef struct
{
	uint8_t type[5];
	uint8_t num[5];
	uint8_t year[2];
	uint8_t month[2];
	uint8_t day[2];
	uint8_t hour[2];
	uint8_t minute[2];
	uint8_t second[2];
}	car_imformation;

__IO uint32_t LCD_uwTick;//LCD进程减速变量
__IO uint32_t KEY_uwTick;//KEY进程减速变量
__IO uint32_t Uart_uwTick;//UART进程减速变量
uint8_t LCD_DispString[20] = {0};//LCD显示字符变量
uint8_t key_value,key_old,key_up,key_down;//按键变量
uint8_t Interface = 0x00;//lcd交互界面变量
float CNBR = 3.5,VNBR = 2.0;//定义两种车型数量
uint16_t IDLE = 8;//空闲车位
uint8_t state = 0x00,led1,led2;//PA7->IO口状态,灯的状态
unsigned char rx_buffer;//
uint8_t str[19]; //串口发送数据存储字符
uint8_t rx_buf[22];//接送数据存储
uint16_t i = 0,j=0;//存储顺序
uint16_t year,month,day,hour,minute,seconds,flag = 2;
uint16_t x = 0;
float money;//计费
car_imformation in_car[8];//已入库车辆数据库
car_imformation ing_car;//将要进来的车的信息



void SystemClock_Config(void);//系统时钟配置
void LCD_Proc(void);//LCD进程函数
uint8_t KEY_Scan(void);//按键扫描函数
void KEY_Proc(void);//按键进程函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//串口中断开启函数
void UART_Proc(void);//串口进程函数
void LED_Disp(uint8_t ucled);//LED点亮函数
_Bool PD_car(void);
uint16_t _exist(void);//判断是否在车库里
void substr(uint8_t* d_str, uint8_t* s_str, uint8_t locate, uint8_t length);//从长字符串里边提取出一段给短字符串


int main(void)
{
 
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_TIM17_Init();
  MX_USART1_UART_Init();
	
	LCD_Init();
	LCD_Clear(Black);
	LCD_SetBackColor(Black);
	LCD_SetTextColor(White);
	
	HAL_UART_Receive_IT(&huart1,&rx_buffer,1);//开启串口中断
	
  while (1)
  {
   LCD_Proc();
	 KEY_Proc();
	 UART_Proc();
  }
}

//按键进程函数
void KEY_Proc(void)
{
	if(uwTick - KEY_uwTick < 20) return;
		KEY_uwTick  = uwTick;
	if (IDLE!=0) led1 = 0x01;
	key_value = KEY_Scan();
	key_down = key_value & (key_value ^ key_old);
	key_up = ~key_value & (key_value ^ key_old);
	key_old = key_value;
	
	if(key_down == 1)
	{
		Interface ^= 0x01;
		LCD_Clear(Black);
	}
		
	if(key_down == 2 && Interface == 0x01)
	{
		CNBR += 0.5;
		VNBR += 0.5;
	}
	if(key_down == 3 && Interface == 0x01)
	{
		CNBR -= 0.5;
		VNBR -= 0.5;
	}
	if(key_down == 4)
	{
		state ^= 0x01;
		
		if(state == 0x00)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_7,GPIO_PIN_RESET);
			led2 = 0x00;
		}
		else
		{
			HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
			led2 = 0x02;
		}				
	}
	LED_Disp(led1|led2);
}

//LCD进程函数
void LCD_Proc(void)
{
	if(uwTick - LCD_uwTick < 50) return;
		LCD_uwTick  = uwTick;
	
	if(Interface == 0x00)
	{
		sprintf((char *)LCD_DispString,"       Date");
		LCD_DisplayStringLine(Line1,LCD_DispString);
		
		sprintf((char *)LCD_DispString,"   CNBR:%.2f",CNBR);
		LCD_DisplayStringLine(Line3,LCD_DispString);
		
		sprintf((char *)LCD_DispString,"   VNBR:%.2f",VNBR);
		LCD_DisplayStringLine(Line5,LCD_DispString);
		
		sprintf((char *)LCD_DispString,"   IDLE:%d",IDLE);
		LCD_DisplayStringLine(Line7,LCD_DispString);
	}
	else
	{
		sprintf((char *)LCD_DispString,"       Para");
		LCD_DisplayStringLine(Line1,LCD_DispString);
		
		sprintf((char *)LCD_DispString,"   CNBR:%.2f",CNBR);
		LCD_DisplayStringLine(Line3,LCD_DispString);
		
		sprintf((char *)LCD_DispString,"   VNBR:%.2f",VNBR);
		LCD_DisplayStringLine(Line5,LCD_DispString);
	}
	
	
}

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';
}

//
void UART_Proc(void)
{
	if(uwTick - Uart_uwTick < 30) return;
		Uart_uwTick  = uwTick;
	
	if(flag == 0)//数据错误
	{
		sprintf((char *)str,"Error");
		HAL_UART_Transmit(&huart1,(uint8_t *)str,strlen(str),100);
		flag = 2;//重置标志
	}
	else if(flag == 1)//数据正确
	{
		//处理数据,将正在入库的车信息存入中转数据库
		substr(ing_car.type,rx_buf,0,4);
		substr(ing_car.num,rx_buf,5,4);
		substr(ing_car.year,rx_buf,10,2);
		substr(ing_car.month,rx_buf,12,2);
		substr(ing_car.day,rx_buf,14,2);
		substr(ing_car.hour,rx_buf,16,2);
		substr(ing_car.minute,rx_buf,18,2);
		substr(ing_car.second,rx_buf,20,2);
		
		if(isexist()!=8) //出去的车
		{
			//计算车费
			if((ing_car.minute[0]-in_car[j].minute[0])*10+(ing_car.minute[1]-in_car[j].minute[1])!=0||(ing_car.second[0]-in_car[j].second[0])*10+(ing_car.second[1]-in_car[j].second[1])!=0)
				hour+=1;
			hour = ((ing_car.day[0]-in_car[j].day[0])*10+(ing_car.day[1]-in_car[j].day[1]))*24+(ing_car.hour[0]-in_car[j].hour[0])*10+(ing_car.hour[1]-in_car[j].hour[1]);
			if(ing_car.type[0]=='C')
					money = hour*CNBR;
			else money = hour*VNBR;
			
			//发送车费信息
			sprintf((char *)str,"%s:%s:%d:%.2f",ing_car.type,ing_car.num,hour,money);
			HAL_UART_Transmit(&huart1,str,strlen((const char *)str),20);
			
			//将对应出去的车的车位置零
			for(x=0;x<4;x++)
			in_car[j].type[x] = 0;
			IDLE+=1;
		}
		else if((IDLE>0))//进来的车
		{
			for(x=0;x<8;x++)
			{
				if(in_car[x].type[0]==0)//证明这个车位是空的
				{
					substr(in_car[x].type,rx_buf,0,4);
					substr(in_car[x].num,rx_buf,5,4);
					substr(in_car[x].year,rx_buf,10,2);
					substr(in_car[x].month,rx_buf,12,2);
					substr(in_car[x].day,rx_buf,14,2);
					substr(in_car[x].hour,rx_buf,16,2);
					substr(in_car[x].minute,rx_buf,18,2);
					substr(in_car[x].second,rx_buf,20,2);
					break;
				}
			}
			
			IDLE-=1;
		}
		flag = 2;//重置标志
	}
}

uint16_t _exist(void)//判断是否在车库里
{
	for(j=0;j<8;j++)
	{
		if((ing_car.type[0]==in_car[j].type[0])
			&&(strcmp((const char *)ing_car.num,(const char *)in_car[j].num)==0))	
			return j;//将要出去的车
	}
	return 8;//将要进来的车
}

//按键扫描函数
uint8_t KEY_Scan(void)
{
	uint8_t value;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)==0)
		value = 1;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)==0)
		value = 2;
	if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)==0)
		value = 3;
	if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==0)
		value = 4;
	
	return value;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	rx_buf[i] = rx_buffer;
	i++;
	
	if(i==22)//一组数据接受完了
	{
		flag = PD_car();//判断数据是否符合规矩
		i=0;//重置数据
	}

	HAL_UART_Receive_IT(&huart1,&rx_buffer,1);//开启串口中断
}

//VNBR:A325:200202120000
_Bool PD_car(void)
{
	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]==':'))
	{
		if((rx_buf[10]>='0')&&(rx_buf[10]<='9') && (rx_buf[11]>='0')&&(rx_buf[11]<='9') && (rx_buf[12]>='0')&&(rx_buf[12]<='9') && (rx_buf[13]>='0')&&(rx_buf[13]<='9')
			&& (rx_buf[14]>='0')&&(rx_buf[14]<='9') && (rx_buf[15]>='0')&&(rx_buf[15]<='9') && (rx_buf[16]>='0')&&(rx_buf[16]<='9')&& (rx_buf[17]>='0')&&(rx_buf[17]<='9')
		&& (rx_buf[18]>='0')&&(rx_buf[18]<='9')&& (rx_buf[19]>='0')&&(rx_buf[19]<='9')&& (rx_buf[20]>='0')&&(rx_buf[20]<='9')&& (rx_buf[21]>='0')&&(rx_buf[21]<='9'))
		{
			year = (rx_buf[10]-'0')*10+(rx_buf[11]-'0');
			day = (rx_buf[12]-'0')*10+(rx_buf[13]-'0');
			month = (rx_buf[14]-'0')*10+(rx_buf[15]-'0');
			hour = (rx_buf[16]-'0')*10+(rx_buf[17]-'0');
			minute = (rx_buf[18]-'0')*10+(rx_buf[19]-'0');
			seconds = (rx_buf[20]-'0')*10+(rx_buf[21]-'0');
			
			if((day>31)||(month>12)||(hour>24)||(minute>59)||(seconds>59))
				return 0;
			else return 1;
		}
		else return 0;
	}
	else return 0;
}

void LED_Disp(uint8_t ucled)
{
	//先关闭所有的灯
	HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|
	GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
	
	//点灯
	HAL_GPIO_WritePin(GPIOC,ucled<<8,GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);

}

六:遇到的问题

1.解题逻辑不够清晰!
2.串口理解不够深刻!
这里串口的配置老是忘记对端口进行选择,以至于每次配置完都是PC8,PC9,而参看产品手册可以知道实际上是PA9,PA10
在这里插入图片描述
3.代码的冗长,不够精简!
对于串口接受这一段代码,完全可以接受22个字符再进入中断,不用一个个字符接受!而且在中断回调函数中,最好不要写太多的东西!

七、总结

1.在拿到题目的第一时间,应该先对整体有个大概的把握,对细节有足够的关注!这样不至于在编写过程中出现逻辑混乱的问题!

2.逻辑问题漏洞十足!这次因为一个isexist函数的返回值界定没注意,让我再次找了一个下午的bug,不断地调试,找问题!这也可能和我编程习惯有关系!主要逻辑部分喜欢全部写完再去调试,而不是一步一步调试!下次争取改正!

3.对于串口部分,这次是吃了大亏的,这个串口助手本身就不够稳定!在调试过程中,USB线有一丢丢移动,都有可能会是的串口无法接发,这个问题曾经困扰我一个下午,一度怀疑自己串口代码写错了,但是经过检查,实在是没有问题,在休息过后重新插上发现又能用,实在难搞!

整体来说,这一届的题目难度不算特别大,最主要的是对于串口收发数据的处理部分以及一些细节的初始状态!对于性能的要求,主频不要给太低就行,还有就是按键部分的响应时间,在减速函数部分给低点就行!

以上纯属个人理解,第一次写博客,希望能够各位看官提出意见,相互交流学习!之后本人也会逐渐通过学习提高写博客的能力!冲!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值