如何用定时器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 计数器,确保控制周期与通信同步,提升运动控制精度。
相关推荐
charlie1145141912 小时前
嵌入式C++教程实战之Linux下的单片机编程(9):HAL时钟使能 —— 不开时钟,外设就是一坨睡死的硅
linux·开发语言·c++·单片机·嵌入式硬件·c
钿驰科技2 小时前
水泵无刷电机驱动板如何实现恒压控制?
单片机·嵌入式硬件
xingzhemengyou12 小时前
STM32 DMA
stm32·单片机·嵌入式硬件
森利威尔电子-2 小时前
森利威尔 SL3160A 降压型 DC - DC 转换器:10V - 150V 宽输入,稳出 5V/2.5A
单片机·嵌入式硬件·集成电路·芯片·电源芯片
清风6666662 小时前
基于单片机与WiFi通信的教室人数与照明上位机监控系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
Topplyz3 小时前
DC-DC的自举电容工作原理
嵌入式硬件·硬件工程·电容·电源
ytttr8733 小时前
四线制步进电机驱动器设计详解
单片机·嵌入式硬件
独小乐3 小时前
013.定时器之系统Tick实现|千篇笔记实现嵌入式全栈/裸机篇
linux·笔记·单片机·嵌入式硬件·arm
三佛科技-134163842124 小时前
无线遥控器开关方案开发 ,无线遥控器开关MCU控制方案设计-基于国产单片机
单片机·嵌入式硬件·物联网·智能家居·pcb工艺