第 8 章 STM32 工程实战篇:从零新建工程 + 完整可编译 FOC 工程模板
·
0. 本章目标
本章不讲原理,只做可直接落地的工程:
- 基于 STM32F103 / F407 通用工程模板
- 时钟、GPIO、PWM、ADC、中断完整配置
- 全套 FOC 代码整合:Clark/Park/SVPWM/SMO
- 双闭环 + 启动 + 保护 + 串口调试
- 提供清晰工程结构,你只需要改电机参数就能跑
1. 硬件平台说明
- MCU:STM32F103C8T6 / STM32F407IGT6(通用兼容)
- PWM:TIM1 高级定时器,中心对齐,3 对互补输出
- ADC:PA0 PA1 双电阻电流采样
- 母线电压:PB0
- 驱动芯片:IR2104 / DRV8301 / EG2131
- 串口:USART1 打印调试信息(Id/Iq/theta/speed)
- PWM 频率:16kHz(ARR=7200-1,72MHz)
2. 工程目录结构(标准嵌入式结构)
Project/
├─ User/
│ ├─ main.c // 主函数、启动逻辑
│ ├─ foc.c/.h // FOC 主流程
│ ├─ transform.c/.h // Clark Park
│ ├─ svpwm.c/.h // SVPWM
│ ├─ smo.c/.h // SMO观测器 + PLL
│ ├─ pid.c/.h // PI控制器
│ └─ current.c/.h // 电流采样、校准
├─ BSP/
│ ├─ bsp_gpio.c/.h
│ ├─ bsp_pwm.c/.h
│ ├─ bsp_adc.c/.h
│ └─ bsp_uart.c/.h
└─ Core/ // 标准库/HAL库文件
3. 关键外设配置(核心代码)
3.1 PWM 配置(TIM1 中心对齐 + 死区)
void PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
// PA8 PA9 PA10 → PWM1/2/3
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 定时器配置:中心对齐模式1
TIM_TimeBaseStruct.TIM_Period = 7199; // 16kHz
TIM_TimeBaseStruct.TIM_Prescaler = 0;
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);
// 通道配置
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStruct);
TIM_OC2Init(TIM1, &TIM_OCInitStruct);
TIM_OC3Init(TIM1, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE); // 主输出使能
TIM_Cmd(TIM1, ENABLE);
// 中心对齐中断
TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE);
NVIC_EnableIRQ(TIM1_CC_IRQn);
}
3.2 ADC 双路同步采样配置
void ADC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
ADC_InitTypeDef ADC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOA, &GPIO_InitStruct);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;
ADC_InitStruct.ADC_ScanConvMode = DISABLE;
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE;
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStruct);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
}
u16 Get_ADC(u8 ch)
{
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
return ADC_GetConversionValue(ADC1);
}
4. 全局 FOC 变量定义
// 电流
float Iu, Iv, Iw;
float Ialpha, Ibeta;
float Id, Iq;
// 电压
float Vd, Vq;
float Valpha, Vbeta;
// 角度与速度
float theta;
float speed_real;
float Iq_ref;
// 校准
u16 Offset_U, Offset_V;
#define CURRENT_GAIN 0.0025f
// PID
PID_t PID_Id, PID_Iq, PID_Speed, PLL_PID;
5. 电流采样与偏置校准
void Current_Calib(void)
{
u32 sumu = 0, sumv = 0;
for(int i=0; i<1024; i++)
{
sumu += Get_ADC(ADC_Channel_0);
sumv += Get_ADC(ADC_Channel_1);
}
Offset_U = sumu / 1024;
Offset_V = sumv / 1024;
}
void Current_Get(void)
{
float adcu = Get_ADC(ADC_Channel_0);
float adcv = Get_ADC(ADC_Channel_1);
Iu = (adcu - Offset_U) * CURRENT_GAIN;
Iv = (adcv - Offset_V) * CURRENT_GAIN;
Iw = -Iu - Iv;
}
6. 完整 FOC 中断函数(最重要)
void TIM1_CC_IRQHandler(void)
{
if(TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);
// 1. 采样电流
Current_Get();
// 2. Clark
Clark(Iu, Iv, Iw, &Ialpha, &Ibeta);
// 3. SMO观测 + PLL角度
SMO_Calc(Valpha, Vbeta, Ialpha, Ibeta);
PLL_Calc();
theta = smo.theta_hat;
// 4. Park
Park(Ialpha, Ibeta, theta, &Id, &Iq);
// 5. 电流环PI
Vd = PID_Calc(&PID_Id, 0.0f, Id);
Vq = PID_Calc(&PID_Iq, Iq_ref, Iq);
// 6. 弱磁控制(可选)
// WeakField_Calc();
7. 反Park
InvPark(Vd, Vq, theta, &Valpha, &Vbeta);
// 8. SVPWM
SVPWM_Calc(Valpha, Vbeta);
}
}
7. 启动流程(上电直接运行)
int main(void)
{
SystemInit();
PWM_Init();
ADC_Init();
UART1_Init();
// 电流校准
Current_Calib();
// PID参数初始化
PID_Init(&PID_Id, 2.0f, 0.1f, 0, 10, -10);
PID_Init(&PID_Iq, 2.0f, 0.1f, 0, 10, -10);
PID_Init(&PID_Speed, 0.5f, 0.05f,0, 5, -5);
PID_Init(&PLL_PID, 10.0f, 20.0f,0, 100,-100);
// SMO参数
smo.Rs = 0.5f;
smo.Ls = 0.001f;
smo.Ts = 1.0f/16000.0f;
smo.K_smo = 0.1f;
// 开环拖动
OpenLoop_Drag();
// 主循环
while(1)
{
if(flag_1ms)
{
flag_1ms = 0;
// 速度环
speed_real = smo.omega_hat / (2*PI) * 60.0f;
Iq_ref = PID_Calc(&PID_Speed, 2000.0f, speed_real);
}
// 串口打印调试
printf("Id=%.2f, Iq=%.2f, speed=%.1f\r\n", Id, Iq, speed_real);
}
}
8. 开环拖动 I/F 启动
void OpenLoop_Drag(void)
{
float open_theta = 0;
int cnt = 0;
while(cnt < 3000) // 拖动约300ms
{
open_theta += 0.005f;
if(open_theta > 2*PI) open_theta -= 2*PI;
// 固定Iq开环
Vq = 0.5f;
Vd = 0;
InvPark(Vd, Vq, open_theta, &Valpha, &Vbeta);
SVPWM_Calc(Valpha, Vbeta);
cnt++;
Delay_ms(1);
}
}
9. 工程使用说明(一步一步跑起来)
- 新建 Keil 工程,选择对应 MCU
- 添加上述所有 .c 文件
- 根据实际电机修改:
smo.Rs相电阻smo.Ls相电感CURRENT_GAIN采样放大倍数- PWM 频率 ARR
- 下载程序
- 串口看打印:Id、Iq、speed 稳定无跳变
- 电机应:
- 启动顺滑
- 运行安静
- 调速线性
10. 常见调试问题
- 电机抖动不转
- 电流方向反 → Iu = -Iu
- 角度方向反 → theta = -theta
- 发热大
- PID 参数过大
- 电流采样不准
- 低速啸叫
- SMO 增益不合理
- PLL 参数不对
- 高速上不去
- 未开弱磁
- 母线电压不足
更多推荐


所有评论(0)