Pages: [1]   Go Down
Author Topic: Need help with PWM registers/analogWrite  (Read 764 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
//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;
  }
  
}
« Last Edit: May 10, 2013, 05:46:25 am by svein » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Rest of the code:

Code:
//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);
 
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 7
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Pages: [1]   Go Up
Jump to: