Jump to content
43oh

3phase variable speed motor drive


Recommended Posts

Hi there
Here is my 3phase variable speed motor drive booster pack

This has been in my mind for some years, but I always thought that a 3phase variable speed inverter drive is
beyond my humble hobbyist scope. Too complicated for my old 8-bit mind ;-)

Such a inverter contains:
6 high voltage FETs or IGBTs, 6 gatedrives, at least one DSP, a protection concept,
all the software to create the 3-phase PWM, dead time control.....

Still that was for quite some time on my long-term "to do" list, with no chance to actually materialize it,
not enough time, too many other things to do.

When playing around with the PWM module of the TM4C123 I found out that creating a 3phase PWM
signal with this module is actually pretty easy.
Combined that with an integrated Power Module such as the FSB50550 (Fairchild).



So here it is: a booster pack for the Tiva Launchpad which drives big-ass 3phase motors.

The booster pack contains the following:
- the FSB50550 power module (6 FETs 500V 1.4Ohm, Gatedrivers, Bootstrap diodes, Temp sensor)
- snubber capacitor
Power supply: everything is powered from one DC source, 20V or (much) more.
- 15V switchmode power supply from the high voltage side, built around a LNK304, for the FSB50550
- 3.3V switchmode power supply from the 15V to power the Launchpad, built around a LT1376
Measurement:
- Passive voltage dividier to measure the input voltage
- Sense resistor and LM339 comparator for overcurrent detection
Display:
- Nokia 5110 display
Potentiometer for motor speed and direction


The software is based on Energia using Tiva Ware function calls for all the PWM stuff.
It is still work in progress, very basic and at the moment consists of:

- calculate the sinwave lookup table at startup
- PWM initialisation (PWM set to 15625 Hz, deadtime 1us, sync on)
- a timer interrupt run every 10uSecs, do update the 3 PWD duty cycles
- ADC measurement of temperature, voltage, current (moving average)
- fault interrupt

The main program is very short, the display is updated twice a second and the modulation factor is calculated
out of the potentiometer speed setting and the applied DC voltage.
Sudden changes in motor frequency are limited in the software, to prevent the motor to feed back energy and cause
overvoltage.

The motor on the picture is a 1/2hp, 900rpm, 6-pole motor, 12 kg of Italian steel and copper, probably 50 years old.
For playing around, I apply about 50% of rated volt/hz, so current and maximum torque is reduced.
Currently I use my dual 35V 4A lab supply, series connected, as a power source.

 

here is the code:

//simple 3phase frequency converter 
//27.9.2014 by maelli

#define dots   192  //dots per halfhave, must be divisible with 3
#define period 5120 //80Mhz/5120 = 15625 switching frequency
#define dt     80   //deadtime 80Mhz / 80 = 1uS 
#define PART_TM4C123GH6PM

#include <stdint.h>
#include <stdbool.h>

#include "inc/hw_ints.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_types.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/pwm.h" 
#include "LCD_5110.h"
#include "inc/tm4c123gh6pm.h"

LCD_5110 myScreen (33,37,36,35,34,38,17);

char               celsius[3]={0x7f,'C',0x00};
uint16_t           a,dire=0,modu,tensec;
uint32_t           timecount,sintable[dots];
volatile int32_t   irqcount,timeset;
volatile uint32_t  temperature, voltage, current, poti;

void setup(){
  myScreen.begin();
  myScreen.setBacklight(0);
  myScreen.text(0, 0, "3ph Converter");

  for(int i=0;i<dots;i++) sintable[i]=sinf((i*3.14159)/dots)*(period/2-dt); 

  unsigned long ulPeriod;
  unsigned int Hz = 10000;   // interupt frequency in Hz  
  ulPeriod = (SysCtlClockGet() / Hz);
  initTimer();
  charge_gdu();
  ROM_TimerLoadSet(TIMER0_BASE, TIMER_A,ulPeriod -1);
  initPWM();
}
 
