CW32L011无感无刷驱动器代码详解

CW32L011无感无刷驱动器代码详解

概述

本文基于CW32开源项目,项目地址为:https://oshwhub.com/beauty_light/cw32l011-dian-ji-qu-dong

CW32官网为:https://www.whxy.com/

水平有限,如有错误请指正谅解。

硬件平台: CW32L011 Cortex-M0+ MCU (96MHz)
驱动方式: 三相PWM驱动(6步换向)
控制方式: 反电动势过零检测无传感器控制
PWM频率: 15kHz
项目结构:

bldc project/
├── USER/
│   ├── src/main.c              # 主程序入口
│   ├── src/interrupts_cw32l011.c # 中断处理
│   └── inc/main.h              # 头文件
└── BLDC-MOTOR/
    ├── init.C                   # 外设初始化
    ├── global.c/global.h        # 全局变量和宏定义
    ├── motor.c/motor.h          # 电机换向控制
    ├── sensorless.c/sensorless.h # 无传感器控制(过零检测)
    ├── control.c/control.h      # 电机状态机控制
    └── compu.c/compu.h          # 采样计算函数

第1部分:外设初始化详解

所有外设初始化在main()函数中按顺序调用,位于init.C文件中。

1.1 系统时钟配置 (RCC_Configuration)

void RCC_Configuration(void)
{
    SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV1); // 96MHz
}

功能: 启用内部高速时钟(HSI)并配置为96MHz主频

时钟树分析:

graph TD A[HSI OSC<br/>内部高速时钟] --> B[SYSCTRL_HSIOSC_DIV1<br/>1分频] B --> C[系统时钟 SYSCLK<br/>96MHz] C --> D[ATIM<br/>高级定时器] C --> E[BTIM1/2/3<br/>基础定时器] C --> F[ADC<br/>模数转换器] C --> G[GPIO<br/>通用输入输出] D --> D1[PWM输出<br/>15kHz三相] E --> E1[定时中断<br/>1ms] E --> E2[换向计时<br/>BTIM2] E --> E3[延迟换向<br/>BTIM3] F --> F1[6通道采样<br/>BEMF/电压/电流]

说明: CW32L011使用内部RC振荡器,通过分频器配置为96MHz,为所有外设提供时钟源

1.2 GPIO引脚配置

代码中定义了以下引脚宏(定义在global.h):

1.2.1 控制引脚

引脚 端口 编号 功能 模式
LED PC13 Pin_13 运行指示灯 推挽输出
DR PA9 Pin_9 方向控制输入 上拉输入
EN PC15 Pin_15 使能控制输入 上拉输入
FG PA12 Pin_12 转速反馈输出 推挽输出

控制逻辑:

  • EN引脚: 外部开关控制电机使能
    • EN = 1: 电机可运行
    • EN = 0: 电机停止
  • DR引脚: 外部开关控制旋转方向
    • DR = 1: 正转
    • DR = 0: 反转

1.2.2 PWM输出引脚 (三相桥臂)

引脚 端口 编号 功能 复用功能
PWM_AH PB5 Pin_5 A相上桥臂 ATIM_CH1
PWM_AL PA15 Pin_15 A相下桥臂 普通GPIO
PWM_BH PB6 Pin_6 B相上桥臂 ATIM_CH2
PWM_BN PB3 Pin_3 B相下桥臂 普通GPIO
PWM_CH PB7 Pin_7 C相上桥臂 ATIM_CH3
PWM_CN Pin_4 Pin_4 C相下桥臂 普通GPIO

1.2.3 GPIO操作宏定义

// LED控制
#define LEDON    GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_Pin_RESET)  // 低电平点亮
#define LEDOFF   GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_Pin_SET)   // 高电平熄灭

// 上桥臂控制(通过ATIM PWM输出)
#define PWM_AH_ON  GPIO_WritePin(PWM_AP_PORT, PWM_AP_PIN, GPIO_Pin_SET)
#define PWM_AH_OFF GPIO_WritePin(PWM_AP_PORT, PWM_AP_PIN, GPIO_Pin_RESET)
#define PWM_BH_ON  GPIO_WritePin(PWM_BP_PORT, PWM_BP_PIN, GPIO_Pin_SET)
#define PWM_BH_OFF GPIO_WritePin(PWM_BP_PORT, PWM_BP_PIN, GPIO_Pin_RESET)
#define PWM_CH_ON  GPIO_WritePin(PWM_CP_PORT, PWM_CP_PIN, GPIO_Pin_SET)
#define PWM_CH_OFF GPIO_WritePin(PWM_CP_PORT, PWM_CP_PIN, GPIO_Pin_RESET)

// 下桥臂控制(通过GPIO直接控制)
#define PWM_AL_ON  GPIO_WritePin(PWM_AN_PORT, PWM_AN_PIN, GPIO_Pin_SET)
#define PWM_AL_OFF GPIO_WritePin(PWM_AN_PORT, PWM_AN_PIN, GPIO_Pin_RESET)
#define PWM_BL_ON  GPIO_WritePin(PWM_BN_PORT, PWM_BN_PIN, GPIO_Pin_SET)
#define PWM_BL_OFF GPIO_WritePin(PWM_BN_PORT, PWM_BN_PIN, GPIO_Pin_RESET)
#define PWM_CL_ON  GPIO_WritePin(PWM_CN_PORT, PWM_CN_PIN, GPIO_Pin_SET)
#define PWM_CL_OFF GPIO_WritePin(PWM_CN_PORT, PWM_CN_PIN, GPIO_Pin_RESET)

1.2.4 初始化函数详解

void LEDConfiguration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __SYSCTRL_GPIOA_CLK_ENABLE(); 
    __SYSCTRL_GPIOB_CLK_ENABLE(); 
    __SYSCTRL_GPIOC_CLK_ENABLE(); 
        
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出
    GPIO_InitStruct.Pins = LED_GPIO_PIN;
    GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);
        
    LEDON;    // 默认点亮LED表示上电
}

void DR_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // ... 时钟使能 ...
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP;  // 上拉输入
    GPIO_InitStruct.Pins = DR_GPIO_PIN;
    GPIO_Init(DR_GPIO_PORT, &GPIO_InitStruct);  
}

void EN_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // ... 时钟使能 ...
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP;  // 上拉输入
    GPIO_InitStruct.Pins = EN_GPIO_PIN;
    GPIO_Init(EN_GPIO_PORT, &GPIO_InitStruct);  
}

void FG_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    // ... 时钟使能 ...
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  // 推挽输出
    GPIO_InitStruct.Pins = FG_GPIO_PIN;
    GPIO_Init(FG_GPIO_PORT, &GPIO_InitStruct);  
}

1.3 ADC配置 (ADC_Configuration)

功能: 配置6通道ADC用于采集模拟信号

1.3.1 采样通道分配

数组索引 引脚 ADC通道 功能 用途
SampleData[0] PA06 AIN6 U相端电压 BEMF检测
SampleData[1] PA07 AIN7 V相端电压 BEMF检测
SampleData[2] PB00 AIN8 W相端电压 BEMF检测
SampleData[3] PA03 AIN3 反电动势 过零检测
SampleData[4] PB01 AIN9 电位器 速度设定
SampleData[5] PA08 AIN10 电源电压 过压/欠压检测

1.3.2 引脚配置

// 使能ADC和GPIO时钟
__SYSCTRL_ADC_CLK_ENABLE();
__SYSCTRL_GPIOA_CLK_ENABLE();
__SYSCTRL_GPIOB_CLK_ENABLE();

// 配置模拟输入引脚
PA06_ANALOG_ENABLE();   // AIN6 - U相电压
PA07_ANALOG_ENABLE();   // AIN7 - V相电压
PB00_ANALOG_ENABLE();   // AIN8 - W相电压
PB01_ANALOG_ENABLE();   // AIN9 - 电位器
PA08_ANALOG_ENABLE();   // AIN10 - 电源电压
PA03_ANALOG_ENABLE();   // AIN3 - BEMF
PA04_ANALOG_ENABLE();   // AIN4 - 备用
PA05_ANALOG_ENABLE();   // AIN5 - 备用

