1. 项目概述
最近在做一个基于STM32的电源控制项目,用的是经典的STM32F103C8T6最小系统板。这个蓝色小药丸虽然已经面世十多年,但在工业控制和电源管理领域依然活跃。本文将记录我使用标准外设库(StdPeriph)实现PWM电源控制的全过程,从基础原理到实际代码实现,包含那些官方手册不会告诉你的实战经验。
电源控制(PWR)是嵌入式系统设计中一个看似简单实则暗藏玄机的功能模块。它直接关系到系统功耗、稳定性和响应速度。通过STM32的PWM输出控制MOSFET或IGBT,我们可以实现从简单的LED调光到复杂的开关电源设计。选择F103C8T6不仅因为它的性价比,更因为其丰富的外设资源足够应对大多数中小功率电源控制场景。
2. 硬件设计与选型
2.1 核心器件选型
我的硬件方案采用典型的"MCU+驱动+功率管"三级架构:
- 主控:STM32F103C8T6(72MHz Cortex-M3内核)
- 驱动芯片:IR2104(半桥驱动)
- 功率MOSFET:IRF540N(100V/33A)
注意:虽然F103的GPIO可以直接驱动小功率MOSFET,但为了可靠性和安全性,强烈建议使用专用驱动芯片。IR2104的1.5A驱动能力可以确保MOSFET快速开关,减少导通损耗。
2.2 关键电路设计
电源部分采用两级滤波设计:
- 输入级:100μF电解电容 + 100nF陶瓷电容
- 输出级:220μF低ESR电容 + 10μF陶瓷电容
PWM输出电路特别注意了以下几点:
- 在MCU和驱动芯片间加入74HC14施密特触发器做信号整形
- 驱动芯片自举电容选用0.1μF/50V陶瓷电容
- MOSFET栅极串联10Ω电阻抑制振铃
3. 软件环境搭建
3.1 开发工具链
我选择的开发环境组合:
- IDE:Keil MDK v5.37
- 编译器:ARMCC v6.16
- 调试器:ST-Link V2
- 库版本:STM32F10x_StdPeriph_Lib_V3.5.0
实操心得:虽然HAL库现在更流行,但在电源控制这种对时序要求严格的应用中,标准外设库的直接寄存器操作方式反而更可控。特别是PWM死区时间配置,StdPeriph的精度更高。
3.2 工程配置关键点
新建工程时特别注意了这些配置:
c复制// 在stm32f10x_conf.h中启用必要外设
#define _GPIO
#define _RCC
#define _TIM
#define _PWR
#define _BKP
// 系统时钟配置(72MHz)
void RCC_Configuration(void) {
RCC_DeInit();
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);
}
4. PWM电源控制实现
4.1 定时器配置
使用TIM1的CH1和CH1N输出互补PWM:
c复制void TIM1_PWM_Init(uint16_t arr, uint16_t psc) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 时基配置
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = arr/2; // 初始占空比50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
// 死区时间配置(重要!)
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 0x5F; // 约2us死区
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
4.2 动态调节实现
通过修改CCR寄存器实现占空比动态调节:
c复制void PWM_SetDuty(uint16_t duty) {
if(duty > TIM1->ARR) duty = TIM1->ARR;
TIM1->CCR1 = duty;
}
// 使用ADC采样实现闭环控制
void ADC_IRQHandler(void) {
if(ADC_GetITStatus(ADC1, ADC_IT_EOC) == SET) {
uint16_t adcValue = ADC_GetConversionValue(ADC1);
uint16_t newDuty = (adcValue * TIM1->ARR) / 4095;
PWM_SetDuty(newDuty);
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
}
}
5. 电源管理技巧
5.1 低功耗模式应用
在待机时启用STOP模式:
c复制void Enter_StopMode(void) {
// 配置唤醒源
PWR_WakeUpPinCmd(ENABLE);
// 进入STOP模式
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
// 唤醒后时钟重新配置
SystemInit();
}
5.2 保护机制实现
过流保护硬件+软件双重方案:
- 硬件:电流检测电阻+比较器快速关断
- 软件:ADC采样+看门狗
c复制void IWDG_Init(uint8_t prv, uint16_t rlv) {
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(prv);
IWDG_SetReload(rlv);
IWDG_ReloadCounter();
IWDG_Enable();
}
void Check_Current(void) {
static uint16_t overCurrentCount = 0;
uint16_t current = Get_CurrentADC();
if(current > CURRENT_LIMIT) {
overCurrentCount++;
if(overCurrentCount > 3) {
PWM_Shutdown(); // 立即关闭PWM输出
IWDG_Init(4, 625); // 4秒后复位
while(1);
}
} else {
overCurrentCount = 0;
}
}
6. 调试与优化
6.1 常见问题排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| MOSFET发热严重 | 死区时间不足 | 增大TIM_BDTRInitStructure.TIM_DeadTime |
| PWM输出不稳定 | 时钟配置错误 | 检查RCC配置和示波器波形 |
| ADC采样跳动大 | 未添加滤波 | 硬件RC滤波+软件移动平均 |
6.2 性能优化技巧
- 中断优化:
c复制// 将PWM频率设置为18kHz(人耳听不见)
TIM1_PWM_Init(3999, 0); // 72MHz/(3999+1)=18kHz
// 优化ADC采样时机
void TIM2_IRQHandler(void) {
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
- 代码效率提升:
- 使用寄存器直接操作替代库函数
- 关键代码放在RAM中执行(通过__attribute__((section(".ramcode"))))
- 启用FPU进行浮点运算(如需)
7. 进阶应用扩展
7.1 数字PID实现
在电源闭环控制中加入PID算法:
c复制typedef struct {
float Kp, Ki, Kd;
float integral;
float prev_error;
} PID_Controller;
float PID_Update(PID_Controller* pid, float setpoint, float measurement) {
float error = setpoint - measurement;
pid->integral += error;
if(pid->integral > INTEGRAL_LIMIT) pid->integral = INTEGRAL_LIMIT;
else if(pid->integral < -INTEGRAL_LIMIT) pid->integral = -INTEGRAL_LIMIT;
float derivative = error - pid->prev_error;
pid->prev_error = error;
return pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;
}
7.2 通信接口添加
通过USART实现PC控制:
c复制void USART1_IRQHandler(void) {
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t cmd = USART_ReceiveData(USART1);
Process_Command(cmd); // 解析控制命令
}
}
void Send_Telemetry(void) {
printf("V_in:%.2fV, I_out:%.3fA, Duty:%d%%\r\n",
Get_InputVoltage(), Get_OutputCurrent(),
(TIM1->CCR1*100)/TIM1->ARR);
}
这个项目从最初的原型到稳定运行,我经历了多次MOSFET爆炸、程序跑飞、PWM失控等各种状况。最终得出的经验是:电源设计必须预留足够的余量,关键保护电路一个都不能少,所有参数调整都要循序渐进。现在这个控制器已经连续工作超过2000小时,输出稳定性保持在±1%以内。