【蓝桥杯嵌入式】【复盘】第15届省赛真题
1. 前言
最近在准备16届的蓝桥杯嵌入式赛道的国赛,打算出一个系列的博客,记录STM32G431RBT6这块比赛用板上所有模块可能涉及到的所有考点,如果有错误或者遗漏欢迎各位大佬斧正。
本系列博客会分为以下两大类:
1.1. 单独模块的讲解
在这部分,我会分享自己总结的各个模块的相关配置、代码书写模板,涉及到的大致框架如下:

这个框架后续可能会不断更新,欢迎各位给出建议。
这一大类相关的文章链接如下(持续补充中):
【蓝桥杯嵌入式】【模块】二、LED相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】三、LCD相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】四、按键相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】五、ADC相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】六、PWM相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】七、IIC相关配置及代码模板-CSDN博客
【蓝桥杯嵌入式】【模块】八、UART相关配置及代码模板-CSDN博客
1.2. 蓝桥杯各届的真题、模拟题复盘及个人答案
在这一部分,我会分享个人练过的所有题的复盘思路及代码,每篇文章结构如下:

这一大类相关的文章链接如下(持续补充中):
【蓝桥杯嵌入式】【复盘】第13届国赛真题_蓝桥杯嵌入式13届国赛题-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超限与突变测量、按键长按。
更多推荐
所有评论(0)