daniel23
Published © LGPL

PWM variable speed for DC motor (120V-15A) IGBT switching

Arduino controlled PWM chopper, acceleration and deceleration ramp, motor braking, OLED display and a little more...

AdvancedWork in progress3,968
PWM variable speed for DC motor (120V-15A) IGBT switching

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
IGBT Single Transistor, 120 A
IGBT Single Transistor, 120 A
Maybe one could be enough ?
×2
1.3 inch I2C OLED display
×1
Transformer 400V to 48V - 250VA
×3
Bridge Rectifier Diode, Three Phase
Bridge Rectifier Diode, Three Phase
×1
IR2109
×1
LM393 chip
Texas Instruments LM393 chip
×1
Resistors, capacitors, relays, etc.
×1
Heathsink eg. 40x20mm, 200mm length
Thermal resistance : about 3 K/W
×1
Diode eg. BYV74-400
Maybe one could be enough ?
×2

Story

Read more

Schematics

PWM variable speed for DC motor

PDF file

Update schema sheet_1 only

PDF file

Code

DC_motor_speed_control_IGBT_V2

Arduino
Code for arduino with ATmega328P - Arduino nano recommended
#include <PWM.h>                // https://github.com/RCS101/PWM/blob/master/PWM.h
#include "U8glib.h"             // https://github.com/olikraus/u8glib/ 
//#include <digitalWriteFast.h>   // https://github.com/mpflaga/Arduino-digitalWriteFast

/*     In I2C Fast Mode (400 kbit/s) termination of the SDA and SCL bus with 2.2K pullups
       (tested with 2 pairs of RJ45 cat4 cable up to 7m)
       Arduino I2C bus:   A4 = SDA    A5 = SCL
*/
U8GLIB_SH1106_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_FAST);
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST);
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK);    // go down to 100 kbit/s if problem occurs
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
//U8GLIB_SSD1309_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST);

// Pin Declaration
const byte Cde_Reverse = 11;      // "Forward/reverse" command
const byte Cde_EvAir = 10;        // "Air blowing" command
const byte Rel_Precharge = 5;     // "Precharge" relay (in fact not needed)
const byte Rel_Alim = 4;          // "Power ON" relay
const byte Rel_Reverse = 3;       // "Forward/reverse" relay
const byte Rel_Freinage = 6;      // "Brake" relay
const byte Rel_EvAir = 7;         // "Air blowing" solenoid valve relay
const byte led_I_limit = 12;      // current limit signalisation
const byte Pwm_Inhibit = 8;       // Inhibition du driver PWM IR2109
const byte Pwm_IGBT = 9;          // PWM output pin
const byte PinI_limit = 2;        // Current limitation Input (low if limitation activated)
const byte Pot_Nmax = A0;         // Maximum speed potentiometer
const byte Pot_Pedal = A1;        // Pedal potentiometer (20kOhm pressed if V <= 3.3V)
const byte I_measurePin = A2;     // Current measurement input
const byte Temp_measurePin = A2;  // temperature measurement input (for later use)
// const byte LED_BUILTIN = 13;   // For cycle time measurement (ramp UP or ramp DOWN in progress)
//
byte nbrLoop = 0;     // loop counter (refresh display takes long: do it only every 8 loops to save time)

// Dclaration des constantes et variables globales
const int32_t frequency = 20000;                // PWM frequency (in Hz)
const uint16_t hysteresis = 2;                  // something like a "deadzone"
const uint16_t rampStepTime = 5000;             // Ramp times between each increment
const uint16_t rampDownStepTime = 800;          // Ramp times between each decrement
/* decay time shall be about 2s from full speed to allow decrease of motorspeed. Else brake resistor may overheath  *
*/
const int16_t maxPedalPressed = 1023 * 2 / 3 ;  // ------ TO BE UPDATED AFTER TESTS ------
// byte PwmValue = 0;                           // low = IGBT blocked, motor stopped
/*      prechargeTime can be reduced by taking into account the reset time from the arduino       */
const uint16_t prechargeTime = 1000;            // charging time of the power capacitors
volatile bool flag_I_limit = false;
volatile bool startBrakeFlag = false;           // initiate the brake cycle
volatile bool brakeActiveFlag = false;
/*        Test and correct brakeTime value for full spindle stop from max RPM       */
const uint16_t brakeTime = 1500;        // Time brake relay shall remain closed (=>to be tested)
const uint16_t fadeTime = 8000;         // Time before the display is dimmed
volatile int16_t actualSpeed;
volatile int16_t Nmax = 0;              // Nmax potentiometer value
uint16_t V_pedal = 0;                   // Pedal potentiometer value
const float scale_I = 1.15;             // Not exact current measured - Only indicative value !!!

