Go Down

Topic: Need help with PWM registers/analogWrite (Read 977 times) previous topic - next topic

svein

May 09, 2013, 07:37 pm Last Edit: May 10, 2013, 12:46 pm by svein Reason: 1
Hello.
Im creating a motor controller for a 3 phase AC motor.
I'm having a lot of difficulty with generating the correct pwm signals. I need to generate a signal with 10kHz frequency, the period must be center aligned, and I need to update the duty cycle every 10kHz.
I tried this using analogWrite, but the pwm signales where shifted, and only left aligned. So I went on the great internet in search for some help and found some code of interest. The problem I have with this code is that when I try to update the duty cycle the pwm signal ends up as a giant noisy blur. It seems like it doesnt reset properly and write the new duty cycle. Any help would be much appreciated!

Code: [Select]

//Encoder library
#include <Encoder.h>

// Black magic, function for setting up timers
void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
 pmc_set_writeprotect(false);
 pmc_enable_periph_clk((uint32_t)irq);
 TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK4);
 uint32_t rc = VARIANT_MCK/128/frequency; //128 because we selected TIMER_CLOCK4 above
 TC_SetRA(tc, channel, rc/2); //50% high, 50% low
 TC_SetRC(tc, channel, rc);
 TC_Start(tc, channel);
 tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
 tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
 NVIC_EnableIRQ(irq);
}

 

//assigning pins to variables
int aPin=59;
int bPin=58;
int cPin=57;
int vfcPin=55;
int ibuckPin=62;
int ipmsmPin=60;
int iscPin=61;
int pwmbuckPin=4;
int pwmtisPin=0;
int pwmaPin=6;
int pwmbPin=8;
int pwmcPin=9;
int pwmostPin=0;
int pwmpikPin=0;

//int enablebuckPin=3;//not assigned on the physical board yet
int encA=30;//30 on new board.
int encB=28;//28
int encZ=26;//26
//int pwmfcPin=2;
int lemref=66;

//declaring variables used for calculations and I/O
volatile int a=0;
volatile int b=0;
volatile int c=0;
volatile int oldPos= -999;
volatile int newPos= 0;
volatile int enablebuck=0;

volatile int da=0;
volatile int db=0;
volatile int dc=0;

volatile int dbuck=0;
volatile int ibuck=0;
volatile int iboost=0;
volatile int iq=0;
volatile int id=0;
volatile int ia,ib,ic;
volatile int vqc=0;
volatile int vdc=0;
volatile int pos=0;
int idref=0;
volatile int iqref = 0.5*10000;
volatile int k=0;
int i=0;
int kpdq=2;
int kidq=1000;//1250
int kpspeed=0;
int kispeed=0;
int Ts=1;//0,00004 s (1/25 kHz) scaled
volatile int vd,vq;
volatile int prevvd,prevvq,preverrd,preverrq,va,vb,vc,ysat,prevysat,ysatd,ysatq,prevysatd,prevysatq,vdaw,vqaw,errord,errorq,vdint,vqint,errordold,errorqold,errordaw,errorqaw,errordawold,errorqawold=0;
int kaw=200;

//Look up table for cosine/sine function
int table[]={10000,9998,9994,9986,9976,9962,9945,9925,9903,9877,9848,9816,9781,9744,9703,9659,9613,9563,9511,9455,9397,9336,9272,9205,9135,9063,8988,8910,8829,8746,8660,8572,8480,8387,8290,8192,8090,7986,7880,7771,7660,7547,7431,7314,7194,7071,6947,6820,6691,6561,6428,6293,6157,6018,5878,5736,5592,5446,5299,5150,5000,4848,4695,4540,4384,4226,4067,3907,3746,3584,3420,3256,3090,2924,2756,2588,2419,2250,2079,1908,1736,1564,1392,1219,1045,872,698,523,349,175,0};

//setup:
//*Assigning pins as either input or output
//*starting timers
void setup(){

 //asssigning pinmodes
 pinMode(aPin,INPUT);
 pinMode(bPin,INPUT);
 pinMode(cPin,INPUT);
 
 pinMode(vfcPin,INPUT);
 
 pinMode(pwmaPin,OUTPUT);
 pinMode(pwmbPin,OUTPUT);
 pinMode(pwmcPin,OUTPUT);

 pinMode(pwmbuckPin,OUTPUT);  
 pinMode(ipmsmPin,INPUT);
 pinMode(ibuckPin,INPUT);
 pinMode(encZ,INPUT);
 attachInterrupt(encZ,reset,RISING);
 analogWrite(lemref,127);

 
 //More timer fun!
 ADC->ADC_WPMR=0x00; //disable write protection on MR
 ADC->ADC_MR |= 0x200080; //set ADC in free running mode and settling time is 9 times ADC clock
 ADC->ADC_CR=2; //enable ADC clock
 ADC->ADC_CHER=0x1FF; //enable A0->A7+A8
 


 startTimer(TC1, 0, TC3_IRQn, 10000);//timer for pwm write inverter and sync buck

 startTimer(TC1, 1, TC4_IRQn, 10000); //timer for reading analog inputs and calculating duty cycles
 
}
Encoder myEnc(encA,encB);