1.3.3 ADC初始化配置

ADC_InitTypeDef ADC_InitStructure = {0};

// ADC时钟: 48MHz / (15+1) = 2MHz (满足ADC最高14MHz要求)
ADC_InitStructure.ADC_ClkDiv = ADC_Clk_Div16;  // 或者 ADC_Clk_Div2 配合其他分频

// 单次转换模式(不是连续扫描)
ADC_InitStructure.ADC_ConvertMode = ADC_ConvertMode_OneShot;

// 使能所有6个序列通道
ADC_InitStructure.ADC_SQREns = ADC_SqrEns0to5;

// 配置各通道
// 通道0: AIN6 (U相) - SampleData[0]
ADC_InitStructure.ADC_IN0.ADC_InputChannel = ADC_InputCH6;
ADC_InitStructure.ADC_IN0.ADC_SampTime = ADC_SampTime9Clk;

// 通道1: AIN7 (V相) - SampleData[1]
ADC_InitStructure.ADC_IN1.ADC_InputChannel = ADC_InputCH7;
ADC_InitStructure.ADC_IN1.ADC_SampTime = ADC_SampTime9Clk;

// 通道2: AIN8 (W相) - SampleData[2]
ADC_InitStructure.ADC_IN2.ADC_InputChannel = ADC_InputCH8;
ADC_InitStructure.ADC_IN2.ADC_SampTime = ADC_SampTime9Clk;

// 通道3: AIN3 (BEMF) - SampleData[3]
ADC_InitStructure.ADC_IN3.ADC_InputChannel = ADC_InputCH3;
ADC_InitStructure.ADC_IN3.ADC_SampTime = ADC_SampTime9Clk;

// 通道4: AIN9 (电位器) - SampleData[4]
ADC_InitStructure.ADC_IN4.ADC_InputChannel = ADC_InputCH9;
ADC_InitStructure.ADC_IN4.ADC_SampTime = ADC_SampTime9Clk;

// 通道5: AIN10 (电源电压) - SampleData[5]
ADC_InitStructure.ADC_IN5.ADC_InputChannel = ADC_InputCH10;
ADC_InitStructure.ADC_IN5.ADC_SampTime = ADC_SampTime9Clk;

ADC_Init(&ADC_InitStructure);

// 使能ADC
ADC_Enable();

1.3.4 ADC触发和中断配置

// 清除中断标志
ADC_ClearITPendingAll();

// 使能序列转换完成中断 (EOS = End Of Sequence)
ADC_ITConfig(ADC_IT_EOS, ENABLE);

// 使能NVIC中的ADC中断
NVIC_EnableIRQ(ADC_IRQn);

// 配置外部触发:ATIM CC4事件触发ADC转换
// ATIM_CH4的比较事件触发ADC,确保与PWM同步
ADC_ExtTrigCfg(ADC_TRIG_ATIMCC4, ENABLE);

关键设计点:

  • ADC由ATIM_CH4触发,确保每次PWM周期中间采样
  • 采样时间9个ADC时钟,在2MHz ADC时钟下约4.5μs
  • 6通道顺序采样总时间约27μs

1.4 BLDC PWM配置 (BLDC_Configuration)

功能: 配置高级定时器(ATIM)产生三相PWM波形

1.4.1 PWM参数计算

#define PWM_PERIOD 6400    // PWM周期值
// PWM频率 = 96MHz / PWM_PERIOD = 96MHz / 6400 = 15kHz
// PWM分辨率 = 6400 = 2^12.65 ≈ 12.65位

#define OnepercentPWM (PWM_PERIOD) / 100  // 64
// 1%占空比对应的CCR值

1.4.2 引脚复用配置

void BLDC_Configuration(void)
{
    // 使能时钟
    __SYSCTRL_ATIM_CLK_ENABLE();
    __SYSCTRL_GPIOA_CLK_ENABLE();
    __SYSCTRL_GPIOB_CLK_ENABLE();
    
    // 配置上桥臂引脚为复用功能
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pins = PWM_AP_PIN | PWM_BP_PIN; 
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
    GPIO_InitStruct.Pins = PWM_CP_PIN;
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
    
    // 配置引脚复用为ATIM功能
    PB05_AFx_ATIMCH1();  // PB5 → ATIM_CH1 (A相上桥)
    PB06_AFx_ATIMCH2();  // PB6 → ATIM_CH2 (B相上桥)
    PB07_AFx_ATIMCH3();  // PB7 → ATIM_CH3 (C相上桥)
    
    // 配置下桥臂引脚为普通GPIO
    GPIO_InitStruct.IT = GPIO_IT_NONE;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pins = PWM_AN_PIN;  // PA15
    GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
    GPIO_InitStruct.Pins = PWM_BN_PIN | PWM_CN_PIN;  // PB3, PB4
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
    
    // 初始化:下桥臂全部导通(续流)
    PWM_AL_ON;
    PWM_BL_ON;
    PWM_CL_ON;
}

1.4.3 ATIM定时器初始化

ATIM_InitTypeDef ATIM_InitStruct;
ATIM_OCInitTypeDef ATIM_OCInitStruct = {0};

// 基础定时器配置
ATIM_InitStruct.BufferState = ENABLE;              // 使能影子寄存器
ATIM_InitStruct.CounterAlignedMode = ATIM_COUNT_ALIGN_MODE_EDGE;  // 边缘对齐
ATIM_InitStruct.CounterDirection = ATIM_COUNTING_UP;              // 向上计数
ATIM_InitStruct.CounterOPMode = ATIM_OP_MODE_REPETITIVE;          // 重复计数模式
ATIM_InitStruct.Prescaler = 1 - 1;           // 预分频 = 1, 时钟 = 96MHz
ATIM_InitStruct.ReloadValue = PWM_PERIOD - 1;  // 自动重装载 = 6400-1 = 6399
ATIM_InitStruct.RepetitionCounter = 0;       // 重复计数器 = 0
ATIM_Init(&ATIM_InitStruct);

1.4.4 PWM输出通道配置

// 输出比较初始化
ATIM_OCInitStruct.BufferState = ENABLE;      // 使能CCR影子寄存器
ATIM_OCInitStruct.OCComplement = DISABLE;    // 不使用互补通道
ATIM_OCInitStruct.OCFastMode = DISABLE;     // 禁用快速模式
ATIM_OCInitStruct.OCInterruptState = DISABLE; // 不使用输出比较中断
ATIM_OCInitStruct.OCMode = ATIM_OCMODE_PWM1;  // PWM1模式:高电平有效
ATIM_OCInitStruct.OCPolarity = ATIM_OCPOLARITY_NONINVERT; // 正极性

// 初始化4个通道
ATIM_OC1Init(&ATIM_OCInitStruct);  // A相
ATIM_OC2Init(&ATIM_OCInitStruct);  // B相
ATIM_OC3Init(&ATIM_OCInitStruct);  // C相
ATIM_OC4Init(&ATIM_OCInitStruct);  // ADC触发

// 设置初始占空比
ATIM_SetCompare1(0);   // A相占空比 0%
ATIM_SetCompare2(0);   // B相占空比 0%
ATIM_SetCompare3(0);   // C相占空比 0%
ATIM_SetCompare4(PWM_PERIOD / 2);  // ADC触发点:周期中部

// 使能各通道输出
ATIM_CH1Config(ENABLE);
ATIM_CH2Config(ENABLE);
ATIM_CH3Config(ENABLE);
ATIM_CH4Config(ENABLE);

// 使能PWM主输出
ATIM_CtrlPWMOutputs(ENABLE);

