1. 前言

最近在准备16届的蓝桥杯嵌入式赛道的国赛,打算出一个系列的博客,记录STM32G431RBT6这块比赛用板上所有模块可能涉及到的所有考点,如果有错误或者遗漏欢迎各位大佬斧正。

本系列博客会分为以下两大类:

1.1. 单独模块的讲解

在这部分,我会分享自己总结的各个模块的相关配置、代码书写模板,涉及到的大致框架如下:

 这个框架后续可能会不断更新,欢迎各位给出建议。

这一大类相关的文章链接如下(持续补充中):

【蓝桥杯嵌入式】【模块】一、系统初始化-CSDN博客

【蓝桥杯嵌入式】【模块】二、LED相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】三、LCD相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】四、按键相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】五、ADC相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】六、PWM相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】七、IIC相关配置及代码模板-CSDN博客

【蓝桥杯嵌入式】【模块】八、UART相关配置及代码模板-CSDN博客

1.2. 蓝桥杯各届的真题、模拟题复盘及个人答案

在这一部分,我会分享个人练过的所有题的复盘思路及代码,每篇文章结构如下:

这一大类相关的文章链接如下(持续补充中):

【蓝桥杯嵌入式】【复盘】第13届国赛真题_蓝桥杯嵌入式13届国赛题-CSDN博客

【蓝桥杯嵌入式】【复盘】第14届国赛真题-CSDN博客

【蓝桥杯嵌入式】【复盘】第15届省赛真题-CSDN博客

【蓝桥杯嵌入式】【复盘】第15届国赛真题-CSDN博客


以下是本篇博客正文内容:

2. 4t评测结果

4t平台网址:学单片机,上4T - 4T评测网

测试拿到了满分。


3. 个人解答代码

仓库地址:lanqiao/15_true_shengsai at main · Dukiyaaa/lanqiao

如果不嫌麻烦的话,可以点个star~


4. 重点和易错点

这一套题,根据我个人的做题经历,我认为应该关注的有以下几点:

1. pwm相关的超限报警、频率突变测量。

2. 按键长按。

4.1. pwm超限报警、频率突变测量

我的核心代码如下:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance == TIM2)
	{
		uint32_t tmp = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
		
		if(tmp != 0)
		{
			freA = 8000000.0f / (float)tmp;
		}
		
		if(freA > 20000)
		{
			freA = 20000;
		}
		
		if(freA < 400)
		{
			freA = 400;
		}
		
		freA += PX;

		__HAL_TIM_SET_COUNTER(&htim2, 0);
		HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
	}
	
	if(htim->Instance == TIM3)
	{
		uint32_t tmp = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
		
		if(tmp != 0)
		{
			freB = 8000000.0f / (float)tmp;
		}
		
		if(freB > 20000)
		{
			freB = 20000;
		}
		
		if(freB < 400)
		{
			freB = 400;
		}
		
		freB += PX;
		
		__HAL_TIM_SET_COUNTER(&htim3, 0);
		HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
	}
}
float faMAX, fbMAX, faMIN, fbMIN;

bool NHA_lock = 1, NHB_lock = 1;
void pwm_task(void)
{	
	// 超限
	if((NHA_lock == 0) && ((freA - (float)PH) > 10))
	{
		NHA++;
		NHA_lock = 1;
	}
	else if((NHA_lock == 1) && (((float)PH - freA) > 10))
	{
		NHA_lock = 0;
	}
	
	if((NHB_lock == 0) && ((freB - (float)PH) > 10))
	{
		NHB++;
		NHB_lock = 1;
	}
	else if((NHB_lock == 1) && (((float)PH - freB) > 10))
	{
		NHB_lock = 0;
	}
	
	// 突变,3s判断一次
	if(freA > faMAX)
	{
		faMAX = freA;
	}
	
	if(freB > fbMAX)
	{
		fbMAX = freB;
	}
	
	if((freA >= 0) && (freA < faMIN))
	{
		faMIN = freA;
	}
	
	if((freB >= 0) && (freB < fbMIN))
	{
		fbMIN = freB;
	}
	
	if(is_3s == 1)
	{
		if(((faMAX - faMIN) - PD) >= 5)
		{
			NDA++;
		}
		
		if(((fbMAX - fbMIN) - PD) >= 5)
		{
			NDB++;
		}
		
		faMAX = -1;
		faMIN = 40000;
		fbMAX = -1;
		fbMIN = 40000;
		is_3s = 0;
	}
	
}

 1. 关于超限预警,需要注意的是防抖的处理,蓝桥杯的要求默认情况下都是如果一直超限的话,超限次数不累加,所以建立一个flag用于控制超限次数的增加。

