Go Down

Topic: Reading 9-10 analog inputs with ADC (Read 2552 times) previous topic - next topic

svein

Apr 17, 2013, 03:22 pm Last Edit: Apr 17, 2013, 06:13 pm by svein Reason: 1
Hello guys!
I'm trying to use an Arduino Due to control a 3-phase inverter (used to drive permanent magnet ac motor) and a buck converter. For this task I will need to read at least 6 current measurements, some voltage levels and 3 pulse trains from an encoder. I also need to send out 5 PWM signals. I have managed to read the encoder pulses and send out PWM.

The problem at hand is now to read the analog inputs, fast and accurate enough. I tried with the analogRead, but this can only run 10000 sps, which is not near fast enough. I scanned this forum in search for help and found something similar to what I need (http://arduino.cc/forum/index.php/topic,137635.15.html). I tried this at the lab, and the result wasnt to good. The sampled data was very noisy, and didnt read what I expected.

I want to read the analog inputs with 150kHz freq (the current measures at least). Some analog inputs can be read slower. Any ideas how to solve this problem? Thanks in advance

Code: [Select]
#include <Encoder.h>
// Black magic
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 and declaring encoder
int aPin=57;
int bPin=58;
int cPin=59;
int vfcPin=55;
int ibuckPin=62;
int ipmsmPin=60;
int iscPin=61;
int pwmbuckPin=3;
int pwmaPin=5;
int pwmbPin=6;
int pwmcPin=7;
int enablebuckPin=50;//not assigned on the physical board yet
int encA=26;
int encB=24;
int encZ=22;
int pwmfcPin=2;

volatile long a=0;
volatile long b=0;
volatile long c=0;
volatile long oldPos= -999;
volatile long newPos= 0;
volatile int enablebuck=0;
volatile long da=0;
volatile long db=0;
volatile long dc=0;
volatile long dbuck=0;
volatile long ibuck=0;
volatile long iboost=0;
//setup:
//*Assigning pins as either input or output
//*starting timers
void setup(){
 //setting up serial communication to computer, for testing purposes only
 Serial.begin(9600);
 Serial.println("Motor encoder test:");
 
 pinMode(aPin,INPUT);
 pinMode(bPin,INPUT);
 pinMode(cPin,INPUT);
 
 pinMode(vfcPin,INPUT);
 
 pinMode(pwmaPin,OUTPUT);
 pinMode(pwmbPin,OUTPUT);
 pinMode(pwmcPin,OUTPUT);
 pinMode(enablebuckPin,OUTPUT);
 pinMode(pwmbuckPin,OUTPUT);  
 pinMode(ipmsmPin,INPUT);
 pinMode(ibuckPin,INPUT);
 pinMode(encZ,INPUT);
 attachInterrupt(encZ,reset,RISING);
 
 ADC->ADC_MR |= 0x80; //set ADC in free running mode
 ADC->ADC_CR=2; //enable ADC clock
 ADC->ADC_CHER=0x1FF; //enable A0->A7+A8
 
 // Start timer. Parameters are:

 // TC1 : timer counter. Can be TC0, TC1 or TC2
 // 0   : channel. Can be 0, 1 or 2
 // TC3_IRQn: irq number. See table.
 // 50000  : frequency (in Hz)
 // The interrupt service routine is TC3_Handler. See table.

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

 startTimer(TC1, 1, TC4_IRQn, 50000); //timer for reading analog inputs
 
 startTimer(TC1, 2, TC5_IRQn, 2);

 // Paramters table:
 // TC0, 0, TC0_IRQn  =>  TC0_Handler()
 // TC0, 1, TC1_IRQn  =>  TC1_Handler()
 // TC0, 2, TC2_IRQn  =>  TC2_Handler()
 // TC1, 0, TC3_IRQn  =>  TC3_Handler()
 // TC1, 1, TC4_IRQn  =>  TC4_Handler()
 // TC1, 2, TC5_IRQn  =>  TC5_Handler()
 // TC2, 0, TC6_IRQn  =>  TC6_Handler()
 // TC2, 1, TC7_IRQn  =>  TC7_Handler()
 // TC2, 2, TC8_IRQn  =>  TC8_Handler()
 
}
Encoder myEnc(encA,encB);


void loop(){
  newPos = myEnc.read();
 if (newPos != oldPos){
   oldPos=newPos;
   //writes to the serial port, for testing purposes only
   Serial.println(newPos);
 }
 
}


// This interrupt routine is:
//*writing duty cycles to the sync. buck gate drivers
//*writing duty cycles to the inverter gate drivers
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);
 
 //writing duty cycles to the inverterboard
 analogWrite(pwmaPin,251);//replace 128 with the calculated values da
 analogWrite(pwmbPin,128);//replace 128 with the calculated values db
 analogWrite(pwmcPin,0);//replace 128 with the calculated values dc
 analogWrite(pwmbuckPin,200);
 analogWrite(pwmfcPin,32);
 
}


