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主频
时钟树分析:
说明: 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时序图:
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采样时序图:
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的过零点发生在转子磁极与定子绕组轴线垂直时
重要特性:
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 过零检测时序图
第4部分:电机启动流程
4.1 启动流程概述
无传感器BLDC电机无法在静止状态下获取转子位置,因此采用"预驱动"(Open Loop Start)启动方法:
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 速度控制流程图
第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 状态转换图
第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电机(无刷直流电机)采用电子换向代替机械换向器:
8.1.2 六步换向法
三相星形连接的BLDC电机,通过按特定顺序给两相通电来产生旋转磁场:
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 换向时序图
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 | 模拟 | 电源电压 |

浙公网安备 33010602011771号