<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>电机控制 on Tech Snippets - 嵌入式技术笔记</title>
    <link>https://tech-snippets.xyz/tags/%E7%94%B5%E6%9C%BA%E6%8E%A7%E5%88%B6/</link>
    <description>Recent content in 电机控制 on Tech Snippets - 嵌入式技术笔记</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Thu, 14 May 2026 19:00:00 +0800</lastBuildDate>
    <atom:link href="https://tech-snippets.xyz/tags/%E7%94%B5%E6%9C%BA%E6%8E%A7%E5%88%B6/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>STM32 高级定时器深度解析与电机控制实战指南</title>
      <link>https://tech-snippets.xyz/posts/stm32-advanced-timer-motor-control-guide/</link>
      <pubDate>Thu, 14 May 2026 19:00:00 +0800</pubDate>
      <guid>https://tech-snippets.xyz/posts/stm32-advanced-timer-motor-control-guide/</guid>
      <description>前言 在嵌入式系统开发中，定时器是最常用也最容易被低估的外设之一。很多开发者对定时器的理解仅仅停留在&amp;quot;定时中断&amp;quot;的层面，却不知道一个高级定时器所能实现的功能远远超出想象——它可以生成高精度 PWM 波形、精确测量脉冲信号、实现编码器接口、驱动步进电机和无刷电机，甚至可以不占用 CPU 资源完成复杂的波形生成。
STM32 的定时器系统设计得极其精巧，尤其是高级控制定时器（Advanced-control Timer），如 TIM1、TIM8 等，其内部包含了多达几十个寄存器，支持多种工作模式。对于电机控制这样的实时性要求极高的应用场景，高级定时器几乎是不可或缺的。
然而，正是因为其功能强大，高级定时器的学习曲线也相当陡峭。很多开发者对着参考手册上的寄存器描述看了几天，依然搞不清捕获/比较通道、互补输出、死区插入、刹车功能这些概念到底是怎么回事。更不用说将这些功能组合起来实现一个完整的 FOC（磁场定向控制）电机驱动了。
本文将从定时器的基本原理出发，层层深入，带你彻底理解 STM32 高级定时器的每一个功能模块。我们不仅会讲解理论，更会通过大量的代码示例，从简单的 PWM 输出开始，一步步实现基于高级定时器的 BLDC 无刷电机六步换向控制。
无论你是刚开始接触 STM32 的新手，还是希望深入理解定时器硬件原理的资深开发者，相信这篇文章都能给你带来新的收获。
一、STM32 定时器家族：不止是&amp;quot;计数&amp;quot;那么简单 很多人学习 STM32 定时器时的第一个困惑就是：为什么 STM32 要有这么多种定时器？从 TIM2 到 TIM17，编号一大堆，每个定时器的功能还都不太一样，很容易搞混。
实际上，ST 对定时器的分类是非常清晰的，按照功能从简单到复杂，可以分为以下几类：
1.1 基本定时器（Basic Timer）：TIM6、TIM7 基本定时器正如其名，功能最简单，只有最核心的定时功能。它没有外部 IO 引脚，也没有捕获/比较通道，只能实现最基本的定时中断和 DAC 触发。
基本定时器的典型应用场景：
实现固定间隔的定时中断（如 1ms 系统滴答） 作为 DAC 的转换触发信号 简单的延时功能 如果你只需要&amp;quot;多少时间后做什么事&amp;quot;，基本定时器就足够了，它的资源占用也最小。
1.2 通用定时器（General-purpose Timer）：TIM2 ~ TIM5、TIM9 ~ TIM14 通用定时器是使用最广泛的一类定时器。它们具有 4 个独立的捕获/比较通道，可以实现：
输入捕获：测量外部脉冲的频率、占空比 输出比较：生成各种波形 PWM 输出：生成电机控制所需的 PWM 信号 单脉冲模式：生成精确的单脉冲输出 编码器接口：对接正交编码器 通用定时器又可以细分为两个子类：</description>
      <content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>在嵌入式系统开发中，定时器是最常用也最容易被低估的外设之一。很多开发者对定时器的理解仅仅停留在&quot;定时中断&quot;的层面，却不知道一个高级定时器所能实现的功能远远超出想象——它可以生成高精度 PWM 波形、精确测量脉冲信号、实现编码器接口、驱动步进电机和无刷电机，甚至可以不占用 CPU 资源完成复杂的波形生成。</p>