void setup() {
  Serial.begin(115200);
  while (!Serial);
  u8g.setFont(u8g_font_tpssb);  // font selection : https://github.com/olikraus/u8glib/wiki/fontsize
  u8g.setColorIndex(1);         // Instruction to set pixel to on. (displays the pixel in white)
  uint8_t contrast = u8g.setContrast(1);    //u8g.sleepOn();
  InitTimersSafe();             // initialize all timers except for 0, to save time keeping functions
  bool success = SetPinFrequencySafe(Pwm_IGBT, frequency);  //sets frequency for the specified pin
  /*
     Before setting the pin as output a pull-up resistor maintains a high level
  */
  digitalWrite(Pwm_Inhibit, HIGH);   // ensure that IR2009 remains inhibited
  pinMode(Pwm_Inhibit, OUTPUT);      // when the pin is set as output during setup
  digitalWrite(Pwm_Inhibit, HIGH);   //

  pinMode(Pwm_IGBT, OUTPUT);
  pwmWrite(Pwm_IGBT, 0);

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PinI_limit, INPUT_PULLUP);
  pinMode(Cde_Reverse, INPUT_PULLUP);
  pinMode(Cde_EvAir, INPUT_PULLUP);
  pinMode(Rel_Precharge, OUTPUT);
  pinMode(Rel_Alim, OUTPUT);
  pinMode(Rel_Freinage, OUTPUT);
  pinMode(Rel_Reverse, OUTPUT);
  pinMode(Rel_EvAir, OUTPUT);
  pinMode(led_I_limit, OUTPUT);
  digitalWrite(Rel_Alim, LOW);
  digitalWrite(Rel_Freinage, LOW);
  digitalWrite(Rel_Reverse, LOW);
  digitalWrite(Rel_EvAir, LOW);
  digitalWrite(Rel_EvAir, LOW);
  digitalWrite(led_I_limit, LOW);      // LED is OFF
  //  digitalWrite(Rel_Precharge, HIGH);    // Start charging power capacitors (Not longer needed if relay removed)
  /*     Precharge relay can be removed by putting the preload resistor
         permanently in parallel on the power relay "Rel_Alim" ...*/
  delay(prechargeTime);
  digitalWrite(Rel_Alim, HIGH);         // Give full power by shunting the precharge resistor
  // digitalWrite(Rel_Precharge, LOW);     // Not longer needed if relay removed ...
  delay(200);
  attachInterrupt(digitalPinToInterrupt( PinI_limit), irq_I_limit, FALLING);
  digitalWrite(Pwm_Inhibit, LOW);      // remove the inhibition of the IR2009 output
}


