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

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

 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博客


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

2. 4t评测结果

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

错了一个评测点,源于对赛题要求的理解方式不确定,这里我先保持自己的题目理解方法。


3. 个人解答代码

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

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


4. 重点和易错点

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

1. 按键双长按。

2. 路径行进相关的逻辑

4.1 按键双长按

我的核心代码如下:

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(ST == 0){// 收到了目的地坐标,切换为运行,否则不变if((FP[0] != -1) && (FP[1] != -1)){ST = 1;}else{ST = 0;}}else if(ST == 1){ST = 2;}else if(ST == 2){ST = 1;}break;case 1:key_buf[i].key_is_down = 0;lcd_num++;if(lcd_num > 2){lcd_num = 0;}if(lcd_num == 1){is_select_R = 1;}break;case 2:key3_step = 1;break;case 3:key4_step = 1;break;}}if(key_buf[2].key_is_down == 0){if(key3_step == 1){key3_step = 0;// 按下了if(key_buf[2].key_is_long == 0){// 短按// 参数界面起作用if(lcd_num == 1){is_select_R ^= 1;}}else if(key_buf[2].key_is_long == 1){// 长按if(lcd_num == 0){if(ST == 0){if(key_buf[3].key_is_long == 1){// 清0:TS和TTTS = 0;TT = 0;}}}}}key_buf[2].key_time = 0;key_buf[2].key_is_long = 0;}if(key_buf[3].key_is_down == 0){if(key4_step == 1){key4_step = 0;// 按下了if(key_buf[3].key_is_long == 0){// 短按// 参数界面起作用if(lcd_num == 1){if(is_select_R == 1){R += 0.1f;if(R > 2.09f){R = 1.0f;}}else if(is_select_R == 0){B += 10;if(B > 100){B = 10;}}}}else if(key_buf[3].key_is_long == 1){// 长按if(lcd_num == 0){if(ST == 0){if(key_buf[2].key_is_long == 1){// 清0:TS和TTTS = 0;TT = 0;}}}}}key_buf[3].key_time = 0;key_buf[3].key_is_long = 0;}}
}

基于状态机思想,在按键3/4按下后进入下一个状态,下一个状态的逻辑在按键松开后触发,松开后判断是长按还是短按,如果是长按,则需要同步判断另外一个按键是否也是长按。

4.2 路径行进相关的逻辑

这部分是这套题的重点和难点,感觉已经无关乎于单片机平台了,核心的考察在于代码逻辑。 

我的核心代码如下:

void work_task(void)
{	// 更新TP,如果当前的TP被删除了则需要更新if((pos[TP_ptr] == -1) && (pos[TP_ptr + 1] == -1)){if(pos_ptr == 0){}else{TP_ptr += 2;if(TP_ptr >= 399){TP_ptr = 0;}}}else{TP[0] = pos[TP_ptr];TP[1] = pos[TP_ptr + 1];}if(ST == 1){// 更新TP后,需要计算当前路径的方向参数if(check_distance_flag == 0){check_distance_flag = 1;distance = sqrt((TP[0] - CP[0]) * (TP[0] - CP[0]) + (TP[1] - CP[1]) * (TP[1] - CP[1]));cos_theta = (TP[0] - CP[0]) /  distance;sin_theta = (TP[1] - CP[1]) /  distance;left_distance = distance;second_flag = 0;}// 每秒执行的行进逻辑if(second_flag == 1){second_flag = 0;left_distance = left_distance - SE;TT++;TS += SE;if(left_distance < 0){// 已抵达TP,TP需要更新为下一个路径点CP[0] = TP[0];CP[1] = TP[1];pos[TP_ptr] = -1;pos[TP_ptr + 1] = -1;TP_ptr += 2;if(TP_ptr >= 399){TP_ptr = 0;}TP[0] = pos[TP_ptr];TP[1] = pos[TP_ptr + 1];RN--;if(RN == 0){CP[0] = FP[0];CP[1] = FP[1];TS += left_distance;}check_distance_flag = 0;// 到达目的地相关的逻辑if((CP[0] == FP[0]) && (CP[1] == FP[1])){ST = 0;FP[0] = -1;FP[1] = -1;TP_ptr = 0;pos_ptr = 0;RN = 0;}}else{CP[0] += (SE * cos_theta);CP[1] += (SE * sin_theta);}}}else{second_flag = 0;}
}

