<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>MSPM0G3507 &#8211; Cloudlay</title>
	<atom:link href="https://cloudlay.cn/tag/mspm0g3507/feed/" rel="self" type="application/rss+xml" />
	<link>https://cloudlay.cn</link>
	<description>life</description>
	<lastBuildDate>Sun, 14 Jun 2026 17:08:32 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://cloudlay.cn/wp-content/uploads/2026/01/avatar.ico</url>
	<title>MSPM0G3507 &#8211; Cloudlay</title>
	<link>https://cloudlay.cn</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>从0到1带你打电赛·小车电控篇(三)：整车怎么搭——三人分工、硬件选型与「算法和芯片解耦」的代码框架</title>
		<link>https://cloudlay.cn/nuedc-car-03-build-and-architecture/</link>
					<comments>https://cloudlay.cn/nuedc-car-03-build-and-architecture/#respond</comments>
		
		<dc:creator><![CDATA[云间辞]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 17:08:32 +0000</pubDate>
				<category><![CDATA[嵌入式]]></category>
		<category><![CDATA[MSPM0G3507]]></category>
		<category><![CDATA[STM32]]></category>
		<category><![CDATA[团队分工]]></category>
		<category><![CDATA[智能小车]]></category>
		<category><![CDATA[电赛]]></category>
		<category><![CDATA[软件架构]]></category>
		<guid isPermaLink="false">https://cloudlay.cn/nuedc-car-03-build-and-architecture/</guid>

					<description><![CDATA[📚 本文是 「从 0 到 1 带你打电赛 · 小车电控篇」 系列（共 12 篇）第 3 篇。 第1篇 · 拿奖 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="ds-series" style="border:1px solid #4488ff33;background:#4488ff0d;border-radius:8px;padding:.8em 1.1em;margin:1.2em 0">
<p style="margin:0 0 .5em"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本文是 <strong>「从 0 到 1 带你打电赛 · 小车电控篇」</strong> 系列（共 12 篇）第 3 篇。</p>
<ol style="margin:.2em 0 0;padding-left:1.4em">
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-01-how-to-score/">第1篇 · 拿奖逻辑：把比赛拆成小目标</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-02-history/">第2篇 · 赛题进化史与押题</a></li>
<li style="margin:.15em 0"><strong>第3篇 · 整车搭建与代码框架（本篇）</strong></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-04-motor-power/">第4篇 · 电机驱动与电源地基</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-05-sensing/">第5篇 · 感知：灰度/电磁/编码器/IMU</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-06-pid-basics/">第6篇 · PID 入门：搞懂 P/I/D</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-07-pid-advanced/">第7篇 · PID 进阶：串级+工程补丁</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-08-pid-tuning/">第8篇 · PID 调参实战(核心)</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-09-advanced-control/">第9篇 · 进阶控制：几时该上</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-10-vision-comm/">第10篇 · K230 视觉与通信协议</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-11-architecture-fsm/">第11篇 · 状态机与整车软件</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-12-field-manual/">第12篇 · 现场作战+避坑+开源</a></li>
</ol>
</div>
<p>前两篇我们把&#8221;这比赛在比什么&#8221;&#8221;出题人爱出什么&#8221;聊清楚了。从这一篇开始，我们正式动手——但先别急着掏出电烙铁。一支队伍的上限，往往不是某个调参的瞬间决定的，而是开赛前那个&#8221;0 阶段&#8221;就埋下的：人怎么分工、主控选谁、代码骨架怎么搭。</p>
<p>这就好比盖楼。地基和承重结构得在挖第一锹土之前想清楚，等楼盖到一半才发现柱子位置不对，那不是修修补补能解决的，是要拆了重来的。而电赛只有四天三夜，没有&#8221;重来&#8221;的预算。这一篇我们就把这套地基打牢，让你后面写算法、调参数的时候，能做到&#8221;现场只改参数，不改架构&#8221;。</p>
<h2>三个人，怎么分这摊活</h2>
<p>小车赛道的队伍通常是三个人。最舒服、也最常见的分工是这样切的：</p>
<ul>
<li><strong>硬件 / 机械</strong>：负责选电机、画 PCB 或飞线、电源、机械结构、传感器布板与焊接。这个人手要稳、要懂模拟电路，是整车能不能&#8221;通电不冒烟&#8221;的第一道关。</li>
<li><strong>电控软件</strong>：负责主控固件——电机驱动、PID、循迹、状态机、各路通信的整合。这是把所有零件&#8221;捏成一辆会跑的车&#8221;的人。</li>
<li><strong>视觉算法</strong>：负责摄像头那一摊——巡线、识别色块/二维码/数字，把图像处理成一个个&#8221;结论&#8221;发给主控。</li>
</ul>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 分工不是切三块互不相干的地</p>
<p>三块活之间咬合得非常紧。硬件选了什么电机，直接决定电控能不能跑闭环；视觉发什么格式的数据，电控就得按什么格式去解析。所以<strong>接口要在开工第一天就定死</strong>：电机驱动用哪个芯片、编码器多少线、视觉和主控之间的通信帧长什么样。接口一旦钉死，三个人就能各干各的，最后再联调——而不是天天互相等。</p>
</div>
<p>这里先埋个伏笔：视觉和主控之间&#8221;只传结论不传整张图&#8221;的协议怎么设计，是《视觉与通信》一篇的重头戏，这里点到为止。</p>
<h2>主控选谁：为什么 2024 年起电赛主推 TI 的 MSPM0</h2>
<p>如果你是 2023 年以前打电赛，主控基本就是 STM32。但从 2024 年开始，电赛官方主推的是 <strong>TI 的 MSPM0G3507</strong>，尤其是智能小车这类题（比如 2024 年 H 题&#8221;自动行驶小车&#8221;）。很多人第一反应是&#8221;我 STM32 还没玩明白，又要换芯片？&#8221;——别慌。这俩的关系，我们后面会讲透：STM32 是你练手的&#8221;训练场&#8221;，MSPM0 是你上场的&#8221;主战场&#8221;，而且<strong>练通的算法能整段搬过去</strong>。</p>
<p>先看 MSPM0G3507 这颗芯片本身。它是一颗 Arm Cortex-M0+ 内核、主频 80MHz 的&#8221;混合信号&#8221;单片机。所谓&#8221;混合信号&#8221;，意思是它不只擅长跑数字逻辑，模拟外设也特别强——这正好戳中小车的需求。核心规格：</p>
<table>
<thead>
<tr>
<th>资源</th>
<th>MSPM0G3507</th>
<th>对小车意味着什么</th>
</tr>
</thead>
<tbody>
<tr>
<td>内核 / 主频</td>
<td>Cortex-M0+ @ 80MHz</td>
<td>跑 PID、状态机绰绰有余</td>
</tr>
<tr>
<td>Flash / SRAM</td>
<td>128KB / 32KB</td>
<td>装得下整车固件</td>
</tr>
<tr>
<td>ADC</td>
<td>2 个 12 位、4Msps、最多 17~27 通道、带 FIFO</td>
<td>可同步采样、一次扫完 8 路灰度</td>
</tr>
<tr>
<td>片上运放 OPA</td>
<td>2 个零漂斩波、可编程增益最高 32 倍</td>
<td>灰度/微弱信号免外挂运放</td>
</tr>
<tr>
<td>定时器</td>
<td>7 个（2 个高级 TIMA + 5 个 TIMG）、最多 22 路 PWM</td>
<td>出 PWM 驱动电机、测编码器</td>
</tr>
<tr>
<td>通信</td>
<td>4×UART、2×SPI、2×I2C、1×CAN-FD</td>
<td>接屏、蓝牙、陀螺仪、上位机</td>
</tr>
</tbody>
</table>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 片上运放是个隐藏福利</p>
<p>MSPM0 自带的 2 个运放（OPA），可以理解成<strong>手机自带的美颜相机</strong>：以前你采灰度这种微弱信号，得在板子上外挂一颗运放芯片来放大，费板子、费焊盘；现在芯片里就带了能放大信号的&#8221;内置镜头&#8221;，放大完直接喂给 ADC。STM32 的 G0 系列没有集成运放，从 STM32 迁到 MSPM0，正好能用片上 OPA 把外部分立运放替掉。</p>
</div>
<h3>一个必须提前知道的&#8221;反直觉硬伤&#8221;：硬件编码器接口只有一路</h3>
<p>这是 MSPM0 相对 STM32 最大的坑，<strong>选型和布线之前必须想清楚</strong>，否则到现场才发现就晚了。</p>
<p>STM32 几乎每个定时器都能配成编码器接口模式，你想测两个电机的转速，随便挑两个定时器就行。但 MSPM0G <strong>全芯片只有一个定时器（常说的 TIMG8）硬件支持 QEI 正交编码器解码</strong>。也就是说，硬件层面你只能&#8221;白嫖&#8221;一路编码器。</p>
<p>那双电机怎么办？两条路：</p>
<ul>
<li><strong>路线 A（硬件 QEI）</strong>：用那唯一一路 TIMG8，硬件自动计数 + 判方向，几乎不占 CPU。这是首选。</li>
<li><strong>路线 B（GPIO 外部中断）</strong>：A/B 两相各接一个 GPIO，上升沿触发中断，在中断里读另一相的电平判方向，再用一个 20ms 周期的定时器去数脉冲算速度。代价是高转速时<strong>容易漏脉冲、把中断占满</strong>。</li>
</ul>
<p>所以双电机的典型方案是：一路走硬件 QEI，另一路退回 GPIO 中断（给它最高优先级），或者上一颗外部计数芯片。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 真实踩坑</p>
<p>很多新手在 MSPM0 上想给两个电机各配一路硬件编码器，结果发现只有一个定时器支持 QEI，第二路只能退回 GPIO 中断，高速时丢脉冲、速度测不准，车跑起来一边快一边慢。这个坑的根源不是代码写错，而是<strong>选型时没数清楚硬件资源</strong>。</p>
</div>
<p>编码器测速本身的原理很直白，就像<strong>数脉冲过了几个</strong>：车每跑一段，电机转一点，编码器吐出一串脉冲；你每隔固定时间（比如 20ms）数一次这段时间内新增了多少脉冲，数得越多说明跑得越快。A、B 相脉冲的先后顺序，还能告诉你是前进还是后退。具体的编码器接法和 IMU 融合，我们留到《感知：灰度/电磁/编码器/IMU》一篇细讲。</p>
<h2>开发环境：SysConfig、IDE 和两种下载方式</h2>
<h3>SysConfig：TI 版的&#8221;装修图纸软件&#8221;</h3>
<p>TI 给 MSPM0 配了一个图形化配置工具叫 <strong>SysConfig</strong>，作用相当于 STM32 玩家熟悉的 CubeMX。</p>
<p>它就像<strong>装修前用图纸软件拖拽布局</strong>：你在图上点哪个插座（引脚）接什么电器（外设），软件自动帮你把&#8221;水电图&#8221;（一个叫 <code>ti_msp_dl_config.h</code> 的头文件，里面有 <code>SYSCFG_DL_init()</code> 初始化函数）画好，你不用自己一根根去配寄存器。配 PWM 在 TIMER-PWM 分类下、配编码器在 TIMER-QEI、配 ADC 在 ADC12、配运放在 OPA。配完它会生成一堆实例宏，比如 <code>PWM_0_INST</code>、<code>QEI_0_INST</code>、<code>UART_0_INST</code>，你代码里直接用这些名字就行。</p>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> SysConfig 第一坑：改完一定要保存</p>
<p>SysConfig 改完配置如果<strong>不保存</strong>，生成的 <code>ti_msp_dl_config.h</code> 不会更新，你编译出来的还是旧配置——明明改了引脚却没生效，能让你查半天。每次改完务必 Ctrl+S。</p>
</div>
<p>配 PWM 的时候，有个关系要记牢：</p>
<p class="ds-math" style="overflow-x:auto">$$\text{频率}=\frac{\text{时钟}}{\text{分频}\cdot(\text{period}+1)}$$</p>
<p>向上计数时，占空比 $=\frac{\text{CCR}}{\text{period}+1}$。立创开发板的电机驱动例程用 80MHz 时钟、period 设 10000，实测出来约 8kHz。运行时动态改占空比就一行：</p>
<pre><code class="language-c">// 设置占空比 (向上计数: duty = CCR/(period+1))
DL_TimerG_setCaptureCompareValue(PWM_0_INST, 1000, DL_TIMER_CC_1_INDEX);
// 如果 SysConfig 里没勾 "Start Timer"，需要手动启动一次
DL_TimerG_startCounter(PWM_0_INST);
// 读编码器 QEI 计数
uint32_t cnt = DL_Timer_getTimerCount(QEI_0_INST);</code></pre>
<p>注意 MSPM0 的 API 全是 <code>DL_</code> 前缀（DriverLib 的意思），这和 STM32 的 <code>HAL_</code>/<code>LL_</code> 不一样。再记两个小口诀：PWM 占空比建议<strong>别超过 95%</strong>；改 PWM 频率（改 period）会同时改变占空比的分辨率（period 越小，占空比能分的档越粗），要兼顾。PWM 频率电赛常用 8kHz 到 32kHz 这个区间，低于 20kHz 左右电机会有可闻的&#8221;啸叫&#8221;，想安静点就往高了选。</p>
<h3>IDE：CCS / Keil / IAR 怎么挑</h3>
<ul>
<li><strong>CCS</strong>：TI 官方的，免费，基于 Eclipse，<strong>内置 SysConfig</strong>，配套最全。新手最省心。</li>
<li><strong>Keil MDK</strong>：电赛圈最主流，因为大家生态熟、学校积累多。但要注意版本——<strong>需要 uVision v5.38a 以上 + AC6 编译器 v6.16 以上</strong>，版本太旧装不上 MSPM0 的支持包。</li>
<li><strong>IAR</strong>：也支持，用的人相对少。</li>
<li><strong>VSCode</strong>：它本身不是官方 IDE，但社区常用 VSCode + Keil（靠插件）或 VSCode + CMake + GCC 来获得更现代的编辑体验，编译下载还是靠 Keil 或命令行。</li>
</ul>
<p>一句话：图省心、从零起步用 CCS；要沿用学校积累、跟队友保持一致就用 Keil。</p>
<h3>下载：SWD 仿真器 vs BSL 串口</h3>
<p>这俩的区别，可以类比成上网方式：</p>
<ul>
<li><strong>SWD（专线宽带）</strong>：用板载的 XDS110 或外接 DAP-Link / J-Link，四根线（SWCLK / SWDIO / GND / 3V3）。不仅能下载，还能<strong>实时看程序在干嘛、打断点单步调试</strong>。开发期首选。</li>
<li><strong>BSL 串口（手机热点应急）</strong>：用 TI 的 UniFlash 工具，通过 USB 转串口把程序灌进去，没仿真器也能用，但<strong>只能灌不能调</strong>。</li>
</ul>
<p>BSL 这条路有两个非常容易翻车的细节：</p>
<div class="ds-callout ds-callout-danger" style="border-left:4px solid #ff1744;background:#ff174414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff1744"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f525.png" alt="🔥" class="wp-smiley" style="height: 1em; max-height: 1em;" /> BSL 两大坑</p>
<ol>
<li><strong>只认 <code>.txt</code> / <code>.hex</code> 文件，绝不认 CCS 生成的 <code>.out</code></strong>。有人拿 <code>.out</code> 去烧，UniFlash 死活不认，还以为是工具坏了。</li>
<li><strong>按键时序要掐准</strong>：同时按住 BSL + RST 约 5 秒 → 松开 RST → 3 秒内点 Load Image → 烧完（约 10~20 秒）松开 BSL → 再按一次 RST。时序没掐准就反复连不上。</li>
</ol>
</div>
<p>结论很简单：开发期老老实实用板载 XDS110 或 DAP-Link 走 SWD，能调试又省事；BSL 留作&#8221;手头没仿真器&#8221;时的应急手段。</p>
<h2>重头戏：让&#8221;算法和芯片解耦&#8221;的分层框架</h2>
<p>到这里，才是这一篇真正的核心。前面铺垫了这么多，就是为了引出一个问题：<strong>STM32 练手、MSPM0 上场，凭什么练通的算法能整段搬过去？</strong></p>
<p>答案不在芯片，在<strong>代码架构</strong>。</p>
<h3>为什么要分层</h3>
<p>设想一下，如果你的 PID 函数里直接写满了 <code>HAL_GPIO_WritePin(...)</code> 这种 STM32 专属的芯片 API，那换到 MSPM0 的时候，整个 PID 函数都得跟着改——因为它和具体芯片&#8221;焊死&#8221;在一起了。这就是没分层的下场：换芯片 = 满地找改 = 平移变重写。</p>
<p>分层架构的思路，像<strong>点外卖</strong>：</p>
<ul>
<li><strong>你（App 应用层）</strong>：只对着 App 下单，决定&#8221;我要吃啥、车要怎么跑&#8221;，从不进后厨。这里放状态机和决策。</li>
<li><strong>App 平台（中间件层）</strong>：把订单翻译给骑手和商家，承载 PID、滤波、通信协议、元素识别这些<strong>和芯片无关</strong>的算法。</li>
<li><strong>骑手 / 灶台（驱动层 + HAL）</strong>：干脏活累活，真正去配寄存器、读写引脚。</li>
</ul>
<p>换城市（换芯片）时，你点外卖的方式（算法）一字不改，变的只是当地的骑手和店家（驱动层）。这就是&#8221;算法和芯片解耦&#8221;的本质。</p>
<p>这套范式不是我们瞎编的，它直接借鉴了智能车圈久经考验的<strong>逐飞（SeekFree）开源库</strong>的分层结构：</p>
<figure class="ds-diagram" style="text-align:center;margin:1.3em 0"><img decoding="async" src="https://cloudlay.cn/wp-content/uploads/dianseiche/5aa6d106b13e.png" alt="电赛小车电控 · 示意图" loading="lazy" style="max-width:100%;height:auto;background:#fff;border-radius:8px;padding:6px;box-shadow:0 1px 6px rgba(0,0,0,.25)"></figure>
<h3>一条不能破的铁律</h3>
<p>分层能不能起作用，全看一条铁律守不守得住：</p>
<div class="ds-callout ds-callout-important" style="border-left:4px solid #00bcd4;background:#00bcd414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bcd4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 铁律：上层只调下层、下层绝不反调；上层禁止直接调芯片 API</p>
<p>这就像公司汇报：下属向上汇报（对上提供接口），老板不会替下属干活，下属也别绕过流程直接跑去动机房（直接调 <code>HAL_GPIO_WritePin</code> 这类最底层 API）。一旦中间件、应用层里混进了芯片专属 API，分层就破了，换芯片时你得满地找它们、一个个改，平移直接变成重写。</p>
</div>
<p>逐飞库就是这么干的：设备层（管摄像头/陀螺/屏的初始化）<strong>只允许调驱动层的接口</strong>，不准碰最底层的芯片 API。这样换 MCU 只改驱动层，中间件和 App 原样平移。</p>
<p>这套库有多能打？同一份逐飞库已经移植到了 RT1064、英飞凌 TC264、STC32，以及 <strong>TI 的 MSPM0G3507（对应 2025 TI 板电赛）</strong>——上层算法共用，差异全集中在底层驱动。这就是分层解耦价值的活证据。</p>
<h3>一个可照抄的目录骨架</h3>
<p>落到实处，你的工程目录可以这么切：</p>
<pre><code class="language-plaintext">project/
├── App/                 # 应用层：你写的核心逻辑
│   ├── fsm.c/h          #   状态机：待命/起步/直道/弯道/路口/停靠/找回/急停
│   └── control_task.c   #   控制环调度：采集→融合→串级PID→输出→保护
├── Middleware/          # 中间件层：芯片无关的算法
│   ├── pid.c/h          #   PID（位置式/增量式）
│   ├── filter.c/h       #   滤波（一阶低通/互补）
│   └── protocol.c/h     #   通信帧解析
├── Driver/              # 驱动层：换芯片主要改这里
│   ├── motor.c/h        #   电机：把"带符号速度"翻译成PWM+方向
│   ├── encoder.c/h      #   编码器读速
│   ├── gray.c/h         #   灰度采集
│   └── uart.c/h         #   串口收发
└── HAL/                 # 芯片相关：DriverLib / 寄存器
    └── (SysConfig 生成的 ti_msp_dl_config.h 等)</code></pre>
<p>关键在于<strong>接口的方向</strong>：App 调中间件和驱动，中间件和驱动里<strong>绝不出现任何芯片专属 API</strong>。比如电机驱动这一层，对上只暴露一个&#8221;给我一个带符号的速度&#8221;的接口，符号决定正反转、绝对值决定 PWM 大小，至于底下怎么配 PWM 寄存器，全藏在驱动层里：</p>
<pre><code class="language-c">// 中间件层：PID 算出一个带符号输出，完全不知道芯片是谁
int pwm = (int)pid_calc(&amp;speed_pid, target_speed, get_encoder_count());

// 驱动层：把带符号输出翻译成具体的正反转 + PWM（这层才碰芯片）
void motor_set(int pwm) {
    if (pwm &gt; 0)       set_motor(0, pwm);   // 正转
    else if (pwm &lt; 0)  set_motor(-pwm, 0);  // 反转（取绝对值）
    else               stop_motor();
}</code></pre>
<p><code>pid_calc</code> 这个函数里只有数学，没有一行芯片代码，所以它在 STM32 上调通了，搬到 MSPM0 上一个字都不用改。这就是&#8221;练通的算法整段平移&#8221;的底气。</p>
<h2>STM32 <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> MSPM0 平移心法</h2>
<p>很多人迁移时的第一反应是&#8221;把每一个 <code>HAL_xxx</code> 逐行翻译成 <code>DL_xxx</code>&#8220;。<strong>这是最痛苦、最容易错的做法，千万别这么干。</strong></p>
<p>TI 官方在《从 STM32 到 MSPM0 迁移指南》（文档号 ZHCABX9B，能搜到）里给出的标准心法是：<strong>先把你的应用逻辑理解透，然后拿一份最接近的 MSPM0 官方例程当骨架，把你的 PID、循迹这些算法层原样搬进去，只重写外设的初始化和读写。</strong></p>
<div class="ds-callout ds-callout-example" style="border-left:4px solid #7c4dff;background:#7c4dff14;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#7c4dff"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f9e9.png" alt="🧩" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 一个好懂的比喻</p>
<p>把算法从 STM32 平移到 MSPM0，好比把同一道菜从燃气灶搬到电磁炉。<strong>菜谱（算法逻辑）一字不改</strong>，只是把&#8221;开燃气阀&#8221;的动作换成&#8221;按电磁炉按钮&#8221;（HAL 换成 DriverLib）。聪明的做法不是把每个燃气动作硬翻译，而是直接拿一份电磁炉的现成菜谱当模板，把你的料倒进去。</p>
</div>
<p>官方给的工具链对照表，迁移时贴在显示器边上很有用：</p>
<table>
<thead>
<tr>
<th>STM32（ST 工具）</th>
<th>MSPM0（TI 工具）</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>CubeIDE</td>
<td>CCS</td>
<td>IDE</td>
</tr>
<tr>
<td>CubeMX</td>
<td>SysConfig</td>
<td>图形化配置</td>
</tr>
<tr>
<td>CubeProgrammer</td>
<td>UniFlash</td>
<td>烧录</td>
</tr>
<tr>
<td>CubeMonitor</td>
<td>GuiComposer</td>
<td>上位机监控</td>
</tr>
<tr>
<td>HAL 库</td>
<td>TI Driver</td>
<td>高层驱动</td>
</tr>
<tr>
<td>LL 库</td>
<td>DriverLib（<code>DL_</code> 前缀）</td>
<td>底层驱动（TI 建议大多数人直接用这个，省内存、性能好）</td>
</tr>
</tbody>
</table>
<p>有几个外设层面的差异，是平移时最容易&#8221;反咬&#8221;算法层假设的地方，列成清单逐条核对：</p>
<ul>
<li>☐ <strong>GPIO 拆成了两块</strong>：STM32 一个 GPIO 概念，MSPM0 拆成 GPIO（读写 / 中断）+ IOMUX（引脚复用），配引脚和读写是分开的。</li>
<li>☐ <strong>硬件编码器接口只有一路 QEI</strong>（前面强调过的硬伤），双电机方案要提前定好。</li>
<li>☐ <strong>UART 的 FIFO 只有 4 字节</strong>（STM32 是 8 字节），高速收发更容易溢出，而且不支持自动波特率检测。</li>
<li>☐ <strong>UART 中断里不要照搬习惯去调 <code>NVIC_ClearPendingIRQ()</code></strong>——官方教程明确提醒过，照搬 STM32 写法会出问题。</li>
<li>☐ <strong>时钟树、NVIC、中断模型</strong>和 STM32 不一样，从官方 example 起步能帮你绕开大部分坑。</li>
</ul>
<p>官方迁移指南把整个流程总结成五步，照着走就行：① 对照产品系列表选定 MSPM0 器件 → ② 订 LaunchPad 开发板（LP-MSPM0G3507）→ ③ 装 IDE + SDK → ④ 软件移植（从最接近的官方例程改起，<strong>不是逐行替换 API</strong>）→ ⑤ 调试验证。</p>
<h2>有哪些现成的模板和库可以站在肩膀上</h2>
<p>完全从零搭框架很费时间。下面这几个仓库都是经过电赛验证的，按你的处境挑一个当起点：</p>
<ul>
<li><strong><a href="https://github.com/TexasInstruments/mspm0-sdk" target="_blank" rel="noopener noreferrer">TI 官方 mspm0-sdk</a></strong>：DriverLib + 海量外设例程（GPIO / Timer / PWM / ADC / UART / DMA / OPA 全都有）。这是所有上层方案的底座，也是&#8221;从最接近的 example 改起&#8221;那个 example 的来源。一手权威，必装。</li>
<li><strong><a href="https://github.com/ZhijianLi2003/ZLC_MSPM0_Peripheral_Library" target="_blank" rel="noopener noreferrer">ZLC_MSPM0_Peripheral_Library</a></strong>：2024 电赛 H 题一等奖（山大威海）的外设库，编码器、电机（DRV8701E 双路）、舵机 PWM、8 路灰度、IMU、无线串口一应俱全，还附了设计报告和环境配置文档。<strong>想看一等奖的完整车软件栈长什么样，看这个。</strong></li>
<li><strong><a href="https://github.com/menoking/PIDCarTemplate-MSPM0G3507" target="_blank" rel="noopener noreferrer">PIDCarTemplate-MSPM0G3507</a></strong>：两轮 / 四轮 PID 小车模板，封装好了 Delay / Encoder / Motor / 按键（短按长按双击）/ MPU6050 / OLED / 蓝牙 / 灰度，clone 下来调 API 就能开发。<strong>从零起步直接套模板的首选。</strong></li>
<li><strong><a href="https://github.com/woai66/SeekFree_MSPM0G3507_Opensource_Library" target="_blank" rel="noopener noreferrer">woai66 的逐飞 MSPM0 移植库</a></strong>：把上面讲的逐飞分层库移植到了 MSPM0，对应 2025 TI 板电赛。<strong>想要&#8221;芯片解耦平移&#8221;那套完整范本，看它。</strong></li>
<li><strong><a href="https://github.com/danshoujieyi/TI-MSPM0G3507" target="_blank" rel="noopener noreferrer">danshoujieyi/TI-MSPM0G3507</a></strong>：电赛指定板模板，裸机版 + FreeRTOS 版 + RT-Thread 版都有（Keil5 + VSCode）。想上 RTOS 或要 VSCode 工作流的直接用。</li>
<li><strong><a href="https://github.com/zhzhongshi/MSPM0-CMAKE-GCC-Template" target="_blank" rel="noopener noreferrer">MSPM0-CMAKE-GCC-Template</a></strong>：GCC + CMake 模板，想脱离 Keil、用 VSCode + 命令行现代工作流的首选。</li>
</ul>
<p>更全的开源仓库点评（含视觉车、平衡车、仿真工具），我们放在收官的《现场作战手册》一篇里集中评注，这里先给够你起步用的几个。</p>
<h2>一些选型和架构上的经验之谈</h2>
<p>最后几条是过来人趟出来的，写代码之前先记在心里。</p>
<div class="ds-callout ds-callout-tip" style="border-left:4px solid #00bfa6;background:#00bfa614;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bfa6"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 电机选型，先于算法</p>
<p>一等奖队伍（ZLC）选的是带霍尔编码器的减速直流电机，而不是步进或空心杯——<strong>步进体积大、空心杯没法测速</strong>，只有带编码器的减速直流电机才能跑闭环 PID。换句话说，<strong>要做速度 / 位置闭环，选型阶段就必须锁定带编码器的电机</strong>，减速比还会影响测速分辨率和扭矩。算法再强，没编码器也没法闭环。</p>
</div>
<div class="ds-callout ds-callout-warning" style="border-left:4px solid #ff9100;background:#ff910014;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#ff9100"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> MPU6050 的温漂是头号坑</p>
<p>2024 H 题有队伍用 MPU6050 做无指示线直行，发现陀螺仪温漂严重——偏航角随温度漂移，车越跑越偏，直道走不直。消费级 MPU6050 精度有限，<strong>上电要静置做零偏校准</strong>，长时间跑要考虑温补或换更高级的 IMU（如 ICM、IMU660ra），直行最好<strong>融合编码器双反馈</strong>，而不是只信陀螺仪。陀螺仪具体怎么校准、怎么融合，留到《感知：灰度/电磁/编码器/IMU》一篇。</p>
</div>
<div class="ds-callout ds-callout-important" style="border-left:4px solid #00bcd4;background:#00bcd414;padding:.6em 1em;margin:1.2em 0;border-radius:6px">
<p style="margin:0 0 .45em;font-weight:700;color:#00bcd4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 调度方式：电赛首选前后台裸机，别盲目上 RTOS</p>
<p>整车软件的调度，<strong>主流且最稳的选择是&#8221;前后台裸机&#8221;</strong>：主循环（后台）跑显示、通信这些慢任务，定时器中断（前台）跑固定周期的控制环。它时序确定、开销极小、容易验证。</p>
<p>要不要上 FreeRTOS / RT-Thread，像<strong>出门用不用导航</strong>：去楼下小卖部（任务才 3~5 个）凭记忆最快；只有跨城多点配送（任务多、要真并发）才值得开导航——而导航本身也耗油（RTOS 内核要占几 KB，还可能引入优先级反转、同步 bug）。任务少时硬上 RTOS，纯属给自己加难度。</p>
</div>
<p>调度、控制环时序、状态机怎么把所有模块串成一辆会跑的车，是《状态机与整车软件》那一篇的主题，到时候我们会给出完整的主控制环伪代码和状态图。</p>
<p>把这一篇的地基打牢，你就有了一套&#8221;算法写一次、芯片换着用&#8221;的框架。但框架终究是个空壳，真正让车动起来的第一步，是让电机听话——电机驱动、PWM 和电源这套电控地基，我们下一篇见。</p>
<div class="ds-series" style="border:1px solid #4488ff33;background:#4488ff0d;border-radius:8px;padding:.8em 1.1em;margin:1.2em 0">
<p style="margin:0 0 .5em"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本文是 <strong>「从 0 到 1 带你打电赛 · 小车电控篇」</strong> 系列（共 12 篇）第 3 篇。</p>
<ol style="margin:.2em 0 0;padding-left:1.4em">
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-01-how-to-score/">第1篇 · 拿奖逻辑：把比赛拆成小目标</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-02-history/">第2篇 · 赛题进化史与押题</a></li>
<li style="margin:.15em 0"><strong>第3篇 · 整车搭建与代码框架（本篇）</strong></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-04-motor-power/">第4篇 · 电机驱动与电源地基</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-05-sensing/">第5篇 · 感知：灰度/电磁/编码器/IMU</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-06-pid-basics/">第6篇 · PID 入门：搞懂 P/I/D</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-07-pid-advanced/">第7篇 · PID 进阶：串级+工程补丁</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-08-pid-tuning/">第8篇 · PID 调参实战(核心)</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-09-advanced-control/">第9篇 · 进阶控制：几时该上</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-10-vision-comm/">第10篇 · K230 视觉与通信协议</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-11-architecture-fsm/">第11篇 · 状态机与整车软件</a></li>
<li style="margin:.15em 0"><a href="https://cloudlay.cn/nuedc-car-12-field-manual/">第12篇 · 现场作战+避坑+开源</a></li>
</ol>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://cloudlay.cn/nuedc-car-03-build-and-architecture/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
