一、有感方波控制
根据HALL信号来进行,换相操作。HALL安装位置一般以120°或60°为主。两种方式所获去的HALL值会有所区别。下面以120°为例:
#include <stdio.h>
#define CW 0
#define CCW 1
unsigned char Motor_CommutateStep; //换相步序
unsigned char HALL_currentvalue; //HALL当前值
unsigned char HALL_nextvalue; //HALL下一个值
//定义换相步序
unsigned char HallToMotorCwTable[8] = { 6, 2, 0, 1, 4, 3, 5, 6 };
unsigned char HallToMotorCCwTable[8] = { 6, 5, 3, 4, 1, 0, 2, 6 };
/*获取当前Hall值*/
unsigned char GetHallValue(){
static unsigned char Hall_currentvalue = 0;
//get value from register
// user code:
return Hall_currentvalue;
}
/*
value phase
3 -------> UV
1 -------> UW
5 -------> VW
4 -------> VU
6 -------> WU
2 -------> WV
*/
void MotorCommutatePhase(unsigned char dirct, unsigned char hallValue){
if(dirct == CW){
Motor_CommutateStep = HallToMotorCwTable[hallValue]; //根据hallvalue 生成一种映射关系。
}else{
Motor_CommutateStep = HallToMotorCCwTable[hallValue];
}
Motor_CommutateStep +=5;
Motor_CommutateStep %=6;
switch(Motor_CommutateStep){
case 0: //U相上管开,V相下管开,其余关闭
//user code:
break;
case 1: //U相上管开,W相下管开,其余关闭
//user code:
break;
case 2: //V相上管开,W相下管开,其余关闭
//user code:
break;
case 3: //V相上管开,U相下管开,其余关闭
//user code:
break;
case 4: //W相上管开,U相下管开,其余关闭
//user code:
break;
case 5: //W相上管开,V相下管开,其余关闭
//user code:
break;
default:
//disable pwm output
break;
}
}
应为是有感所以比较简单。代码中霍尔值与换相步序做了下映射,使得代码看起来美观一些。看不明白用笔画画。
下面是对下次相序的预测,一般来说用于判断换相是否正确。
//预测下一次的HALL值,用于判断换相是否正确
unsigned char PredictNextHallValue(unsigned char direct, unsigned char currenthallvalue){
unsigned char NextHallValue
if(direct == CW){ //正向
swtich(currenthallvalue){
case 3:
NextHallValue = 1;
break;
case 1:
NextHallValue = 5;
break;
case 5:
NextHallValue = 4;
break;
case 4:
NextHallValue = 6;
break;
case 5:
NextHallValue = 2;
break;
case 2:
NextHallValue = 3;
break;
default:
break;
}
}else{ //反向
swtich(currenthallvalue){
case 3:
NextHallValue = 2;
break;
case 2:
NextHallValue = 6;
break;
case 6:
NextHallValue = 4;
break;
case 4:
NextHallValue = 5;
break;
case 5:
NextHallValue = 1;
break;
case 1:
NextHallValue = 3;
break;
default:
break;
}
}
}
二、无感方波控制
1、不使用比较器
采用ADC中断的方式,实时计算三相的反电动势,来判断是否过零点。
#include <stdio.h>
#define CW 0
#define CCW 1
unsigned int motorSectionTime; //两次过零点的时间,一般用一个定时器来纪录
unsigned char motorWaitChangePhaseFlag; // 等待换相标志
typedef struct{
int phase_A_EMF_AD;
int phase_B_EMF_AD;
int phase_C_EMF_AD;
}ADC_VALUE;
ADC_VALUE gADC_value; //三相反电动势
typedef enum{
POS_UNKNOW = 0,
POS_AB = 1,
POS_AC = 2,
POS_BC = 3,
POS_BA = 4,
POS_CA = 5,
POS_CB = 6,
} enu_motorpos_t;
enu_motorpos_t motorfluxposition;
//ADC中断
unsigned char motorFindZerocrossFlag;
void ADC_IRQHandler(void){
static uint16_t motorbemfpositive = 0;
static int16_t motorbemf = 0;
static uint16_t motorbemfmid = 0;
swtich(motorfluxposition){
case POS_AB: {
motorbemfmid = (gADC_value.phase_A_EMF_AD + gADC_value.phase_B_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_C_EMF_AD;
break;
}
case POS_CB:{
motorbemfmid = (gADC_value.phase_C_EMF_AD + gADC_value.phase_B_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_A_EMF_AD;
break;
}
case POS_CA:{
motorbemfmid = (gADC_value.phase_C_EMF_AD + gADC_value.phase_A_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_B_EMF_AD;
break;
}
case POS_BA:{
motorbemfmid = (gADC_value.phase_B_EMF_AD + gADC_value.phase_A_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_C_EMF_AD;
break;
}
case POS_BC:{
motorbemfmid = (gADC_value.phase_C_EMF_AD + gADC_value.phase_B_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_A_EMF_AD;
break;
}
case POS_AC:{
motorbemfmid = (gADC_value.phase_C_EMF_AD + gADC_value.phase_A_EMF_AD)>>1;
motorbemfpositive = gADC_value.phase_C_EMF_AD;
break;
}
default:
break;
}
if(motorFindZerocrossFlag ==0 &&motorWaitChangePhaseFlag ==0){ //等待换相完毕,未找到过零点
if(dirct == CW){
//反电动势上升
if(motorfluxposition == POS_AB || motorfluxposition == POS_BC || motorfluxposition == POS_CA){
if(motorbemfpositive >= (motorbemfmid + 30)){
motorFindZerocrossFlag = 1; //上升找到过零点
}
}else{ //反电动势下降
if(motorbemfpositive <= (motorbemfmid - 30)){
motorFindZerocrossFlag = 1; //下降找到过零点
}
}
}else{
if(motorfluxposition == POS_AB || motorfluxposition == POS_BC || motorfluxposition == POS_CA){
if(motorbemfpositive <= (motorbemfmid - 30)){
motorFindZerocrossFlag = 1;
}
}else{
if(motorbemfpositive >= (motorbemfmid + 30)){
motorFindZerocrossFlag = 1;
}
}
}
}
if(motorFindZerocrossFlag == 1){
// motorSectionTime = GetTimerCnt(); //获取两次换相时间
motorWaitChangePhaseFlag = 1;
// OpenWaitChangePhaseTimer(motorSectionTime >> 2); //motorSectionTime的时间刚好是物理上的60°
//可用作换相提前角 这个时间为了触发 Timer_IRQHandler
//来进行换相
motorFindZerocrossFlag = 0;
}
}
有必要说明的是,反电动势过零点是存在上升沿河下降沿的,要区分一下。
当找到过零点时,需要记录上一次与当前的间隔时间,用于实现换相提前角的操作。
motorSectionTimes是用于触发比较器的溢出中断。这样既可以实现所谓换相提前与滞后。
//比较器溢出中断。
void Timer_IRQHandler(void){
if(motorWaitChangePhaseFlag){
if(dirct == CW){
motorfluxposition+=1;
if(motorfluxposition == 7){
motorfluxposition = POS_AB;
}
}else{
motorfluxposition-=1;
if(motorfluxposition == POS_UNKNOW){
motorfluxposition = POS_CB;
}
}
motorWaitChangePhaseFlag = 0;
}
}
注意体会motorWaitChangePhaseFlag和motorFindZerocrossFlag这两个标志所起到的作用。
void MotorCommutatePhase(enu_motorpos_t motorfluxposition){
switch(motorfluxposition){
case POS_AB:{
//A相上管开,B相下管开,其余关闭
break;
}
case POS_CB:{
//C相上管开,B相下管开,其余关闭
break;
}
case POS_CA:{
//C相上管开,A相下管开,其余关闭
break;
}
case POS_BA:{
//B相上管开,A相下管开,其余关闭
break;
}
case POS_BC:{
//B相上管开,C相下管开,其余关闭
break;
}
case POS_AC:{
//A相上管开,C相下管开,其余关闭
break;
}
default:
break;
}
}
这种控制方式就相对而言比较麻烦一点,需要ADC,一个定时器和一个比较器。
2、使用比较器(2024.3.11)
大体思路为:
通过比较器来确定悬空相是否过零点,每一次触发中断都去采集一下过零点的时间,
用作换相延时(定时器的溢出中断)。在定时器的溢出中断中实现换相操作。
上面的方式比较容易一些。
//无感方波,使用比较器
//比较器(CMP):用于过零点检测。
//定时器1 :用于检测2次过零点的时间。
//定时器2 :用于进行换相操作。
//用于确定悬空相作为输入比较
unsigned char SetCMPTable[6] = {
CMP_WC_TRGS, //ab--C
CMP_WB_TRGS, //ac--B
CMP_WA_TRGS, //bc--A
CMP_WC_TRGS, //ba--C
CMP_WB_TRGS, //ca--B
CMP_WA_TRGS, //cb--A
};
code uint8_t driveCCwTable[8] =
{
DRIVE_A_B, // 0
DRIVE_A_C, // 1
DRIVE_B_C, // 2
DRIVE_B_A, // 3
DRIVE_C_A, // 4
DRIVE_C_B, // 5
DRIVE_OFF,
DRIVE_OFF
};
code uint8_t driveCwTable[8] =
{
DRIVE_B_A, // 3
DRIVE_C_A, // 4
DRIVE_C_B, // 5
DRIVE_A_B, // 0
DRIVE_A_C, // 1
DRIVE_B_C, // 2
DRIVE_OFF,
DRIVE_OFF
};
//放置于比较中断函数中
void CompareProgramTask(void){
//获取过零点的时间
//getzerocrossingtime();
//....................
//获得过零点时间可设置换相延时时间。
}
void isrT2(void){
if(dirct == CW){
motorfluxposition +=1;
motorfluxposition %=6;
}else{
motorfluxposition +=5;
motorfluxposition %=6;
}
//change_phase(driveCwTable[motorfluxposition]); //每过一次零点换一次相
if(dirct == CW) {
//CMP2CON = SetCMPTable[motorfluxposition]; //根据当前相序 设置悬空相的输入用作比较
if(motorfluxposition & 0x01 == 0x01){
// CMPINT2 = CMP2INT_FALL; //寄存器操作下降沿
}else{
//CMPINT2 = CMP2INT_RISE; //寄存器操作上升沿
}
}else{
//CMP2CON = SetCMPTable[motorfluxposition]; //根据当前相序 设置悬空相的输入用作比较
if(motorfluxposition & 0x01 == 0x00){
// CMPINT2 = CMP2INT_FALL; //寄存器操作下降沿
}else{
//CMPINT2 = CMP2INT_RISE; //寄存器操作上升沿
}
}
}