//This interrupt routine is:
//*reading currents from inverter (3 readings)
void TC4_Handler()
{
 TC_GetStatus(TC1,1);
 //a=analogRead(aPin);
 //b=analogRead(bPin);
 //c=analogRead(cPin);
 while((ADC->ADC_ISR & 0x1FF)!=0x1FF);
 a=ADC->ADC_CDR[4]; //read data on A3 pin
 b=ADC->ADC_CDR[3]; //read data on A4 pin
 c=ADC->ADC_CDR[2]; //read data on A5 pin
}
void TC5_Handler()
{
 TC_GetStatus(TC1,2);
}
void reset(){
 myEnc.write(0);
 newPos=0;
 oldPos=0;
}

ssclark88

I'm also trying to figure out how to read inputs quickly. I have an application that:
Reads 8 inputs
Switches outputs
Repeats

I can successfully read the inputs at 300ks/s, with clean data. If I set delayMicroseconds to anything less than 10, I start to get garbage.
Hopefully there is something in here that can help you.
Code: [Select]

int z = 100;
uint16_t a0[14][8][100];
int t = 0;
int x = 0;
int y = 0;
int n = 0;

void setup() {
  Serial.begin(57600);
  Serial.println("begin");

for(n=24;n<38;n++){
    pinMode(n, INPUT);   
}
   
  ADC->ADC_MR |= 0x800080; // FREERUN = 1 and ANACH = 1
  //ADC->ADC_MR |=0x00300000; // Settling time to 17 clock cycles, (haven't tested this yet, but might be helpful)
  ADC->ADC_CR=2;            //start
  ADC->ADC_CHER=0xFF;      //Enable channel A0-A7
  ADC->ADC_WPMR = 0x00;     //Disables the write protect key, WPEN
// ADC->ADC_CGR=0xFFFF;    //Set Gain = 4 on channel A0-A7
  ADC->ADC_ACR |= 0x100;  //for sampling rates above 500MHz, not sure what this does exactly

}

void loop() {
  pinMode(28,OUTPUT);
  digitalWrite(28,LOW);
  delayMicroseconds(10); 
  while((ADC->ADC_ISR & 0xC0)==0); // wait for first analog channel to finish
  while(ADC->ADC_CDR[5]>4000){}   // wait for impact
  pinMode(28, INPUT);       
  t = micros();// reset timer 
  for(n=0;n<z;n++){ 
    for(x=0;x<14;x++){
      pinMode(24+x,OUTPUT);
      digitalWrite(24+x,LOW);
      delayMicroseconds(10); // slight delay to let the output settle.
      while((ADC->ADC_ISR & 0xC0)==0); // wait for analog channel to finish
      a0[x][0][n]=ADC->ADC_CDR[0];              // read data
      a0[x][1][n]=ADC->ADC_CDR[1];              // read data
      a0[x][2][n]=ADC->ADC_CDR[2];              // read data
      a0[x][3][n]=ADC->ADC_CDR[3];              // read data
      a0[x][4][n]=ADC->ADC_CDR[4];              // read data
      a0[x][5][n]=ADC->ADC_CDR[5];              // read data
      a0[x][6][n]=ADC->ADC_CDR[6];              // read data
      a0[x][7][n]=ADC->ADC_CDR[7];              // read data
      pinMode(24+x, INPUT);

    }
  }
 
  Serial.print(micros()-t);Serial.print("\t"); Serial.println("Microseconds");
  for(n=0;n<z;n++){ 
    for(y=0;y<8;y++){
      for(x=0;x<14;x++){
         Serial.print(a0[x][y][n]);Serial.print("\t");         
      }
      Serial.println();
    }
    Serial.println();
  }
  delay(100000); //long delay to let the serial monitor finish
 
}


It might help to disable the unused inputs, and then re-enable them in your TC4_Handler
Code: [Select]
ADC->ADC_CHDR = 0x1E3;
//read pin 2, 3 & 4
ADC->ADC_CHER = 0x1E3;


Also, look at the settling time function for the ADC conversion
Code: [Select]
ADC->ADC_MR |=0x00300000;

svein

Thanks for the tip!
I added the settling time now, and hope it will be good enough. The readings are pretty noisy before I sample them, so I might need some filtering it seems.
I will try to get the motor running at the lab tomorrow, I'll let you know if it works (if you are interested).

Go Up