void loop(){
 
  newPos = myEnc.read();
  if (newPos != oldPos){
   oldPos=newPos;
   pos=0.0439*2*newPos; //(360/2000) From pulses to degrees
 }
 
}    

void TC4_Handler()
{
 TC_GetStatus(TC1,1);

 while((ADC->ADC_ISR & 0x1FF)!=0x1FF);//waiting for data on A0-A8
 a=ADC->ADC_CDR[2]; //read data on A5 pin
 b=ADC->ADC_CDR[3]; //read data on A4 pin
 c=ADC->ADC_CDR[4]; //read data on A3 pin

//scale measerued signals  
 ia=(129*a-270000)/100;
 ib=(129*b-270000)/100;
 ic=(129*c-267000)/100;

 
 //Park transformation, from adc to dq0 ref system
 //id and iq are scaled with 10000
   
 id=(67*(cosinus(pos)*ia+cosinus(pos-120)*ib+cosinus(pos+120)*ic))/10000;

 iq=(67*(-cosinus(90-pos)*ia-cosinus(90-pos-120)*ib-cosinus(90-pos+120)*ic))/10000;

 //scale down id and iq to be 10000 times actual value (instead of 10^8 timesactual value)

 
 //PI controller with anti windup

 errord=0-id;
 vdint=vdint+(kidq/100)*(errord)+100*kpdq*(errord-errordold)+(kaw/200)*(errordaw+errordawold);
 vd=vdint;
 errordawold=errordaw;
 errordold=errord;
 if(vd>28000000){
   errordaw=28000000-vd;
   vd=28000000;
 }
 else if(vd<-28000000){
   errordaw=-28000000-vd;
   vd= -28000000;
 }
 else{
   errordaw=0;
 }

 errorq=iqref-iq;
 vqint=vqint+(kidq/100)*(errorq)+(kaw/200)*(errorqaw+errorqawold)+100*kpdq*(errorq-errorqold);
 vq=vqint;
 errorqawold=errorqaw;
 errorqold=errorq;
 if(vq>28000000){
   errorqaw=28000000-vq;
   vq=28000000;
 }
 else if(vq<-28000000){
   errorqaw=-28000000-vq;
   vq= -28000000;
 }
 else{
   errorqaw=0;
 }
 //scaling vd and vq down to 100 times actual value
 vdc=vd/10000;
 vqc=vq/10000;
 //dq0 to abc (inverse park transformation)
 //va,vb,vc is calculated 10^6 times bigger than actual value  
 va=cosinus(pos)*vdc-cosinus(90-pos)*vqc;

 vb=cosinus(pos-120)*vdc-cosinus(90-pos-120)*vqc;

 vc=cosinus(pos+120)*vdc-cosinus(90-pos+120)*vqc;

 //convert voltage references to duty cycles
 da=((va/2800)+10000)/79;
 if(da>255){
   da=255;
 }
 if(da<0){
   da=0;
 }
 db=((vb/2800)+10000)/79;
 if(db>255){
   db=255;
 }
 if(db<0){
   db=0;
 }
 dc=((vc/2800)+10000)/79;
 if(dc>255){
   dc=255;
 }
 if(dc<0){
   dc=0;
 }
 
}

svein

Rest of the code:

Code: [Select]
//reset the position every index puls
void reset(){
  myEnc.write(0);
  newPos=0;
  oldPos=-999;
}

//lookup table for cosine function
//Works for all angles between -720 and 720 degrees.
int cosinus(int posit){
 
   if(posit>720)
  {
    posit=posit-720;
  }
  if(posit<-1080)
  {
    posit=posit+1080;
  }
  if((posit<-720)&&(posit<= -1080))
  {
    posit=posit+720;
  }
  if((posit>360)&&(posit<=720))
  {
    posit=posit-360;
  }
  if((posit<-360)&&(posit<= -720))
  {
    posit=posit+720;
  }
  if((posit<0) && (posit<=-360))
  {
    posit=posit+360;
  }
 
  if((posit<=90) && (posit>=0))
    {
        i=posit;
        return table[i];
    }
  else if((posit>90) && (posit<=180))
  {
    i=(((posit-90)/90)-1)*(-90);
    return table[i]*(-1);
  }
  else if((posit>180) && (posit<=270))
  {
    i=(posit-180);
    return table[i]*(-1);
  }
  else if((posit>270) && (posit<=360))
  {
    posit=posit-180;
    i=(((posit-90)/90)-1)*(-90);
    return table[i];
  }
}

