项目笔记| 基于Arduino和IR2101的无刷直流电机控制器

本文介绍如何使用 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
}

参考资料

MOSFETs: IRLZ44
Flyback Diodes: MUR120G
MOSFET Drivers: IR2101
5V Linear Regulator: LM7805
MOSFET Driver supply regulator: LM317

相关推荐

  1. 基于单片机直流电控制

    2024-07-21 17:32:04       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 17:32:04       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 17:32:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 17:32:04       45 阅读
  4. Python语言-面向对象

    2024-07-21 17:32:04       55 阅读

热门阅读

  1. resultMap

    2024-07-21 17:32:04       16 阅读
  2. Python编程防止计算机休眠,保持唤醒状态

    2024-07-21 17:32:04       14 阅读
  3. 力扣题解(盈利计划)

    2024-07-21 17:32:04       18 阅读
  4. Mysql在linux安装报错

    2024-07-21 17:32:04       17 阅读
  5. 大型网站核心架构要素

    2024-07-21 17:32:04       15 阅读
  6. 看过来!看过来!python九大数据类型大整合!

    2024-07-21 17:32:04       15 阅读
  7. centos软件安装

    2024-07-21 17:32:04       20 阅读
  8. 内存屏障:程序员的“隐形护盾”

    2024-07-21 17:32:04       17 阅读
  9. 比较 WordPress 的 Baklib 和 BetterDocs

    2024-07-21 17:32:04       18 阅读