文章目录
-
- 一、技术背景与核心原理
-
- [1.1 核心组件说明](#1.1 核心组件说明)
- [1.2 整体控制流程](#1.2 整体控制流程)
- 二、硬件电路设计
-
- [2.1 核心电路拓扑](#2.1 核心电路拓扑)
- [2.2 引脚分配表](#2.2 引脚分配表)
- 三、软件实现步骤(基于HAL库)
-
- [3.1 开发环境准备](#3.1 开发环境准备)
- [3.2 底层驱动配置(STM32CubeMX)](#3.2 底层驱动配置(STM32CubeMX))
-
- [3.2.1 硬件比较器配置](#3.2.1 硬件比较器配置)
- [3.2.2 DAC配置(比较器参考电压)](#3.2.2 DAC配置(比较器参考电压))
- [3.2.3 TIM1 PWM配置(SVPWM输出)](#3.2.3 TIM1 PWM配置(SVPWM输出))
- [3.3 FOC算法核心实现](#3.3 FOC算法核心实现)
-
- [3.3.1 Clark变换(三相→αβ轴)](#3.3.1 Clark变换(三相→αβ轴))
- [3.3.2 Park变换(αβ→dq轴)](#3.3.2 Park变换(αβ→dq轴))
- [3.3.3 PI调节器(dq轴电流调节)](#3.3.3 PI调节器(dq轴电流调节))
- [3.3.4 反Park变换(dq→αβ轴)](#3.3.4 反Park变换(dq→αβ轴))
- [3.3.5 SVPWM生成](#3.3.5 SVPWM生成)
- [3.4 主函数实现](#3.4 主函数实现)
- 四、调试与验证
-
- [4.1 硬件调试步骤](#4.1 硬件调试步骤)
- [4.2 软件调试步骤](#4.2 软件调试步骤)
- [4.3 常见问题及解决](#4.3 常见问题及解决)
- 五、性能优化
-
- [5.1 算法优化](#5.1 算法优化)
- [5.2 硬件优化](#5.2 硬件优化)
- 总结
一、技术背景与核心原理
磁场定向控制(Field-Oriented Control, FOC)是永磁同步电机(PMSM)高精度控制的核心算法,其本质是将三相静止坐标系下的电流转换为旋转dq坐标系,实现转矩和磁通的解耦控制。STM32F303系列MCU内置硬件比较器(COMP)模块,可替代传统ADC采样实现电流的快速捕获,大幅提升FOC算法的响应速度和控制精度。
1.1 核心组件说明
- STM32F303:内置3个硬件比较器、高级定时器(TIM1/TIM8)、12位ADC,专为电机控制优化
- 硬件比较器(COMP):可将模拟电流信号与参考阈值比较,输出数字电平,响应时间<1μs
- FOC算法核心:Clark变换→Park变换→PI调节→反Park变换→SVPWM输出
1.2 整体控制流程
以下是基于硬件比较器的FOC算法完整执行流程,流程图采用深色底、白色字体设计,保证视觉清晰:
未达标
达标
系统初始化
硬件比较器配置
电流采样通道校准
定时器PWM初始化
电机参数读取
Clark变换
三相电流→αβ轴
Park变换
αβ轴→dq轴
d/q轴PI调节
反Park变换
dq轴→αβ轴
SVPWM生成
驱动逆变器输出
电机转速反馈
稳定运行
二、硬件电路设计
2.1 核心电路拓扑
- 电流采样电路 :采用分流电阻+运算放大器+硬件比较器架构
- 分流电阻:10mΩ/2W,串联在逆变器下桥臂
- 运放:LMV358,将电流信号放大100倍
- COMP输入:运放输出接STM32F303的COMP1_INP引脚(PA0)
- 比较器参考电压:通过DAC输出可调阈值,接COMP1_INM引脚(PA2)
- PWM输出电路:TIM1_CH1~CH6(PA8、PA9、PA10、PB13、PB14、PB15)接驱动芯片IR2104
- 电源电路:3.3V给MCU供电,12V给驱动电路供电,24V给电机供电
2.2 引脚分配表
| 功能 | STM32引脚 | 连接对象 |
|---|---|---|
| COMP1_INP | PA0 | 相电流采样运放输出 |
| COMP1_INM | PA2 | DAC1_OUT1(参考电压) |
| COMP1_OUT | PA1 | TIM1_ETR(触发输入) |
| TIM1_CH1~CH6 | PA8-PA10/PB13-PB15 | IR2104输入 |
| 编码器A/B | PB0/PB1 | 电机编码器输出 |
| UART1 | PA9/PA10 | 调试串口 |
三、软件实现步骤(基于HAL库)
3.1 开发环境准备
- 软件:STM32CubeMX 6.8.0 + Keil MDK 5.38
- 固件库:STM32F3xx HAL库 1.12.0
- 配置步骤:
- 新建工程,选择STM32F303C8T6
- 开启HSE(8MHz外部晶振),系统时钟配置为72MHz
- 开启SWD调试接口
3.2 底层驱动配置(STM32CubeMX)
3.2.1 硬件比较器配置
c
/**
* @brief COMP初始化函数
* @param 无
* @retval 无
*/
void COMP1_Init(void)
{
COMP_HandleTypeDef hcomp1;
// COMP1基础配置
hcomp1.Instance = COMP1;
hcomp1.Init.InvertingInput = COMP_INVERTINGINPUT_DAC1_CH1; // 反相输入接DAC1_CH1
hcomp1.Init.NonInvertingInput = COMP_NONINVERTINGINPUT_IO1; // 同相输入接PA0
hcomp1.Init.Output = COMP_OUTPUT_TIM1_ETR; // 输出接TIM1触发输入
hcomp1.Init.OutputPol = COMP_OUTPUTPOLARITY_NONINVERTED; // 输出极性不反转
hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW; // 低迟滞
hcomp1.Init.BlankingSrce = COMP_BLANKINGSRCE_NONE; // 无消隐
hcomp1.Init.Mode = COMP_MODE_HIGHSPEED; // 高速模式
hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; // 关闭窗口模式
hcomp1.Init.WindowOutput = COMP_WINDOWOUTPUT_EACH; // 窗口输出独立
if (HAL_COMP_Init(&hcomp1) != HAL_OK)
{
Error_Handler(); // 初始化失败处理
}
// 启用COMP1
HAL_COMP_Start(&hcomp1);
}
/**
* @brief COMP MSP初始化函数
* @param hcomp: COMP句柄
* @retval 无
*/
void HAL_COMP_MspInit(COMP_HandleTypeDef* hcomp)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hcomp->Instance==COMP1)
{
// 启用SYSCFG时钟
__HAL_RCC_SYSCFG_CLK_ENABLE();
// 启用GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
/** COMP1 GPIO配置
PA0 ------> COMP1_INP
PA2 ------> COMP1_INM
PA1 ------> COMP1_OUT
*/
GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // 模拟模式
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_COMP1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
3.2.2 DAC配置(比较器参考电压)
c
/**
* @brief DAC初始化函数
* @param 无
* @retval 无
*/
void DAC1_Init(void)
{
DAC_HandleTypeDef hdac1;
DAC_ChannelConfTypeDef sConfig = {0};
// DAC基础配置
hdac1.Instance = DAC1;
if (HAL_DAC_Init(&hdac1) != HAL_OK)
{
Error_Handler();
}
// DAC通道1配置(COMP1参考电压)
sConfig.DAC_Trigger = DAC_TRIGGER_NONE; // 无触发
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // 启用输出缓冲
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 设置初始参考电压(0-3.3V对应0-4095)
// 示例:设置为1.65V(2048)
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 2048);
// 启用DAC通道1
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
}
/**
* @brief DAC MSP初始化函数
* @param hdac: DAC句柄
* @retval 无
*/
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hdac->Instance==DAC1)
{
// 启用DAC时钟
__HAL_RCC_DAC1_CLK_ENABLE();
// 启用GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
/** DAC1 GPIO配置
PA4 ------> DAC1_OUT1
*/
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
}
3.2.3 TIM1 PWM配置(SVPWM输出)
c
/**
* @brief TIM1初始化函数(SVPWM输出)
* @param 无
* @retval 无
*/
void TIM1_PWM_Init(void)
{
TIM_HandleTypeDef htim1;
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
// TIM1基础配置
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0; // 不分频
htim1.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim1.Init.Period = 7199; // 周期=72MHz/(7199+1)=10kHz(SVPWM频率)
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
// PWM通道配置
sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM模式1
sConfigOC.Pulse = 0; // 初始占空比0
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(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// 通道2配置
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
// 通道3配置
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
// 互补通道配置(下桥臂)
if (HAL_TIMEx_PWMN_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_PWMN_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIMEx_PWMN_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
// 死区时间配置(防止上下桥臂直通)
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 100; // 死区时间100ns
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_ENABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
// 启用PWM输出
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_3);
}
/**
* @brief TIM1 MSP初始化函数
* @param htim: TIM1句柄
* @retval 无
*/
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(htim_pwm->Instance==TIM1)
{
// 启用TIM1时钟
__HAL_RCC_TIM1_CLK_ENABLE();
// 启用GPIOA/GPIOB时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/** TIM1 GPIO配置
PA8 ------> TIM1_CH1
PA9 ------> TIM1_CH2
PA10 ------> TIM1_CH3
PB13 ------> TIM1_CH1N
PB14 ------> TIM1_CH2N
PB15 ------> TIM1_CH3N
*/
// 上桥臂PWM输出
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 下桥臂PWM输出
GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF6_TIM1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
}
3.3 FOC算法核心实现
3.3.1 Clark变换(三相→αβ轴)
c
/**
* @brief Clark变换:将三相电流转换为αβ轴电流
* @param Ia: A相电流 (mA)
* @param Ib: B相电流 (mA)
* @param Ic: C相电流 (mA)
* @param Ialpha: α轴电流输出指针
* @param Ibeta: β轴电流输出指针
* @retval 无
*/
void Clark_Transform(int16_t Ia, int16_t Ib, int16_t Ic, float *Ialpha, float *Ibeta)
{
// Clark变换公式(等功率变换)
// Iα = Ia
// Iβ = (Ib - Ic)/√3
*Ialpha = (float)Ia;
*Ibeta = (float)(Ib - Ic) / 1.73205080757f;
// 电流限幅(防止溢出)
if(*Ialpha > 32767.0f) *Ialpha = 32767.0f;
if(*Ialpha < -32768.0f) *Ialpha = -32768.0f;
if(*Ibeta > 32767.0f) *Ibeta = 32767.0f;
if(*Ibeta < -32768.0f) *Ibeta = -32768.0f;
}
3.3.2 Park变换(αβ→dq轴)
c
/**
* @brief Park变换:将αβ轴电流转换为dq轴电流
* @param Ialpha: α轴电流
* @param Ibeta: β轴电流
* @param Theta: 转子电角度 (rad)
* @param Id: d轴电流输出指针
* @param Iq: q轴电流输出指针
* @retval 无
*/
void Park_Transform(float Ialpha, float Ibeta, float Theta, float *Id, float *Iq)
{
float cos_theta = cosf(Theta);
float sin_theta = sinf(Theta);
// Park变换公式
*Id = Ialpha * cos_theta + Ibeta * sin_theta;
*Iq = -Ialpha * sin_theta + Ibeta * cos_theta;
// 电流限幅
if(*Id > 32767.0f) *Id = 32767.0f;
if(*Id < -32768.0f) *Id = -32768.0f;
if(*Iq > 32767.0f) *Iq = 32767.0f;
if(*Iq < -32768.0f) *Iq = -32768.0f;
}
3.3.3 PI调节器(dq轴电流调节)
c
// PI参数结构体
typedef struct
{
float Kp; // 比例系数
float Ki; // 积分系数
float Integral; // 积分项
float OutMax; // 输出上限
float OutMin; // 输出下限
float Ref; // 参考值
float Fdb; // 反馈值
float Out; // 输出值
} PI_HandleTypeDef;
// PI参数初始化
PI_HandleTypeDef PI_Id = {1.0f, 0.1f, 0.0f, 32767.0f, -32768.0f, 0.0f, 0.0f, 0.0f};
PI_HandleTypeDef PI_Iq = {1.0f, 0.1f, 0.0f, 32767.0f, -32768.0f, 0.0f, 0.0f, 0.0f};
/**
* @brief PI调节器计算函数
* @param hpi: PI句柄
* @param Ref: 参考值
* @param Fdb: 反馈值
* @retval PI输出值
*/
float PI_Calc(PI_HandleTypeDef *hpi, float Ref, float Fdb)
{
float error = Ref - Fdb;
// 比例项
hpi->Out = hpi->Kp * error;
// 积分项(积分限幅防止饱和)
hpi->Integral += hpi->Ki * error;
if(hpi->Integral > hpi->OutMax) hpi->Integral = hpi->OutMax;
if(hpi->Integral < hpi->OutMin) hpi->Integral = hpi->OutMin;
// 总输出
hpi->Out += hpi->Integral;
// 输出限幅
if(hpi->Out > hpi->OutMax) hpi->Out = hpi->OutMax;
if(hpi->Out < hpi->OutMin) hpi->Out = hpi->OutMin;
// 更新参数
hpi->Ref = Ref;
hpi->Fdb = Fdb;
return hpi->Out;
}
3.3.4 反Park变换(dq→αβ轴)
c
/**
* @brief 反Park变换:将dq轴电压转换为αβ轴电压
* @param Vd: d轴电压
* @param Vq: q轴电压
* @param Theta: 转子电角度 (rad)
* @param Valpha: α轴电压输出指针
* @param Vbeta: β轴电压输出指针
* @retval 无
*/
void InvPark_Transform(float Vd, float Vq, float Theta, float *Valpha, float *Vbeta)
{
float cos_theta = cosf(Theta);
float sin_theta = sinf(Theta);
// 反Park变换公式
*Valpha = Vd * cos_theta - Vq * sin_theta;
*Vbeta = Vd * sin_theta + Vq * cos_theta;
// 电压限幅
if(*Valpha > 32767.0f) *Valpha = 32767.0f;
if(*Valpha < -32768.0f) *Valpha = -32768.0f;
if(*Vbeta > 32767.0f) *Vbeta = 32767.0f;
if(*Vbeta < -32768.0f) *Vbeta = -32768.0f;
}
3.3.5 SVPWM生成
c
/**
* @brief SVPWM生成函数
* @param Valpha: α轴电压
* @param Vbeta: β轴电压
* @param htim: TIM句柄
* @retval 无
*/
void SVPWM_Generate(float Valpha, float Vbeta, TIM_HandleTypeDef *htim)
{
float T0, T1, T2;
float Ta, Tb, Tc;
uint32_t CCR1, CCR2, CCR3;
float Vdc = 24.0f; // 母线电压(V)
float Ts = 100.0f; // PWM周期(μs)
float Vref = Vdc / 2.0f; // 参考电压
// 计算扇区
float U = Valpha;
float V = (-0.5f * Valpha) + (0.8660254f * Vbeta);
float W = (-0.5f * Valpha) - (0.8660254f * Vbeta);
uint8_t sector = 0;
if(U>0) sector |= 0x01;
if(V>0) sector |= 0x02;
if(W>0) sector |= 0x04;
// 计算基本电压矢量作用时间
float X = Vbeta / Vref * Ts;
float Y = (Valpha * 0.8660254f + Vbeta * 0.5f) / Vref * Ts;
float Z = (-Valpha * 0.8660254f + Vbeta * 0.5f) / Vref * Ts;
// 根据扇区计算T1、T2
switch(sector)
{
case 1: T1=Z; T2=Y; break;
case 2: T1=X; T2=Z; break;
case 3: T1=X; T2=Y; break;
case 4: T1=Y; T2=X; break;
case 5: T1=Z; T2=X; break;
case 6: T1=Y; T2=Z; break;
default: T1=0; T2=0; break;
}
// 计算零矢量作用时间
T0 = Ts - T1 - T2;
// 计算各相占空比
switch(sector)
{
case 1:
Ta = (Ts - T1 - T2)/4;
Tb = Ta + T1/2;
Tc = Tb + T2/2;
break;
case 2:
Ta = (Ts - T1 - T2)/4 + T2/2;
Tb = (Ts - T1 - T2)/4;
Tc = Ta + T1/2;
break;
case 3:
Ta = (Ts - T1 - T2)/4 + T1/2;
Tb = (Ts - T1 - T2)/4;
Tc = Tb + T2/2;
break;
case 4:
Ta = (Ts - T1 - T2)/4 + T1/2 + T2/2;
Tb = (Ts - T1 - T2)/4 + T2/2;
Tc = (Ts - T1 - T2)/4;
break;
case 5:
Ta = (Ts - T1 - T2)/4 + T2/2;
Tb = (Ts - T1 - T2)/4 + T1/2 + T2/2;
Tc = (Ts - T1 - T2)/4;
break;
case 6:
Ta = (Ts - T1 - T2)/4;
Tb = (Ts - T1 - T2)/4 + T1/2 + T2/2;
Tc = (Ts - T1 - T2)/4 + T1/2;
break;
default:
Ta = Ts/2;
Tb = Ts/2;
Tc = Ts/2;
break;
}
// 转换为CCR寄存器值(72MHz时钟,10kHz PWM)
CCR1 = (uint32_t)(Ta * 72.0f);
CCR2 = (uint32_t)(Tb * 72.0f);
CCR3 = (uint32_t)(Tc * 72.0f);
// 限幅保护
if(CCR1 > 7199) CCR1 = 7199;
if(CCR2 > 7199) CCR2 = 7199;
if(CCR3 > 7199) CCR3 = 7199;
if(CCR1 < 0) CCR1 = 0;
if(CCR2 < 0) CCR2 = 0;
if(CCR3 < 0) CCR3 = 0;
// 更新PWM占空比
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_1, CCR1);
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_2, CCR2);
__HAL_TIM_SET_COMPARE(htim, TIM_CHANNEL_3, CCR3);
}
3.4 主函数实现
c
// 全局变量定义
float Ia, Ib, Ic; // 三相电流
float Ialpha, Ibeta; // αβ轴电流
float Id, Iq; // dq轴电流
float Vd, Vq; // dq轴电压
float Valpha, Vbeta; // αβ轴电压
float Theta = 0.0f; // 转子电角度
uint16_t Speed_Ref = 1000; // 转速参考值(rpm)
int main(void)
{
// 1. 系统初始化
HAL_Init();
SystemClock_Config(); // 配置72MHz系统时钟
MX_GPIO_Init(); // GPIO初始化
MX_USART1_UART_Init(); // 串口初始化(115200bps)
COMP1_Init(); // 比较器初始化
DAC1_Init(); // DAC初始化
TIM1_PWM_Init(); // PWM初始化
// 2. 电机参数初始化
PI_Id.Ref = 0.0f; // d轴电流参考值(弱磁控制时可调整)
PI_Iq.Ref = 1000.0f; // q轴电流参考值(对应转矩)
// 3. 主循环
while (1)
{
// 3.1 读取三相电流(通过硬件比较器捕获)
Ia = COMP_Get_Current(1); // A相电流
Ib = COMP_Get_Current(2); // B相电流
Ic = COMP_Get_Current(3); // C相电流
// 3.2 Clark变换
Clark_Transform(Ia, Ib, Ic, &Ialpha, &Ibeta);
// 3.3 读取转子电角度(编码器或霍尔传感器)
Theta = Encoder_Get_Theta();
// 3.4 Park变换
Park_Transform(Ialpha, Ibeta, Theta, &Id, &Iq);
// 3.5 PI调节
Vd = PI_Calc(&PI_Id, PI_Id.Ref, Id);
Vq = PI_Calc(&PI_Iq, PI_Iq.Ref, Iq);
// 3.6 反Park变换
InvPark_Transform(Vd, Vq, Theta, &Valpha, &Vbeta);
// 3.7 SVPWM生成
SVPWM_Generate(Valpha, Vbeta, &htim1);
// 3.8 转速闭环(可选)
uint16_t Speed_Fdb = Encoder_Get_Speed();
if(Speed_Fdb < Speed_Ref)
{
PI_Iq.Ref += 10.0f; // 增加q轴电流,提升转矩
if(PI_Iq.Ref > 3000.0f) PI_Iq.Ref = 3000.0f;
}
else if(Speed_Fdb > Speed_Ref)
{
PI_Iq.Ref -= 10.0f; // 减小q轴电流,降低转矩
if(PI_Iq.Ref < 0.0f) PI_Iq.Ref = 0.0f;
}
// 3.9 调试信息输出
HAL_Delay(10);
printf("Speed:%d rpm, Ia:%d mA, Ib:%d mA, Ic:%d mA\r\n",
Speed_Fdb, (int)Ia, (int)Ib, (int)Ic);
}
}
/**
* @brief 硬件比较器电流读取函数
* @param phase: 相序(1-A相,2-B相,3-C相)
* @retval 电流值 (mA)
*/
int16_t COMP_Get_Current(uint8_t phase)
{
int16_t current = 0;
switch(phase)
{
case 1:
// 读取COMP1输出电平,计算A相电流
if(HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUTLEVEL_HIGH)
{
current = (int16_t)(HAL_DAC_GetValue(&hdac1, DAC_CHANNEL_1) * 0.1f);
}
else
{
current = -(int16_t)(HAL_DAC_GetValue(&hdac1, DAC_CHANNEL_1) * 0.1f);
}
break;
case 2:
// B相电流读取(COMP2)
// 代码同A相,略
break;
case 3:
// C相电流读取(COMP3)
// 代码同A相,略
break;
default:
current = 0;
break;
}
return current;
}
/**
* @brief 编码器角度读取函数
* @retval 转子电角度 (rad)
*/
float Encoder_Get_Theta(void)
{
// 读取编码器计数值,转换为电角度
static uint16_t encoder_cnt = 0;
encoder_cnt = __HAL_TIM_GET_COUNTER(&htim2); // TIM2为编码器定时器
float theta = (float)encoder_cnt * 2 * 3.1415926f / 1024.0f; // 1024线编码器
return theta;
}
/**
* @brief 编码器转速读取函数
* @retval 转速 (rpm)
*/
uint16_t Encoder_Get_Speed(void)
{
static uint16_t last_cnt = 0;
uint16_t curr_cnt = __HAL_TIM_GET_COUNTER(&htim2);
uint16_t diff = curr_cnt - last_cnt;
if(diff > 512) diff = 1024 - diff;
last_cnt = curr_cnt;
// 计算转速 (rpm) = (脉冲数 * 60) / (编码器线数 * 减速比 * 采样时间)
uint16_t speed = (uint16_t)((float)diff * 60.0f / (1024.0f * 1.0f * 0.01f));
return speed;
}
/**
* @brief 错误处理函数
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
// 关闭PWM输出
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_2);
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_3);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_1);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_2);
HAL_TIMEx_PWMN_Stop(&htim1, TIM_CHANNEL_3);
// 输出错误信息
printf("Error occurred! PWM stopped.\r\n");
// 死循环
while(1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 闪烁LED提示错误
HAL_Delay(500);
}
}
四、调试与验证
4.1 硬件调试步骤
- 电源调试 :
- 测量3.3V/12V/24V电源电压,确保稳定无纹波
- 检查驱动芯片IR2104的VCC和VDD电压
- 比较器调试 :
- 用信号发生器输入0-3.3V模拟电流信号,测量COMP输出电平是否正确
- 调整DAC参考电压,验证比较器阈值是否准确
- PWM调试 :
- 示波器测量TIM1输出PWM波形,检查频率(10kHz)和死区时间(100ns)
- 空载时测量三相PWM占空比是否对称
4.2 软件调试步骤
- 串口调试 :
- 查看串口输出的电流、转速数据是否合理
- 验证Clark/Park变换结果是否正确
- 电机空载调试 :
- 逐步增加q轴电流参考值,观察电机是否平稳启动
- 测量电机相电流,确保无过流
- 带载调试 :
- 加载额定负载,验证转速和转矩是否稳定
- 检查电机温升,确保在正常范围内
4.3 常见问题及解决
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 电机无法启动 | PWM输出异常 | 检查TIM1配置,确认死区时间设置 |
| 电机抖动 | 电流采样误差大 | 校准比较器阈值,优化电流采样电路 |
| 电流过流保护 | PI参数过大 | 减小PI_Kp和PI_Ki值,增加积分限幅 |
| 转速波动大 | 编码器信号干扰 | 编码器信号线增加屏蔽层,接地处理 |
| 比较器输出不稳定 | 模拟信号噪声 | 增加RC滤波电路,优化PCB布线 |
五、性能优化
5.1 算法优化
- 定点数优化:将浮点数运算改为定点数运算,提升运算速度
- 查表法优化:将sin/cos函数替换为查表法,减少计算时间
- 中断优化:将FOC算法放在TIM1更新中断中执行,保证实时性
5.2 硬件优化
- PCB布线 :
- 模拟地和数字地分开,单点接地
- 电流采样电路远离功率电路,减少电磁干扰
- 滤波电路 :
- 在COMP输入引脚增加RC滤波(1kΩ+100nF)
- 电源输入端增加π型滤波电路
总结
- STM32F303硬件比较器可实现电流的快速采样,相比ADC采样大幅提升FOC算法响应速度,核心优势是<1μs的响应时间和硬件级别的过流保护能力。
- FOC算法实现的核心步骤为:Clark变换→Park变换→PI调节→反Park变换→SVPWM生成,每个环节都需做好参数限幅和异常处理以保证系统稳定。
- 实际落地时需重点关注硬件电路的抗干扰设计和软件算法的实时性优化,调试过程应遵循"先硬件后软件、先空载后带载"的原则,逐步验证每个模块的功能。