void TC3_Handler()
{
  // You must do TC_GetStatus to "accept" interrupt
  // As parameters use the first two parameters used in startTimer (TC1, 0 in this case)
  TC_GetStatus(TC1, 0);

    pmc_enable_periph_clk(ID_PWM);
    //20kHz freq, gives 10kHz since the signals is center aligned
    PWMC_ConfigureClocks(20000*255, 0, VARIANT_MCK );
  // SETUP PINS 6 T/M 9
  uint32_t chan6 = g_APinDescription[6].ulPWMChannel;
  uint32_t chan7 = g_APinDescription[7].ulPWMChannel;
  uint32_t chan8 = g_APinDescription[8].ulPWMChannel;
  uint32_t chan9 = g_APinDescription[9].ulPWMChannel;
 
  int duty_100 = 255;
  int duty_75 = (((duty_100+1)/4)*3)-1;
  int duty_50 = ((duty_100+1)/2)-1;
  int duty_25 = ((duty_100+1)/4)-1;
 

  static uint8_t PWMEnabled = 0;
  static uint8_t ADCEnabled = 0;
  static uint8_t pin6Enabled = 0;
  static uint8_t pin7Enabled = 0;
  static uint8_t pin8Enabled = 0;
  static uint8_t pin9Enabled = 0;

    PIO_Configure(g_APinDescription[6].pPort, g_APinDescription[6].ulPinType, g_APinDescription[6].ulPin, g_APinDescription[6].ulPinConfiguration);
    PIO_Configure(g_APinDescription[7].pPort, g_APinDescription[7].ulPinType, g_APinDescription[7].ulPin, g_APinDescription[7].ulPinConfiguration);
    PIO_Configure(g_APinDescription[8].pPort, g_APinDescription[8].ulPinType, g_APinDescription[8].ulPin, g_APinDescription[8].ulPinConfiguration); 
    PIO_Configure(g_APinDescription[9].pPort, g_APinDescription[9].ulPinType, g_APinDescription[9].ulPin, g_APinDescription[9].ulPinConfiguration);
 
    // CONFIGURE CHANNELS
    PWMC_ConfigureChannel(PWM, chan6, PWM_CMR_CPRE_CLKA, (1 << 8), 0);
    PWMC_ConfigureChannel(PWM, chan7, PWM_CMR_CPRE_CLKA, (1 << 8), 0);
    PWMC_ConfigureChannel(PWM, chan8, PWM_CMR_CPRE_CLKA, (1 << 8), 0);           
    PWMC_ConfigureChannel(PWM, chan9, PWM_CMR_CPRE_CLKA, (1 << 8), 0);
         
    // SET PERIODS
    PWMC_SetPeriod(PWM, chan6, duty_100);
    //PWMC_SetPeriod(PWM, chan7, duty_100);
    PWMC_SetPeriod(PWM, chan8, duty_100);
    PWMC_SetPeriod(PWM, chan9, duty_100);  // PWM_MAX_DUTY_CYCLE (100%)
       
    // SET DUTY CYCLES   
    PWMC_SetDutyCycle(PWM, chan6, da);   
    //PWMC_SetDutyCycle(PWM, chan7, 127); 
    PWMC_SetDutyCycle(PWM, chan8, db);   
    PWMC_SetDutyCycle(PWM, chan9, dc); 
           
    //SYNC CHANNELS
    PWMC_ConfigureSyncChannel(PWM, ( PWM_SCM_SYNC4 |  PWM_SCM_SYNC5 | PWM_SCM_SYNC6 | PWM_SCM_SYNC7), PWM_SCM_UPDM_MODE2, 0, 0); 
    PWMC_SetSyncChannelUpdateUnlock(PWM);              // PWM_SCUC set UPDULOCK to 1, update next period
    PWMC_SetSyncChannelUpdatePeriod(PWM, duty_100);    //  update period of the synchronous channels PWM_SCUP
               
    // SET CHANNEL 0 FOR SYNC
    PWMC_ConfigureChannel(PWM, 0, PWM_CMR_CPRE_CLKA, (1 << 8), 0);
    PWMC_SetPeriod(PWM, 0, duty_100);
    PWMC_SetDutyCycle(PWM, 0, duty_50);
    PWMC_EnableChannel(PWM, 0);

}

svein

If anyone know how to center align and sync the analogWrite() function it would also work.  When I tried analogWrite it was left aligned (the period) and the 3 pwm signales where shifted by some 7-8┬ÁS of each other.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy