1. 系统配置和头文件
1.1 主程序头文件 (main.h)
c
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f1xx_hal.h"
#include <stdio.h>
// PWM通道定义
#define PWM_CHANNEL_1 TIM_CHANNEL_1
#define PWM_CHANNEL_1N TIM_CHANNEL_1
#define PWM_CHANNEL_2 TIM_CHANNEL_2
#define PWM_CHANNEL_2N TIM_CHANNEL_2
#define PWM_CHANNEL_3 TIM_CHANNEL_3
#define PWM_CHANNEL_3N TIM_CHANNEL_3
// 死区时间定义 (单位: ns)
#define DEAD_TIME_NS 1000 // 1us死区时间
// 引脚定义
// TIM8 CH1/CH1N: PC6/PA7
// TIM8 CH2/CH2N: PC7/PB0
// TIM8 CH3/CH3N: PC8/PB1
#define TIM8_CH1_PIN GPIO_PIN_6
#define TIM8_CH1_PORT GPIOC
#define TIM8_CH1N_PIN GPIO_PIN_7
#define TIM8_CH1N_PORT GPIOA
#define TIM8_CH2_PIN GPIO_PIN_7
#define TIM8_CH2_PORT GPIOC
#define TIM8_CH2N_PIN GPIO_PIN_0
#define TIM8_CH2N_PORT GPIOB
#define TIM8_CH3_PIN GPIO_PIN_8
#define TIM8_CH3_PORT GPIOC
#define TIM8_CH3N_PIN GPIO_PIN_1
#define TIM8_CH3N_PORT GPIOB
// 函数声明
void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_TIM8_Init(void);
void PWM_SetDutyCycle(uint8_t channel, float duty_cycle);
void PWM_SetFrequency(uint32_t frequency_hz);
void PWM_EnableOutputs(void);
void PWM_DisableOutputs(void);
void PWM_BreakEnable(void);
void PWM_BreakDisable(void);
void PWM_SetDeadTime(uint16_t dead_time_ns);
void Error_Handler(void);
#endif /* __MAIN_H */
2. 主程序实现
2.1 主程序 (main.c)
c
#include "main.h"
TIM_HandleTypeDef htim8;
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
// 全局变量
uint32_t pwm_frequency = 20000; // 默认20kHz
uint32_t timer_clock = 72000000; // TIM8时钟频率
uint16_t pwm_period = 0; // 自动重载值
int main(void)
{
// HAL库初始化
HAL_Init();
// 系统时钟配置
SystemClock_Config();
// 外设初始化
MX_GPIO_Init();
MX_TIM8_Init();
// 设置PWM频率为20kHz
PWM_SetFrequency(20000);
// 设置死区时间为1us
PWM_SetDeadTime(DEAD_TIME_NS);
// 设置初始占空比
PWM_SetDutyCycle(PWM_CHANNEL_1, 0.5f); // 50%占空比
PWM_SetDutyCycle(PWM_CHANNEL_2, 0.5f);
// 使能PWM输出
PWM_EnableOutputs();
// 主循环
while (1)
{
// 可以在这里添加占空比调节逻辑
// 例如:按键控制、ADC反馈、通信控制等
// 模拟呼吸灯效果
static float duty = 0.0f;
static int8_t dir = 1;
duty += 0.01f * dir;
if(duty > 0.9f) {
duty = 0.9f;
dir = -1;
} else if(duty < 0.1f) {
duty = 0.1f;
dir = 1;
}
PWM_SetDutyCycle(PWM_CHANNEL_1, duty);
HAL_Delay(10);
}
}
/**
* @brief 系统时钟配置
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
// 更新系统时钟变量
SystemCoreClockUpdate();
// 获取TIM8时钟频率
// TIM8在APB2总线上,如果APB2分频为1,则时钟为系统时钟
if(RCC_ClkInitStruct.APB2CLKDivider == RCC_HCLK_DIV1) {
timer_clock = SystemCoreClock;
} else {
timer_clock = SystemCoreClock * 2;
}
}
/**
* @brief GPIO初始化
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
// 配置TIM8通道1引脚
GPIO_InitStruct.Pin = TIM8_CH1_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TIM8_CH1_PORT, &GPIO_InitStruct);
// 配置TIM8通道1N引脚
GPIO_InitStruct.Pin = TIM8_CH1N_PIN;
HAL_GPIO_Init(TIM8_CH1N_PORT, &GPIO_InitStruct);
// 配置TIM8通道2引脚
GPIO_InitStruct.Pin = TIM8_CH2_PIN;
HAL_GPIO_Init(TIM8_CH2_PORT, &GPIO_InitStruct);
// 配置TIM8通道2N引脚
GPIO_InitStruct.Pin = TIM8_CH2N_PIN;
HAL_GPIO_Init(TIM8_CH2N_PORT, &GPIO_InitStruct);
// 配置TIM8通道3引脚
GPIO_InitStruct.Pin = TIM8_CH3_PIN;
HAL_GPIO_Init(TIM8_CH3_PORT, &GPIO_InitStruct);
// 配置TIM8通道3N引脚
GPIO_InitStruct.Pin = TIM8_CH3N_PIN;
HAL_GPIO_Init(TIM8_CH3N_PORT, &GPIO_InitStruct);
}
3. TIM8 PWM配置
3.1 TIM8初始化 (tim8_pwm.c)
c
#include "main.h"
extern TIM_HandleTypeDef htim8;
extern TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig;
extern TIM_OC_InitTypeDef sConfigOC;
/**
* @brief TIM8 PWM初始化
*/
void MX_TIM8_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
// 使能TIM8时钟
__HAL_RCC_TIM8_CLK_ENABLE();
// 配置TIM8
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0; // 不分频
htim8.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1; // 中心对齐模式1
htim8.Init.Period = 3599; // 默认20kHz (72MHz / 3600 = 20kHz)
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_PWM_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
// 配置时钟源
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(&htim8, &sClockSourceConfig);
// 配置PWM模式
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM模式1
sConfigOC.Pulse = 1800; // 默认50%占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 有效电平为高
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; // 互补通道有效电平为高
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
// 配置通道1
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 配置通道2
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
// 配置通道3
if (HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
// 配置主模式
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig);
// 配置断路和死区时间
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 72; // 1us死区时间 (72MHz时钟)
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig);
}
/**
* @brief 设置PWM占空比
* @param channel: PWM通道
* @param duty_cycle: 占空比 (0.0 - 1.0)
*/
void PWM_SetDutyCycle(uint8_t channel, float duty_cycle)
{
uint32_t pulse = 0;
// 限制占空比范围
if(duty_cycle < 0.0f) duty_cycle = 0.0f;
if(duty_cycle > 1.0f) duty_cycle = 1.0f;
// 计算比较值
// 注意:在中心对齐模式下,比较值应该是周期值的一半
pulse = (uint32_t)(duty_cycle * htim8.Init.Period);
// 设置比较值
__HAL_TIM_SET_COMPARE(&htim8, channel, pulse);
}
/**
* @brief 设置PWM频率
* @param frequency_hz: 频率值 (Hz)
*/
void PWM_SetFrequency(uint32_t frequency_hz)
{
TIM_Base_InitTypeDef TIM_BaseStruct = {0};
uint32_t prescaler = 0;
uint32_t period = 0;
// 计算预分频和自动重载值
if(frequency_hz > 0)
{
// 计算最小预分频值
prescaler = (timer_clock / (frequency_hz * 65536)) - 1;
if(prescaler < 0) prescaler = 0;
// 计算周期值
period = (timer_clock / ((prescaler + 1) * frequency_hz)) - 1;
// 如果周期值太大,增加预分频
if(period > 65535)
{
prescaler = (timer_clock / (frequency_hz * 65536)) - 1;
period = (timer_clock / ((prescaler + 1) * frequency_hz)) - 1;
}
pwm_frequency = frequency_hz;
pwm_period = period;
// 停止定时器
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_3);
// 更新定时器参数
TIM_BaseStruct.Prescaler = prescaler;
TIM_BaseStruct.Period = period;
TIM_BaseStruct.CounterMode = TIM_COUNTERMODE_CENTERALIGNED1;
TIM_BaseStruct.ClockDivision = TIM_CLOCKDIVISION_DIV1;
TIM_BaseStruct.RepetitionCounter = 0;
TIM_BaseStruct.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if(HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
// 重新启动PWM
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
}
}
/**
* @brief 设置死区时间
* @param dead_time_ns: 死区时间 (纳秒)
*/
void PWM_SetDeadTime(uint16_t dead_time_ns)
{
// 计算死区时间寄存器值
// DT = DTG[7:0] * Tdtg
// 其中Tdtg是定时器时钟周期
float timer_period_ns = 1e9f / timer_clock; // 定时器时钟周期(纳秒)
uint16_t dead_time_ticks = (uint16_t)(dead_time_ns / timer_period_ns);
// 限制死区时间最大值
if(dead_time_ticks > 0xFF) // DTG寄存器只有8位
{
dead_time_ticks = 0xFF;
}
// 停止PWM输出
PWM_DisableOutputs();
// 重新配置死区时间
sBreakDeadTimeConfig.DeadTime = dead_time_ticks;
if(HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
// 重新使能PWM输出
PWM_EnableOutputs();
}
/**
* @brief 使能PWM输出
*/
void PWM_EnableOutputs(void)
{
// 使能通道1和互补通道1
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_1);
// 使能通道2和互补通道2
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_2);
// 使能通道3和互补通道3
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_3);
// 使能主输出
__HAL_TIM_MOE_ENABLE(&htim8);
}
/**
* @brief 禁用PWM输出
*/
void PWM_DisableOutputs(void)
{
// 禁用主输出
__HAL_TIM_MOE_DISABLE(&htim8);
// 停止通道1和互补通道1
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim8, TIM_CHANNEL_1);
// 停止通道2和互补通道2
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Stop(&htim8, TIM_CHANNEL_2);
// 停止通道3和互补通道3
HAL_TIM_PWM_Stop(&htim8, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Stop(&htim8, TIM_CHANNEL_3);
}
/**
* @brief 使能断路功能
*/
void PWM_BreakEnable(void)
{
sBreakDeadTimeConfig.BreakState = TIM_BREAK_ENABLE;
if(HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 禁用断路功能
*/
void PWM_BreakDisable(void)
{
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
if(HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 错误处理
*/
void Error_Handler(void)
{
// 可以在这里添加错误处理代码
// 例如:闪烁LED、重启系统等
while(1)
{
// 死循环
}
}
4. PWM控制高级功能
4.1 同步触发和刹车控制 (pwm_control.c)
c
#include "main.h"
/**
* @brief 配置PWM同步触发
* @param master_mode: 主模式选择
* @param slave_mode: 从模式选择
*/
void PWM_ConfigSync(uint32_t master_mode, uint32_t slave_mode)
{
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
// 配置主模式
sMasterConfig.MasterOutputTrigger = master_mode;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig);
// 配置从模式
sSlaveConfig.SlaveMode = slave_mode;
sSlaveConfig.InputTrigger = TIM_TS_ITR0; // 内部触发0
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerPrescaler = TIM_TRIGGERPRESCALER_DIV1;
sSlaveConfig.TriggerFilter = 0;
HAL_TIM_SlaveConfigSynchronization(&htim8, &sSlaveConfig);
}
/**
* @brief 配置互补输出极性
* @param channel: 通道
* @param polarity: 主通道极性
* @param npolarity: 互补通道极性
*/
void PWM_ConfigPolarity(uint32_t channel, uint32_t polarity, uint32_t npolarity)
{
TIM_OC_InitTypeDef sConfigOC = {0};
// 获取当前配置
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = __HAL_TIM_GET_COMPARE(&htim8, channel);
sConfigOC.OCPolarity = polarity;
sConfigOC.OCNPolarity = npolarity;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
// 重新配置通道
if(HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, channel) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 配置输出比较空闲状态
* @param channel: 通道
* @param idle_state: 空闲状态
* @param nidle_state: 互补通道空闲状态
*/
void PWM_ConfigIdleState(uint32_t channel, uint32_t idle_state, uint32_t nidle_state)
{
TIM_OC_InitTypeDef sConfigOC = {0};
// 获取当前配置
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = __HAL_TIM_GET_COMPARE(&htim8, channel);
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = idle_state;
sConfigOC.OCNIdleState = nidle_state;
// 重新配置通道
if(HAL_TIM_PWM_ConfigChannel(&htim8, &sConfigOC, channel) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 设置PWM对齐模式
* @param mode: 对齐模式
* 可选值:
* TIM_COUNTERMODE_UP - 边沿对齐模式(向上计数)
* TIM_COUNTERMODE_DOWN - 边沿对齐模式(向下计数)
* TIM_COUNTERMODE_CENTERALIGNED1 - 中心对齐模式1
* TIM_COUNTERMODE_CENTERALIGNED2 - 中心对齐模式2
* TIM_COUNTERMODE_CENTERALIGNED3 - 中心对齐模式3
*/
void PWM_SetAlignmentMode(uint32_t mode)
{
// 停止PWM输出
PWM_DisableOutputs();
// 更新对齐模式
htim8.Init.CounterMode = mode;
if(HAL_TIM_PWM_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
// 重新使能PWM输出
PWM_EnableOutputs();
}
5. 死区时间计算模块
5.1 死区时间计算 (deadtime_calc.c)
c
#include "main.h"
#include <math.h>
/**
* @brief 计算死区时间寄存器值
* @param dead_time_ns: 死区时间(纳秒)
* @param timer_freq: 定时器时钟频率(Hz)
* @return 死区时间寄存器值
*
* 死区时间计算公式:
* DT = (DTG[7:0] + 1) * Tdtg
* 其中Tdtg是定时器时钟周期
*
* 根据DTG[7:5]的值,公式不同:
* 000: DT = DTG[7:0] * Tdtg
* 001: DT = (64 + DTG[5:0]) * 2 * Tdtg
* 010: DT = (32 + DTG[4:0]) * 8 * Tdtg
* 011: DT = (32 + DTG[4:0]) * 16 * Tdtg
* 1xx: DT = (32 + DTG[4:0]) * 32 * Tdtg
*/
uint32_t CalculateDeadTimeReg(uint32_t dead_time_ns, uint32_t timer_freq)
{
float timer_period_ns = 1e9f / timer_freq; // 定时器时钟周期(纳秒)
uint32_t dtg_value = 0;
// 计算需要的时钟周期数
float required_cycles = dead_time_ns / timer_period_ns;
if(required_cycles <= 127.0f)
{
// 模式0: DT = DTG[7:0] * Tdtg
dtg_value = (uint32_t)required_cycles;
}
else if(required_cycles <= 254.0f)
{
// 模式1: DT = (64 + DTG[5:0]) * 2 * Tdtg
uint32_t cycles = (uint32_t)(required_cycles / 2.0f);
if(cycles < 64) cycles = 64;
if(cycles > 127) cycles = 127;
dtg_value = ((cycles - 64) & 0x3F) | 0x40; // 设置DTG[6]=1
}
else if(required_cycles <= 1016.0f)
{
// 模式2: DT = (32 + DTG[4:0]) * 8 * Tdtg
uint32_t cycles = (uint32_t)(required_cycles / 8.0f);
if(cycles < 32) cycles = 32;
if(cycles > 63) cycles = 63;
dtg_value = ((cycles - 32) & 0x1F) | 0x80; // 设置DTG[7]=1
}
else if(required_cycles <= 2032.0f)
{
// 模式3: DT = (32 + DTG[4:0]) * 16 * Tdtg
uint32_t cycles = (uint32_t)(required_cycles / 16.0f);
if(cycles < 32) cycles = 32;
if(cycles > 63) cycles = 63;
dtg_value = ((cycles - 32) & 0x1F) | 0xC0; // 设置DTG[7:6]=11
}
else
{
// 模式4: DT = (32 + DTG[4:0]) * 32 * Tdtg
uint32_t cycles = (uint32_t)(required_cycles / 32.0f);
if(cycles < 32) cycles = 32;
if(cycles > 63) cycles = 63;
dtg_value = ((cycles - 32) & 0x1F) | 0xE0; // 设置DTG[7:5]=111
}
return dtg_value;
}
/**
* @brief 验证死区时间设置
* @param dtg_value: 死区时间寄存器值
* @param timer_freq: 定时器时钟频率(Hz)
* @return 实际死区时间(纳秒)
*/
float VerifyDeadTime(uint32_t dtg_value, uint32_t timer_freq)
{
float timer_period_ns = 1e9f / timer_freq;
float dead_time_ns = 0.0f;
if((dtg_value & 0x80) == 0)
{
// 模式0
if((dtg_value & 0x40) == 0)
{
dead_time_ns = dtg_value * timer_period_ns;
}
// 模式1
else
{
uint32_t dtg_low = dtg_value & 0x3F;
dead_time_ns = (64 + dtg_low) * 2 * timer_period_ns;
}
}
else
{
uint32_t dtg_low = dtg_value & 0x1F;
if((dtg_value & 0xC0) == 0x80)
{
// 模式2
dead_time_ns = (32 + dtg_low) * 8 * timer_period_ns;
}
else if((dtg_value & 0xE0) == 0xC0)
{
// 模式3
dead_time_ns = (32 + dtg_low) * 16 * timer_period_ns;
}
else
{
// 模式4
dead_time_ns = (32 + dtg_low) * 32 * timer_period_ns;
}
}
return dead_time_ns;
}
6. 中断处理
6.1 中断配置 (tim8_it.c)
c
#include "main.h"
// 中断回调变量
volatile uint8_t pwm_update_flag = 0;
volatile uint8_t break_event_flag = 0;
/**
* @brief 配置TIM8中断
*/
void PWM_ConfigInterrupts(void)
{
// 使能TIM8中断
HAL_NVIC_SetPriority(TIM8_UP_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);
HAL_NVIC_SetPriority(TIM8_BRK_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM8_BRK_IRQn);
HAL_NVIC_SetPriority(TIM8_TRG_COM_IRQn, 0, 2);
HAL_NVIC_EnableIRQ(TIM8_TRG_COM_IRQn);
HAL_NVIC_SetPriority(TIM8_CC_IRQn, 0, 3);
HAL_NVIC_EnableIRQ(TIM8_CC_IRQn);
// 使能更新中断
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_UPDATE);
// 使能断路中断
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_BREAK);
// 使能比较中断
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_CC1);
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_CC2);
__HAL_TIM_ENABLE_IT(&htim8, TIM_IT_CC3);
}
/**
* @brief TIM8更新中断处理
*/
void TIM8_UP_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_UPDATE) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&htim8, TIM_IT_UPDATE) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_UPDATE);
pwm_update_flag = 1;
// 可以在这里添加周期更新处理代码
}
}
}
/**
* @brief TIM8断路中断处理
*/
void TIM8_BRK_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_BREAK) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&htim8, TIM_IT_BREAK) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_BREAK);
break_event_flag = 1;
// 处理断路事件
// 可以在这里添加紧急停机、故障记录等代码
}
}
}
/**
* @brief TIM8比较中断处理
*/
void TIM8_CC_IRQHandler(void)
{
// 处理比较中断
if(__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_CC1) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&htim8, TIM_IT_CC1) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_CC1);
// 处理通道1比较事件
}
}
if(__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_CC2) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&htim8, TIM_IT_CC2) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_CC2);
// 处理通道2比较事件
}
}
if(__HAL_TIM_GET_FLAG(&htim8, TIM_FLAG_CC3) != RESET)
{
if(__HAL_TIM_GET_IT_SOURCE(&htim8, TIM_IT_CC3) != RESET)
{
__HAL_TIM_CLEAR_IT(&htim8, TIM_IT_CC3);
// 处理通道3比较事件
}
}
}
7. PWM控制应用示例
7.1 电机控制应用 (motor_control.c)
c
#include "main.h"
#include <math.h>
// 电机控制结构体
typedef struct {
float duty_a; // A相占空比
float duty_b; // B相占空比
float duty_c; // C相占空比
float frequency; // PWM频率
float deadtime; // 死区时间
uint8_t enabled; // 使能状态
} MotorControl_t;
MotorControl_t motor = {0};
/**
* @brief 初始化电机控制
*/
void Motor_Init(void)
{
motor.frequency = 20000.0f; // 20kHz
motor.deadtime = 1000.0f; // 1us
motor.enabled = 0;
// 初始化PWM
PWM_SetFrequency((uint32_t)motor.frequency);
PWM_SetDeadTime((uint16_t)motor.deadtime);
// 设置初始占空比
motor.duty_a = 0.0f;
motor.duty_b = 0.0f;
motor.duty_c = 0.0f;
PWM_SetDutyCycle(PWM_CHANNEL_1, motor.duty_a);
PWM_SetDutyCycle(PWM_CHANNEL_2, motor.duty_b);
PWM_SetDutyCycle(PWM_CHANNEL_3, motor.duty_c);
}
/**
* @brief 设置三相PWM
* @param u: U相电压 (-1.0 到 1.0)
* @param v: V相电压 (-1.0 到 1.0)
* @param w: W相电压 (-1.0 到 1.0)
*/
void Motor_SetThreePhase(float u, float v, float w)
{
// 限制输入范围
if(u < -1.0f) u = -1.0f;
if(u > 1.0f) u = 1.0f;
if(v < -1.0f) v = -1.0f;
if(v > 1.0f) v = 1.0f;
if(w < -1.0f) w = -1.0f;
if(w > 1.0f) w = 1.0f;
// 转换为占空比 (0.0 到 1.0)
motor.duty_a = (u + 1.0f) * 0.5f;
motor.duty_b = (v + 1.0f) * 0.5f;
motor.duty_c = (w + 1.0f) * 0.5f;
// 设置PWM占空比
PWM_SetDutyCycle(PWM_CHANNEL_1, motor.duty_a);
PWM_SetDutyCycle(PWM_CHANNEL_2, motor.duty_b);
PWM_SetDutyCycle(PWM_CHANNEL_3, motor.duty_c);
}
/**
* @brief 生成SVPWM波形
* @param angle: 电角度 (弧度)
* @param magnitude: 调制深度 (0.0 到 1.0)
*/
void Motor_SVPWM(float angle, float magnitude)
{
// 限制调制深度
if(magnitude < 0.0f) magnitude = 0.0f;
if(magnitude > 1.0f) magnitude = 1.0f;
// 三相正弦波
float u = magnitude * sinf(angle);
float v = magnitude * sinf(angle + 2.0f * 3.1415926f / 3.0f);
float w = magnitude * sinf(angle - 2.0f * 3.1415926f / 3.0f);
// 设置三相PWM
Motor_SetThreePhase(u, v, w);
}
/**
* @brief 使能电机驱动
*/
void Motor_Enable(void)
{
if(!motor.enabled)
{
motor.enabled = 1;
PWM_EnableOutputs();
}
}
/**
* @brief 禁用电机驱动
*/
void Motor_Disable(void)
{
if(motor.enabled)
{
motor.enabled = 0;
PWM_DisableOutputs();
// 设置占空比为0
motor.duty_a = 0.0f;
motor.duty_b = 0.0f;
motor.duty_c = 0.0f;
PWM_SetDutyCycle(PWM_CHANNEL_1, 0.0f);
PWM_SetDutyCycle(PWM_CHANNEL_2, 0.0f);
PWM_SetDutyCycle(PWM_CHANNEL_3, 0.0f);
}
}
/**
* @brief 紧急停止
*/
void Motor_EmergencyStop(void)
{
// 使能断路功能
PWM_BreakEnable();
// 禁用输出
Motor_Disable();
// 可以添加故障记录等其他处理
}
参考代码 STM32的TIM8的PWM两路带死区的程序 www.youwenfan.com/contentcsv/71035.html
8. Keil工程配置
8.1 工程配置要点
-
Device: STM32F103ZE (或其他支持TIM8的型号)
-
Target: 设置晶振频率为8MHz
-
C/C++: 添加包含路径和预定义符号
Define: USE_HAL_DRIVER, STM32F103xE Include Paths: 添加所有必要的HAL库路径 -
Debug: 配置ST-Link调试器
-
Utilities: 配置Flash烧录算法
8.2 链接脚本
scatter
LR_IROM1 0x08000000 0x00080000 { ; load region size_region
ER_IROM1 0x08000000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
}
9. 调试和测试
9.1 测试程序
c
// 测试PWM输出的主循环
void Test_PWM_Output(void)
{
// 测试不同频率
uint32_t test_frequencies[] = {10000, 15000, 20000, 25000, 30000};
uint8_t freq_index = 0;
// 测试不同死区时间
uint16_t test_deadtimes[] = {500, 1000, 1500, 2000}; // ns
uint8_t deadtime_index = 0;
while(1)
{
// 测试频率切换
PWM_SetFrequency(test_frequencies[freq_index]);
freq_index = (freq_index + 1) % 5;
// 测试死区时间切换
PWM_SetDeadTime(test_deadtimes[deadtime_index]);
deadtime_index = (deadtime_index + 1) % 4;
// 测试占空比扫频
for(float duty = 0.0f; duty <= 1.0f; duty += 0.01f)
{
PWM_SetDutyCycle(PWM_CHANNEL_1, duty);
PWM_SetDutyCycle(PWM_CHANNEL_2, 1.0f - duty);
HAL_Delay(10);
}
for(float duty = 1.0f; duty >= 0.0f; duty -= 0.01f)
{
PWM_SetDutyCycle(PWM_CHANNEL_1, duty);
PWM_SetDutyCycle(PWM_CHANNEL_2, 1.0f - duty);
HAL_Delay(10);
}
HAL_Delay(1000);
}
}
10. 注意事项
-
硬件连接:
- 确保TIM8的互补输出引脚正确连接
- 注意引脚的最大驱动电流
- 添加必要的保护电路(如TVS、RC缓冲)
-
死区时间设置:
- 死区时间必须大于MOSFET/IGBT的开关时间
- 考虑温度对开关时间的影响
- 死区时间过大会导致输出失真
-
EMC考虑:
- 使用屏蔽电缆连接功率器件
- 添加RC缓冲电路减少开关噪声
- 合理布局PCB,减少辐射
-
安全保护:
- 实现过流、过压、过热保护
- 添加硬件断路输入
- 实现软件看门狗
-
调试技巧:
- 使用示波器观察PWM波形
- 验证死区时间是否准确
- 检查互补信号是否有重叠