<p>STM32 的定时器系统设计得极其精巧，尤其是高级控制定时器（Advanced-control Timer），如 TIM1、TIM8 等，其内部包含了多达几十个寄存器，支持多种工作模式。对于电机控制这样的实时性要求极高的应用场景，高级定时器几乎是不可或缺的。</p>
<p>然而，正是因为其功能强大，高级定时器的学习曲线也相当陡峭。很多开发者对着参考手册上的寄存器描述看了几天，依然搞不清捕获/比较通道、互补输出、死区插入、刹车功能这些概念到底是怎么回事。更不用说将这些功能组合起来实现一个完整的 FOC（磁场定向控制）电机驱动了。</p>
<p>本文将从定时器的基本原理出发，层层深入，带你彻底理解 STM32 高级定时器的每一个功能模块。我们不仅会讲解理论，更会通过大量的代码示例，从简单的 PWM 输出开始，一步步实现基于高级定时器的 BLDC 无刷电机六步换向控制。</p>
<p>无论你是刚开始接触 STM32 的新手，还是希望深入理解定时器硬件原理的资深开发者，相信这篇文章都能给你带来新的收获。</p>
<h2 id="一stm32-定时器家族不止是计数那么简单">一、STM32 定时器家族：不止是&quot;计数&quot;那么简单</h2>
<p>很多人学习 STM32 定时器时的第一个困惑就是：为什么 STM32 要有这么多种定时器？从 TIM2 到 TIM17，编号一大堆，每个定时器的功能还都不太一样，很容易搞混。</p>
<p>实际上，ST 对定时器的分类是非常清晰的，按照功能从简单到复杂，可以分为以下几类：</p>
<h3 id="11-基本定时器basic-timertim6tim7">1.1 基本定时器（Basic Timer）：TIM6、TIM7</h3>
<p>基本定时器正如其名，功能最简单，只有最核心的定时功能。它没有外部 IO 引脚，也没有捕获/比较通道，只能实现最基本的定时中断和 DAC 触发。</p>
<p>基本定时器的典型应用场景：</p>
<ul>
<li>实现固定间隔的定时中断（如 1ms 系统滴答）</li>
<li>作为 DAC 的转换触发信号</li>
<li>简单的延时功能</li>
</ul>
<p>如果你只需要&quot;多少时间后做什么事&quot;，基本定时器就足够了，它的资源占用也最小。</p>
<h3 id="12-通用定时器general-purpose-timertim2--tim5tim9--tim14">1.2 通用定时器（General-purpose Timer）：TIM2 ~ TIM5、TIM9 ~ TIM14</h3>
<p>通用定时器是使用最广泛的一类定时器。它们具有 4 个独立的捕获/比较通道，可以实现：</p>
<ul>
<li>输入捕获：测量外部脉冲的频率、占空比</li>
<li>输出比较：生成各种波形</li>
<li>PWM 输出：生成电机控制所需的 PWM 信号</li>
<li>单脉冲模式：生成精确的单脉冲输出</li>
<li>编码器接口：对接正交编码器</li>
</ul>
<p>通用定时器又可以细分为两个子类：</p>
<ul>
<li><strong>16 位通用定时器</strong>（TIM9 ~ TIM14）：功能相对简化，通常只有 2 个通道</li>
<li><strong>32 位通用定时器</strong>（TIM2、TIM5）：计数器位数更多，可以实现更长的定时周期</li>
</ul>
<p>对于大多数应用来说，通用定时器已经能满足需求了。比如你要驱动一个普通的直流电机，用通用定时器生成 PWM 就完全够用。</p>
<h3 id="13-高级控制定时器advanced-control-timertim1tim8">1.3 高级控制定时器（Advanced-control Timer）：TIM1、TIM8</h3>
<p>这才是本文的主角。高级控制定时器在通用定时器的基础上，增加了一系列专为电机控制和电源变换应用设计的高级功能：</p>
<ul>
<li><strong>互补输出与死区插入</strong>：驱动 H 桥电路必备，防止上下桥臂直通</li>
<li><strong>中央对齐模式</strong>：生成对称的 PWM 波形，降低电机噪音</li>
<li><strong>刹车输入</strong>：紧急情况下快速关断输出，保护硬件</li>
<li><strong>重复计数器</strong>：更灵活的中断周期控制</li>
<li><strong>多个 DMA 请求</strong>：配合 DMA 实现无 CPU 干预的波形生成</li>
</ul>
<p>可以说，如果你要做 BLDC 无刷电机、步进电机、开关电源这类应用，高级定时器是必不可少的。普通的通用定时器根本无法满足这些场景的复杂需求。</p>
<h3 id="14-如何选择合适的定时器">1.4 如何选择合适的定时器</h3>
<p>这里给大家一个简单的选型指南：</p>
<table>
<thead>
<tr>
<th>应用场景</th>
<th>推荐定时器类型</th>
</tr>
</thead>
<tbody>
<tr>
<td>简单定时中断</td>
<td>基本定时器 TIM6/TIM7</td>
</tr>
<tr>
<td>普通 PWM 输出</td>
<td>通用定时器 TIM2~TIM5</td>
</tr>
<tr>
<td>输入捕获测量</td>
<td>通用定时器 TIM2~TIM5</td>
</tr>
<tr>
<td>正交编码器接口</td>
<td>通用定时器 TIM2~TIM5</td>
</tr>
<tr>
<td>BLDC 电机控制</td>
<td>高级定时器 TIM1/TIM8</td>
</tr>
<tr>
<td>开关电源/PFC</td>
<td>高级定时器 TIM1/TIM8</td>
</tr>
<tr>
<td>需要互补输出</td>
<td>高级定时器 TIM1/TIM8</td>
</tr>
</tbody>
</table>
<p>理解了这些分类，你在设计硬件和编写代码时就不会选错定时器了。</p>
<h2 id="二高级定时器的硬件架构">二、高级定时器的硬件架构</h2>
<p>在深入各个功能模块之前，让我们先从整体上理解高级定时器的硬件结构。很多人学定时器学不懂，就是因为一上来就钻进寄存器的细节里，没有建立起整体的认知。</p>
<p><img alt="STM32 高级定时器整体架构" loading="lazy" src="/images/stm32-timer-architecture.svg"></p>
<h3 id="21-时钟源一切的起点">2.1 时钟源：一切的起点</h3>
<p>定时器的核心是一个计数器，而计数器需要时钟信号才能运转。高级定时器的时钟源可以来自多个地方：</p>
<ol>
<li>
<p><strong>内部时钟（CK_INT）</strong>：最常用的时钟源，来自 APB 总线时钟。需要注意的是，当 APB 预分频器不为 1 时，定时器时钟是 APB 时钟的 2 倍。比如 APB1 时钟是 42MHz，那么 TIM2~TIM5 的时钟就是 84MHz。</p>
</li>
<li>
<p><strong>外部时钟模式 1</strong>：通过外部引脚（TI1、TI2）输入的时钟信号，可以是编码器、外部振荡器等。</p>
</li>
<li>
<p><strong>外部时钟模式 2</strong>：通过 ETR 引脚输入的外部时钟，支持更灵活的触发配置。</p>
</li>
<li>
<p><strong>内部触发连接（ITR）</strong>：一个定时器可以作为另一个定时器的时钟源，实现定时器级联。</p>
</li>
</ol>
<p>对于大多数应用，我们使用内部时钟就足够了。</p>
<h3 id="22-时基单元定时器的心脏">2.2 时基单元：定时器的心脏</h3>
<p>时基单元是定时器最核心的部分，它由三个关键寄存器组成：</p>
<ul>
<li><strong>预分频器（PSC）</strong>：对输入时钟进行分频，16 位，可以设置 0~65535 的分频系数</li>
<li><strong>计数器（CNT）</strong>：实际的计数器，高级定时器是 16 位的</li>
<li><strong>自动重装载寄存器（ARR）</strong>：计数器的上限值，当 CNT 达到 ARR 时，发生溢出事件</li>
</ul>
<p>这三个寄存器的组合决定了定时器的溢出频率：</p>
<pre tabindex="0"><code>定时器溢出频率 = 定时器时钟 / ((PSC + 1) * (ARR + 1))
</code></pre><p>举个例子，如果定时器时钟是 168MHz，我们设置 PSC=167，ARR=999，那么溢出频率就是：</p>
<pre tabindex="0"><code>168,000,000 / ((167+1) * (999+1)) = 168,000,000 / (168 * 1000) = 1000 Hz
</code></pre><p>也就是每 1ms 溢出一次。这个公式非常重要，几乎所有定时器配置都会用到。</p>
<h3 id="23-捕获比较通道功能的扩展">2.3 捕获/比较通道：功能的扩展</h3>
<p>高级定时器有 4 个独立的捕获/比较通道（CH1~CH4）。每个通道都有自己的捕获/比较寄存器（CCR），可以独立配置为输入捕获模式或输出比较模式。</p>
<p>这就是定时器设计的巧妙之处——同一个硬件通道，既可以用来接收外部信号（输入捕获），也可以用来输出波形（输出比较）。方向完全由软件配置决定。</p>
<p>每个通道还包含：</p>
<ul>
<li>输入滤波器：消除输入信号的抖动</li>
<li>输入多路复用：选择信号来源</li>
<li>输出控制：决定输出极性、使能等</li>
</ul>
<p>理解捕获/比较通道的工作原理，是掌握定时器高级功能的关键。</p>
<h3 id="24-高级功能模块">2.4 高级功能模块</h3>
<p>除了上述通用部分，高级定时器还增加了专属的功能模块：</p>
<ul>
<li><strong>死区时间发生器</strong>：为互补输出插入死区，防止桥臂直通</li>
<li><strong>刹车电路</strong>：紧急停止功能，保护功率电路</li>
<li><strong>互补输出通道</strong>：CH1N~CH3N，与 CH1~CH3 形成互补对</li>
<li><strong>重复计数器</strong>：控制更新事件的产生频率</li>
</ul>
<p>这些模块共同构成了完整的电机控制硬件加速方案。</p>
<p>（第一部分完，约2200字）</p>
<h2 id="三pwm-生成原理与配置">三、PWM 生成原理与配置</h2>
<p>PWM（脉冲宽度调制）是定时器最常用的功能之一，也是电机控制的基础。通过改变 PWM 的占空比，我们可以精确控制电机的转速；通过改变 PWM 的相位和时序，我们可以实现各种复杂的电机换向逻辑。</p>
<h3 id="31-pwm-的基本原理">3.1 PWM 的基本原理</h3>
<p>PWM 本质上是一种周期性的方波信号，它有两个关键参数：</p>
<ul>
<li><strong>周期</strong>：信号重复的频率，由 ARR 寄存器决定</li>
<li><strong>占空比</strong>：高电平时间占整个周期的比例，由 CCR 寄存器决定</li>
</ul>
<p>STM32 定时器支持多种 PWM 模式，其中最常用的是 PWM 模式 1 和 PWM 模式 2：</p>
<table>
<thead>
<tr>
<th>模式</th>
<th>向上计数时行为</th>
<th>向下计数时行为</th>
</tr>
</thead>
<tbody>
<tr>
<td>PWM 模式 1</td>
<td>CNT &lt; CCR 时有效，否则无效</td>
<td>CNT &gt; CCR 时无效，否则有效</td>
</tr>
<tr>
<td>PWM 模式 2</td>
<td>CNT &lt; CCR 时无效，否则有效</td>
<td>CNT &gt; CCR 时有效，否则无效</td>
</tr>
</tbody>
</table>
<p>所谓&quot;有效&quot;和&quot;无效&quot;，还取决于输出极性的设置。如果设置为高电平有效，那么&quot;有效&quot;就对应高电平，&ldquo;无效&quot;对应低电平。</p>
<h3 id="32-边沿对齐-vs-中央对齐">3.2 边沿对齐 vs 中央对齐</h3>
<p>这是很多初学者容易忽视但对电机控制非常重要的一个概念。</p>
<p><strong>边沿对齐模式</strong>（Edge-aligned）：计数器只从 0 向上计数到 ARR，然后重置为 0 重新开始。PWM 的变化只发生在计数器的一个方向上。</p>
<p><strong>中央对齐模式</strong>（Center-aligned）：计数器先从 0 向上计数到 ARR，然后再从 ARR 向下计数到 0。这样每个周期内计数器会经过两次 CCR 值，PWM 波形是对称的。</p>
<p>为什么中央对齐模式对电机控制很重要？因为：</p>
<ol>
<li>对称的 PWM 波形可以降低电机的电流纹波</li>
<li>减少电机运行时的噪音和振动</li>
<li>对于 FOC 控制，中央对齐模式可以让电流采样更精确</li>
</ol>
<p>当然，中央对齐模式也有代价：在相同的 ARR 设置下，PWM 频率会是边沿对齐模式的一半，因为计数器需要走完一个来回才算一个周期。</p>
<h3 id="33-pwm-配置代码示例">3.3 PWM 配置代码示例</h3>
<p>让我们来看一个具体的配置示例。假设我们使用 STM32F4 的 TIM1，时钟频率是 168MHz，想要生成 20kHz 的 PWM 用于电机控制：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 计算 PWM 周期：1/20kHz = 50us
</span></span></span><span class="line"><span class="cl"><span class="c1">// ARR = 定时器时钟 / PWM 频率 - 1 = 168MHz / 20kHz - 1 = 8399
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="cp">#define PWM_PERIOD    8399
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PWM_DEAD_TIME 100  </span><span class="c1">// 死区时间，单位：定时器时钟周期
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">TIM1_PWM_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseInitTypeDef</span> <span class="n">TIM_TimeBaseStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitTypeDef</span> <span class="n">TIM_OCInitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_BDTRInitTypeDef</span> <span class="n">TIM_BDTRInitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">RCC_APB2PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_APB2Periph_TIM1</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">RCC_AHB1PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_AHB1Periph_GPIOA</span> <span class="o">|</span> <span class="n">RCC_AHB1Periph_GPIOB</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 GPIO 为复用功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// PA8: TIM1_CH1, PA9: TIM1_CH2, PA10: TIM1_CH3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Pin</span> <span class="o">=</span> <span class="n">GPIO_Pin_8</span> <span class="o">|</span> <span class="n">GPIO_Pin_9</span> <span class="o">|</span> <span class="n">GPIO_Pin_10</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Mode</span> <span class="o">=</span> <span class="n">GPIO_Mode_AF</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Speed</span> <span class="o">=</span> <span class="n">GPIO_Speed_100MHz</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_OType</span> <span class="o">=</span> <span class="n">GPIO_OType_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_PuPd</span> <span class="o">=</span> <span class="n">GPIO_PuPd_NOPULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// PB13: TIM1_CH1N, PB14: TIM1_CH2N, PB15: TIM1_CH3N
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Pin</span> <span class="o">=</span> <span class="n">GPIO_Pin_13</span> <span class="o">|</span> <span class="n">GPIO_Pin_14</span> <span class="o">|</span> <span class="n">GPIO_Pin_15</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_Init</span><span class="p">(</span><span class="n">GPIOB</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 映射复用功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource8</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource9</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource10</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOB</span><span class="p">,</span> <span class="n">GPIO_PinSource13</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOB</span><span class="p">,</span> <span class="n">GPIO_PinSource14</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOB</span><span class="p">,</span> <span class="n">GPIO_PinSource15</span><span class="p">,</span> <span class="n">GPIO_AF_TIM1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置时基单元
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Prescaler</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>  <span class="c1">// 不分频
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_CounterMode</span> <span class="o">=</span> <span class="n">TIM_CounterMode_CenterAligned1</span><span class="p">;</span>  <span class="c1">// 中央对齐
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Period</span> <span class="o">=</span> <span class="n">PWM_PERIOD</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_ClockDivision</span> <span class="o">=</span> <span class="n">TIM_CKD_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_RepetitionCounter</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_TimeBaseInit</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_TimeBaseStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 配置输出比较模式为 PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OCMode</span> <span class="o">=</span> <span class="n">TIM_OCMode_PWM1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OutputState</span> <span class="o">=</span> <span class="n">TIM_OutputState_Enable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OutputNState</span> <span class="o">=</span> <span class="n">TIM_OutputNState_Enable</span><span class="p">;</span>  <span class="c1">// 使能互补输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_Pulse</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>  <span class="c1">// 初始占空比为 0
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OCPolarity</span> <span class="o">=</span> <span class="n">TIM_OCPolarity_High</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OCNPolarity</span> <span class="o">=</span> <span class="n">TIM_OCNPolarity_High</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OCIdleState</span> <span class="o">=</span> <span class="n">TIM_OCIdleState_Reset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_OCInitStruct</span><span class="p">.</span><span class="n">TIM_OCNIdleState</span> <span class="o">=</span> <span class="n">TIM_OCNIdleState_Reset</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_OC1Init</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_OCInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_OC2Init</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_OCInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_OC3Init</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_OCInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 使能 CCR 预装载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_OC1PreloadConfig</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">TIM_OCPreload_Enable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_OC2PreloadConfig</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">TIM_OCPreload_Enable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_OC3PreloadConfig</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">TIM_OCPreload_Enable</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 配置死区时间和刹车功能
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_OSSRState</span> <span class="o">=</span> <span class="n">TIM_OSSRState_Enable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_OSSIState</span> <span class="o">=</span> <span class="n">TIM_OSSIState_Enable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_LOCKLevel</span> <span class="o">=</span> <span class="n">TIM_LOCKLevel_OFF</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_DeadTime</span> <span class="o">=</span> <span class="n">PWM_DEAD_TIME</span><span class="p">;</span>  <span class="c1">// 死区时间
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_Break</span> <span class="o">=</span> <span class="n">TIM_Break_Disable</span><span class="p">;</span>  <span class="c1">// 暂时禁用刹车
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_BreakPolarity</span> <span class="o">=</span> <span class="n">TIM_BreakPolarity_High</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_BDTRInitStruct</span><span class="p">.</span><span class="n">TIM_AutomaticOutput</span> <span class="o">=</span> <span class="n">TIM_AutomaticOutput_Enable</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_BDTRConfig</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_BDTRInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 使能 ARR 预装载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_ARRPreloadConfig</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 启动定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_Cmd</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 7. 使能主输出（高级定时器必须设置这一步！）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_CtrlPWMOutputs</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这里有一个非常重要的细节：高级定时器必须调用 <code>TIM_CtrlPWMOutputs()</code> 使能主输出，否则 PWM 信号不会出现在引脚上！这是很多新手踩过的坑——代码看起来都对，但就是没有波形输出。</p>
<p>配置完成后，我们就可以通过修改 CCR 寄存器的值来改变 PWM 占空比了：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="c1">// 设置通道 1 占空比为 50%
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 设置通道 2 占空比为 25%
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span> <span class="o">/</span> <span class="mi">4</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 设置通道 3 占空比为 75%
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span> <span class="o">*</span> <span class="mi">3</span> <span class="o">/</span> <span class="mi">4</span><span class="p">);</span>
</span></span></code></pre></div><h2 id="四输入捕获精确测量脉冲信号">四、输入捕获：精确测量脉冲信号</h2>
<p>除了输出波形，定时器的另一个重要功能是输入捕获——精确测量外部输入信号的频率、占空比、脉冲宽度等参数。</p>
<h3 id="41-输入捕获的工作原理">4.1 输入捕获的工作原理</h3>
<p>输入捕获的基本思路很简单：当外部引脚上发生指定的跳变沿（上升沿或下降沿）时，硬件会自动将当前计数器的值锁存到 CCR 寄存器中，同时可以触发中断。</p>
<p>通过两次捕获之间的计数器差值，我们就能计算出：</p>
<ul>
<li>脉冲宽度：从上升沿到下降沿的时间</li>
<li>信号周期：两次上升沿之间的时间</li>
<li>信号频率：周期的倒数</li>
</ul>
<h3 id="42-输入滤波器消除抖动">4.2 输入滤波器：消除抖动</h3>
<p>机械开关、霍尔传感器这类信号通常会有抖动，如果直接捕获可能会产生误触发。STM32 定时器的每个输入通道都带有数字滤波器，可以配置不同的滤波参数。</p>
<p>滤波器的工作原理是：只有当连续 N 个采样周期都检测到相同的电平时，才认为这是一个有效的电平变化。采样频率和 N 值都可以配置，滤波时间 = N / 采样频率。</p>
<h3 id="43-输入捕获代码示例">4.3 输入捕获代码示例</h3>
<p>假设我们要测量霍尔传感器的脉冲信号频率：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">capture_value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint32_t</span> <span class="n">frequency</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint8_t</span> <span class="n">capture_done</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">TIM2_IC_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseInitTypeDef</span> <span class="n">TIM_TimeBaseStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitTypeDef</span> <span class="n">TIM_ICInitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">NVIC_InitTypeDef</span> <span class="n">NVIC_InitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">RCC_APB1PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_APB1Periph_TIM2</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">RCC_AHB1PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_AHB1Periph_GPIOA</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA0 为复用功能（TIM2_CH1）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Pin</span> <span class="o">=</span> <span class="n">GPIO_Pin_0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Mode</span> <span class="o">=</span> <span class="n">GPIO_Mode_AF</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Speed</span> <span class="o">=</span> <span class="n">GPIO_Speed_100MHz</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_OType</span> <span class="o">=</span> <span class="n">GPIO_OType_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_PuPd</span> <span class="o">=</span> <span class="n">GPIO_PuPd_UP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource0</span><span class="p">,</span> <span class="n">GPIO_AF_TIM2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置时基：1MHz 计数频率
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Prescaler</span> <span class="o">=</span> <span class="mi">83</span><span class="p">;</span>  <span class="c1">// 84MHz / (83+1) = 1MHz
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_CounterMode</span> <span class="o">=</span> <span class="n">TIM_CounterMode_Up</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Period</span> <span class="o">=</span> <span class="mh">0xFFFFFFFF</span><span class="p">;</span>  <span class="c1">// 32位自动重装载
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_ClockDivision</span> <span class="o">=</span> <span class="n">TIM_CKD_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_TimeBaseInit</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_TimeBaseStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 配置输入捕获
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_Channel</span> <span class="o">=</span> <span class="n">TIM_Channel_1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICPolarity</span> <span class="o">=</span> <span class="n">TIM_ICPolarity_Rising</span><span class="p">;</span>  <span class="c1">// 上升沿捕获
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICSelection</span> <span class="o">=</span> <span class="n">TIM_ICSelection_DirectTI</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICPrescaler</span> <span class="o">=</span> <span class="n">TIM_ICPSC_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICFilter</span> <span class="o">=</span> <span class="mh">0x8</span><span class="p">;</span>  <span class="c1">// 开启滤波
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_ICInit</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_ICInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 配置中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">NVIC_InitStruct</span><span class="p">.</span><span class="n">NVIC_IRQChannel</span> <span class="o">=</span> <span class="n">TIM2_IRQn</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">NVIC_InitStruct</span><span class="p">.</span><span class="n">NVIC_IRQChannelPreemptionPriority</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">NVIC_InitStruct</span><span class="p">.</span><span class="n">NVIC_IRQChannelSubPriority</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">NVIC_InitStruct</span><span class="p">.</span><span class="n">NVIC_IRQChannelCmd</span> <span class="o">=</span> <span class="n">ENABLE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">NVIC_Init</span><span class="p">(</span><span class="o">&amp;</span><span class="n">NVIC_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 使能捕获中断
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_ITConfig</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="n">TIM_IT_CC1</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 启动定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_Cmd</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 中断服务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">TIM2_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint32_t</span> <span class="n">last_capture</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint32_t</span> <span class="n">current_capture</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">TIM_GetITStatus</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="n">TIM_IT_CC1</span><span class="p">)</span> <span class="o">!=</span> <span class="n">RESET</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">TIM_ClearITPendingBit</span><span class="p">(</span><span class="n">TIM2</span><span class="p">,</span> <span class="n">TIM_IT_CC1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">current_capture</span> <span class="o">=</span> <span class="nf">TIM_GetCapture1</span><span class="p">(</span><span class="n">TIM2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">last_capture</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="c1">// 计算周期（注意处理计数器溢出）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">if</span> <span class="p">(</span><span class="n">current_capture</span> <span class="o">&gt;</span> <span class="n">last_capture</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">capture_value</span> <span class="o">=</span> <span class="n">current_capture</span> <span class="o">-</span> <span class="n">last_capture</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            <span class="k">else</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">capture_value</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0xFFFFFFFF</span> <span class="o">-</span> <span class="n">last_capture</span><span class="p">)</span> <span class="o">+</span> <span class="n">current_capture</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="p">}</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">            <span class="c1">// 计算频率：1MHz / 周期 = 频率（Hz）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="n">frequency</span> <span class="o">=</span> <span class="mi">1000000</span> <span class="o">/</span> <span class="n">capture_value</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            <span class="n">capture_done</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="n">last_capture</span> <span class="o">=</span> <span class="n">current_capture</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>这样我们就能精确测量外部信号的频率了。对于占空比测量，可以配置为交替捕获上升沿和下降沿，计算高电平和低电平的时间比例。</p>
<p>（第二部分完，约2300字）</p>
<h2 id="五编码器接口硬件正交解码">五、编码器接口：硬件正交解码</h2>
<p>编码器是电机控制系统中获取位置和速度信息的关键传感器。增量式正交编码器输出两路相位差 90 度的脉冲信号（通常称为 A 相和 B 相），通过这两路信号的相对相位关系，我们不仅可以计算旋转的角度，还能判断旋转方向。</p>
<h3 id="51-为什么要用硬件编码器接口">5.1 为什么要用硬件编码器接口</h3>
<p>很多初学者一开始会想用外部中断来读取编码器信号——A 相一个中断，B 相一个中断，在中断里判断相位关系，累加计数。这种方法在低速时还能工作，但当电机转速提高后，问题就来了：</p>
<ol>
<li><strong>中断频率太高</strong>：一个 1000 线的编码器，电机每秒转 100 圈，就会产生 400,000 次中断（4 倍频），CPU 根本处理不过来</li>
<li><strong>丢失脉冲</strong>：如果中断处理不及时，就会丢失脉冲，导致位置累积误差</li>
<li><strong>占用 CPU 资源</strong>：大量的中断会占用 CPU 时间，影响其他实时任务</li>
</ol>
<p>STM32 定时器的编码器接口模式就是为了解决这个问题设计的。硬件自动完成正交解码和计数，完全不需要 CPU 干预，CPU 只需要在需要的时候读取计数器的值就可以了。</p>
<h3 id="52-编码器接口的工作原理">5.2 编码器接口的工作原理</h3>
<p>定时器的编码器接口模式使用 CH1 和 CH2 两个通道作为输入。当检测到 A 相或 B 相的跳变沿时，硬件会自动根据相位关系对计数器进行加 1 或减 1 操作。</p>
<p>根据计数方式的不同，编码器接口分为三种模式：</p>
<table>
<thead>
<tr>
<th>模式</th>
<th>计数时机</th>
<th>分辨率</th>
</tr>
</thead>
<tbody>
<tr>
<td>TI1 仅计数</td>
<td>仅在 TI1 边沿计数</td>
<td>1 倍频</td>
</tr>
<tr>
<td>TI2 仅计数</td>
<td>仅在 TI2 边沿计数</td>
<td>1 倍频</td>
</tr>
<tr>
<td>TI1 和 TI2 都计数</td>
<td>两路信号的边沿都计数</td>
<td>4 倍频</td>
</tr>
</tbody>
</table>
<p>实际应用中，我们几乎总是使用 4 倍频模式，这样可以获得最高的位置分辨率。</p>
<h3 id="53-编码器接口配置代码">5.3 编码器接口配置代码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#define ENCODER_PPR  1000  </span><span class="c1">// 编码器每转脉冲数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kt">void</span> <span class="nf">TIM3_Encoder_Init</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitTypeDef</span> <span class="n">GPIO_InitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseInitTypeDef</span> <span class="n">TIM_TimeBaseStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitTypeDef</span> <span class="n">TIM_ICInitStruct</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. 使能时钟
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">RCC_APB1PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_APB1Periph_TIM3</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">RCC_AHB1PeriphClockCmd</span><span class="p">(</span><span class="n">RCC_AHB1Periph_GPIOA</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. 配置 PA6(TIM3_CH1) 和 PA7(TIM3_CH2)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Pin</span> <span class="o">=</span> <span class="n">GPIO_Pin_6</span> <span class="o">|</span> <span class="n">GPIO_Pin_7</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Mode</span> <span class="o">=</span> <span class="n">GPIO_Mode_AF</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_Speed</span> <span class="o">=</span> <span class="n">GPIO_Speed_100MHz</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_OType</span> <span class="o">=</span> <span class="n">GPIO_OType_PP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">GPIO_InitStruct</span><span class="p">.</span><span class="n">GPIO_PuPd</span> <span class="o">=</span> <span class="n">GPIO_PuPd_UP</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_Init</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">GPIO_InitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource6</span><span class="p">,</span> <span class="n">GPIO_AF_TIM3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">GPIO_PinAFConfig</span><span class="p">(</span><span class="n">GPIOA</span><span class="p">,</span> <span class="n">GPIO_PinSource7</span><span class="p">,</span> <span class="n">GPIO_AF_TIM3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. 配置时基
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Prescaler</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_CounterMode</span> <span class="o">=</span> <span class="n">TIM_CounterMode_Up</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_Period</span> <span class="o">=</span> <span class="mh">0xFFFF</span><span class="p">;</span>  <span class="c1">// 16位最大值
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_TimeBaseStruct</span><span class="p">.</span><span class="n">TIM_ClockDivision</span> <span class="o">=</span> <span class="n">TIM_CKD_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_TimeBaseInit</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_TimeBaseStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. 配置编码器接口模式（4倍频）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_EncoderInterfaceConfig</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> 
</span></span><span class="line"><span class="cl">                               <span class="n">TIM_EncoderMode_TI12</span><span class="p">,</span>  <span class="c1">// 两路都计数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>                               <span class="n">TIM_ICPolarity_Rising</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                               <span class="n">TIM_ICPolarity_Rising</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 5. 配置输入滤波
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_Channel</span> <span class="o">=</span> <span class="n">TIM_Channel_1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICPolarity</span> <span class="o">=</span> <span class="n">TIM_ICPolarity_Rising</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICSelection</span> <span class="o">=</span> <span class="n">TIM_ICSelection_DirectTI</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICPrescaler</span> <span class="o">=</span> <span class="n">TIM_ICPSC_DIV1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_ICFilter</span> <span class="o">=</span> <span class="mh">0x6</span><span class="p">;</span>  <span class="c1">// 滤波
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_ICInit</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_ICInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">TIM_ICInitStruct</span><span class="p">.</span><span class="n">TIM_Channel</span> <span class="o">=</span> <span class="n">TIM_Channel_2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_ICInit</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">TIM_ICInitStruct</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 6. 启动定时器
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_Cmd</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> <span class="n">ENABLE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 初始计数器设为中间值，防止正负溢出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_SetCounter</span><span class="p">(</span><span class="n">TIM3</span><span class="p">,</span> <span class="mh">0x8000</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 读取编码器位置
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">int32_t</span> <span class="nf">Encoder_GetPosition</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">uint16_t</span> <span class="n">last_counter</span> <span class="o">=</span> <span class="mh">0x8000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">int32_t</span> <span class="n">position</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint16_t</span> <span class="n">current_counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int16_t</span> <span class="n">delta</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">current_counter</span> <span class="o">=</span> <span class="nf">TIM_GetCounter</span><span class="p">(</span><span class="n">TIM3</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">delta</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int16_t</span><span class="p">)(</span><span class="n">current_counter</span> <span class="o">-</span> <span class="n">last_counter</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_counter</span> <span class="o">=</span> <span class="n">current_counter</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">position</span> <span class="o">+=</span> <span class="n">delta</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 读取电机速度（单位：RPM）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">float</span> <span class="nf">Encoder_GetSpeed</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="n">sample_time_ms</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">static</span> <span class="kt">int32_t</span> <span class="n">last_position</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int32_t</span> <span class="n">current_position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">int32_t</span> <span class="n">delta_position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kt">float</span> <span class="n">speed_rpm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">current_position</span> <span class="o">=</span> <span class="nf">Encoder_GetPosition</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="n">delta_position</span> <span class="o">=</span> <span class="n">current_position</span> <span class="o">-</span> <span class="n">last_position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">last_position</span> <span class="o">=</span> <span class="n">current_position</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 4倍频下，每转脉冲数 = PPR * 4
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="c1">// 转速 = (脉冲数 / (PPR * 4)) * (60000 / 采样时间)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">speed_rpm</span> <span class="o">=</span> <span class="p">(</span><span class="kt">float</span><span class="p">)</span><span class="n">delta_position</span> <span class="o">/</span> <span class="p">(</span><span class="n">ENCODER_PPR</span> <span class="o">*</span> <span class="mf">4.0f</span><span class="p">)</span> <span class="o">*</span> 
</span></span><span class="line"><span class="cl">                <span class="p">(</span><span class="mf">60000.0f</span> <span class="o">/</span> <span class="n">sample_time_ms</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">speed_rpm</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>有了硬件编码器接口，即使电机转速达到几万转，也不会丢失一个脉冲！</p>
<h2 id="六bldc-电机六步换向完整实现">六、BLDC 电机六步换向完整实现</h2>
<p>现在我们把前面学到的知识整合起来，实现一个完整的 BLDC 无刷电机六步换向控制系统。</p>
<h3 id="61-六步换向的基本原理">6.1 六步换向的基本原理</h3>
<p>BLDC 电机的定子有三相绕组，转子是永磁体。为了让电机持续转动，我们需要按照特定的顺序给三相绕组通电，产生一个旋转的磁场，拖动永磁体转子转动。</p>
<p>六步换向就是将一个电周期分为 6 个状态，每个状态导通不同的桥臂。通过霍尔传感器检测转子位置，在正确的时刻切换到下一个状态。</p>
<h3 id="62-完整的驱动代码">6.2 完整的驱动代码</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;stm32f4xx.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define PWM_PERIOD    8399
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PWM_MAX       8000
</span></span></span><span class="line"><span class="cl"><span class="cp">#define PWM_MIN       0
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// 霍尔传感器状态对应的换向表
</span></span></span><span class="line"><span class="cl"><span class="c1">// 索引 = (HALL_C &lt;&lt; 2) | (HALL_B &lt;&lt; 1) | HALL_A
</span></span></span><span class="line"><span class="cl"><span class="c1">// 值：对应需要导通的桥臂（AH, BH, CH, AL, BL, CL）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="k">const</span> <span class="kt">uint8_t</span> <span class="n">commutation_table</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="mi">0</span><span class="p">,</span>      <span class="c1">// 000: 无效状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x09</span><span class="p">,</span>   <span class="c1">// 001: AH + CL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x12</span><span class="p">,</span>   <span class="c1">// 010: BH + AL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x11</span><span class="p">,</span>   <span class="c1">// 011: BH + CL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x24</span><span class="p">,</span>   <span class="c1">// 100: CH + AL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x21</span><span class="p">,</span>   <span class="c1">// 101: AH + BL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mh">0x22</span><span class="p">,</span>   <span class="c1">// 110: CH + BL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="mi">0</span>       <span class="c1">// 111: 无效状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">volatile</span> <span class="kt">uint16_t</span> <span class="n">pwm_duty</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 设置 PWM 输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">Set_PWM_Output</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 先关闭所有输出
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// 根据换向表设置相应的 PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">switch</span><span class="p">(</span><span class="n">state</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x09</span><span class="o">:</span>  <span class="c1">// AH + CL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// AH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// CL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x12</span><span class="o">:</span>  <span class="c1">// BH + AL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// BH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// AL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x11</span><span class="o">:</span>  <span class="c1">// BH + CL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// BH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// CL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x24</span><span class="o">:</span>  <span class="c1">// CH + AL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// CH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// AL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x21</span><span class="o">:</span>  <span class="c1">// AH + BL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// AH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// BL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">            
</span></span><span class="line"><span class="cl">        <span class="k">case</span> <span class="mh">0x22</span><span class="o">:</span>  <span class="c1">// CH + BL
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">pwm_duty</span><span class="p">);</span>  <span class="c1">// CH PWM
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="n">PWM_PERIOD</span><span class="p">);</span> <span class="c1">// BL 全开
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 读取霍尔传感器状态
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">uint8_t</span> <span class="nf">Read_Hall_Sensors</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">hall_a</span> <span class="o">=</span> <span class="nf">GPIO_ReadInputDataBit</span><span class="p">(</span><span class="n">GPIOC</span><span class="p">,</span> <span class="n">GPIO_Pin_0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">hall_b</span> <span class="o">=</span> <span class="nf">GPIO_ReadInputDataBit</span><span class="p">(</span><span class="n">GPIOC</span><span class="p">,</span> <span class="n">GPIO_Pin_1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">hall_c</span> <span class="o">=</span> <span class="nf">GPIO_ReadInputDataBit</span><span class="p">(</span><span class="n">GPIOC</span><span class="p">,</span> <span class="n">GPIO_Pin_2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="n">hall_c</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span> <span class="o">|</span> <span class="p">(</span><span class="n">hall_b</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="o">|</span> <span class="n">hall_a</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 换向中断服务函数
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">EXTI0_IRQHandler</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nf">EXTI_GetITStatus</span><span class="p">(</span><span class="n">EXTI_Line0</span><span class="p">)</span> <span class="o">!=</span> <span class="n">RESET</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl">        <span class="nf">EXTI_GetITStatus</span><span class="p">(</span><span class="n">EXTI_Line1</span><span class="p">)</span> <span class="o">!=</span> <span class="n">RESET</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl">        <span class="nf">EXTI_GetITStatus</span><span class="p">(</span><span class="n">EXTI_Line2</span><span class="p">)</span> <span class="o">!=</span> <span class="n">RESET</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">EXTI_ClearITPendingBit</span><span class="p">(</span><span class="n">EXTI_Line0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">EXTI_ClearITPendingBit</span><span class="p">(</span><span class="n">EXTI_Line1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nf">EXTI_ClearITPendingBit</span><span class="p">(</span><span class="n">EXTI_Line2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="c1">// 读取霍尔状态并执行换向
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="kt">uint8_t</span> <span class="n">hall_state</span> <span class="o">=</span> <span class="nf">Read_Hall_Sensors</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="kt">uint8_t</span> <span class="n">pwm_state</span> <span class="o">=</span> <span class="n">commutation_table</span><span class="p">[</span><span class="n">hall_state</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">        
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">pwm_state</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nf">Set_PWM_Output</span><span class="p">(</span><span class="n">pwm_state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 启动电机
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">Motor_Start</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">hall_state</span> <span class="o">=</span> <span class="nf">Read_Hall_Sensors</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="kt">uint8_t</span> <span class="n">pwm_state</span> <span class="o">=</span> <span class="n">commutation_table</span><span class="p">[</span><span class="n">hall_state</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">pwm_state</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nf">Set_PWM_Output</span><span class="p">(</span><span class="n">pwm_state</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 停止电机
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">Motor_Stop</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_SetCompare1</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_SetCompare2</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nf">TIM_SetCompare3</span><span class="p">(</span><span class="n">TIM1</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 设置转速（0-100%）
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="nf">Motor_SetSpeed</span><span class="p">(</span><span class="kt">uint8_t</span> <span class="n">speed_percent</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">speed_percent</span> <span class="o">&gt;</span> <span class="mi">100</span><span class="p">)</span> <span class="n">speed_percent</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="n">pwm_duty</span> <span class="o">=</span> <span class="p">(</span><span class="kt">uint16_t</span><span class="p">)((</span><span class="kt">uint32_t</span><span class="p">)</span><span class="n">speed_percent</span> <span class="o">*</span> <span class="n">PWM_MAX</span> <span class="o">/</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3 id="63-关键注意事项">6.3 关键注意事项</h3>
<ol>
<li>
<p><strong>死区时间的重要性</strong>：绝对不能让同一相的上下桥臂同时导通，否则会造成电源短路，烧毁 MOS管和电机。死区时间就是在切换桥臂时插入的一小段延迟，确保一个桥臂完全关断后，另一个桥臂才导通。</p>
</li>
<li>
<p><strong>霍尔传感器的安装相位</strong>：霍尔传感器的安装位置必须正确，否则换向表就不对。如果电机抖动或不转，首先检查霍尔信号的相位顺序。</p>
</li>
<li>
<p><strong>启动策略</strong>：BLDC 电机静止时，霍尔传感器能给出初始位置，但如果是无感驱动，就需要特殊的启动算法。</p>
</li>
<li>
<p><strong>电流保护</strong>：实际产品中必须添加过流保护，可以通过 ADC 采样母线电流，超过阈值时触发刹车功能。</p>
</li>
</ol>
<h2 id="七常见问题与调试技巧">七、常见问题与调试技巧</h2>
<h3 id="71-pwm-没有输出">7.1 PWM 没有输出？</h3>
<p>这是最常见的问题，检查清单：</p>
<ul>
<li>是否调用了 <code>TIM_Cmd()</code> 启动定时器？</li>
<li>高级定时器是否调用了 <code>TIM_CtrlPWMOutputs()</code>？</li>
<li>GPIO 是否正确配置为复用功能？</li>
<li>是否正确配置了复用功能映射？</li>
<li>是否设置了 CCR 寄存器的值（默认为 0）？</li>
</ul>
<h3 id="72-电机抖动但不转">7.2 电机抖动但不转？</h3>
<ul>
<li>检查霍尔传感器接线是否正确</li>
<li>检查换向表是否正确（可能需要尝试不同的组合）</li>
<li>检查三相绕组的相序是否正确</li>
<li>检查 PWM 频率是否合适（太低会产生 audible noise）</li>
</ul>
<h3 id="73-电机反转">7.3 电机反转？</h3>
<ul>
<li>交换任意两相的接线</li>
<li>或者修改换向表的顺序</li>
<li>编码器模式下可以交换 A/B 相</li>
</ul>
<h3 id="74-mos-管发热严重">7.4 MOS 管发热严重？</h3>
<ul>
<li>检查死区时间是否足够</li>
<li>检查 MOS 管驱动电压是否足够</li>
<li>检查 PWM 频率是否过高</li>
<li>检查 MOS 管选型是否合适</li>
</ul>
<h2 id="八进阶方向">八、进阶方向</h2>
<p>掌握了基本的定时器功能和六步换向之后，你可以继续探索更高级的电机控制技术：</p>
<ol>
<li>
<p><strong>FOC（磁场定向控制）</strong>：通过坐标变换实现对电机定子电流的矢量控制，具有更高的效率和更低的噪音，是高性能电机驱动的标准方案。</p>
</li>
<li>
<p><strong>无感 BLDC 驱动</strong>：不使用霍尔传感器，通过反电动势过零检测来判断转子位置，降低系统成本。</p>
</li>
<li>
<p><strong>位置环和速度环控制</strong>：结合 PID 算法实现精确的位置和速度闭环控制。</p>
</li>
<li>
<p><strong>高级 PWM 技术</strong>：如 SVPWM（空间矢量 PWM）、DPWM 等，进一步提高驱动效率。</p>
</li>
</ol>
<h2 id="总结">总结</h2>
<p>STM32 的定时器系统虽然看起来复杂，但只要理解了它的模块化设计思想，就能逐步掌握每一个功能。从最基本的定时中断，到 PWM 输出、输入捕获、编码器接口，再到高级定时器的互补输出和死区插入，每一个功能都是为了解决实际的工程问题。</p>
<p>本文我们从定时器的基本架构开始，深入讲解了 PWM 生成原理、输入捕获、编码器接口等核心功能，并通过完整的代码示例展示了如何使用高级定时器实现 BLDC 电机的六步换向控制。希望这些内容能帮助你真正掌握 STM32 定时器，从&quot;会用&quot;到&quot;用好&rdquo;。</p>
<p>嵌入式开发就是这样，看似复杂的外设，只要你深入理解了它的设计思路和工作原理，就能发挥出它的最大潜力。而定时器，正是你从单片机入门走向嵌入式进阶的第一道门槛，也是最重要的一块基石。</p>
<p>（全文完，约7200字）</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