// 使能定时器
ATIM_Cmd(ENABLE);

PWM时序图:

gantt title PWM周期时序图 (15kHz = 66.67μs) dateFormat X axisFormat %s section 计数器 计数器 0→6399 :0, 6400 section PWM输出 高电平 (占空比) :0, 3199 低电平 :3199, 6400 section ADC触发 CCR4触发点 :crit, 3200, 3201

1.5 基础定时器配置

1.5.1 BTIM1 - 系统定时器 (1ms中断)

void BTIM1_Configuration(void)
{
    BTIM_TimeBaseInitTypeDef BTIM_TimeBaseInitStruct = {0};
    
    // 使能BTIM时钟
    __SYSCTRL_BTIM123_CLK_ENABLE();
    
    // 使能NVIC中断
    __disable_irq();
    NVIC_EnableIRQ(BTIM1_IRQn);
    __enable_irq();
    
    // 定时器配置
    BTIM_TimeBaseInitStruct.BTIM_Mode = BTIM_MODE_TIMER;           // 定时器模式
    BTIM_TimeBaseInitStruct.BTIM_CountMode = BTIM_COUNT_MODE_REPETITIVE;  // 重复模式
    BTIM_TimeBaseInitStruct.BTIM_Period = 1000 - 1;                // 1000计数
    BTIM_TimeBaseInitStruct.BTIM_Prescaler = 96 - 1;              // 96分频 = 1MHz
    
    // 定时周期 = 1MHz / 1000 = 1kHz = 1ms
    BTIM_TimeBaseInit(CW_BTIM1, &BTIM_TimeBaseInitStruct);
    
    // 使能更新中断
    BTIM_ITConfig(CW_BTIM1, BTIM_IT_UPDATE, ENABLE);
    
    // 使能定时器
    BTIM_Cmd(CW_BTIM1, ENABLE);
}

1.5.2 SENSORLESS_TIM_Config - 换向定时器

void SENSORLESS_TIM_Config(void)
{
    BTIM_TimeBaseInitTypeDef BTIM_InitStruct = {0};
    
    // 使能BTIM2和BTIM3时钟
    __SYSCTRL_BTIM123_CLK_ENABLE();
    
    // 使能BTIM3中断
    __disable_irq(); 
    NVIC_EnableIRQ(BTIM3_IRQn); 
    __enable_irq();
    
    // BTIM2和BTIM3配置相同
    BTIM_InitStruct.BTIM_Mode = BTIM_MODE_TIMER;
    BTIM_InitStruct.BTIM_CountMode = BTIM_COUNT_MODE_REPETITIVE;
    BTIM_InitStruct.BTIM_Period = 65530;         // 最大周期
    BTIM_InitStruct.BTIM_Prescaler = 12 - 1;     // 12分频 = 8MHz
    
    BTIM_TimeBaseInit(CW_BTIM2, &BTIM_InitStruct);
    BTIM_TimeBaseInit(CW_BTIM3, &BTIM_InitStruct);
    
    // BTIM2: 自由运行计数器(用于测量换向间隔)
    BTIM_Cmd(CW_BTIM2, ENABLE);
    
    // BTIM3: 换向定时器(初始禁用,由软件触发)
    BTIM_Cmd(CW_BTIM3, DISABLE);
    
    // 使能BTIM3更新中断
    BTIM_ITConfig(CW_BTIM3, BTIM_IT_UPDATE, ENABLE);
}

定时器用途:

  • BTIM2: 自由运行计数器,每次换向时读取计数值,计算电机实际转速
  • BTIM3: 延迟换向定时器,过零检测后延迟固定时间触发换向

第2部分:各外设中断详解

2.1 BTIM1_IRQHandler - 系统定时器中断

位置: main.c:117-139

触发频率: 1kHz (每1ms触发一次)

中断处理流程:

void BTIM1_IRQHandler(void)
{
    // 检查是否为更新中断
    if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_UPDATE))
    {
        // 清除中断标志
        BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_UPDATE); 
        
        // 1. ADC采样计数(用于控制电流采样频率)
        Icount++;       
        
        // 2. 通用临时计时器(用于软件延时)
        TimeCountTemp++;       
        
        // 3. 速度设定采样计时(每20ms采样一次)
        TimeCountCompuSpeed++; 
        
        // 4. 启动延时计时
        TimeCountSTDelay++;   
        
        // 5. 瞬时速度计时
        TimeCountRealSpd++;   
        if(TimeCountRealSpd >= 20)  // 20ms
        {
            HALLcountTemp = HALLcount;  // 保存瞬时计数值
            HALLcount = 0;               // 清零重新计数
            TimeCountRealSpd = 0;
        }
        
        // 6. PID控制计时
        TimeCountPID++;        
        
        // 7. 平均速度计时(100ms)
        TimeCountAvgSpd++;     
        if(TimeCountAvgSpd >= 100)
        {
            hte = HALLcount1;   // 保存100ms内的换向次数
            HALLcount1 = 0;
            TimeCountAvgSpd = 0;
        }
        
        // 8. 启动加速计时
        TimeCountStartUP++;    
        
        // 9. 电压电流采样计时(100ms)
        TimeCountVI++;         
    }
}

时间参数汇总:

变量 周期 用途
Icount 每2次(2ms) 控制电流采样
TimeCountCompuSpeed 20ms 速度设定采样
TimeCountSTDelay 可变 启动延时
TimeCountRealSpd 20ms 瞬时速度计算
TimeCountPID 可变 PWM调节
TimeCountAvgSpd 100ms 平均速度计算
TimeCountVI 100ms 电压电流采样

2.2 ADC_IRQHandler - ADC转换完成中断

位置: sensorless.c:73-120

触发条件: ADC序列转换完成(EOS = End Of Sequence)

执行流程:

void ADC_IRQHandler(void)
{
    // 检查序列结束标志
    if (CW_ADC->ISR_f.EOS)
    {
        // 1. 清除中断标志
        ADC_ClearITPendingAll();
        
        // 2. 读取6通道转换结果
        ADC_GetSqr3Result(&SampleData[0]);  // U相 - 通道3
        ADC_GetSqr1Result(&SampleData[1]);  // V相 - 通道1
        ADC_GetSqr2Result(&SampleData[2]);  // W相 - 通道2
        ADC_GetSqr0Result(&SampleData[3]);  // BEMF - 通道0
        ADC_GetSqr4Result(&SampleData[4]);  // 电位器 - 通道4
        ADC_GetSqr5Result(&SampleData[5]);  // 电源电压 - 通道5
        
        // 3. 电机运行时的电流采样(用于过零检测)
        if(Motor_Start_F == 1)
        {
            // 根据当前换向步骤选择对应的相进行采样
            if(bHallStartStep1 == 0 || bHallStartStep1 == 5)  // AB相通电
            {
                if(SampleData[1] < DIin)  // V相 < 虚拟中性点
                {
                    bb += DIin - SampleData[1];
                    bbc++;
                    if(bbc >= 30)
                    {
                        bbc = 0;
                        sumb = bb;
                        bb = 0;
                    }
                }
            }
            else if(bHallStartStep1 == 1 || bHallStartStep1 == 2)  // AC相通电
            {
                if(SampleData[2] < DIin)  // W相 < 虚拟中性点
                {
                    cc += DIin - SampleData[2];
                    ccc++;
                    if(ccc >= 30)
                    {
                        ccc = 0;
                        sumc = cc;
                        cc = 0;
                    }
                }
            }
            else if(bHallStartStep1 == 3 || bHallStartStep1 == 4)  // BA相通电
            {
                if(SampleData[0] < DIin)  // U相 < 虚拟中性点
                {
                    aa += DIin - SampleData[0];
                    aac++;
                    if(aac >= 30)
                    {
                        aac = 0;
                        suma = aa;
                        aa = 0;
                    }
                }
            }
        }
        
        // 4. 速度设定值滤波(滑动平均)
        aavsp += SampleData[4];  // 累加电位器ADC值
        aacvsp++;
        if(aacvsp >= 200)  // 200 * 1ms = 200ms 采样一次
        {
            aacvsp = 0;
            sumavsp = aavsp;  // 保存平均值
            aavsp = 0;
        }
        
        // 5. 过零检测处理(仅在电机运行且无故障时)
        if(Motor_Start_F == 0 || ErrorCode != 0) return;
        if(Sta != 2) return;
        ADCS_chuli();  // 处理反电动势
    }
}

