本文介绍如何使用 Arduino UNO 板构建无传感器无刷直流 (BLDC) 电机控制器或简单的 ESC(电子速度控制器)。
无刷直流电机有两种类型:有传感器和无传感器。有感无刷直流电机内置3个霍尔效应传感器,这些传感器检测无刷直流电机的转子位置。控制有传感器的BLDC电机很容易,因为我们知道转子的位置,就像在下面的项目中所做的那样:
项目电路原理图如下图所示。
/* Sensorless brushless DC (BLDC) motor control with Arduino UNO (Arduino DIY ESC).* This is a free software with NO WARRANTY.* http://simple-circuit.com/*/#define SPEED_UP A0 // BLDC motor speed-up button
#define SPEED_DOWN A1 // BLDC motor speed-down button
#define PWM_MAX_DUTY 255
#define PWM_MIN_DUTY 50
#define PWM_START_DUTY 100byte bldc_step = 0, motor_speed;
unsigned int i;
void setup() {DDRD |= 0x38; // Configure pins 3, 4 and 5 as outputsPORTD = 0x00;DDRB |= 0x0E; // Configure pins 9, 10 and 11 as outputsPORTB = 0x31;// Timer1 module setting: set clock source to clkI/O / 1 (no prescaling)TCCR1A = 0;TCCR1B = 0x01;// Timer2 module setting: set clock source to clkI/O / 1 (no prescaling)TCCR2A = 0;TCCR2B = 0x01;// Analog comparator settingACSR = 0x10; // Disable and clear (flag bit) analog comparator interruptpinMode(SPEED_UP, INPUT_PULLUP);pinMode(SPEED_DOWN, INPUT_PULLUP);
}
// Analog comparator ISR
ISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; i++) {if(bldc_step & 1){if(!(ACSR & 0x20)) i -= 1;}else {if((ACSR & 0x20)) i -= 1;}}bldc_move();bldc_step++;bldc_step %= 6;
}
void bldc_move(){ // BLDC motor commutation functionswitch(bldc_step){case 0:AH_BL();BEMF_C_RISING();break;case 1:AH_CL();BEMF_B_FALLING();break;case 2:BH_CL();BEMF_A_RISING();break;case 3:BH_AL();BEMF_C_FALLING();break;case 4:CH_AL();BEMF_B_RISING();break;case 5:CH_BL();BEMF_A_FALLING();break;}
}void loop() {SET_PWM_DUTY(PWM_START_DUTY); // Setup starting PWM with duty cycle = PWM_START_DUTYi = 5000;// Motor startwhile(i > 100) {delayMicroseconds(i);bldc_move();bldc_step++;bldc_step %= 6;i = i - 20;}motor_speed = PWM_START_DUTY;ACSR |= 0x08; // Enable analog comparator interruptwhile(1) {while(!(digitalRead(SPEED_UP)) && motor_speed < PWM_MAX_DUTY){motor_speed++;SET_PWM_DUTY(motor_speed);delay(100);}while(!(digitalRead(SPEED_DOWN)) && motor_speed > PWM_MIN_DUTY){motor_speed--;SET_PWM_DUTY(motor_speed);delay(100);}}
}void BEMF_A_RISING(){ADCSRB = (0 << ACME); // Select AIN1 as comparator negative inputACSR |= 0x03; // Set interrupt on rising edge
}
void BEMF_A_FALLING(){ADCSRB = (0 << ACME); // Select AIN1 as comparator negative inputACSR &= ~0x01; // Set interrupt on falling edge
}
void BEMF_B_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR |= 0x03;
}
void BEMF_B_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR &= ~0x01;
}
void BEMF_C_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR |= 0x03;
}
void BEMF_C_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR &= ~0x01;
}void AH_BL(){PORTD &= ~0x28;PORTD |= 0x10;TCCR1A = 0; // Turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)TCCR2A = 0x81; //
}
void AH_CL(){PORTD &= ~0x30;PORTD |= 0x08;TCCR1A = 0; // Turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)TCCR2A = 0x81; //
}
void BH_CL(){PORTD &= ~0x30;PORTD |= 0x08;TCCR2A = 0; // Turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)TCCR1A = 0x21; //
}
void BH_AL(){PORTD &= ~0x18;PORTD |= 0x20;TCCR2A = 0; // Turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)TCCR1A = 0x21; //
}
void CH_AL(){PORTD &= ~0x18;PORTD |= 0x20;TCCR2A = 0; // Turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)TCCR1A = 0x81; //
}
void CH_BL(){PORTD &= ~0x28;PORTD |= 0x10;TCCR2A = 0; // Turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)TCCR1A = 0x81; //
}void SET_PWM_DUTY(byte duty){if(duty < PWM_MIN_DUTY)duty = PWM_MIN_DUTY;if(duty > PWM_MAX_DUTY)duty = PWM_MAX_DUTY;OCR1A = duty; // Set pin 9 PWM duty cycleOCR1B = duty; // Set pin 10 PWM duty cycleOCR2A = duty; // Set pin 11 PWM duty cycle
}
传感器BLDC电机的换向是根据霍尔效应传感器状态完成的。
无传感器BLDC电机没有任何传感器来检测其转子位置,其换向基于定子绕组中产生的BEMF(反电动势)。
无传感器BLDC电机控制的主要优点是系统成本较低,主要缺点是电机必须以最小速率移动才能产生足够的BEMF来检测。
无传感器BLDC电机控制的主要优点是系统成本较低,主要缺点是电机必须以最小速率移动才能产生足够的BEMF来检测。
如上图所示,BEMF信号与霍尔效应传感器信号不同步(相移30°)。在每个通电序列中,两个绕组通电(一个连接到正极,另一个连接到负极),第三个绕组保持打开(浮动)。浮动绕组用于检测过零,因此,所有 3 个过零点的组合用于生成通电序列。我们总共有 6 个事件:
A阶段过零:从高到低,从低到高
B阶段过零:从高到低,从低到高
C阶段过零:从高到低,从低到高
如何检测过零事件?
检测过零事件的最简单方法是使用比较器。比较器有 3 个主端子:2 个输入(正负)和一个输出。如果正电压大于负电压,则比较器输出为逻辑高电平,如果正电压低于负电压,则逻辑低电平。
这个项目基本上需要 3 个比较器,连接如下图所示(B 阶段示例)。每个阶段都需要一个类似的电路。
所有 3 个比较器的虚拟自然点都是相同的,它是使用 3 个电阻器产生的。当浮动(开路)绕组中产生的BEMF越过零点向正侧时,比较器输出从低电平过渡到高电平。当浮动绕组中产生的BEMF越过零点向负侧时,比较器输出从高电平过渡到低电平。通过具有三个这样的比较器电路,每个相位上一个电路可产生三个数字信号,对应于绕组中的BEMF信号。这三个信号的组合用于推导换向序列。
项目电路原理图如下图所示:
请注意,所有接地端子都连接在一起。
在电路中有 2 个按钮,一个用于提高 BLDC 电机速度,第二个用于降低它。
前三个33k(连接到电机相位)和三个10k电阻用作分压器,因为我们不能为微控制器提供12V,其他三个33k电阻产生虚拟自然点。虚拟自然点连接到Arduino引脚6。
Arduino UNO板基于ATmega328P微控制器,该微控制器具有一个模拟比较器。该比较器的正输入位于 Arduino uno 引脚 6 (AIN0) 上,负输入可以是引脚 7 (AIN1)、A0 (ADC0)、A1 (ADC1)、A2 (ADC2)、A3 (ADC3)、A4 (ADC4) 或 A5 (ADC5)。因此,我将虚拟自然点连接到模拟比较器的正极引脚(引脚 6),将 A 相 BEMF 连接到引脚 7 (AIN1),将 B 相 BEMF 连接到引脚 A2,将 C 相 BEMF 连接到引脚 A3。每次比较器都会将虚拟点与一相的 BEMF 进行比较(这是在软件中完成的)。这最大限度地减少了所需的硬件并简化了电路。
IR2104S芯片用于控制每相的高端和低端MOSFET。高压侧和低压侧之间的切换是根据控制线IN和SD完成的。下图显示了输入和输出时序图:
三IR2104S的 SD 线分别连接到引脚 11、10 和 9,用于相 A、相 B 和相 C。Arduino UNO可以在该引脚上生成PWM信号,其中只有高侧MOSFET是PWM的。
使用Arduino代码的无传感器BLDC电机控制
下面的代码不使用任何 BLDC 电机库。
如上所述,Arduino 引脚 9、10 和 11 可以生成 PWM 信号,其中引脚 9 和引脚 10 与 Timer1 模块(OC1A 和 OC1B)相关,引脚 11 与 Timer2 模块 (OC2A) 相关。两个定时器模块都配置为生成频率约为 31KHz、分辨率为 8 位的 PWM 信号。当按下按钮时,PWM信号的占空比会通过写入其寄存器(OCR1A、OCR1B和OCR2A)来更新(加速或减速)。
模拟比较器将正输入AIN0(Arduino引脚6)与负输入(AIN1(引脚7)、ADC2(引脚A2)或ADC3(引脚A3))进行比较。当正引脚电压高于负引脚电压时,设置模拟比较器ACO的输出,当正引脚电压低于负引脚电压时,ACO被清除。
在这个项目中,我使用了模拟比较器中断,并在上升时使用中断(从低到高的转换)和下降时的中断(从高到低的转换),这使得过零事件中断微控制器。
要完全理解代码,请阅读 ATmega328 数据表!
/* Sensorless brushless DC (BLDC) motor control with Arduino UNO (Arduino DIY ESC).* This is a free software with NO WARRANTY.* https://simple-circuit.com/*/#define SPEED_UP A0
#define SPEED_DOWN A1
#define PWM_MAX_DUTY 255
#define PWM_MIN_DUTY 50
#define PWM_START_DUTY 100byte bldc_step = 0, motor_speed;
unsigned int i;
void setup() {DDRD |= 0x38; // Configure pins 3, 4 and 5 as outputsPORTD = 0x00;DDRB |= 0x0E; // Configure pins 9, 10 and 11 as outputsPORTB = 0x31;// Timer1 module setting: set clock source to clkI/O / 1 (no prescaling)TCCR1A = 0;TCCR1B = 0x01;// Timer2 module setting: set clock source to clkI/O / 1 (no prescaling)TCCR2A = 0;TCCR2B = 0x01;// Analog comparator settingACSR = 0x10; // Disable and clear (flag bit) analog comparator interruptpinMode(SPEED_UP, INPUT_PULLUP);pinMode(SPEED_DOWN, INPUT_PULLUP);
}
// Analog comparator ISR
ISR (ANALOG_COMP_vect) {// BEMF debouncefor(i = 0; i < 10; i++) {if(bldc_step & 1){if(!(ACSR & 0x20)) i -= 1;}else {if((ACSR & 0x20)) i -= 1;}}bldc_move();bldc_step++;bldc_step %= 6;
}
void bldc_move(){ // BLDC motor commutation functionswitch(bldc_step){case 0:AH_BL();BEMF_C_RISING();break;case 1:AH_CL();BEMF_B_FALLING();break;case 2:BH_CL();BEMF_A_RISING();break;case 3:BH_AL();BEMF_C_FALLING();break;case 4:CH_AL();BEMF_B_RISING();break;case 5:CH_BL();BEMF_A_FALLING();break;}
}void loop() {SET_PWM_DUTY(PWM_START_DUTY); // Setup starting PWM with duty cycle = PWM_START_DUTYi = 5000;// Motor startwhile(i > 100) {delayMicroseconds(i);bldc_move();bldc_step++;bldc_step %= 6;i = i - 20;}motor_speed = PWM_START_DUTY;ACSR |= 0x08; // Enable analog comparator interruptwhile(1) {while(!(digitalRead(SPEED_UP)) && motor_speed < PWM_MAX_DUTY){motor_speed++;SET_PWM_DUTY(motor_speed);delay(100);}while(!(digitalRead(SPEED_DOWN)) && motor_speed > PWM_MIN_DUTY){motor_speed--;SET_PWM_DUTY(motor_speed);delay(100);}}
}void BEMF_A_RISING(){ADCSRB = (0 << ACME); // Select AIN1 as comparator negative inputACSR |= 0x03; // Set interrupt on rising edge
}
void BEMF_A_FALLING(){ADCSRB = (0 << ACME); // Select AIN1 as comparator negative inputACSR &= ~0x01; // Set interrupt on falling edge
}
void BEMF_B_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR |= 0x03;
}
void BEMF_B_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 2; // Select analog channel 2 as comparator negative inputACSR &= ~0x01;
}
void BEMF_C_RISING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR |= 0x03;
}
void BEMF_C_FALLING(){ADCSRA = (0 << ADEN); // Disable the ADC moduleADCSRB = (1 << ACME);ADMUX = 3; // Select analog channel 3 as comparator negative inputACSR &= ~0x01;
}void AH_BL(){PORTB = 0x04;PORTD &= ~0x18;PORTD |= 0x20;TCCR1A = 0; // Turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)TCCR2A = 0x81; //
}
void AH_CL(){PORTB = 0x02;PORTD &= ~0x18;PORTD |= 0x20;TCCR1A = 0; // Turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)TCCR2A = 0x81; //
}
void BH_CL(){PORTB = 0x02;PORTD &= ~0x28;PORTD |= 0x10;TCCR2A = 0; // Turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)TCCR1A = 0x21; //
}
void BH_AL(){PORTB = 0x08;PORTD &= ~0x28;PORTD |= 0x10;TCCR2A = 0; // Turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)TCCR1A = 0x21; //
}
void CH_AL(){PORTB = 0x08;PORTD &= ~0x30;PORTD |= 0x08;TCCR2A = 0; // Turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)TCCR1A = 0x81; //
}
void CH_BL(){PORTB = 0x04;PORTD &= ~0x30;PORTD |= 0x08;TCCR2A = 0; // Turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)TCCR1A = 0x81; //
}void SET_PWM_DUTY(byte duty){if(duty < PWM_MIN_DUTY)duty = PWM_MIN_DUTY;if(duty > PWM_MAX_DUTY)duty = PWM_MAX_DUTY;OCR1A = duty; // Set pin 9 PWM duty cycleOCR1B = duty; // Set pin 10 PWM duty cycleOCR2A = duty; // Set pin 11 PWM duty cycle
}
参考资料
- [1] 【YouTube@Hyperspace Pirate】Brushless DC Speed Controller
- [2] 使用Arduino和IR2101的无刷直流电机控制器
- [3] 使用Arduino的无传感器BLDC电机控制 – DIY ESC
- [4] Sensorless brushless motor control with PIC16F887
MOSFETs: IRLZ44
Flyback Diodes: MUR120G
MOSFET Drivers: IR2101
5V Linear Regulator: LM7805
MOSFET Driver supply regulator: LM317