2026年电赛校赛备战 MSPM0G3507 + Keil 讲解:前提篇

个人主页:
个人专栏:
C语言
嵌入式小白启动!
重要OJ算法题详解
蓝桥杯备战
C++从菜鸟到强手
python启航
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
项目背景:以 2025 年电赛 E 题“小车 + 自行瞄准装置”为例,使用 MSPM0G3507、Keil、SysConfig 完成底盘循迹,并结合 STM32F103C8T6 完成云台瞄准与双主控联调。
这篇是整个系列的第一篇,主要讲“开工前要准备什么”。我不会一上来就塞满 PID、串口协议和状态机细节,而是先把赛题拆开:它到底要求我们做什么,为什么这个项目最后会被拆成“小车底盘”和“云台瞄准”两部分,以及用 Keil + SysConfig 开发 MSPM0G3507 时要提前踩平哪些坑。
为了让小白也能顺着看下去,这篇会先把项目里最常见的名词讲明白。你可以把它当成这套工程的“入门词典”:
- MSPM0G3507:底盘主控芯片,负责让小车跑起来。
- STM32F103C8T6:云台主控芯片,负责让激光对准目标。
- Keil:写代码、编译、下载程序的开发环境。
- SysConfig:TI 的外设配置工具,相当于帮你生成底层初始化代码。
- 灰度传感器:用来识别黑线的“眼睛”。
- TB6612:电机驱动芯片,负责把 MCU 的控制信号变成电机能用的电流输出。
- 编码器:测电机转了多少、转得多快的反馈器件。
- PID:让系统自动修正误差的一种控制方法。
- UART:串口通信,两块主控之间靠它交换状态。
- I2C:一种常用的外设总线,这里用来驱动 OLED。
- CAN:更适合工业控制的总线,云台用它控制步进驱动器。
如果你之前没接触过这些词,没关系,后面都会在对应篇幅里继续解释。
后面三篇会分别展开:
-
上篇:MSPM0G3507 小车篇
重点讲 8 路灰度循迹、TB6612 四驱、电机编码器、速度环、循迹环、直角转弯、缺口穿越、计圈和 OLED 调试。 -
中篇:STM32 云台篇
重点讲 STM32F103C8T6 控制二维云台、视觉模块串口解析、CAN 步进电机、激光控制、按键和云台状态机。 -
下篇:综合联调篇
重点讲 MSPM0 与 STM32 的 UART 通信、底盘圈进度同步、云台画圆/画矩形、安全策略和最终调试经验。
该篇所涉及代码均在GitHub仓库
一、先看赛题:它不是单纯的循迹小车
2025 年电赛 E 题的核心可以概括成一句话:
小车沿着规定黑线跑,瞄准装置要在静止或运动过程中用激光对目标靶完成瞄准、画圆或画矩形等动作。
如果只看“小车”两个字,很容易把它当成传统循迹车来做:灰度传感器采线、PID 修正方向、跑完指定圈数停车。但这道题的难点不只在底盘,而在于它把两个系统强行绑到了一起:
- 底盘系统:负责沿赛道稳定行驶,处理直角转弯、长缺口、圈数统计、速度控制。
- 瞄准系统:负责识别目标靶,控制激光落点,完成靶心瞄准、画圆、画矩形。
- 综合系统:小车运动时,瞄准装置还要知道底盘状态,例如当前是否在跑、跑到一圈的哪个进度、是否通信正常。
所以这道题不能只按“写一个主循环”去想,它更像一个小型嵌入式系统工程:传感器、执行器、通信、控制算法、安全逻辑都要同时考虑。
二、赛道与任务拆解
根据项目整理文档,赛道是一个约 100cm x 100cm 的正方形平台,四条边构成小车行驶路线。小车需要逆时针沿 A -> B -> C -> D -> A 行驶。
这里先把“赛道”两个字说清楚。比赛里说的赛道,不是普通操场上的一条黑线,而是一个带有明确边界、转角、缺口和标记的任务场地。你要做的不是沿着线“随便走走”,而是要让车知道:
- 当前是不是还在黑线上;
- 什么时候走到拐角;
- 什么时候遇到没有黑线的缺口;
- 一圈究竟有没有走完;
- 走完以后是否停车。
这就是为什么底盘程序里一定要有状态机,而不是只写一个“看到黑线就偏转”的死循环。
其中有两类边:
AB、CD:全程有黑线,适合正常循迹。BC、DA:两端各有一段黑线,中间存在约60cm的无黑线缺口。
这意味着底盘不能只会“看到黑线就跟”,还必须能处理“短时间看不到线但仍然要继续直行”的情况。也就是说,程序里要区分:
- 正常循迹;
- 进入直角转弯;
- 丢线穿越缺口;
- 重新找线;
- 完成一圈或多圈后停车;
- 发生异常时停车。
项目中底盘状态机集中在:
car/empty/app/app_chassis.c
入口很简单:
int main(void)
{
SYSCFG_DL_init();
app_chassis_init();
while (1) {
app_chassis_loop();
}
}
真正的工作不是写在 while(1) 里硬等,而是通过 1ms 定时中断调度:
void TIMER_1MS_INST_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(TIMER_1MS_INST)) {
case DL_TIMER_IIDX_ZERO:
app_chassis_on_1ms_tick();
break;
default:
break;
}
}
这就是后面小车篇要重点讲的结构:定时中断负责周期任务,主循环负责 OLED 刷新和通信发送这类后台任务。
三、为什么我把项目拆成双主控
这个项目最终采用:
MSPM0G3507 底盘主控 + STM32F103C8T6 云台主控
原因很直接:底盘和云台的实时任务不一样,硬件生态也不一样。
MSPM0G3507 负责底盘
MSPM0G3507 这块板适合做小车底盘控制,主要任务包括:
- 8 路灰度传感器采样;
- TB6612 电机驱动;
- 编码器测速;
- 速度 PID;
- 循迹 PID;
- 直角转弯和缺口处理;
- K1/K2 按键;
- OLED 显示;
- 向 STM32 上报底盘状态。
当前工程路径:
car/empty/
关键文件:
car/empty/empty.c
car/empty/app/app_chassis.c
car/empty/app/chassis_state.h
car/empty/bsp/bsp_track8.c
car/empty/bsp/bsp_motor_tb6612.c
car/empty/bsp/bsp_encoder.c
car/empty/protocol/protocol_stm32.c
STM32F103C8T6 负责云台
STM32F103C8T6 负责瞄准系统,主要任务包括:
- 接收 MaxicamPro 视觉模块输出的
dx/dy偏差; - 控制 X/Y 两轴步进电机;
- 控制激光开关;
- OLED 显示云台状态;
- KEY8 切换模式;
- 与 MSPM0 交换状态;
- 在底盘失联时关闭激光。
当前工程路径:
gimbal_stm32f103c8t6/
关键文件:
gimbal_stm32f103c8t6/Core/Inc/gimbal_config.h
gimbal_stm32f103c8t6/Core/Src/main.c
gimbal_stm32f103c8t6/App/Src/app_gimbal.c
gimbal_stm32f103c8t6/App/Src/bsp_uart_vision.c
gimbal_stm32f103c8t6/App/Src/bsp_uart_mspm0.c
gimbal_stm32f103c8t6/App/Src/bsp_step_motor.c
两块板如何协作
两块板通过串口通信:
MSPM0 PB2 / UART3_TX -> STM32 PA3 / USART2_RX
MSPM0 PB3 / UART3_RX <- STM32 PA2 / USART2_TX
GND -> GND
串口参数:
115200 8N1
3.3V TTL
通信帧格式:
A5 5A VER MSG_ID SEQ LEN PAYLOAD CRC8
方向和含义:
| 方向 | MSG_ID | 周期 | 作用 |
|---|---|---|---|
| MSPM0 -> STM32 | 0x10 |
20ms / 50Hz | 上报底盘模式、圈数、赛段、圈进度、速度、循迹误差 |
| STM32 -> MSPM0 | 0x20 |
50ms / 20Hz | 上报云台状态、视觉状态、激光状态、错误信息 |
这个设计的好处是:底盘和云台可以先独立调通,最后再联调。比赛项目最怕“一上来全接一起,然后不知道哪里坏了”,双主控拆分后排查会清楚很多。
四、开发环境准备
这套项目的重点是 MSPM0G3507 + Keil + SysConfig。STM32 端也用 Keil,但 MSPM0 的环境更容易卡在 SDK、SysConfig、Keil 工程和生成文件之间,所以这里先把准备项列清楚。
1. 必备软件
建议准备:
- Keil MDK-ARM;
- TI MSPM0 SDK;
- TI SysConfig;
- J-Link 驱动;
- 串口助手或 VOFA+;
- STM32CubeMX,云台端如需重新改外设配置时使用;
- Git 或至少能管理工程版本的备份习惯。
先别急着装一堆工具,先理解它们各自的分工:
- Keil:写代码、编译、下载程序。
- SDK:芯片厂家提供的底层驱动库。
- SysConfig:图形化配置外设并生成初始化代码。
- J-Link:把程序烧进板子并支持调试。
- 串口助手 / VOFA+:用来观察运行中的数据。
- CubeMX:如果你还要动 STM32 的外设,它就是云台侧的配置工具。
本项目中 MSPM0 的 Keil 工程是:
car/empty/keil/empty_LP_MSPM0G3507_nortos_keil.uvprojx
SysConfig 配置文件是:
car/empty/empty.syscfg
Keil 构建前会通过脚本重新生成 MSPM0 的外设初始化文件:
car/empty/keil/generate_syscfg.bat
生成结果主要是:
car/empty/ti_msp_dl_config.c
car/empty/ti_msp_dl_config.h
这里有一个很重要的概念:不要手改 ti_msp_dl_config.c/.h 里的外设初始化逻辑。这些文件是 SysConfig 生成的,真正要改引脚、PWM、UART、I2C、定时器,应该改 .syscfg,再让 Keil 或脚本重新生成。
2. MSPM0 下载与调试
MSPM0G3507 使用 SWD 下载调试。项目中 SWD 引脚不要被占用:
SWDIO = PA19
SWCLK = PA20
调车时最常用的不是断点,而是:
- OLED 看状态;
- 串口看数据;
- 低速跑车看现象;
- 每改一两个参数就烧录验证。
这个项目的底盘 OLED 显示了很多关键信息,例如模式、目标圈数、赛段、速度、循迹误差、传感器 bitmask、PWM 和通信状态。后面小车篇会专门讲怎么看这些信息。
3. STM32 云台环境
STM32 端 Keil 工程:
gimbal_stm32f103c8t6/MDK-ARM/gimbal_stm32f103c8t6.uvprojx
统一配置文件:
gimbal_stm32f103c8t6/Core/Inc/gimbal_config.h
云台端比较关键的配置包括:
#define VISION_UART_BAUD 115200
#define MSPM0_UART_BAUD 115200
#define GIMBAL_CAN_BAUD 500000UL
#define GIMBAL_HEARTBEAT_PERIOD 50u
#define CHASSIS_LOST_MS 300u
这里面 CHASSIS_LOST_MS 很关键:如果 STM32 超过一定时间收不到 MSPM0 的底盘状态,就会关闭激光。这不是为了炫技,而是比赛中非常实用的安全逻辑。
五、需要提前掌握的知识
如果你准备复现或改这个项目,建议先补下面这些知识。不需要一开始全部精通,但至少要知道每个模块在系统里起什么作用。
1. C 语言与嵌入式基本功
至少要熟悉:
struct结构体;enum状态枚举;volatile的意义;- 中断函数;
- 位运算;
- 定时任务调度;
- 头文件和源文件分层。
比如底盘状态不是散落在一堆全局变量里,而是集中在 chassis_state_t 这类结构体中。这样 OLED、通信、控制算法都能围绕统一状态工作。
2. GPIO、PWM、UART、I2C、定时器
本项目涉及的外设很多:
| 外设 | 在项目中的作用 |
|---|---|
| GPIO | 按键、LED、蜂鸣器、电机方向、激光开关 |
| PWM | TB6612 电机调速 |
| UART | MSPM0 和 STM32 通信、视觉模块通信 |
| I2C | OLED 显示 |
| Timer | 1ms 系统节拍、周期调度 |
| CAN | STM32 控制 CAN 步进电机 |
学习顺序建议:
GPIO -> PWM -> Timer -> UART -> I2C -> CAN
如果还没接触过 MSPM0,不建议一上来就写完整小车。可以先做:
- 点灯;
- 按键控制 LED;
- PWM 控制一个电机;
- OLED 显示一行字;
- 读取 8 路灰度;
- 串口发送一帧状态。
这些小实验跑通后,再进入完整工程会舒服很多。
3. PID 与状态机
这道题里 PID 不是只有一个。
底盘至少有:
- 左轮速度 PID;
- 右轮速度 PID;
- 循迹 PID。
云台也有:
- X 轴瞄准 PID;
- Y 轴瞄准 PID。
但比 PID 更重要的是状态机。比如底盘不可能永远处于“正常循迹”,它至少有:
IDLE 待机
RUN 正常行驶
CORNER 转弯
DONE 完成
FAULT 故障
云台也有:
IDLE
AIM
SCAN
CIRCLE
RECT
RESET
如果没有状态机,代码很容易变成几十个 if 互相打架,调试时会非常痛苦。
4. 调试意识
比赛项目能不能成,很多时候不取决于你会不会写出“看起来很高级”的算法,而取决于你能不能快速定位问题。
建议每个模块都留下可观察信息:
- OLED 显示当前状态;
- 串口能输出关键变量;
- 按键能手动启停;
- 电机可以低速测试;
- 激光可以单独开关;
- 通信有在线/离线标志。
本项目中 MSPM0 OLED 上的 G+ / G-,STM32 OLED 上的 C:1 / C:0 就是为了联调时快速判断两块板有没有正常通信。
六、硬件准备清单
以下是本项目实际涉及的硬件类型,具体型号可以按自己手头器件替换,但接口和电气特性要对上。
底盘部分
- MSPM0G3507 开发板;
- 小车底盘;
- 4 个直流减速电机;
- 4 路或 2 路编码器;
- 2 块 TB6612 电机驱动;
- 8 路灰度传感器;
- OLED 显示屏;
- K1/K2 按键;
- 电池、电源模块、开关、杜邦线。
这些器件分别做什么:
- 底盘主控:管车跑不跑、怎么跑。
- 电机:真正带动车轮转动的执行器。
- TB6612:电机和主控之间的“放大器”,因为 MCU 不能直接带电机。
- 灰度传感器:识别黑线和白底的传感器。
- 编码器:告诉主控电机到底转了多少。
- OLED:把内部状态显示出来,方便调试。
- 按键:让你不用改代码就能启动、停止、切换目标圈数。
云台部分
- STM32F103C8T6;
- 二维云台结构;
- 两个步进电机;
- CAN 步进电机驱动器;
- CAN 收发器;
- MaxicamPro 或类似视觉模块;
- 激光模块;
- OLED 显示屏;
- KEY8 按键。
这些器件分别做什么:
- STM32:云台的大脑。
- 步进电机:让云台按角度细致移动。
- CAN 步进驱动器:接收速度命令并驱动步进电机。
- 视觉模块:告诉云台靶心偏哪儿了。
- 激光模块:最终指向目标的光点。
- OLED:显示模式、误差和联动状态。
- KEY8:让云台在调试时更容易操作。
联调注意事项
最容易出问题的不是代码,而是接线和供电:
- 两块主控必须共地;
- 电机电源和逻辑电源不要混接;
- TB6612 的
VM接电机电压,VCC接逻辑电压; - 激光不要直接由 GPIO 供电,建议用 MOS 或三极管控制;
- STM32 的 CAN_TX/CAN_RX 不能直接接 CANH/CANL,中间必须有 CAN 收发器;
- 串口 TX/RX 要交叉连接;
- 调试器不要当作电机供电电源。
七、从代码看系统总体结构
为了让后面三篇更好接上,这里先浅浅看一下项目目录。
MSPM0 底盘目录
car/empty/
├── empty.c # main 入口
├── empty.syscfg # SysConfig 外设配置
├── ti_msp_dl_config.c/.h # SysConfig 生成文件
├── app/
│ ├── app_chassis.c # 底盘主状态机和周期调度
│ └── chassis_state.h # 底盘状态和配置结构体
├── bsp/
│ ├── bsp_track8.c # 8 路灰度
│ ├── bsp_motor_tb6612.c # TB6612 电机驱动
│ ├── bsp_encoder.c # 编码器测速
│ ├── bsp_oled.c # OLED 显示
│ └── bsp_key.c # 按键扫描
├── control/
│ └── pid.c # 通用 PID
└── protocol/
├── crc8.c
├── protocol_frame.c
└── protocol_stm32.c # MSPM0 与 STM32 通信
可以看到,工程不是把所有东西堆在 main.c,而是分成:
app:应用逻辑;bsp:板级硬件驱动;control:控制算法;protocol:通信协议。
这种结构非常适合写博客讲解,也适合比赛后复盘。
STM32 云台目录
gimbal_stm32f103c8t6/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ └── gimbal_config.h
│ └── Src/
│ └── main.c
├── App/
│ ├── Inc/
│ └── Src/
│ ├── app_gimbal.c
│ ├── bsp_uart_vision.c
│ ├── bsp_uart_mspm0.c
│ ├── bsp_step_motor.c
│ └── pid.c
└── MDK-ARM/
└── gimbal_stm32f103c8t6.uvprojx
云台的配置更集中,很多题目相关参数都在:
gimbal_stm32f103c8t6/Core/Inc/gimbal_config.h
比如:
#define GIMBAL_MODE_AIM 1u
#define GIMBAL_MODE_DRAW_CIRCLE 3u
#define GIMBAL_MODE_DRAW_RECT 4u
#define GIMBAL_CIRCLE_RADIUS_ERR 20.0f
#define GIMBAL_RECT_HALF_X_ERR 42.0f
#define GIMBAL_RECT_HALF_Y_ERR 30.0f
这些值不是“写死就一定对”,而是要根据摄像头安装角度、靶纸距离、激光偏移进行现场标定。
八、我的整体开发路线
这个项目比较推荐按下面顺序做,不要一开始就全系统联调。
第一步:只调 MSPM0 底盘
目标:
- OLED 正常显示;
- K1/K2 正常;
- 灰度传感器 bitmask 正常;
- 电机方向正确;
- 编码器方向正确;
- 小车能低速循迹;
- 能处理直角转弯;
- 能通过缺口;
- 能按目标圈数停车。
这一阶段不需要云台参与。
第二步:只调 STM32 云台
目标:
- OLED 正常显示;
- KEY8 能切换模式;
- 视觉模块能输出目标偏差;
- 云台能根据
dx/dy调整; - 激光能受控开关;
- 画圆和画矩形轨迹能独立跑。
这一阶段不需要小车参与。
第三步:只调双主控通信
目标:
- MSPM0 周期发送底盘状态;
- STM32 能解析
0x10底盘帧; - STM32 周期发送心跳;
- MSPM0 能解析
0x20云台帧; - OLED 能显示在线状态。
这一阶段可以先不让电机转,只验证通信和状态。
第四步:低速综合测试
目标:
- 小车低速跑一圈;
- 云台保持目标瞄准;
- 底盘失联时云台关闭激光;
- 小车停止时云台状态可控;
- 圈进度能用于同步画圆或画矩形。
综合测试时一定要低速开始,尤其是激光和电机都接上的情况下。
九、Keil + SysConfig 开发 MSPM0 的几个坑
1. 生成文件和用户代码要分清
MSPM0 工程里有一类文件是 SysConfig 生成的:
ti_msp_dl_config.c
ti_msp_dl_config.h
这类文件不要当成普通业务代码改。否则下一次生成时,你的改动可能会被覆盖。
建议把自己的代码写在:
app/
bsp/
control/
protocol/
SysConfig 只负责外设底层初始化。
2. 改了 SysConfig 后要确认 Keil 工程也包含相关文件
添加新模块后,Keil 工程里也要包含对应 .c 文件。比如协议相关文件:
protocol/crc8.c
protocol/protocol_frame.c
protocol/protocol_stm32.c
如果只把文件放到目录里,却没有加入 Keil 工程,编译时就会出现链接错误。
3. 引脚复用要提前规划
MSPM0G3507 的引脚资源并不是无限的。PWM、UART、I2C、编码器、OLED、按键、灰度传感器都要占引脚。
本项目中底盘一侧比较关键的引脚有:
| 功能 | 引脚 |
|---|---|
| TB6612 PWM | PB04、PB01、PB06、PB07 |
| 8 路灰度 | PB25、PB24、PB20、PA14、PB18、PB19、PB10、PA07 |
| OLED I2C | PA31、PA28 |
| K1/K2 | PB26、PA25 |
| STM32 UART | PB02、PB03 |
| SWD | PA19、PA20 |
尤其要注意 SWD 引脚不要被误占用,否则下载调试会非常麻烦。
4. 先让系统“可观察”,再追求效果
调车不是玄学,前提是你能看到内部状态。
这个项目里我比较依赖 OLED 显示:
RUN N2 L0 S0 U+ G+
VL 30 VR 29
E -120 M3F P0000
F0000 PWM 850
含义大致是:
RUN:当前模式;N2:目标圈数;VL/VR:左右轮速度;E:循迹误差;M:灰度传感器 mask;P:圈进度;G+:云台在线;F0000:故障码。
后面小车篇会详细讲这些数据怎么用来调 PID、判断灰度极性、定位转弯失败。
十、第一篇总结
这篇主要是把项目的“地图”画出来:
- 这不是单纯的循迹车,而是底盘、云台、视觉、通信一起工作的系统;
- MSPM0G3507 更适合负责底盘实时控制;
- STM32F103C8T6 更适合负责云台、视觉、CAN 步进和激光;
- Keil + SysConfig 开发 MSPM0 时,要分清生成文件和用户代码;
- 开工前要准备好 GPIO、PWM、UART、I2C、Timer、PID、状态机等基础知识;
- 调试时要先保证系统可观察,再谈控制效果。
从下一篇开始,我们正式进入 MSPM0G3507 小车篇。我会从 empty.c 的入口开始,逐步讲到 app_chassis.c 里的 1ms 调度、8 路灰度循迹、速度 PID、循迹 PID、TB6612 四驱、直角转弯和缺口穿越。也就是说,下一篇会真正开始“让车跑起来”。
更多推荐
所有评论(0)