ADC采样时序图:

sequenceDiagram participant P as PWM周期 participant T as ATIM_CH4触发 participant A as ADC序列转换 participant I as ADC_IRQHandler P->>T: CCR4=3200周期中部 T->>A: 触发ADC转换 A->>A: CH0→CH1→CH2→CH3→CH4→CH5 A->>I: EOS中断标志置位 I->>I: 读取6通道结果 I->>I: 处理BEMF/速度/电压

2.3 BTIM3_IRQHandler - 换向定时器中断

位置: sensorless.c:41-70

触发条件: BTIM3定时器溢出(延迟换向时间到达)

处理流程:

void BTIM3_IRQHandler(void)
{
    if(BTIM_GetITStatus(CW_BTIM3, BTIM_IT_UPDATE))
    {
        BTIM_ClearITPendingBit(CW_BTIM3, BTIM_IT_UPDATE);
        
        // 状态1: 首次换向(启动阶段)
        if(Sta == 1)
        {
            Sta = 2;  // 进入正常运行状态
            BTIM_Cmd(CW_BTIM3, DISABLE);  // 禁用定时器
        }
        
        // 状态3: 正常运行中的延迟换向
        else if(Sta == 3 && StOk == 1)
        {
            // 禁用定时器
            BTIM_Cmd(CW_BTIM3, DISABLE);
            
            // 计算下一步换向步骤
            if(Dir == 1)  // 正转: 0→1→2→3→4→5→0
            {
                bHallStartStep1++;
                if(bHallStartStep1 >= 6) bHallStartStep1 = 0;
            }
            else          // 反转: 0→5→4→3→2→1→0
            {
                if(bHallStartStep1 == 0) bHallStartStep1 = 5;
                else bHallStartStep1--;
            }
            
            // 执行换向
            Commutation(bHallStartStep1, Motor_Start_F);
        }
    }
}

第3部分:过零检测原理与实现

3.1 反电动势(BEMF)原理

3.1.1 物理原理

无传感器BLDC电机控制的核心是利用反电动势(Back Electromotive Force, BEMF)检测转子位置。

基本原理:

  • 当电机转子旋转时,定子绕组切割磁力线产生感应电动势
  • 这个感应电动势的方向与供电电压相反,故称"反电动势"
  • BEMF的幅值与转子转速成正比:E = K_e × ω
  • BEMF的过零点发生在转子磁极与定子绕组轴线垂直时

重要特性:

graph LR subgraph "转子位置与BEMF关系" N[N极] -->|"60°"| Z1[过零点] Z1 -->|"60°"| S[S极] S -->|"60°"| Z2[过零点] Z2 -->|"60°"| N2[N极] end Z1 -.->|BEMF波形| W[上升] Z2 -.->|BEMF波形| W2[下降] style N fill:#ffcdd2 style S fill:#bbdefb style Z1 fill:#fff9c4 style Z2 fill:#fff9c4 style W fill:#c8e6c9 style W2 fill:#ffcdd2

3.1.2 过零检测与换向的关系

  • 过零时刻: 转子磁极与定子绕组轴线垂直
  • 换向时刻: 过零后延迟30°电角度
  • 原因: 等待转子到达下一个磁极位置后再换向

3.2 BEMF检测电路

3.2.1 端电压采样

代码使用三个端电压采样(U、V、W相),通过ADC的SampleData[0-2]读取:

// ADC通道配置(init.C中)
PA06_ANALOG_ENABLE();   // AIN6 → U相端电压 → SampleData[0]
PA07_ANALOG_ENABLE();   // AIN7 → V相端电压 → SampleData[1]
PB00_ANALOG_ENABLE();   // AIN8 → W相端电压 → SampleData[2]
PA03_ANALOG_ENABLE();   // AIN3 → BEMF → SampleData[3]

3.2.2 虚拟中性点

代码使用DIin作为虚拟中性点电压(center voltage),在启动前采样:

// main.c:47
DIin = SampleData[0];  // 上电后采样U相电压作为虚拟中性点

原理: 在星形连接的电机中,虚拟中性点电压 = (U + V + W) / 3,但由于电机绕组对称性,可以用一个相电压作为参考。

3.3 过零检测实现

3.3.1 换向表定义

// 不同方向下的过零边沿类型
// Dir=0: 反转, Dir=1: 正转
const unsigned char TAB_RFling[2][6] = {
    {RISING, FALLING, RISING, FALLING, RISING, FALLING},  // 反转
    {FALLING, RISING, FALLING, RISING, FALLING, RISING}   // 正转
};

// BEMF采样通道选择(根据步骤选择检测哪一相)
// 实际使用中SampleData[3]固定为AIN3(BEMF)
const unsigned char TAB_BEMFChannel[2][6] = {
    {3, 2, 1, 3, 2, 1},  // 反转
    {3, 2, 1, 3, 2, 1}  // 正转
};

3.3.2 换向表详解

Step 通电相 正转Dir=1 反转Dir=0 检测边沿 说明
0 AB FALLING RISING A相下降沿 A相
1 AC RISING FALLING C相上升沿 C相
2 BC FALLING RISING B相下降沿 B相
3 BA RISING FALLING A相上升沿 A相
4 CA FALLING RISING C相下降沿 C相
5 CB RISING FALLING B相上升沿 B相

注意: 代码中实际使用固定的SampleData[3](AIN3),而不是根据步骤切换通道。

3.3.3 过零检测处理函数

void ADCS_chuli(void)
{
    unsigned char hx = 0;        // 过零检测标志
    unsigned int thre = 0;        // 阈值
    
    // 1. 获取当前BEMF值和边沿类型
    BEMFConvertedValue = SampleData[3];  // AIN3采样值
    RisingFalling = TAB_RFling[Dir][bHallStartStep1];
    
    // 2. 计算动态阈值(电源电压的一半)
    // 随电源电压变化,自动调整检测阈值
    thre = SampleData[5] >> 1;  // Vbus / 2
    
    // 3. 检测下降沿过零
    if(RisingFalling == FALLING)
    {
        if(BEMFConvertedValue < thre)  // BEMF低于阈值
        {
            // 连续检测2次确认(消抖)
            static unsigned char cou = 0;
            cou++;
            if(cou >= 2)
            {
                cou = 0;
                Sta = 3;              // 标记检测到过零
                StCountComm++;        // 连续检测计数
                FFlag = 1;
                hx = 1;               // 触发换向
            }
        }
        else 
        {
            static unsigned char cou = 0;
            cou = 0;  // 未检测到过零,清除计数
        }
    }
    
    // 4. 检测上升沿过零
    else if(RisingFalling == RISING)
    {
        if(BEMFConvertedValue > thre)  // BEMF高于阈值
        {
            static unsigned char cou = 0;
            cou++;
            if(cou >= 2)
            {
                cou = 0;
                Sta = 3;
                StCountComm++;
                FFlag = 1;
                hx = 1;
            }
        }
        else 
        {
            static unsigned char cou = 0;
            cou = 0;
        }
    }
    
    // 5. 连续检测到20次有效过零后认为启动成功
    #define STCount 20
    if(StCountComm >= STCount && StOk == 0)
    {
        StOk = 1;  // 启动成功标志
    }
    
    // 6. 执行换向
    if(StOk == 1 && hx == 1)
    {
        // 测量换向时间间隔
        unsigned int StepTime = BTIM_GetCounter(CW_BTIM2);
        
        if(StepTime > 2000)  // 低速时使用定时器延迟换向
        {
            // 延迟时间为StepTime的1/8(约30°电角度)
            BTIM_SetAutoreload(CW_BTIM3, StepTime >> 3);
            BTIM_SetCounter(CW_BTIM3, 0);
            BTIM_Cmd(CW_BTIM3, ENABLE);
        }
        else  // 高速时直接换向
        {
            if(Dir == 1)  // 正转
            {
                bHallStartStep1++;
                if(bHallStartStep1 >= 6) bHallStartStep1 = 0;
            }
            else          // 反转
            {
                if(bHallStartStep1 == 0) bHallStartStep1 = 5;
                else bHallStartStep1--;
            }
            Commutation(bHallStartStep1, Motor_Start_F);
        }
    }
}

