STM32 定时器(主从模式实现 3路PWM相位差)


🔧 ​一、硬件配置(以TIM1为主,TIM2/TIM3/TIM4为从)​

1. 主定时器(TIM1)配置
cpp 复制代码
#include "stm32f10x.h"

// 全局配置
#define PWM_PERIOD 999      // ARR值(72MHz/(999+1)=72kHz)
#define DUTY_CYCLE 500      // 统一占空比50%(固定值)
#define PHASE_OFFSET_1 0    // TIM2相位偏移(0°)
#define PHASE_OFFSET_2 333  // TIM3相位偏移(120°)
#define PHASE_OFFSET_3 666  // TIM4相位偏移(240°)

// 主定时器TIM1配置(触发源)
void TIM1_MasterConfig(void) 
{
    // 1. 开启时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA |     RCC_APB2Periph_AFIO, ENABLE);
    
    // 2. 配置PA9为复用推挽输出(TIM1_CH2)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 初始化TIM1时基单元
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    TIM_BaseStruct.TIM_Period =DUTY_CYCLE;
    TIM_BaseStruct.TIM_Prescaler = 0;
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM1, &TIM_BaseStruct);

    // 4. 配置TIM1通道2为PWM模式(触发源)
    TIM_OCInitTypeDef TIM_OCStruct;
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCStruct.TIM_Pulse = 500;  // 初始占空比50%
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC2Init(TIM1, &TIM_OCStruct);

    // 5. 设置TIM1的触发输出(OC2Ref作为触发源)
    TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_OC2Ref); // 关键配置
    TIM_CtrlPWMOutputs(TIM1, ENABLE);  // 高级定时器需使能主输出
}
2. 从定时器配置(TIM2/TIM3/TIM4)​
cpp 复制代码
// 从定时器TIM2配置(0°相位)
void TIM2_SlaveConfig(void) 
{
    // 1. 开启时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 配置PA0为复用推挽输出(TIM2_CH1)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 初始化TIM2时基单元
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    TIM_BaseStruct.TIM_Period = DUTY_CYCLE;
    TIM_BaseStruct.TIM_Prescaler = 0;
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_BaseStruct);

    // 4. 配置TIM2为从模式(由ITR1触发,即TIM1的触发信号)
    TIM_SelectInputTrigger(TIM2, TIM_TS_ITR1);  // ITR1对应TIM1
    TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);

    // 5. 配置TIM2通道1为PWM模式
    TIM_OCInitTypeDef TIM_OCStruct;
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCStruct.TIM_Pulse = PHASE_DELAY_1;  // 相位偏移量
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCStruct);

    // 6. 设置初始相位偏移
    TIM_SetCounter(TIM2, PHASE_OFFSET_1); // 计数器初始值=相位偏移量
}

// 从定时器TIM3配置(120°相位)
void TIM3_SlaveConfig(void) 
{
      // 1. 开启时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 2. 配置PA6为复用推挽输出(TIM3_CH1)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 3. 初始化TIM3时基单元
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    TIM_BaseStruct.TIM_Period = DUTY_CYCLE;
    TIM_BaseStruct.TIM_Prescaler = 0;
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM3, &TIM_BaseStruct);

    // 4. 配置TIM3为从模式(由ITR1触发)
    TIM_SelectInputTrigger(TIM3, TIM_TS_ITR1);  // ITR1对应TIM1
    TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);

    // 5. 配置TIM3通道1为PWM模式
    TIM_OCInitTypeDef TIM_OCStruct;
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCStruct.TIM_Pulse = PHASE_DELAY_2;  // 120°相位偏移
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM3, &TIM_OCStruct);

    // 6. 设置初始相位偏移
    TIM_SetCounter(TIM3, PHASE_OFFSET_2); // 计数器初始值=相位偏移量
}

// 新增:从定时器TIM4配置(240°相位)
void TIM4_SlaveConfig(void) 
{
     // 1. 开启时钟(TIM4在APB1总线)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // TIM4_CH1对应PB6

    // 2. 配置PB6为复用推挽输出(TIM4_CH1)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;          // PB6 = TIM4_CH1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;    // 复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);             // 初始化GPIOB

    // 3. 初始化TIM4时基单元
    TIM_TimeBaseInitTypeDef TIM_BaseStruct;
    TIM_BaseStruct.TIM_Period = DUTY_CYCLE;           // 自动重装载值
    TIM_BaseStruct.TIM_Prescaler = 0;                 // 无预分频
    TIM_BaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_BaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
    TIM_TimeBaseInit(TIM4, &TIM_BaseStruct);           // 应用配置

    // 4. 配置TIM4为从模式(由ITR2触发,对应TIM1主定时器)
    TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);        // ITR2对应TIM1触发源
    TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset); // 触发模式:上升沿启动计数

    // 5. 配置TIM4通道1为PWM模式
    TIM_OCInitTypeDef TIM_OCStruct;
    TIM_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;         // PWM模式1
    TIM_OCStruct.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
    TIM_OCStruct.TIM_Pulse = PHASE_DELAY;              // 相位偏移量(CCR初始值)
    TIM_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效
    TIM_OC1Init(TIM4, &TIM_OCStruct);                  // 初始化通道1

    // 6. 设置初始相位偏移
    TIM_SetCounter(TIM4, PHASE_OFFSET_3); // 计数器初始值=相位偏移量

    // 7. 启用预装载寄存器(确保相位偏移稳定)
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); // 
    TIM_ARRPreloadConfig(TIM4, ENABLE);               // 使能ARR预装载
}

3. 主函数