void loop(){ 
  if (irqcount>499) {  //20x per sec
    irqcount-=500;
   
    int32_t fsoll=732*(poti-16384);
    int32_t diff=fsoll-timeset;
    if (diff>0){
      if (diff>150000)  timeset+=150000;
      else timeset=fsoll;
    }
    else   {
      if (diff<-150000) timeset-=150000;
      else timeset=fsoll;
    }
    
    modu=abs(timeset)/voltage/16;
    if (modu<(32000/voltage)) modu=32000/voltage;
    
    if (modu>256) modu=256;

    tensec++;
    if (tensec==10) {    //2x per sec we display something
      tensec=0;
      myScreen.text(0, 1, mkstrg((temperature-325)/24,2));
      myScreen.text(2, 1, celsius);
      myScreen.text(5, 1, mkstrg((voltage)/23,3));
      myScreen.text(8, 1, "Volt");
      myScreen.text(0, 2, mkstrg(abs(timeset)/322122,2));
      myScreen.text(2, 2, ".");
      myScreen.text(3, 2, mkstrg(abs((timeset/32212)%10),1));
      myScreen.text(4, 2, "Hz");
      myScreen.text(7, 2, mkstrg(current,4));
      myScreen.text(11, 2, "mA");
      if (timeset<0)  myScreen.text(0, 3, "links ");
      else            myScreen.text(0, 3, "rechts");
    }
  }
}

String mkstrg(int d,uint8_t l){
  char display[l+1];
  int q=1;
  display[l]=0;
  for (uint8_t a=l;a;a--){
    display[a-1]=0x30+(d%(q*10))/q;
    q*=10;
  }
  return display;
}

void initTimer(){  
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
  ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);   // 32 bits Timer
  TimerIntRegister(TIMER0_BASE, TIMER_A, Timer0Isr);     // Registering  isr       
  ROM_TimerEnable(TIMER0_BASE, TIMER_A); 
  ROM_IntEnable(INT_TIMER0A); 
  ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);  
}

void charge_gdu(){
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
  
  ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 );
  ROM_GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_0,GPIO_STRENGTH_4MA,GPIO_PIN_TYPE_STD);
  
  GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_7);    //alle 3 oberen ausschalten
  HWREG(GPIO_PORTA_BASE + (GPIO_PIN_7 << 2)) = 0;
  GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_1);
  HWREG(GPIO_PORTD_BASE + (GPIO_PIN_1 << 2)) = 0;
  GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3);
  HWREG(GPIO_PORTF_BASE + (GPIO_PIN_3 << 2)) = 0;
  
  GPIOPinTypeGPIOOutput(GPIO_PORTD_BASE, GPIO_PIN_0);      //auch die 2 letzten aus
  HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = 0;
  GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);
  HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = 0;
  
  GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_6);       //den ersten unteren ein
  HWREG(GPIO_PORTA_BASE + (GPIO_PIN_6 << 2)) = GPIO_PIN_6;
  delay(1);
  HWREG(GPIO_PORTD_BASE + (GPIO_PIN_0 << 2)) = GPIO_PIN_0;
  delay(1);
  HWREG(GPIO_PORTF_BASE + (GPIO_PIN_2 << 2)) = GPIO_PIN_2;
  delay(1);                                                
}
  
void initPWM(){
  ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM1);  //The Tiva Launchpad has two PWM modules (0 and 1). We are using 1
    
  ROM_GPIOPinConfigure(GPIO_PD0_M1PWM0);
  ROM_GPIOPinConfigure(GPIO_PD1_M1PWM1);
  ROM_GPIOPinConfigure(GPIO_PA6_M1PWM2);
  ROM_GPIOPinConfigure(GPIO_PA7_M1PWM3);
  ROM_GPIOPinConfigure(GPIO_PF2_M1PWM6);
  ROM_GPIOPinConfigure(GPIO_PF3_M1PWM7);
  ROM_GPIOPinConfigure(GPIO_PF4_M1FAULT0);

  ROM_GPIOPinTypePWM(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 );
  ROM_GPIOPinTypePWM(GPIO_PORTA_BASE, GPIO_PIN_6 | GPIO_PIN_7 );
  ROM_GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4);
  
  PWM1_0_FLTSEN_R =3;     //PWM fault inverted see page 1169
  GPIO_PORTF_PUR_R=0x10;  //weak pullup for Pin 4   
  
  ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_0, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); 
  ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_1, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); 
  ROM_PWMGenConfigure(PWM1_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_SYNC | PWM_GEN_MODE_FAULT_LEGACY); 
  
  ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_0, period); 
  ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_1, period); 
  ROM_PWMGenPeriodSet(PWM1_BASE, PWM_GEN_3, period); 
  
  ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_0, dt,dt);  
  ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_1, dt,dt); 
  ROM_PWMDeadBandEnable(PWM1_BASE, PWM_GEN_3, dt,dt); 

  ROM_PWMSyncTimeBase(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT);
  
  ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_0);
  ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_1);
  ROM_PWMGenEnable(PWM1_BASE, PWM_GEN_3);

  delay(1);
  
  PWMFaultIntRegister(PWM1_BASE, oh_shit);
  ROM_PWMIntEnable(PWM1_BASE,PWM_INT_FAULT0);
  
  ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, true);
}
 
void Timer0Isr(void) {   //10000x per second
  ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);       // Clear the timer interrupt

  irqcount++;
  timecount+=timeset; // 1 Hz is 192x256*256*256/10000=322122.5
  
  if (timecount> 0xEFFFFFFF) timecount+=0xC0000000;
  if (timecount> 0xBFFFFFFF) timecount-=0xC0000000;;
  a=timecount>>16;
  a=a/(16384/(dots/3*2));      //a immer kleiner 2*dots: C000 *dots/3*2/ 4000= 12 *dots/3*2/4= 2*dots
  if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2+sintable[a]*modu/256);
  else       ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_0,period/2-sintable[a-dots]*modu/256);
  a=a+dots*2/3; if (a>=2*dots) a-=2*dots;
  if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2+sintable[a]*modu/256);
  else       ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_2,period/2-sintable[a-dots]*modu/256);
  a=a+dots*2/3; if (a>=2*dots) a-=2*dots;
  if (a<dots)ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2+sintable[a]*modu/256);
  else       ROM_PWMPulseWidthSet(PWM1_BASE, PWM_OUT_6,period/2-sintable[a-dots]*modu/256);
  
  ROM_PWMSyncUpdate(PWM1_BASE,PWM_GEN_0_BIT |PWM_GEN_1_BIT|PWM_GEN_3_BIT);
  
  switch(irqcount%10){
    case 0:
      temperature=(temperature*127+analogRead(26))/128;
      break;
    case 1:
      voltage=(voltage*31+analogRead(27)*3)/32;
      break;
    case 2:
      current=(current*127+analogRead(25)*8)/128;
      break;
    case 3:
      poti=(poti*127+analogRead(28)*8)/128;
      break;
  }
}
 
 void oh_shit(void) {              //in case of severe overcurrent we shut down!
   ROM_PWMFaultIntClearExt(PWM1_BASE,PWM_INT_FAULT0);
   ROM_PWMOutputState(PWM1_BASE, PWM_OUT_0_BIT | PWM_OUT_1_BIT | PWM1_BASE | PWM_OUT_2_BIT | PWM_OUT_3_BIT |PWM_OUT_6_BIT | PWM_OUT_7_BIT, false);
 }

 

post-3772-14264605471806_thumb.jpg

post-3772-14264605472256_thumb.jpg

post-3772-14264605472606_thumb.jpg

Link to post
Share on other sites

@@timotet

here are some more pictures. The launchpad is sandwiched between the display and my board.

Not much on top, the 50550 hidden under copper heatsink (epoxied on). What looks like a big resistor

is a 1mH inductor.

Yes, this is the first working prototype, and it looks like this. Smallest SMDs I hand solder are 0603.

 

The circuit diagram (Eagle) is no secret either, however, not human readable yet, since I did not find

models for e.g.LNK304. So I used different componens with similar pinout in the diagram, nasty me.

 

I loosely followed this app note:

https://www.fairchildsemi.com/application-notes/AN/AN-9080.pdf

 

Yes, the practical uses are limited...  fractional-horsepower 3phase motors are rare.

checked my drill press, but that has a 2 phase capacitor motor, not a real 3 phase motor with

steinmetz circuit.

 

post-3772-14264605472979_thumb.jpg

post-3772-14264605473444_thumb.jpg

post-3772-14264605473837_thumb.jpg

Link to post
Share on other sites
  • 1 year later...
  • 2 months later...

it is a long time ago since I hacked this together. For the LNK304 based 15V supply I more or less followed this:

 

https://ac-dc.power.com/design-support/reference-designs/design-examples/der-231-132-w-power-supply/

 

Adapt R8 and R9 for different output voltage.

The UF4007 is a cheap but rather fast diode, a normal 1N4007 will NOT work. 

I found this LNK chips very easy to use.

 

But yes, these things handle high voltage without potential separation. I always use a separation transformer when experimenting and keep my hands im my pockets...   

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...