当前位置: 首页 > news >正文

【蓝桥杯嵌入式】【复盘】第13届国赛真题

 1. 前言

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

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

1.1. 单独模块的讲解

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

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

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

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

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

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

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

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

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

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


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

2. 4t评测结果

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

虽然评分结果不高,但是据很多人反馈,这套题的评测系统有真题,很多人的程序题评测点全过也只能到76.5。

至于为什么博主还有三分没拿到,后面会提到。


3. 个人解答代码

仓库地址:lanqiao/13_true at main · Dukiyaaa/lanqiao

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


4. 重点和易错点

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

  1. pwm的捕获与输出
  2. 单个adc的多通道采集
  3. 按键长按
  4. lcd翻转

4.1. pwm的捕获与输出

4.1.1. pwm的捕获

我个人的核心代码如下:

float pa1_fre;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM2){uint32_t tmp = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_2);if(tmp == 0) return;pa1_fre = 1000000.0 / (tmp + 1);__HAL_TIM_SET_COUNTER(&htim2, 0);HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);	// TIM2, ch2, pa1}
}

其实关于pwm捕获频率,网上有很多种解决办法,有比较严谨的求两次捕获差值,有加入了滤波算法的;就我这个代码而言,没有那么复杂,就单纯捕获计数器,捕获之后清0,并且也没加滤波算法。好处是比较方便,不用计算两次捕获的计数器差值,缺点是最开始几次的捕获值可能会不够精准。

不过,这套题的pwm捕获感觉没有什么需要重点关照的,大家按自己平时的风格实现就行。

4.1.2. pwm的输出

我个人的核心代码如下:

void pwm_out_set_fre_duty(uint32_t fre, uint32_t duty)
{uint32_t clock = 80000000;uint32_t div = 10;uint32_t period = clock / div / fre;uint32_t pulse = period * duty / 100;if(pulse > period){pulse = period - 1;}TIM3->ARR = period;TIM3->CCR2 = pulse;HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
}

可以看到,我封装了一个可以输出指定频率的占空比的pwm函数,其内有一些地方需要注意:

1. 在cubemx初始化pwm输出时,我选择了将分频系数设置为10,这是因为在测量时,pwm捕获的频率最低大概在700,而题目有一个分频的要求,分频值最大可以是4,也就是说,pwm输出的最小频率是700 / 4 = 175,所以我们最好要能满足可以输出175hz的pwm。

而pwm输出选用的是TIM3,最大重装载值是65535,要能输出175hz的pwm,预分频系数应该为80000000 / 65535 / 175 = 6.97,选一个比这个数大一点(越小越好,越小的分频系数可以让pwm有更大的调节分辨率)的作为预分频系数初始化就行。

2. 可以看到,我在进行pwm调整时,采用了寄存器直接赋值的办法,这只是我个人的风格,因为在之前的练习中,我发现使用hal库的__HAL_TIM_SET_AUTORELOAD和__HAL_TIM_SET_COMPARE函数效果没有直接寄存器赋值好,会存在pwm输出不正确的情况。

3. 要保证pulse的值小于arr,在之前的练习中(14届省赛),发现如果没有这个限制,常常会出现pwm输出崩溃的情况。

4.1.3. 关于评测结果中的说明

在我的评测结果中,有部分失分是因为pwm的输出不正确:

但是根据题目要求的5%精度,我的输出并没有超过限制,并且其余的pwm输出测试点都是对的,所以目前我不准备改自己的代码,如果各位有发现我代码中导致评测错误的原因,非常欢迎提出!

4.2. 单个adc的多通道采集

在很多练习中,对于adc的要求都是单个adc单通道的,而这套题要求的是单个adc的多通道,即需要同时采集adc2的13和17通道值,于是在cubemx配置以及代码书写上就有了一些小的区分。

4.2.1. cubemx配置adc多通道采集

参考:蓝桥杯嵌入式ADC单通道+多通道(扫描 + 不连续_哔哩哔哩_bilibili

核心,将adc配置成扫描模式+不连续转换

如上图所示,要开启扫描模式和不连续转换模式,并且每次只转换一个值。

在这里配置每个通道的采样周期,越大越好,因为会比较准确。

4.2.2. adc多通道采集代码书写

我的个人核心代码如下:

    HAL_ADCEx_Calibration_Start(&hadc2, ADC_SINGLE_ENDED);for(uint8_t i = 0;i < 2;i++){HAL_ADC_Start(&hadc2);tmp_data[i] = (double)HAL_ADC_GetValue(&hadc2) * 3.3 / 4095.0;HAL_Delay(1);}pa4 = tmp_data[1];pa5 = tmp_data[0];

有以下几点需要注意:

1. 首先开启adc的校准,否则可能会出现测不到3.3v的情况。

2. 使用一个循环采集两次,存在数组。需要特别注意的是,第一次采集到的,反而是配置时作为rank2的通道值(这个我也没弄清楚原因,只是从现象上发现是如此,欢迎各位指正)。

如此,pa4和pa5,便为adc采集两个通道的采集值。

4.3. 按键长按

实际这套题案件长按还是比较常规的,我的核心代码如下:

void key_scan(void)
{key_buffer[0].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key_buffer[1].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key_buffer[2].key_pin_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key_buffer[3].key_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);for(uint8_t i = 0; i < 4; i++){switch(key_buffer[i].key_step){case 0:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_step = 1;}	else{key_buffer[i].key_step = 0;}break;case 1:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_step = 2;key_buffer[i].key_is_down = 1;}	else{key_buffer[i].key_step = 0;key_buffer[i].key_is_down = 0;}break;case 2:if(key_buffer[i].key_pin_state == 0){key_buffer[i].key_time++;if(key_buffer[i].key_time >= 100){key_buffer[i].key_is_long = 1;}}	else{key_buffer[i].key_step = 0;key_buffer[i].key_is_down = 0;}break;}}
}