3.4 关键参数说明

参数 说明
STCount 20 启动成功所需连续过零检测次数
thre Vbus/2 动态阈值,随电源电压变化
换向延迟 StepTime>>3 过零后延迟1/8周期(约30°电角度)
cou阈值 2 连续2次检测到过零才确认(消抖)

3.5 过零检测时序图

sequenceDiagram participant 换向 as 换向步骤 participant 通电 as 通电相 participant BEMF as 悬空相BEMF participant ZC as 过零检测 participant 延迟 as BTIM3延迟 rect rgb(200, 230, 255) Note over 换向,通电: Step 0: AB通电 Note over 通电: A相PWM输出, B相接地 Note over BEMF: W相悬空<br/>检测W相BEMF end BEMF->>ZC: 监测W相BEMF ZC->>ZC: 检测过零 ZC->>延迟: 触发 延迟->>换向: 延迟30°电角度 Note over 换向: 换向到Step1 rect rgb(230, 255, 230) Note over 换向,通电: Step 1: AC通电 Note over 通电: A相PWM输出, C相接地 Note over BEMF: B相悬空<br/>检测B相BEMF end BEMF->>ZC: 监测B相BEMF ZC->>延迟: 触发 延迟->>换向: 延迟30°电角度 Note over 换向: 换向到Step2 rect rgb(255, 240, 220) Note over 换向,通电: Step 2: BC通电 Note over 通电: B相PWM输出, C相接地 Note over BEMF: A相悬空<br/>检测A相BEMF end Note over ZC,延迟: 延迟时间 = StepTime / 8

第4部分:电机启动流程

4.1 启动流程概述

无传感器BLDC电机无法在静止状态下获取转子位置,因此采用"预驱动"(Open Loop Start)启动方法:

flowchart TD A[1. 初始定位] --> B[2. 预驱动] B --> C[3. 检测] C --> D[4. 切换] A -.-|给定固定绕组通电| A B -.-|按顺序强制换向<br/>逐步增加PWM占空比| B C -.-|等待BEMF达到可检测水平<br/>连续20次过零| C D -.-|切换到BEMF闭环控制<br/>进入正常运行| D style A fill:#e1f5fe style B fill:#fff3e0 style C fill:#e8f5e9 style D fill:#f3e5f5

4.2 启动函数实现

unsigned char Sensorless_START(void)
{
    unsigned int Com_time = 0;  // 换向次数计数
    
    // 1. 清零BTIM2计数器(用于测量换向时间)
    BTIM_SetCounter(CW_BTIM2, 0);
    
    // 2. 初始化参数
    Tonoroff = 0;
    CW_ATIM->CCR4 = PWM_PERIOD - 400;  // ADC触发点调整
    
    StOk = 2;   // 启动中标志(2表示正在启动)
    Sta = 0;
    
    // 3. 设置初始PWM占空比(8%)
    MINOUT = PWM_PERIOD * QDPwm / 100;  // QDPwm = 8
    OutPwmValue = PWM_PERIOD * QDPwm / 100;
    
    // 4. 启动电机标志
    Motor_Start_F = 1;
    
    // 5. 初始BTIM2计数值
    CW_BTIM2->CNT = 20000;
    
    // 6. 更新PWM并执行首次换向
    UPPWM();
    Commutation(bHallStartStep1, Motor_Start_F);  // 假设从Step 0开始
    
    // 7. 等待20ms让电机稳定
    TimeCountTemp = 0;
    while(TimeCountTemp < 20);
    TimeCountTemp = 0;
    
    // 8. 预驱动循环(最多60次换向)
    Com_time = 0;
    StCountComm = 0;
    FFlag = 0;
    
    do {
        // 8.1 计算下一步换向
        if(Dir == 1)  // 正转
        {
            bHallStartStep1++;
            if(bHallStartStep1 >= 6) bHallStartStep1 = 0;
        }
        else          // 反转
        {
            if(bHallStartStep1 == 0) bHallStartStep1 = 5;
            else bHallStartStep1--;
        }
        
        // 8.2 检查是否检测到过零
        if(FFlag == 0) StCountComm = 0;
        FFlag = 0;
        
        // 8.3 如果未检测到过零,执行强制换向
        if(StOk == 0)
        {
            Commutation(bHallStartStep1, Motor_Start_F);
        }
        
        // 8.4 等待10ms并检查是否检测到过零
        TimeCountTemp = 0;
        while(TimeCountTemp < 10)
        {
            if((FFlag == 1 || StOk == 1) && TimeCountTemp >= 1) break;
        }
        
        // 8.5 增加换向计数和PWM占空比
        Com_time++;
        OutPwmValue += 5;  // 每次增加约0.8%占空比
        UPPWM();
        
    } while(StOk == 0 && Com_time < 60 && ErrorCode == 0);
    
    // 9. 启动结果判断
    if(StOk == 0)
    {
        // 启动失败
        Motor_Start_F = 0;
        Commutation(0, 0);  // 关闭所有输出
        return 0;
    }
    
    // 检查是否有故障
    if(ErrorCode != 0) 
        return ErrorCode;
    else    
        return 1;  // 启动成功
}

4.3 启动过程详解

4.3.1 初始化阶段

// 初始PWM占空比 = 8%
QDPwm = 8;  // 来自 global.c
MINOUT = PWM_PERIOD * 8 / 100 = 6400 * 0.08 = 512
OutPwmValue = 512

// 初始步进
bHallStartStep1 = 0;  // 从Step 0开始

4.3.2 预驱动循环

第1次换向: Step 0 → Step 1, PWM=512 (8%)
    ↓ 等待10ms, 检测过零? 否
第2次换向: Step 1 → Step 2, PWM=517 (8.1%)
    ↓ 等待10ms, 检测过零? 否
...
第N次换向: 逐步增加PWM占空比
    ↓
检测到过零 (FFlag=1)
    ↓
StOk = 1, 退出循环

4.3.3 启动成功条件

// 条件1: StOk == 1
//   - 连续20次检测到有效过零 (StCountComm >= 20)

// 条件2: Com_time < 60
//   - 最多进行60次换向尝试

// 条件3: ErrorCode == 0
//   - 无故障发生

4.4 启动失败处理

如果启动失败(60次换向内未检测到过零),代码返回0:

if(StOk == 0)
{
    Motor_Start_F = 0;
    Commutation(0, 0);
    return 0;  // 启动失败
}

调用处处理:

void HALL_MOTOR_START(void)
{
    unsigned char coun = 0;
    
    if(MOTORSTATE == STATEERROR) return;
    
    HALLcount = 0;
    QDPwm = PWMMin;  // 使用最小占空比
    
    // 尝试启动,最多2次
    do {
        if(Sensorless_START() == 0)
        {
            coun++;              // 失败次数
            QDPwm += 5;         // 增加占空比重试
        }
        else
        {
            break;              // 成功则退出
        }
    } while(coun < 1 && ErrorCode == 0);
    
    // 2次都失败,报错
    if(coun >= 1 && ErrorCode == 0) 
    {
        ErrorCode = 3;  // 启动失败错误码
        MOTORSTATE = STATEERROR;
    }
}

