Go Down

Topic: Need help with PWM registers/analogWrite (Read 872 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