如何用定时器PWM产生SPWM?--电机驱动控制

用定时器生成 SPWM 的核心逻辑是:以高频三角波为载波,按正弦规律动态更新 PWM 占空比,最终通过低通滤波还原正弦波。以下是面向 STM32 的标准实现方案与代码。

一、核心原理与参数设计

  • 载波:高频三角波(推荐 10--20 kHz,远高于基波),由定时器 ARR/PSC 决定。
  • 调制:低频正弦波(如 50 Hz),通过查表或实时计算映射为 PWM 的 CCR 值。
  • 更新 :在每个载波周期起点更新 CCR,用定时器更新中断DMA实现。
  • 公式
    • PWM 频率:fPWM=ftimer_clk/((PSC+1)×(ARR+1))
    • 占空比映射:CCR=(1+sin(θ)×ARR)/2(归一化到 0--ARR)

二、硬件与参数选型(以 STM32F103 72 MHz 为例)

参数 取值 说明
定时器 TIM1(高级定时器) 支持互补输出与死区,适合电机驱动
载波频率 10 kHz fPWM​=10 kHz
PSC 71 72 MHz / (71+1) = 1 MHz 计数时钟
ARR 99 1 MHz / (99+1) = 10 kHz
基波频率 50 Hz 目标输出频率
采样点数 200 中断频率 = 50 Hz × 200 = 10 kHz

三、标准实现步骤(HAL 库)

1. 生成正弦表(离线 / 初始化)

预先计算一周期正弦值并映射到 0--ARR,提升运行效率。

复制代码
#define SINE_SIZE 200
uint16_t SineTable[SINE_SIZE];
const uint16_t ARR_VAL = 99;  // 对应10 kHz载波

void GenerateSineTable(void) {
  for(int i=0; i<SINE_SIZE; i++) {
    float theta = 2 * 3.14159f * i / SINE_SIZE;
    // 归一化到0~ARR_VAL
    SineTable[i] = (1 + sinf(theta)) * ARR_VAL / 2;
  }
}
2. 定时器 PWM 初始化

配置时基、通道与预装载,确保更新时 CCR 原子生效。

复制代码
TIM_HandleTypeDef htim1;
void MX_TIM1_Init(void) {
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 71;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = ARR_VAL;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_PWM_Init(&htim1);

  TIM_OC_InitTypeDef sConfigOC = {0};
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);

  // 使能预装载
  __HAL_TIM_ENABLE_OCxPRELOAD(&htim1, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}
3. 中断更新 CCR(推荐)

用 TIM_UP 中断按步长遍历正弦表,实现基波输出。

复制代码
uint16_t index = 0;
// 每100 us进入一次(对应10 kHz中断)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if(htim->Instance == TIM2) {  // TIM2用于定时
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, SineTable[index]);
    index = (index + 1) % SINE_SIZE;
  }
}
4. 更高性能:DMA 传输(无 CPU 开销)

将正弦表直接绑定到 CCR,适合高频场景。

复制代码
// 初始化后启动DMA
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)SineTable, SINE_SIZE);

四、关键优化与注意事项

  1. 死区保护:驱动 MOS 管时,高级定时器需配置死区(如 1 µs),防止上下桥直通。

    // 配置死区1 µs(计数时钟1 MHz,死区值=1)
    __HAL_TIM_SET_DEADTIME(&htim1, 1);

  2. 互补输出:三相逆变时,用 CHx 与 CHxN 输出互补信号。

  3. 抗混叠:输出端加 LC 低通滤波器,滤除高频载波,还原平滑正弦波。

  4. 调制度:通过缩放正弦表幅值调整调制度(0--1),避免过调制失真。

五、完整工程流程(CubeMX+HAL)

  1. CubeMX 配置:TIM1 为 PWM 生成模式,TIM2 为 10 kHz 更新中断;使能 GPIO 复用输出。
  2. 生成代码,在main.c中调用GenerateSineTable()
  3. 启动 TIM2 中断与 TIM1 PWM,在回调中更新 CCR 或直接启动 DMA。
  4. 编译下载,用示波器验证 CH1 输出为 SPWM,经 LC 滤波后为正弦波。

六、常见问题与排查

  • 波形失真:检查载波频率是否足够高、正弦表点数是否充足、CCR 更新是否及时。
  • 无输出 :确认 GPIO 复用映射正确、PWM 通道使能、主输出使能(高级定时器需TIM_BDTR寄存器)。
  • 噪声大:增加滤波电容 / LC 参数,优化 PCB 布线,PWM 走线尽量短且远离干扰源。

七、扩展:三相 SPWM 与 EtherCAT 同步

  • 三相:生成三相互差 120° 的正弦表,在 TIM_UP 中断中同时更新 CH1/CH2/CH3。
  • EtherCAT 同步:用 SYNC0 信号初始化 PWM 计数器,确保控制周期与通信同步,提升运动控制精度。
相关推荐
于小猿Sup10 小时前
VMware在Ubuntu22.04驱动Livox Mid360s
linux·c++·嵌入式硬件·自动驾驶
chao18984412 小时前
STM32 HAL库驱动AT24C02 EEPROM例程
stm32·单片机·嵌入式硬件
不会武功的火柴13 小时前
SystemVerilog语法(8)-有限状态机(FSM)
嵌入式硬件·fpga开发·自动化·ic验证·rtl·uvm方法学
嵌入式小站16 小时前
STM32 零基础可移植教程 05:按键消抖,为什么按一次会触发好几次
chrome·stm32·嵌入式硬件
czhaii16 小时前
跟我动手学FX系列PLC GX2环境
嵌入式硬件
2zcode18 小时前
基于STM32的智能扫地机器人设计与实现
stm32·嵌入式硬件·机器人
jllllyuz18 小时前
单相并网逆变器控制代码实现(STM32版)
stm32·单片机·嵌入式硬件
冉卓电子20 小时前
GD32C103RBT6 misc 内核驱动库极简解析
单片机·嵌入式硬件
yongui4783420 小时前
MAX6675 K型热电偶温度采集程序(Keil环境)
单片机·嵌入式硬件
豆包公子20 小时前
AUTOSAR CP XCP 移植到裸机 MCU-实践篇
单片机·嵌入式硬件