第5部分:电机正常运转控制

5.1 运行状态控制

void MotorRunOPEN(void)
{
    // 故障检查
    if(MOTORSTATE == STATEERROR) return;
    
    // 速度设为0时停止
    if(SetSpeed == 0) 
    {     
        MOTORSTATE = STATESTOP;    
    }    
    
    // PWM调节(基于时间间隔)
    if(TimeCountPID >= SpeedD)
    {
        TimeCountPID = 0;
        
        // 加速过程
        if(TargS1 < SetSpeed)
        {
            TargS1++;
            if(TargS1 >= SetSpeed) TargS1 = SetSpeed;
            OutPwmValue = TargS1 * OnepercentPWM;  // 转换为PWM值
            UPPWM();
        }
        // 减速或稳态
        else if(TargS1 > SetSpeed)
        {
            TargS1 = SetSpeed;
            OutPwmValue = TargS1 * OnepercentPWM;
            UPPWM();
        }
    }
}

5.2 PWM更新函数

void UPPWM(void)
{
    // 关闭中断保护临界区
    __disable_irq(); 
    
    // 根据当前步骤更新对应相的PWM占空比
    if(STEP_last == 0 || STEP_last == 1)  // A相通电
    {
        CW_ATIM->CCR1 = OutPwmValue;  // A相PWM输出
        CW_ATIM->CCR2 = 0;            // B相关闭
        CW_ATIM->CCR3 = 0;            // C相关闭
    }
    if(STEP_last == 2 || STEP_last == 3)  // B相通电
    {
        CW_ATIM->CCR1 = 0;
        CW_ATIM->CCR2 = OutPwmValue;  // B相PWM输出
        CW_ATIM->CCR3 = 0;
    }
    if(STEP_last == 4 || STEP_last == 5)  // C相通电
    {
        CW_ATIM->CCR1 = 0;
        CW_ATIM->CCR2 = 0;
        CW_ATIM->CCR3 = OutPwmValue;  // C相PWM输出
    }
    
    // 重新开启中断
    __enable_irq(); 
    
    // 启动下一次BEMF检测
    Tonoroff = 1;
    CW_ATIM->CCR4 = 200;  // 重新触发ADC
}

5.3 速度设定与采样

5.3.1 电位器到PWM的转换

void SampleSpeed(void)
{
    unsigned int tem = 0;
    unsigned int st = 0;
    
    // 获取电位器ADC平均值
    st = sumavsp / 200;
    
    // 死区处理
    if(st < NMINVD - 50)        // 低于最小值-50
    {
        SetSpeed = 0;          // 速度设为0
    }
    else if(st <= NMINVD);      // 在最小值范围内,保持当前
    else if(st <= NMAXVD)       // 在有效范围内
    {
        // 线性插值计算PWM
        // PWM = PWMMin + (PWMMax-PWMMin)/(NMAXVD-NMINVD) * (ADC-NMINVD)
        tem = PWMMin + KPWMN * (st - NMINVD);
        SetSpeed = tem;
    }
    else                        // 超过最大值
    {
        SetSpeed = PWMMax;     // 设为最大PWM
    }
}

5.3.2 参数计算

在main函数初始化时计算斜率:

// 速度设定斜率(用于电位器ADC值到PWM的转换)
KKN = (MAXSPEED - MINSPEED) / (NMAXVD - NMINVD);
KPWMN = (PWMMax - PWMMin) / (NMAXVD - NMINVD);

// 数值代入:
// KKN = (6000 - 200) / (3523 - 200) = 5800 / 3323 ≈ 1.74
// KPWMN = (100 - 20) / (3523 - 200) = 80 / 3323 ≈ 0.024

5.4 速度与转速计算

5.4.1 转速计算公式

// 实际转速计算 (main.c:71)
RealS = hte * 100 / MPolePairs;

// 推导过程:
// - hte: 100ms内的换向次数
// - 电气周期 = 6次换向
// - 机械周期 = 电气周期 / 极对数
// - 转速(rpm) = (换向次数/100ms) * (60s/min) / 6 / 极对数
//             = 换向次数 * 10 / 极对数
// 简化: rpm = 换向次数 * 100 / 极对数 (这里*100可能是为了放大)

// 例如:hte=100, 极对数=4
// RealS = 100 * 100 / 4 = 2500 rpm

5.5 速度控制流程图

flowchart TD subgraph 采样阶段 A[电位器ADC] --> B[sumavsp累加] B --> C{aacvsp >= 200?} C -->|是| D[计算平均值<br/>st = sumavsp/200] C -->|否| 结束1[返回] end D --> E{判断范围} E -->|st < 150| F[SetSpeed = 0] E -->|150 < st <= 200| G[保持当前] E -->|200 < st <= 3523| H[SetSpeed = 线性计算] E -->|st > 3523| I[SetSpeed = 100] F --> 结束2 G --> 结束2 H --> 结束2 I --> 结束2 subgraph PWM调节阶段 J{TimeCountPID >= SpeedD?} J -->|是| L[调整PWM] J -->|否| J L --> M[更新占空比] M --> N[启动BEMF检测] end style A fill:#e3f2fd style J fill:#fff3e0 style N fill:#e8f5e9

第6部分:主函数运转逻辑

6.1 main函数完整流程

int32_t main(void)
{
    unsigned char DZCount = 0;  // 堵转计数
    
    // ==================== 参数初始化 ====================
    // 计算速度转换斜率
    KKN = (MAXSPEED - MINSPEED) / (NMAXVD - NMINVD);  // 速度斜率
    KPWMN = (PWMMax - PWMMin) / (NMAXVD - NMINVD);    // PWM斜率
    
    // ==================== 外设初始化 ====================
    RCC_Configuration();       // 系统时钟 96MHz
    LEDConfiguration();         // LED (PC13)
    DR_Configuration();        // 方向控制 (PA9)
    EN_Configuration();        // 使能控制 (PC15)
    FG_Configuration();        // 转速反馈 (PA12)
    ADC_Configuration();        // 6通道ADC
    BLDC_Configuration();       // 三相PWM (15kHz)
    BTIM1_Configuration();     // 1ms定时中断
    SENSORLESS_TIM_Config();   // 换向定时器
    
    // ==================== 等待稳定 ====================
    for(DZCount = 0; DZCount < 200; DZCount++);  // 延时200ms
    
    // ==================== 零点校准 ====================
    DIin = SampleData[0];  // 采样U相电压作为虚拟中性点
    DZCount = 0;
    PotentialCheck();      // 检查电位器是否归零
    
    // ==================== 主循环 ====================
    while(1)
    {
        // 1. 速度设定采样 (每20ms)
        if(TimeCountCompuSpeed > 20)
        {
            TimeCountCompuSpeed = 0;
            SampleSpeed();  // 采样电位器,计算SetSpeed
        }
        
        // 2. 电流采样 (每2ms)
        if(Icount >= 2)
        {
            Icount = 0;
            SampleOI();  // 采样三相电流
        }
        
        // 3. 电压电流计算 (每100ms)
        if(TimeCountVI >= 100)
        {
            TimeCountVI = 0;
            SampleV();    // 采样电源电压
            SampleI();    // 计算相电流
            
            // 计算实际转速
            // RealS = hte * 100 / MPolePairs;
            
            // 堵转检测(仅在启动和运行阶段)
            if(MOTORSTATE == STATESTARTOPEN || MOTORSTATE == STATERUNOPEN)
            {
                if(RealS < 5)  // 转速低于5 rpm
                {
                    DZCount++;
                    if(DZCount >= 50)  // 持续5秒
                    {
                        DZCount = 0;
                        ErrorCode = 7;  // 堵转保护
                    }
                }
                else
                {
                    DZCount = 0;
                }
            }
        }
        
        // 4. 故障检测
        if(ErrorCode != 0 && MOTORSTATE != STATEERROR && 
           MOTORSTATE != STATEERROROVER)
        {
            MOTORSTATE = STATEERROR;
        }
        
        // 5. 状态机处理
        switch(MOTORSTATE)
        {
            case STATESTARTCHECK:    // 0 - 启动检测
                MotorStartCheck();
                break;
                
            case STATESTARTDELAY:   // 8 - 启动延时
                MotorStartDealy();
                ENCheck();
                break;
                
            case STATESTARTOPEN:    // 1 - 预驱动启动
                MotorStartOPEN();
                ENCheck();
                break;
                
            case STATERUNOPEN:      // 2 - 正常运行
                MotorRunOPEN();
                ENCheck();
                break;
                
            case STATESTOP:         // 3 - 停止
                MotorStop();
                break;
                
            case STATEERROR:        // 5 - 故障
                MotorError();
                break;
                
            case STATEERROROVER:    // 6 - 故障处理完成
                MotorErrorOver();
                break;
                
            case STATWAITSTART:     // 7 - 等待启动
                WaitStart();
                break;
        }
    }
}

