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. 工程使用说明(一步一步跑起来)

  1. 新建 Keil 工程,选择对应 MCU
  2. 添加上述所有 .c 文件
  3. 根据实际电机修改:
    • smo.Rs 相电阻
    • smo.Ls 相电感
    • CURRENT_GAIN 采样放大倍数
    • PWM 频率 ARR
  4. 下载程序
  5. 串口看打印:Id、Iq、speed 稳定无跳变
  6. 电机应:
    • 启动顺滑
    • 运行安静
    • 调速线性

10. 常见调试问题

  1. 电机抖动不转
    • 电流方向反 → Iu = -Iu
    • 角度方向反 → theta = -theta
  2. 发热大
    • PID 参数过大
    • 电流采样不准
  3. 低速啸叫
    • SMO 增益不合理
    • PLL 参数不对
  4. 高速上不去
    • 未开弱磁
    • 母线电压不足

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