1. 方案概述
本方案采用CW32F030C8T6作为主控芯片,采用无感方波控制算法控制无刷直流空心杯电机。CW32F030C8T6是一款高性能、低功耗的32位微控制器,具有丰富的片上外设资源,可以适合用于电机控制。无感方波控制算法是一种简单有效的电机控制算法,不需要使用霍尔传感器,可以降低硬件成本。
本次采用的电机驱动板仍然为CW32_BLDC_EVA V5开发板,具体开发板的信息可以翻看上一节《基于CW32的无刷空心杯电机有感控制驱动方案》,采用的空心杯电机与上一节有所不同,这次使用的空心杯电机的额定电压为 24 V。
由于本次采用无感方案,所以只需要将 U、V、W三相电源接上即可,并且三相的顺序并无强制要求,下面我们重心将放在对于无感方波控制的原理部分。
2. 无感方波控制原理
无感方波控制(Sensorless Square Wave Control)是一种用于无刷直流电机(BLDC)驱动的控制方法。与传统的有感控制方法相比,无感方波控制不需要使用位置或速度传感器来反馈电机状态,而是通过检测电机自身的悬空相反电动势变化(Back Electromotive Force,简称BEMF)来实现控制。
在无感方波控制中,通过检测电机的悬空相电压的过零点,可以推断出电机转子的位置,根据转子位置进行步状态的切换即可控制电机转动。
2.1 梯形波电压
无感方波的驱动电路采用三相全桥逆变电路,在理想的情况下,三相全桥逆变电路的电压波形如下图2-1所示,每相导通角度为120°,相与相之间相隔120°。
图2-1 三相全桥逆变电压波形
无刷直流电机驱动所需的电流波形也是上图里的方波,因为电机存在漏感 L ,定子电流会有一定的上升和下降时间,所以使得理想的方波变成了梯形波。
图2-2 BLDC运行三相电压波形
从图2-2中可以看出,无刷直流电机实际运行时的三相电压波形并不是图2-1里的方波,而是梯形波。由于采用了脉宽调制计数(PWM),所以波形看上去由一道道脉冲组成。
2.2 确定换相信号
无感方波驱动与有感最大的区别就在于获取换相信号的方式不同,有感方波通过检测三相霍尔信号的电平,再根据三相电平确定电机此时应该运行在的步状态;无感方波是检测梯形波“斜线”上的反电动势电压来确定换相时刻。霍尔信号对应的相是确定的,所以电机的供电相也要根据霍尔相的顺序来连接,而无感方波驱动只需要检测“斜线”上的“过零点”确定换相时刻后自动换相到下一步状态,而每一个步状态对应事先已经安排好的开关管通断,所以电机的供电相可以随意连接。
图2-3 反电动势采样电路
图2-3 反电动势采样电路
PA0、PA1、PA5分别对应CW32F030 ADC的0、1、5通道,我们使用ADC采集三相的电压,但在“过零点”比较中我们实际使用的是未导通相,即悬空相的电压。
2.3 电机驱动思路
驱动电机旋转的原理与上一章有感驱动的原理相同,本质上是对电机定子的通电情况进行控制,也称为换相。这里我们结合上一章霍尔传感器的信号波形与电机运行时的三相电压波形来看,如下图。
图2-4 三相霍尔与供电相波形
我们将电机运行状态分为三种: 停止、启动、运行 ,其中停止状态不需要过多关注。首先是电机的启动,启动状态首先要对转子进行定位,因为电机在停止时转子可以在任何位置。确定转子的位置可以给某一步状态对应的 MOS 管通电,等待一小段转子复位的时间后,转子就在此步状态中,然后进入启动阶段。
无感电机的启动也称为“强拖”,以复位时的步状态为基准,手动换步,之后若检测到 “过零点”,则切入第三种正常运行的状态,如果没有检测到,则提高占空比再手动换步,尝试一定次数后如果没有成功则电机启动失败。
正常运行状态时,在每次输出 PWM 脉冲时由比较器触发 ADC 采样,根据当前步状态确定要使用的反电动势在哪一相。取得反电动势后需要判断是否已经来到 “过零点”,这里还需要判断当前步状态是电压上升还是下降状态:上升状态需要判断反电动势大于 “过零点” 值,下降状态需要判断反电动势小于 “过零点”值。还需要注意的是,ADC的采样时刻的选择会影响到 “过零点”值的大小:如果是在 PWM 高电平时采集,则过零点值为电源电压的一半;低电平时的比较值需要自己根据实际大小去调试。通常在 PWM 占空比大于 50% 时采样高电平,低于 50%时采样低电平。
图2-5 采样点比较值选择
在检测到 “过零点” 之后,需要延迟一定的时间再进行换相,以保证电机的转矩。延迟时间由定时器记录的上次换相到本次换相的时间间隔,取其部分大小作为延迟时间。
注意,电机在换相时由于新的电流通路的建立,电压在换相处会产生尖峰毛刺,此时进行 ADC 电压采集到的数据是不准确的,所以在换相后还需要进行退磁状态的判断,如果处于退磁状态,则本次不采样。
3. 软件设计
3.1 MCU资源分配
本次使用到的CW32内部资源如下:
- ATIM :CH1、CH2、CH3 三个通道比较产生 PWM 波用于驱动电机,CH4为芯片内部通道,无外部引脚,只有一路比较捕获寄存器 (ATIM_CH4CCR),且只能用于比较,不能用来捕获。我们使用 CH4 的比较功能触发 DMA 传输。
- DMA :使用4路 DMA 通道:CH1、CH2、CH3、 CH4:
- CH1 将 ADC 单次单通道的采样结果传入 RAM
- CH2 将 ADC 的 CR1 寄存器的配置值从 RAM 传入寄存器
- CH3 将 ADC 的 START 寄存器的配置值从 RAM 传入寄存器
- CH1、CH2、CH3由 ADC 硬件触发,CH4 由 ATIM 硬件触发,启动 ADC
- ADC :ADC 采样的时钟设置需要与 PWM 载波频率结合,计算采样时间;采用单通道单次采样,首次采样由 ATIM 硬件触发,ADC 转换完毕后触发 DMA 传输,通过 DMA 传输自动改变采样通道。这样设置可以实现 ATIM 触发一次就采样五个数据(U、V、W 相电压、母线电压、外部电位器调速电压)
- BTIM1 :BTIM1 设置 1ms 进入一次中断,在中断里改变标志位实现主程序的控制
- BTIM2 :BTIM2 作为换相时间间隔的记录定时器,决定延迟多长时间后换相
- BTIM3 :BTIM3 设置中断,在中断里完成退磁和换相
3.2 部分重要程序介绍
操作 ATIM CH4 的 CCR 寄存器,可以选择 ADC 在一个 PWM 周期内不同位置的采样: CW_ATIM- >CH4CCR=(数字);
首先是核心函数:调制换相 /*step,为当前换相序号,PWM_ON_flag=1时启动PWM输出**Step_Last,记录上一次步状态用于 PWM 占空比的刷新**Step_Time,记录上一次换相时间**Flag_Start_OK,判断电机是否启动成功**Flag_Demagnetize_State,判断退磁状态,1:需要退磁;2:退磁完成;3:检测到过零点,可以换相**HALLcount,记录换相次数用于转速计算*/void Commutation(uint32_t step,uint32_t PWM_ON_flag){ if(PWM_ON_flag==0) //不启动则关闭输出 { CW_ATIM- >CH1CCRA=0;CW_ATIM- >CH2CCRA=0;CW_ATIM- >CH3CCRA=0; PWM_AL_OFF; PWM_BL_OFF;PWM_CL_OFF; CW_ATIM- >CH4CCR=PWM_TS-800; return; } //关闭下管 if(step==0||step==5){PWM_AL_OFF;PWM_CL_OFF;} else if(step==1||step==2){PWM_AL_OFF;PWM_BL_OFF;} else if(step==3||step==4){PWM_BL_OFF;PWM_CL_OFF;} //打开上管 if(step==0||step==1){CW_ATIM- >CH2CCRA=0;CW_ATIM- >CH3CCRA=0;CW_ATIM- >CH1CCRA=OutPwm;} if(step==2||step==3){CW_ATIM- >CH1CCRA=0;CW_ATIM- >CH3CCRA=0;CW_ATIM- >CH2CCRA=OutPwm;} if(step==4||step==5){CW_ATIM- >CH1CCRA=0;CW_ATIM- >CH2CCRA=0;CW_ATIM- >CH3CCRA=OutPwm;} //打开下管 if(step==0||step==5){PWM_BL_ON;}//AB else if(step==1||step==2){PWM_CL_ON;}//AC else if(step==3||step==4){PWM_AL_ON;}//BA Step_Last=step; //判断占空比修改采样时刻 if(OutPwm >=1200&&Flag_ON_or_OFF==0){Flag_ON_or_OFF=1;CW_ATIM- >CH4CCR=300;} else if(OutPwm< 1200&&Flag_ON_or_OFF==1){Flag_ON_or_OFF=0;CW_ATIM- >CH4CCR=PWM_TS-600; } //记录上一次的换相时间 Step_Time=BTIM_GetCounter(CW_BTIM2); BTIM_SetCounter(CW_BTIM2,0); //电机未启动则快速换相 if(Flag_Start_OK==0) BTIM_SetAutoreload(CW_BTIM3,Step_Time/8); else BTIM_SetAutoreload(CW_BTIM3,Step_Time/6);//退磁延迟时间 BTIM_SetCounter(CW_BTIM3,0); BTIM_Cmd(CW_BTIM3, ENABLE); //启动退磁 Flag_Demagnetize_State=1;//退磁状态 HALLcount++; }
接着是第二个核心:换相。 /*Direction,电机运行方向,0:步状态012345,1:步状态054321**Cur_Step,电机目前的步状态,步状态正常运行顺序为 012345、543210。0:AB、1:AC ……*/void BTIM3_IRQHandler(void){ if(BTIM_GetITStatus(CW_BTIM3, BTIM_IT_OV)) { BTIM_ClearITPendingBit(CW_BTIM3, BTIM_IT_OV); if(Flag_Demagnetize_State == 1) //说明退磁结束后第一次进入BTIM3中断 { Flag_Demagnetize_State = 2; //退磁结束标志 BTIM_Cmd(CW_BTIM3, DISABLE); } else if(Flag_Demagnetize_State == 3 && Flag_Start_OK == 1) //退磁完成和启动成功后,决定下一次换相 { BTIM_Cmd(CW_BTIM3, DISABLE); if(Direction == 0) //与RisingFalling的顺序要对应 { Cur_Step++; if(Cur_Step == 6)Cur_Step = 0; } else { if(Cur_Step == 0)Cur_Step = 5; else Cur_Step--; } Commutation(Cur_Step,Motor_Start_F); } }}
过零点比较函数如下,此函数在 ADC 完成五次采样后调用。 /*SampleData[5] U反电动势 V反电动势 母线电压 W反电动势 电位器调速电压值**TAB_BEMFChannel[6]={3,1,0,3,1,0};**TAB_RisingFalling[2][6]={//判断此刻电压为上升沿还是下降沿,Rising=1;Falling=2 {FALLING,RISING,FALLING,RISING,FALLING,RISING}, {RISING,FALLING,RISING,FALLING,RISING,FALLING} }**Flag_ON_or_OFF,高低电平采样标志位 **RisingFalling,上升沿下降沿比较标志位 **Count_0V,过零点检测计数 STCount = 15**Flag_Confirm,启动确认标志位 */void ADC_Process(void){ static uint8_t count = 0; //过零检测计数 uint32_t Voltage_Bus = 0; //母线电压 uint8_t Flag_0V = 0; //成功检测到过零点标志 if(Flag_Demagnetize_State != 2)return; //说明退磁未结束 BEMFConvertedValue =SampleData[TAB_BEMFChannel[Cur_Step]]; //取得反电动势 RisingFalling=TAB_RisingFalling[Direction][Cur_Step]; //判断上升沿还是下降沿 if(Flag_ON_or_OFF == 0)Voltage_Bus = 50; //在PWM低电平时采样则与地比较电压 else Voltage_Bus = SampleData[2]; //在PWM高电平时采样则与电源正极比较电压 if(RisingFalling == FALLING) { if(BEMFConvertedValue < Voltage_Bus) { count++; if(count >= 2) //连续两次都检测到过零,则认为确实过零了 { count = 0; Flag_Demagnetize_State = 3; //退磁完成,可以换相 Count_0V++; Flag_Confirm = 1; Flag_0V = 1; //成功检测到过零点 } } else count = 0; } else if(RisingFalling == RISING) { if(BEMFConvertedValue > Voltage_Bus) { count++; if(count >= 2) { count = 0; Flag_Demagnetize_State = 3; Count_0V++; Flag_Confirm = 1; Flag_0V = 1; } } else count = 0; } if(Count_0V >= STCount && Flag_Start_OK == 0) { Flag_Start_OK = 1; //连续检测到固定数量的过零时,认为启动成功 } if(Flag_Start_OK == 1 && Flag_0V == 1) { Flag_0V = 0; BTIM_SetAutoreload(CW_BTIM3,Step_Time/8); //换相延迟时间 BTIM_SetCounter(CW_BTIM3,0); BTIM_Cmd(CW_BTIM3, ENABLE); } }
最后是电机的启动部分: /*TimeCountTemp,计时,1ms增加1**Com_time, 启动次数**RAMP_TABLE[64],存储时间的数组*/do { if(Direction == 0) //与RisingFalling的顺序要对应 { Cur_Step++; if(Cur_Step >= 6)Cur_Step = 0; //以复位时的步状态为基准,手动换步 } else { if(Cur_Step == 0)Cur_Step = 5; else Cur_Step--; } Flag_Confirm = 0; if(Flag_Start_OK == 0) { Commutation(Cur_Step,Motor_Start_F); } TimeCountTemp = 0; while(TimeCountTemp < RAMP_TABLE[Com_time]) //等待过零点检测 { if(Flag_Confirm == 1 || Flag_Start_OK == 1)break; //启动成功则不再执行do.....while里的内容 } Com_time++; OutPwm+=10; //没有启动则依次提高占空比 }while(Flag_Start_OK==0 && Com_time< 60 && ErrorCode==0); //跳出循环则 启动成功/超出启动次数/启动报错
|