本文介绍如何使用 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 100
byte bldc_step = 0, motor_speed;
unsigned int i;
void setup() {
DDRD |= 0x38; // Configure pins 3, 4 and 5 as outputs
PORTD = 0x00;
DDRB |= 0x0E; // Configure pins 9, 10 and 11 as outputs
PORTB = 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 setting
ACSR = 0x10; // Disable and clear (flag bit) analog comparator interrupt
pinMode(SPEED_UP, INPUT_PULLUP);
pinMode(SPEED_DOWN, INPUT_PULLUP);
}
// Analog comparator ISR
ISR (ANALOG_COMP_vect) {
// BEMF debounce
for(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 function
switch(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_DUTY
i = 5000;
// Motor start
while(i > 100) {
delayMicroseconds(i);
bldc_move();
bldc_step++;
bldc_step %= 6;
i = i - 20;
}
motor_speed = PWM_START_DUTY;
ACSR |= 0x08; // Enable analog comparator interrupt
while(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 input
ACSR |= 0x03; // Set interrupt on rising edge
}
void BEMF_A_FALLING(){
ADCSRB = (0 << ACME); // Select AIN1 as comparator negative input
ACSR &= ~0x01; // Set interrupt on falling edge
}
void BEMF_B_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR |= 0x03;
}
void BEMF_B_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR &= ~0x01;
}
void BEMF_C_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR |= 0x03;
}
void BEMF_C_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR &= ~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 cycle
OCR1B = duty; // Set pin 10 PWM duty cycle
OCR2A = 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 100
byte bldc_step = 0, motor_speed;
unsigned int i;
void setup() {
DDRD |= 0x38; // Configure pins 3, 4 and 5 as outputs
PORTD = 0x00;
DDRB |= 0x0E; // Configure pins 9, 10 and 11 as outputs
PORTB = 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 setting
ACSR = 0x10; // Disable and clear (flag bit) analog comparator interrupt
pinMode(SPEED_UP, INPUT_PULLUP);
pinMode(SPEED_DOWN, INPUT_PULLUP);
}
// Analog comparator ISR
ISR (ANALOG_COMP_vect) {
// BEMF debounce
for(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 function
switch(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_DUTY
i = 5000;
// Motor start
while(i > 100) {
delayMicroseconds(i);
bldc_move();
bldc_step++;
bldc_step %= 6;
i = i - 20;
}
motor_speed = PWM_START_DUTY;
ACSR |= 0x08; // Enable analog comparator interrupt
while(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 input
ACSR |= 0x03; // Set interrupt on rising edge
}
void BEMF_A_FALLING(){
ADCSRB = (0 << ACME); // Select AIN1 as comparator negative input
ACSR &= ~0x01; // Set interrupt on falling edge
}
void BEMF_B_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR |= 0x03;
}
void BEMF_B_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 2; // Select analog channel 2 as comparator negative input
ACSR &= ~0x01;
}
void BEMF_C_RISING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR |= 0x03;
}
void BEMF_C_FALLING(){
ADCSRA = (0 << ADEN); // Disable the ADC module
ADCSRB = (1 << ACME);
ADMUX = 3; // Select analog channel 3 as comparator negative input
ACSR &= ~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 cycle
OCR1B = duty; // Set pin 10 PWM duty cycle
OCR2A = 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