我认为重点如下:

1. TP的更新

TP代表下一个目路径点,我采用了一个数组来存储其x,y坐标值,同时用一个TP_ptr指针来进行维护,TP的更新主要发生在以下三种情况:

a.当前行进到了TP,则TP需要更新到下一个路径点。

b.当前的TP被删除了,则TP需要更新到下一个路径点。

c.走到最终的目标点了,TP变成NF。

具体的实现见代码即可。

2. CP的更新

CP的更新是我的失分点,我的理解如下:

a.当走出一步之后,没有到达TP,则CP正常在x和y方向上加上这一步走了的距离。

b.走出一步之后达到了TP,则CP更新为TP。

这里我的理解应该是不够正确的,因为照这样写出来的代码评分会有一个错误,我猜测正确的逻辑可能如下:

b.走出一步之后,如果超过了TP,则需要调整方向,往更新后的TP方向再迈一步。

但碍于时间问题,这个方法我并没有去细究,我大致的思路是超过TP之后,重新计算一边方向,把超出来的那部分加到CP上,但要这样实现的话,我当前代码的方向更新逻辑就得大改,所以我先保留这个想法。
3. 路径点的存储方式

我使用了一个400大小的固定一维数组来存储,每相邻的两位表示一组x,y坐标,元素类型为int16_t,初始化为全-1,表示没有路径点。使用一个pos_ptr来维护,元素新增时,使用pos_ptr;元素需要删除时,则遍历之后将其值改为-1。具体代码如下:

void uart_task(void)
{if(uart_struct.uart_end_flag == 1){// 处理逻辑if((uart_struct.uart_rcv_buf[0] == '(' )&& (uart_struct.uart_ptr > 0) && (uart_struct.uart_rcv_buf[uart_struct.uart_ptr - 1] == ')')){uint32_t count = 0;for(uint32_t i = 0;i < uart_struct.uart_ptr;i++){if(uart_struct.uart_rcv_buf[i] == ','){count++;}}if(count % 2 == 1){if(ST == 0){// 设置途径,目标点, 尚未检查中间数据格式不符合的情况for(uint32_t i = 1;i < uart_struct.uart_ptr;i++){if((uart_struct.uart_rcv_buf[i] != ',') && ((uart_struct.uart_rcv_buf[i] != ')'))){tmp = tmp * 10 + uart_struct.uart_rcv_buf[i] - '0';}else if(uart_struct.uart_rcv_buf[i] == ','){pos[pos_ptr] = tmp;tmp = 0;pos_ptr++;}else if(uart_struct.uart_rcv_buf[i] == ')'){pos[pos_ptr] = tmp;tmp = 0;pos_ptr++;}}if((pos_ptr % 2) == 0){RN = pos_ptr / 2;FP[0] = pos[pos_ptr - 2];FP[1] = pos[pos_ptr - 1];printf("Got it");}else{pos[pos_ptr] = -1;pos_ptr--;printf("Error");}}else{printf("Busy");}}else{printf("Error");}}else if((uart_struct.uart_rcv_buf[0] == '{' )&& (uart_struct.uart_ptr > 0) && (uart_struct.uart_rcv_buf[uart_struct.uart_ptr - 1] == '}')){uint32_t tmp[2] = {0}, tmp_ptr = 0, tmpX, tmpY;for(uint32_t i = 1;i < uart_struct.uart_ptr;i++){if((uart_struct.uart_rcv_buf[i] != ',') && ((uart_struct.uart_rcv_buf[i] != '}'))){tmp[tmp_ptr] = tmp[tmp_ptr] * 10 + uart_struct.uart_rcv_buf[i] - '0';}else if(uart_struct.uart_rcv_buf[i] == ','){tmp_ptr++;}else if(uart_struct.uart_rcv_buf[i] == ')'){tmp_ptr++;}}tmpX = tmp[0];tmpY = tmp[1];uint32_t i = 0;for(;i < pos_ptr - 1;i += 2){if((pos[i] == tmpX) && (pos[i + 1] == tmpY)){pos[i] = -1;pos[i + 1] = -1;RN--;break;}}if(i < pos_ptr - 1){printf("Got it");}else{printf("Nonexistent");}}else if((uart_struct.uart_rcv_buf[0] == '[' )&& (uart_struct.uart_ptr > 0) && (uart_struct.uart_rcv_buf[uart_struct.uart_ptr - 1] == ']'))		{if(ST == 1){uint8_t tmp;tmp = uart_struct.uart_rcv_buf[1] - '0';if((tmp >= 1) && (tmp <= 4)){scene = tmp - 1;printf("Got it");}else{printf("Device offline");}}else{printf("Device offline");}}else if(uart_struct.uart_rcv_buf[0] == '?' ){switch(ST){case 0:printf("Idle");break;case 1:printf("Busy");break;case 2:printf("Wait");break;}}else if(uart_struct.uart_rcv_buf[0] == '#' ){printf("(%d,%d)", CP[0], CP[1]);}else{printf("Error");}uart_struct.uart_ptr = 0;uart_struct.uart_end_flag = 0;uart_struct.uart_start_flag = 0;uart_struct.uart_time_cnt = 0;memset(uart_struct.uart_rcv_buf, 0 ,sizeof(uart_struct.uart_rcv_buf));}
}

但实际上,我认为这个代码是还有需要优化的地方的:

1. 数组满了之后,需要做一个重置的操作;但是由于测评点没有灌太多的目标点进去,所以暂时没出问题。

2. 查找元素的查找方法可以优化,比如用二分查找。

3. 存储路径点的数据结构可以改成链表,甚至我认为这道题本身就是想考察链表,因为相关的操作用链表实现非常的简单可观,虽然用数组同样也能实现。


5. 总结 

本文总结了个人在练习15届国赛过程中的复盘及易错点、重难点分析,主要内容在按键双长按、路径行进相关的逻辑。

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

相关文章:

  • 7种分类数据编码技术详解:从原理到实战
  • Java基于BS架构的OA流程可视化实战:从工作流引擎到前端交互(附完整源代码+论文框架)
  • 学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”
  • ubuntu22.04有线网络无法连接,图标也没了
  • QT 仿网易云项目
  • React Native 开发环境搭建(全平台详解)
  • LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
  • 开始新的认识,对worldquant(50alpha)
  • NLP-文本表征(2011-2022)
  • 中国政务数据安全建设细化及市场需求分析
  • API网关Envoy的鉴权与限流:构建安全可靠的微服务网关
  • C++--string的模拟实现
  • JS红宝书笔记 - 3.3 变量
  • Spring Boot面试题精选汇总
  • 记录:外扩GPIOD访问报警告
  • 在ARM+Ascend NPU上适配Step-Audio模型
  • AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
  • 初识Docker——容器化革命核心概念
  • 解决HuggingFace不能git clone的问题
  • 基于Spring的Java公共资源模块开发与最佳实践
  • 在 Word中生成目录(Table of Contents, TOC)
  • MaaS与CC有哪些契合点
  • 在 Windows 11/10 中打开任务管理器的 6 种方法(无需 Ctrl+Alt+Delete)
  • npm符号链接
  • 象棋移动-第16届蓝桥STEMA省考Scratch真题第1题
  • 智能体革命:企业如何构建自主决策的AI代理?
  • UML 2.0 图的细分类别及其应用
  • win操作系统安装C++语言开发环境之一, vscode +MinGW ,流程
  • 02-性能方案设计
  • 【Pandas】pandas DataFrame ffill