2. 关于频率突变,我的处理方法就是单纯每3s判断一次这3s内测到的最大最小值,根据要求做一下比较就行。之前想过很多种方法,比如滑动窗口,最后发现像现在这样处理就够了。

3. 可以发现,边界值部分我基本都采用的是做差比较,这种方法用于防止边界情况下的计数抖动。

4.2. 按键长按

我的核心代码如下:

void key_scan(void)
{
	key_buf[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
	key_buf[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
	key_buf[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
	key_buf[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
	
	for(uint8_t i = 0;i < 4;i++)
	{
		switch(key_buf[i].key_step)
		{
			case 0:
			if(key_buf[i].key_pin_state == 0)
			{
				key_buf[i].key_step = 1;
			}			
			else
			{
				key_buf[i].key_step = 0;
				key_buf[i].key_is_down = 0;
				key_buf[i].key_time = 0;
				key_buf[i].key_is_long = 0;
			}
			break;
			case 1:
			if(key_buf[i].key_pin_state == 0)
			{
				key_buf[i].key_step = 2;
				key_buf[i].key_is_down = 1;
			}			
			else
			{
				key_buf[i].key_step = 0;
				key_buf[i].key_is_down = 0;
				key_buf[i].key_time = 0;
				key_buf[i].key_is_long = 0;
			}
			break;
			case 2:
			if(key_buf[i].key_pin_state == 0)
			{
					key_buf[i].key_time++;
					if(key_buf[i].key_time >= 100)
					{
						key_buf[i].key_is_long = 1;
					}					
			}			
			else
			{
				key_buf[i].key_step = 0;
				key_buf[i].key_is_down = 0;
				key_buf[i].key_time = 0;
				key_buf[i].key_is_long = 0;
			}
			break;
		}
	}
}
void key_task(void)
{
	for(uint8_t i = 0;i < 4;i++)
	{
		if(key_buf[i].key_is_down == 1)
		{
			switch(i)
			{
				case 0:
					key_buf[i].key_is_down = 0;
					if(lcd_num == 1)
					{
						switch(is_select)
						{
							case 0:
								PD += 100;
								if(PD > 1000)
								{
									PD = 1000;
								}
							break;
							case 1:
								PH += 100;
								if(PH > 10000)
								{
									PH = 10000;
								}
							break;
							case 2:
								PX += 100;
								if(PX > 1000)
								{
									PX = 1000;
								}
							break;
						}
					}
				break;
				case 1:
					key_buf[i].key_is_down = 0;
					if(lcd_num == 1)
					{
						switch(is_select)
						{
							case 0:
								PD -= 100;
								if(PD < 100)
								{
									PD = 100;
								}
							break;
							case 1:
								PH -= 100;
								if(PH < 1000)
								{
									PH = 1000;
								}
							break;
							case 2:
								PX -= 100;
								if(PX < -1000)
								{
									PX = -1000;
								}
							break;
						}
					}
				break;
				case 2:
					// 短按
					if(lcd_num == 1)
					{
						key_buf[i].key_is_down = 0;
						is_select++;
						if(is_select > 2)
						{
							is_select = 0;
						}
					}
					else if(lcd_num == 0)
					{
						key_buf[i].key_is_down = 0;
						is_fre ^= 1;	// 切换显示模式
					}
					else if(lcd_num == 2)
					{
						if(key_buf[2].key_is_long == 1)
						{
							key3_flag = 1;
						}
					}
				break;
				case 3:
					key_buf[i].key_is_down = 0;
					lcd_num++;
					if(lcd_num > 2)
					{
						lcd_num = 0;
					}
					
					if(lcd_num == 0)
					{
						is_fre = 1;
					}
					
					if(lcd_num == 1)
					{
						is_select = 0;
					}
				break;
			}
		}
		
		if(key3_flag == 1)
		{
			if(key_buf[2].key_is_long == 0)
			{
				key3_flag = 0;
				if(lcd_num == 2)
				{
					NHA = 0;
					NHB = 0;
					NDA = 0;
					NDB = 0;
				}
			}
		}
	}
}

其实比较常规,关于短按的逻辑直接处理就行,但是关于长按,则需要建立标志位,用于实现在松开之后再做处理的逻辑。


5. 总结

本文总结了个人在练习13届省赛过程中的复盘及易错点、重难点分析,主要内容在pwm超限与突变测量、按键长按。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