cpp 复制代码
int main(void) 
{
    TIM1_MasterConfig();
    TIM2_SlaveConfig();
    TIM3_SlaveConfig();
    TIM4_SlaveConfig(); 

    // 注意:先启动从定时器,再启动主定时器!
    TIM_Cmd(TIM2, ENABLE);
    TIM_Cmd(TIM3, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
    TIM_Cmd(TIM1, ENABLE); // 最后启动主定时器

    while (1) 
    {

    }
}

🧩 ​二、核心实现原理

1. ​主从定时器同步机制
  • 主定时器(TIM1)​ ​:

    通过TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_OC2Ref)将通道2的OC2Ref信号作为触发源(TRGO)输出。

    当TIM1的计数器达到CCR2值(占空比点)时,TRGO信号产生一个上升沿,触发所有从定时器同步动作。

  • 从定时器(TIM2/TIM3/TIM4)​ ​:

    配置为复位模式(SlaveMode_Reset)​​:

    cpp 复制代码
    TIM_SelectSlaveMode(TIMx, TIM_SlaveMode_Reset);  // 关键配置

    复位模式的作用 ​:当检测到TRGO上升沿时,从定时器的计数器(CNT)​立即清零并重新计数​。这是实现硬件同步的基础。


⚙️ ​三、相位差的生成逻辑

1. ​初始相位偏移设置
cpp 复制代码
TIM_SetCounter(TIM2, PHASE_OFFSET_1); // TIM2初始CNT=0  
TIM_SetCounter(TIM3, PHASE_OFFSET_2); // TIM3初始CNT=333  
TIM_SetCounter(TIM4, PHASE_OFFSET_3); // TIM4初始CNT=666  
  • 相位偏移原理 ​:

    每个从定时器的计数器初始值不同(0、333、666),但共用相同的PWM周期(ARR=999)

    • TIM2:从0开始计数 → 0°相位(基准)
    • TIM3:从333开始计数 → 延迟了333个计数单位(120°相位差)
    • TIM4:从666开始计数 → 延迟了666个计数单位(240°相位差)
  • 相位差计算公式​:

    相位差角度=(ARR+1计数器初值)×360∘

    例如:333 / 1000 ≈ 120° 。

2. ​复位模式与初始值的协同
  • 首次启动时 ​:

    从定时器的CNT初始值(PHASE_OFFSET_x)直接决定了首个PWM脉冲的起始位置,形成初始相位差。

  • 后续运行中 ​:

    每次TRGO触发信号到来时,从定时器CNT清零 ,但由于所有从定时器在同一时刻清零,​初始相位差保持不变

    例如:

    • TIM2每次从0开始计数 → 始终0°基准
    • TIM3每次从0开始计数,但首次启动时已延迟333 → 后续周期仍保持120°延迟

🔄 ​四、占空比一致性保障

cpp 复制代码
TIM_OCStruct.TIM_Pulse = DUTY_CYCLE; // 所有通道占空比固定为500  
  • 占空比与相位分离控制
    • 相位差 由计数器初值(TIM_SetCounter())控制,不影响脉宽
    • 占空比 由通道的CCR值(TIM_Pulse)独立控制,所有通道设为相同的DUTY_CYCLE=500(50%占空比)。

⚡五**、动态调整相位差的实现**​

cpp 复制代码
// 在main循环中可动态修改  
TIM_SetCounter(TIM3, new_phase); // 实时调整TIM3相位  
  • 操作原理
    直接修改从定时器的CNT寄存器值,立即改变当前计数位置
    • 若将TIM3的CNT从333改为250,则其下一个PWM脉冲的起始边沿会提前83个计数单位(约30°)。
    • 注意 :需在两次TRGO触发之间修改CNT值,避免与复位操作冲突。

🔧 ​六、关键配置细节

1. ​触发链路配置
cpp 复制代码
TIM_SelectInputTrigger(TIMx, TIM_TS_ITR1); // 从定时器选择ITR1触发源  
  • ITR1是STM32内部映射,表示TIM1的TRGO信号直接连接到TIM2/TIM3/TIM4的触发输入。
2. ​启动顺序的重要性
cpp 复制代码
// 先启动从定时器,再启动主定时器  
TIM_Cmd(TIM2, ENABLE);  
TIM_Cmd(TIM3, ENABLE);  
TIM_Cmd(TIM4, ENABLE);  
TIM_Cmd(TIM1, ENABLE);  
  • 为何如此?​
    若先启动主定时器,TRGO信号可能早于从定时器就绪,导致首个同步信号丢失,相位差错误。
3. ​复位模式的局限性
  • 仅适用于周期性信号:从定时器依赖周期性TRGO信号复位,无法处理非周期性触发。
  • 相位调整范围 :计数器初值需满足 0 ≤ PHASE_OFFSET_x ≤ PWM_PERIOD,否则导致脉冲丢失。

💎 ​七、总结:为何能实现相位差可变?​

  1. 硬件同步机制
    主定时器的TRGO信号强制所有从定时器同时复位计数器,确保多路PWM的周期起点严格对齐。
  2. 软件可调的初始相位
    通过TIM_SetCounter()预设不同的计数器初值,在同步复位的基础上引入可控的初始延迟
  3. 动态调整能力
    运行时修改CNT值可实时偏移脉冲位置,且不影响占空比
  4. 低CPU开销
    相位同步由定时器硬件自动完成,无需中断干预,适合高实时性场景(如电机驱动)。

✅ 此方案完美实现了相位差动态可调、占空比一致的三路PWM输出,精度达单时钟周期(如72MHz时误差<14ns),是STM32定时器主从模式的经典应用。