6.2 状态机详细说明

6.2.1 状态定义

// global.h:59-66
#define STATESTARTCHECK   0   // 启动检测状态 - 等待启动条件
#define STATESTARTOPEN    1   // 启动开放 - 预驱动启动过程
#define STATERUNOPEN      2   // 运行开放 - 正常运行状态
#define STATESTOP         3   // 停止状态 - 电机停止
#define STATEERROR        5   // 故障状态 - 检测到故障
#define STATEERROROVER    6   // 故障处理完成 - 等待恢复
#define STATWAITSTART     7   // 等待启动 - 故障后等待重新启动
#define STATESTARTDELAY   8   // 启动延时 - 启动前延时

6.2.2 状态转换图

stateDiagram-v2 [*] --> STATESTARTCHECK : 上电 STATESTARTCHECK --> STATESTARTDELAY : SetSpeed>0 && EN=1 STATESTARTDELAY --> STATESTARTOPEN : 延时完成 STATESTARTOPEN --> STATERUNOPEN : 启动成功 StOk=1 STATERUNOPEN --> STATESTOP : SetSpeed=0 STATERUNOPEN --> STATEERROR : 故障或EN=0 STATERUNOPEN --> STATERUNOPEN : 正常运行 STATESTOP --> STATESTARTCHECK : RealS=0 STATEERROR --> STATEERROROVER : 进入错误处理 STATEERROROVER --> STATWAITSTART : 等待恢复 STATWAITSTART --> STATESTARTCHECK : SetSpeed>0 note right of STATESTARTCHECK: 启动检测<br/>等待条件 note right of STATESTARTDELAY: 启动延时<br/>200ms note right of STATESTARTOPEN: 预驱动启动<br/>最多60次换向 note right of STATERUNOPEN: 正常运行<br/>BEMF闭环 note right of STATESTOP: 电机停止 note right of STATEERROR: 故障状态 note right of STATEERROROVER: 故障闪烁显示 note right of STATWAITSTART: 等待重新启动

第7部分:其他函数汇总

7.1 采样计算函数 (compu.c)

函数名 功能 调用周期 位置
SampleSpeed() 电位器采样转换为速度设定值 20ms compu.c:3
SampleSpeed1() 备用速度采样函数 20ms compu.c:24
SampleOI() 原始电流值累加滤波 2ms compu.c:45
SampleI() 计算实际相电流值,判断过流 100ms compu.c:82
SampleV() 采样电源电压,判断过压/欠压 100ms compu.c:128

7.2 电机控制函数 (control.c)

函数名 功能 状态 位置
MotorStartCheck() 检查启动条件(速度设定、方向) STATESTARTCHECK control.c:5
MotorStartDealy() 启动延时处理 STATESTARTDELAY control.c:38
MotorStartOPEN() 调用无传感器启动函数 STATESTARTOPEN control.c:53
MotorRunOPEN() 正常运行PWM调节 STATERUNOPEN control.c:64
MotorStop() 电机停止处理 STATESTOP control.c:93
MotorError() 故障处理 STATEERROR control.c:113
MotorErrorOver() 故障灯闪烁显示 STATEERROROVER control.c:120
WaitStart() 等待重新启动 STATWAITSTART control.c:159
ENCheck() 检查EN引脚状态 通用 control.c:164

7.3 电机换向函数 (motor.c)

函数名 功能 位置
Commutation() 6步换向控制,输出对应PWM motor.c:10
UPPWM() 更新PWM占空比 motor.c:52
HALL_MOTOR_START() 调用启动函数 motor.c:63
MOTOR_STOP0() 停止电机输出 motor.c:79

7.4 故障代码详解

错误码 含义 触发条件 处理方式
3 启动失败 60次换向未检测到过零 电机停止,LED闪烁3次
4 过流保护 连续30次采样电流>30A 电机停止,LED闪烁4次
6 过压保护 连续30次采样电压>28V 电机停止,LED闪烁6次
7 堵转保护 转速<5rpm持续5秒 电机停止,LED闪烁7次
8 欠压保护 连续30次采样电压<10V 电机停止,LED闪烁8次
10 电位器故障 上电电位器不在零位 等待电位器归零后恢复

第8部分:电机换向与运行深入解析

8.1 三相6步换向原理

8.1.1 BLDC电机基本原理

BLDC电机(无刷直流电机)采用电子换向代替机械换向器:

flowchart LR subgraph 有刷电机 A1[电源] --> B1[电刷] B1 --> C1[换向器] C1 --> D1[绕组] D1 -.->|机械换向| A1 end subgraph 无刷电机 A2[电源] --> B2[电子开关<br/>MOSFET] B2 --> C2[绕组] C2 -.->|转子位置检测| D2[控制器<br/>MCU] D2 -->|电子换向| B2 end style 有刷电机 fill:#ffcdd2 style 无刷电机 fill:#c8e6c9

8.1.2 六步换向法

三相星形连接的BLDC电机,通过按特定顺序给两相通电来产生旋转磁场:

flowchart LR Step0[Step 0: AB] --> Step1[Step 1: AC] Step1 --> Step2[Step 2: BC] Step2 --> Step3[Step 3: BA] Step3 --> Step4[Step 4: CA] Step4 --> Step5[Step 5: CB] Step5 --> Step0 Step0 -->|PWM输出| P0[A相PWM, B相续流] Step1 -->|PWM输出| P1[A相PWM, C相续流] Step2 -->|PWM输出| P2[B相PWM, C相续流] Step3 -->|PWM输出| P3[B相PWM, A相续流] Step4 -->|PWM输出| P4[C相PWM, A相续流] Step5 -->|PWM输出| P5[C相PWM, B相续流] style Step0 fill:#e3f2fd style Step1 fill:#e8f5e9 style Step2 fill:#fff3e0 style Step3 fill:#f3e5f5 style Step4 fill:#e1f5fe style Step5 fill:#fbe9e7

8.2 换向实现详解