在key_scan函数中,我增加了一个按键计数器,当其长按下计数器会不断增加,达到100时,表明按下时间达到了1s(因为我配置的按键计数器中断是10ms触发一次)。

按键业务函数如下:

void key_task(void)
{for(uint8_t i = 0;i < 4;i++){if(key_buffer[i].key_is_down == 1){switch(i){case 0:key_buffer[i].key_is_down = 0;show_num++;if(show_num > 2){show_num = 0;}if(show_num == 2){pa_num = 4;}break;case 1:key_buffer[i].key_is_down = 0;if(show_num == 1){// 频率参数XX++;if(X > 4){X = 1;}eeprom_write(1, X);HAL_Delay(10);}					break;case 2:key_buffer[i].key_is_down = 0;if(show_num == 1){// 电压参数YY++;if(Y > 4){Y = 1;}eeprom_write(0, Y);HAL_Delay(10);}					break;case 3:if(show_num == 0){key4_flag = 0;key_buffer[i].key_is_down = 0;// 启动丿次电压测量功能,lcd显示的电压数据更新一欿pa5_lock = 0;pa4_lock = 0;}else if(show_num == 1){key4_flag = 0;key_buffer[i].key_is_down = 0;// 切换pwm输出模式为分颿/倍频pwm_output_mode ^= 1;}else if(show_num == 2){key4_flag = 1;}break;}}if(key_buffer[3].key_is_down == 0){if(key4_flag == 1){key4_flag = 0;if(key_buffer[3].key_is_long == 1){// 清空				if(pa_num == 4){first4 = 1;N_4 = 0;A_4 = 0;a4 = 0;T_4 = 0;t4 = 0;H_4 = 0;h4 = 0;pa4_sum = 0;}else{first5 = 1;N_5 = 0;A_5 = 0;a5 = 0;T_5 = 0;t5 = 0;H_5 = 0;h5 = 0;		pa5_sum = 0;}}else{if(pa_num == 4){pa_num = 5;}else{pa_num = 4;}}}// 只要key4没按,都需要清0key_buffer[3].key_is_long = 0;key_buffer[3].key_time = 0;}}
}

这里的核心有以下几点:

1.使用了key4_flag标志位来标记按下。由于key4要区分长按和短按,所以在按下的逻辑里仅作标志位处理,将对应的按键时间处理放在松开的逻辑里,如果检测到松开,先判断标志位状态,如果为1,说明是按下后松开,需要进一步判断是长按还是短按;否则,直接跳过。

2. 长按计数清零逻辑,这一步是非常关键的,我在这个bug上卡了很久。一定要注意,只要检测到key4松开,都需要清零其长按计数器以及标志位。

4.4. lcd翻转

lcd的翻转实际不算太难,可以参考我的另一篇博客:【蓝桥杯嵌入式】【模块】三、LCD相关配置及代码模板-CSDN博客

我的核心代码如下:

	if(lcd_mode == 1){LCD_WriteReg(R1, 0x0100);   // set SS and SM bit		  //0x0100LCD_WriteReg(R96, 0xA700);  // Gate Scan Line		  0xA700}else{LCD_WriteReg(R1, 0x0000);   // set SS and SM bit		  //0x0100LCD_WriteReg(R96, 0x2700);  // Gate Scan Line		  0xA700}

关键就是对R1和R96的赋值处理,如果保持lcd.c文件里原本的赋值,便是正常显示顺序;如果采用注释里后面那个赋值,便可做到上下翻转、左右翻转,联合起来便是屏幕整体翻转。


总结

本文总结了个人在练习13届国赛过程中的复盘及易错点、重难点分析,主要内容在pwm、adc、按键长按、lcd翻转。

http://www.xdnf.cn/news/433819.html

相关文章:

  • 微店根据关键词取商品列表 API 返回值说明
  • 【NextPilot日志移植】params.c解析
  • 大白话解释「量化」是什么
  • (1+x)-1次幂 (1-x)-1次幂 泰勒展开式
  • 论坛系统(中-1)
  • PostgreSQL pg_dump 与 Oracle expdp 对比
  • AI大模型从0到1记录学习 linux day22
  • 项目里程碑未被明确,如何有效控制进度
  • 网页常见水印实现方式
  • Memcached 的特性和使用场景介绍,以及集群搭建
  • sqlserver免费版每天备份数据库
  • 英语学习5.12
  • 进程与线程:08 一个实际的 schedule 函数
  • 【周输入】510周阅读推荐-1
  • 如何使用 Qwen3 实现 Agentic RAG?
  • 采用AI神经网络降噪算法的语言降噪消回音处理芯片NR2049-P
  • C++中的虚表和虚表指针的原理和示例
  • While语句数数字
  • SpringBoot核心注解详解:定义、用法与原理
  • MySQL 学习(八)如何打开binlog日志
  • 球球大作战游戏服务器
  • iOS设备投屏Archlinux
  • MYSQL 查询去除小数位后多余的0
  • Linux——守护进程
  • 软考架构师考试-UML图总结
  • EF Core 数据库迁移命令参考
  • KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache
  • 影刀RPA开发-采集爬取京东读书书籍
  • 【React中函数组件和类组件区别】
  • day 22