//  ******************************************************************************
void loop() {
  static unsigned long previousMillis = 0;
  unsigned long currentMillis;
  static int16_t previousNmax = 0;
  flag_I_limit = checkCurrentLimit(flag_I_limit);
  byte targetSpeed = GetSpeedSetpoint();
  //  Serial.print("/  ");
  //  Serial.print (targetSpeed);
  //  Serial.println("  /  ");
  set_motor_speed(targetSpeed);         // target speed to reach (following ramp UP or ramp DOWN)
  digitalWrite(LED_BUILTIN, LOW);
  if ((actualSpeed <= (targetSpeed - 5)) || (actualSpeed >= (targetSpeed + 5))) {
    digitalWrite(LED_BUILTIN, HIGH);    // Speed is NOT reached
    previousMillis = millis();
  }
  if ((previousNmax <= (Nmax - 2)) || (previousNmax  >= (Nmax + 2))) {
    previousMillis = millis();
    previousNmax = Nmax;
  }

  /* --- OLED display dimming --- */
  currentMillis = millis();
  if (currentMillis - previousMillis <= fadeTime) {
    uint8_t contrast = u8g.setContrast(255);
  }
  else {
    uint8_t contrast = u8g.setContrast(1);
  }
  /* ---------------------------- */

  brake(actualSpeed, targetSpeed);      // do not brake if "actualSpeed" is not zero
  checkReverse(actualSpeed);
  blowAir(actualSpeed);
  int16_t Imeasure = measureCurrent(scale_I);
  nbrLoop++;          // refresh display takes too much time.  Do it only every 8 loops
  if (nbrLoop >= 8) {
    displayValues(Nmax , targetSpeed, Imeasure);
    nbrLoop = 0;
  }
}


//  ******************************************************************************
void checkReverse(byte speedVal) {
  static bool prevState = false;
  bool state = digitalRead(Cde_Reverse);
  if (prevState != state) {
    startBrakeFlag = true;
    prevState = state;
  }
  else if ((actualSpeed == 0) && (startBrakeFlag == false) && (brakeActiveFlag == false)) {
    digitalWrite(Rel_Reverse, state);
  }
}

//  ******************************************************************************
void brake(byte valSpeed, byte targetSpeed) {
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();
  if (valSpeed == 0) {                    // wait "actualSpeed" to be null before braking
    if (startBrakeFlag == true) {
      digitalWrite(Rel_Freinage, HIGH);
      previousMillis = currentMillis;
      brakeActiveFlag = true;
      startBrakeFlag = false;
    }
  }
  if (currentMillis - previousMillis >= brakeTime) {
    digitalWrite(Rel_Freinage, LOW);
    brakeActiveFlag = false;
  }
  if (valSpeed >= 3) digitalWrite(Rel_Freinage, LOW);
  if (targetSpeed >= 10) {
    digitalWrite(Rel_Freinage, LOW);
    brakeActiveFlag = false;
    startBrakeFlag = false;
  }
}


//  ******************************************************************************
void blowAir(byte valSpeed) {
  /*   valSpeed == 25 ~ 10% of max speed (no need to blow air below this speed  */
  if ((digitalRead(Cde_EvAir)) && (valSpeed >= 25)) {
    digitalWrite(Rel_EvAir, HIGH);
  }
  else digitalWrite (Rel_EvAir, LOW);
}


//  ******************************************************************************
int16_t measureCurrent(float scale) {
  int16_t measure = analogRead(I_measurePin);
  return measure * scale;
}


//  ******************************************************************************
bool checkCurrentLimit(bool limitFlag) {
  const uint32_t interval = 2000;           // minimal duration of the LED ON state
  static uint32_t previousMillis = 0;
  uint32_t currentMillis = millis();
  if (limitFlag == true) {
    digitalWrite(led_I_limit, HIGH);     // switch the LED ON
    previousMillis = currentMillis;
    limitFlag = false;
  }
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite(led_I_limit, LOW);    // switch the LED OFF
  }
  return limitFlag;
}


//  ******************************************************************************
byte GetSpeedSetpoint() {
  uint16_t PWMSpeed;
  Nmax = 0;
  V_pedal = 0;
  for (byte i = 0; i <= 7; i++) {
    Nmax += analogRead(Pot_Nmax);
    V_pedal += analogRead(Pot_Pedal);
  }
  Nmax = Nmax >> 3;
  V_pedal = V_pedal >> 3;

  if (V_pedal <= maxPedalPressed) {
    PWMSpeed = map(V_pedal, 0, maxPedalPressed, Nmax, 0);
  }
  else {
    PWMSpeed = 0;
  }
  PWMSpeed = PWMSpeed >> 2;             // max value for PWM is 255 (one byte)
  if (PWMSpeed <= 5) PWMSpeed = 0;
  return byte(PWMSpeed);
}