void Commutation(unsigned int step, unsigned int PWM_ON_flag)
{
    // ==================== 停止条件 ====================
    if(PWM_ON_flag == 0 || MOTORSTATE == STATEERROR || 
       MOTORSTATE == STATEERROROVER)
    {
        // 关闭所有PWM输出
        CW_ATIM->CCR1 = 0;
        CW_ATIM->CCR2 = 0;
        CW_ATIM->CCR3 = 0;
        
        // 关闭所有下桥臂
        PWM_AL_OFF;
        PWM_BL_OFF;
        PWM_CL_OFF;
        
        // ADC触发点设置到周期末尾
        CW_ATIM->CCR4 = PWM_PERIOD - 800;
        return;
    }
    
    // ==================== Step 0/5: AB相通电 ====================
    // A相输出PWM, B相接地, C相悬空
    if(step == 0 || step == 5)
    {
        PWM_AL_OFF;  // A相下桥臂关闭
        PWM_CL_OFF;  // C相下桥臂关闭(悬空)
    }
    
    // ==================== Step 1/2: AC相通电 ====================
    // A相输出PWM, C相接地, B相悬空
    else if(step == 1 || step == 2)
    {
        PWM_AL_OFF;  // A相下桥臂关闭
        PWM_BL_OFF;  // B相下桥臂关闭(悬空)
    }
    
    // ==================== Step 3/4: BC相通电 ====================
    // B相输出PWM, A相接地, C相悬空
    else if(step == 3 || step == 4)
    {
        PWM_BL_OFF;  // B相下桥臂关闭
        PWM_CL_OFF;  // C相下桥臂关闭(悬空)
    }
    
    // ==================== 设置PWM占空比 ====================
    // Step 0/1: A相PWM输出
    if(step == 0 || step == 1)
    {
        CW_ATIM->CCR2 = 0;  // B相关闭
        CW_ATIM->CCR3 = 0;  // C相关闭
        CW_ATIM->CCR1 = OutPwmValue;  // A相PWM输出
    }
    
    // Step 2/3: B相PWM输出
    if(step == 2 || step == 3)
    {
        CW_ATIM->CCR1 = 0;
        CW_ATIM->CCR3 = 0;
        CW_ATIM->CCR2 = OutPwmValue;  // B相PWM输出
    }
    
    // Step 4/5: C相PWM输出
    if(step == 4 || step == 5)
    {
        CW_ATIM->CCR1 = 0;
        CW_ATIM->CCR2 = 0;
        CW_ATIM->CCR3 = OutPwmValue;  // C相PWM输出
    }
    
    // ==================== 使能下桥臂(续流) ====================
    // Step 0/5: B相下桥臂导通
    if(step == 0 || step == 5)
    {
        PWM_BL_ON;  // B相下桥臂导通,提供续流通道
    }
    // Step 1/2: C相下桥臂导通
    else if(step == 1 || step == 2)
    {
        PWM_CL_ON;
    }
    // Step 3/4: A相下桥臂导通
    else if(step == 3 || step == 4)
    {
        PWM_AL_ON;
    }
    
    // ==================== 配置BEMF采样通道 ====================
    // 根据当前步骤选择对应的ADC通道
    CW_ADC->SQRCFR_f.SQRCH0 = ADCBEMF[step];
    
    // ==================== 启动换向定时器 ====================
    STEP_last = step;
    
    // 启动BEMF检测
    Tonoroff = 1;
    CW_ATIM->CCR4 = 200;  // 重新触发ADC
    
    // 测量换向时间间隔
    StepTime = BTIM_GetCounter(CW_BTIM2);  // 读取BTIM2计数值
    BTIM_SetCounter(CW_BTIM2, 0);           // 清零BTIM2
    
    // 设置延迟换向时间(过零后延迟约30°电角度)
    BTIM_SetAutoreload(CW_BTIM3, StepTime >> 3);  // StepTime/8
    BTIM_SetCounter(CW_BTIM3, 0);
    BTIM_Cmd(CW_BTIM3, ENABLE);  // 使能延迟定时器
    
    // 标记当前状态
    Sta = 1;  // 换向完成
    
    // 计数(用于转速计算)
    HALLcount++;       // 瞬时计数
    HALLcount1++;     // 100ms累计计数
}

8.3 换向时序图

sequenceDiagram participant PWM as PWM周期 participant CCR as 计数器 participant ADC as ADC触发 participant BEMF as BEMF检测 participant ZC as 过零检测 participant Comm as 换向 rect rgb(240, 240, 255) Note over PWM,CCR: PWM周期 66.67μs PWM->>CCR: 0→6399计数 CCR->>CCR: 达到CCR1=OutPwmValue Note over CCR: 高电平输出 end rect rgb(255, 240, 240) Note over PWM,ADC: PWM周期中部 PWM->>ADC: CCR4=3200触发 ADC->>BEMF: 采样BEMF end rect rgb(240, 255, 240) BEMF->>ZC: BEMF值 ZC->>ZC: 判定过零 ZC->>Comm: 检测到过零 end rect rgb(255, 255, 240) Note over Comm: 延迟StepTime/8 Comm->>Comm: BTIM3定时 Comm->>Comm: 触发换向 end rect rgb(240, 255, 255) Note over Comm: 换向到下一步 Comm->>PWM: 更新PWM输出 end

8.4 反电动势与换向关系表

Step 通电绕组 驱动方式 检测相 BEMF边沿 延迟时间
0 A→B A相PWM, B相续流 A相 FALLING StepTime/8
1 A→C A相PWM, C相续流 C相 RISING StepTime/8
2 B→C B相PWM, C相续流 B相 FALLING StepTime/8
3 B→A B相PWM, A相续流 A相 RISING StepTime/8
4 C→A C相PWM, A相续流 C相 FALLING StepTime/8
5 C→B C相PWM, B相续流 B相 RISING StepTime/8

8.5 关键参数配置说明

// ==================== PWM配置 ====================
#define PWM_PERIOD 6400        // PWM周期 = 96MHz / 6400 = 15kHz
#define OnepercentPWM 64       // 1%占空比 = 6400 / 100

// ==================== 速度设定 ====================
// 电位器ADC值范围
#define NMINVD 200             // 电位器最小ADC值 (~0.24V)
#define NMAXVD 3523            // 电位器最大ADC值 (~4.3V)

// PWM占空比范围
#define PWMMin 20              // 最小PWM占空比 (20%)
#define PWMMax 100             // 最大PWM占空比 (100%)

// 电机转速范围
#define MINSPEED 200           // 最小电机转速 (200 rpm)
#define MAXSPEED 6000          // 最大电机转速 (6000 rpm)

// ==================== 保护参数 ====================
#define ISH 30                 // 过流阈值 (30A)
#define VSH 28                 // 过压阈值 (28V)
#define LSH 10                 // 欠压阈值 (10V)
#define NumErr 30              // 连续30次超限判定为故障

// ==================== 电机参数 ====================
#define MPolePairs 4           // 电机极对数 (8极电机)

附录

A. 代码文件清单

文件 功能描述
main.c 主程序入口,状态机主循环,外设初始化
init.C 所有外设初始化函数实现
global.c 全局变量定义
global.h 全局变量声明,宏定义,状态常量
motor.c 电机换向控制函数实现
motor.h 电机换向函数声明
sensorless.c 无传感器控制核心实现
sensorless.h 无传感器控制函数声明
control.c 电机控制状态机函数实现
control.h 电机控制函数声明
compu.c 采样计算函数实现
compu.h 采样计算函数声明
interrupts_cw32l011.c 中断向量表框架

B. 引脚分配总表

引脚 端口 功能 说明
PC13 LED 指示灯 运行指示
PA9 DR 输入 方向控制
PC15 EN 输入 使能控制
PA12 FG 输出 转速反馈
PB5 PWM_AH 复用 A相上桥PWM
PA15 PWM_AL 输出 A相下桥控制
PB6 PWM_BH 复用 B相上桥PWM
PB3 PWM_BN 输出 B相下桥控制
PB7 PWM_CH 复用 C相上桥PWM
PB4 PWM_CN 输出 C相下桥控制
PA06 AIN6 模拟 U相电压
PA07 AIN7 模拟 V相电压
PB00 AIN8 模拟 W相电压
PA03 AIN3 模拟 BEMF检测
PB01 AIN9 模拟 电位器
PA08 AIN10 模拟 电源电压
posted @ 2026-02-23 14:22  PlayerPencil  阅读(7)  评论(0)    收藏  举报