//  ******************************************************************************
void set_motor_speed(byte targetSpeed) {
  /*  Note that the pwm register shall be feed with inverted value of "targetSpeed" since
      the motor standstill if PWM pin is high (255) and at max speed if PWM pin is low (0)
  */
  static uint32_t previousMicros = 0;
  uint32_t currentMicros = micros();
  byte temp ;
  /*           ramp UP (acceleration)  */
  if ((actualSpeed < targetSpeed) && (brakeActiveFlag == false)) {
    startBrakeFlag = false;                  // true indicates if braking shall be done
    if (currentMicros - previousMicros >= (rampStepTime)) {
      previousMicros = currentMicros;
      if (actualSpeed < 255)  actualSpeed += 2;
      if (actualSpeed >= 255) actualSpeed = 255;
    }
  }
  /*           ramp DOWN (deceleration)  */
  if ((actualSpeed > (targetSpeed + hysteresis)) || (targetSpeed <= hysteresis)) {
    if (currentMicros - previousMicros >= rampDownStepTime) {
      previousMicros = currentMicros;
      if (actualSpeed > 0) {
        actualSpeed -= 2;
        if (actualSpeed <= 0) actualSpeed = 0;
        if (actualSpeed == 0) startBrakeFlag = true;
      }
    }
  }
  // temp = actualSpeed ^ 255;           // invert each bit of "targetSpeed"
  //                                     // Not longer needed since IR2009 rewired
  digitalWrite(LED_BUILTIN, HIGH);
  //  pwmWrite(Pwm_IGBT, temp );
  pwmWrite(Pwm_IGBT, actualSpeed );
}


//  ******************************************************************************
void displayValues(uint16_t val_1, uint16_t val_2, uint16_t val_3) {
  // les valeurs vont de 0  120 avec un offset eventuel...
  val_1 = map(val_1, 0, 1023, 5, 125);
  val_2 = map(val_2, 0, 255, 1, 120);
  val_3 = map(val_3, 0, 1023, 0, 120);
  u8g.firstPage();
  do {
    u8g.drawStr( 1, 30, "0%");      // bauche du cadre "Vitesse" (le cadre complet de
    u8g.drawStr( 55, 30, "50%");    // dimensions variables est dans la procdure "draw()")
    u8g.drawStr( 108, 30, "100");
    u8g.drawHLine(0, 13, 128);
    u8g.drawVLine(4, 14, 5);

    u8g.drawVLine(34, 14, 3);
    u8g.drawVLine(64, 14, 5);
    u8g.drawVLine(94, 14, 3);
    u8g.drawVLine(123, 14, 5);
    u8g.drawStr( 1, 64, "0");       // Cadre "Courant"
    u8g.drawStr( 35, 64, "5A");
    u8g.drawStr( 70, 64, "10A");
    u8g.drawStr( 108, 64, "15A");
    u8g.drawFrame(0, 38, 128, 10);  // on cre le frame de dimensions fixes
    u8g.drawVLine(4, 48, 5);
    u8g.drawVLine(24, 48, 3);
    u8g.drawVLine(44, 48, 5);
    u8g.drawVLine(64, 48, 3);
    u8g.drawVLine(84, 48, 5);
    u8g.drawVLine(104, 48, 3);
    u8g.drawVLine(123, 48, 5);
    draw(val_1, val_2, val_3);  // les 3 paramtres setPoint, rpmSetting, current sont transmis
  } while ( u8g.nextPage() );   // on marque ici la fin de la procdure d'affichage
}


//  ******************************************************************************
void draw(byte setPoint, byte rpmSetting, byte current) {
  u8g.drawFrame(0, 0, setPoint, 14);  // ce frame indiquera la consigne max RPM
  u8g.drawBox(4, 1, rpmSetting , 12);  //  rectangle plein RPM demand...
  u8g.drawBox(4, 41, current, 4);     // rectangle plein courant approximatif
}


//  ******************************************************************************
void irq_I_limit() {
  flag_I_limit = true;
}

Credits

daniel23

daniel23

8 projects • 10 followers

Comments