Arduino DUE ADC

The problem has been solved. So I'm deleting this topic.

Can you please modify your original post to ensure the source code is in a single box. It should look like this.

// Your example source code

You need three ' at the beginning and end in the edit window? When you click on this icon </> you get.

```
type or paste code here
```

This ensures we can get all the source code. Right now if you copy the code there are invisible characters that prevent successful compilation and parts might be missing.

Sorry! I fixed it.

Your issue is printing the data. I run a few tests and analogRead() can be started at 5kHz but you cannot print the data at that speed. I modified your sketch a little bit. Here is the code so you can test this yourself.

#include <DueTimer.h>

const int VOL_PIN = A8;

volatile uint32_t counter = 0 ;
double average = 0 ;
double average1 = 0 ;

volatile bool newSampleAvailable = false;
volatile uint16_t adcValue = 0;


void setup()
{
  Serial.begin( 115200 );//タイマ割込みの設定
  Timer3.attachInterrupt( Timer3_handler ).start( 200 ); //(200μS)

  analogReadResolution( 12 );
}

void loop()
{
  analogTask();
  printCounterTask();
}


void Timer3_handler( void )
{
  counter++;
  adcValue = analogRead( VOL_PIN );
  newSampleAvailable = true;
}


void analogTask()
{
  float volt;
  float current;

  if ( newSampleAvailable )
  {
    newSampleAvailable = false;

    uint16_t value = adcValue;

    volt = value * ( 3.3 / 4095.0 ) ;
    //volt = value * (0.001221) ;

    //current = (volt - 2.5)*(25/0.625) ;
    current = ( ( ( volt - 2.5 ) * 40 ) - average ) ;

    if ( 314 >= counter && counter >= 300 )
    {
      average += current;

      if ( counter == 314 )
      {
        average1 = ( average / 14 ) ;
        //Serial.println(average1);
      }
    }

    //Serial.print( value );
    // Serial.print( "," );
    //Serial.println( volt );
    //Serial.print( "," );
    Serial.println( current ); //TOD0 割込処理
  }
}


void printCounterTask()
{
#define PRINT_COUNTER_INTERVAL          1000

  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis < PRINT_COUNTER_INTERVAL )
  {
    return;
  }
  Serial.print( "Counter: " );
  Serial.println( counter );
  counter = 0;

  previousMillis = millis();
}

Let the sketch run a few seconds then copy the data to a text editor with line count (e.g., Notepad++, you can even use the Arduino IDE).

  • Search for Counter and you should see (plus/minus a few samples):
    Counter: 5002
  • Note the line number
  • Search for the next Counter
  • Note the line number and get the difference
    I get around 1500 prints per second.

What do you want to do with the data?
Do you need all the samples on your PC?

Thanks for the reply. As for me
Since it is not possible to operate at high speed when using library functions, I would like to set registers, generate PWM, and perform AD conversion with overflow interrupt. So I am trying to set up the timer and PWM settings, but I don't know how to do it. Can you please help me?

You can sample at 5kHz but you cannot print the data at that speed.

Can you please specify exactly what you would like to do?

  • How many channels do you want to convert?
  • What do you want to do with the result?
  • How fast do you want to sample?
  • How long do you want to sample?
  • What is the signal source?
    ...

Please do not delete your posts. Other users might find the information useful.

The signal source is a three phase AC and I want to sample it at about 5Khz. Since I am dealing with 3-phase AC, I will use 3 channels. Sampling will be done all the time.

You can simply call analogRead 3 times in the timer ISR handler. I did a test and it seems to work quite nicely. My example copies the result to a buffer to display in the Serial Plotter window.

/*
  This example shows how to use the Arduino Due ADC with a Timer.

  The circuit:
  - Arduino Due

  This example code is in the public domain.
*/

// Install via Library Manager
// https://github.com/ivanseidel/DueTimer
#include <DueTimer.h>


#define VOLTAGE_L1_PIN      A0
#define VOLTAGE_L2_PIN      A1
#define VOLTAGE_L3_PIN      A2
#define ADC_NO_CHANNELS     3

volatile uint16_t adcResult[ADC_NO_CHANNELS];
volatile bool adcFlag = false;


#define DATA_BUFFER_SIZE    500
volatile uint16_t dataBuffer[DATA_BUFFER_SIZE];
uint32_t dataBufferIndex = 0;
volatile uint16_t dataBuffer2[DATA_BUFFER_SIZE];
volatile uint16_t dataBuffer3[DATA_BUFFER_SIZE];


#define BUTTON_START_PIN    10
#define DATA_SEND_INTERVAL  5



void setup()
{
  Serial.begin( 115200 );
  while ( !Serial );

  initADC();
  initTimerADC();
  pinMode( BUTTON_START_PIN, INPUT );
  Serial.println( "A0\tA1\tA2" );
}

void loop()
{
  enum APP_STATE_TYPE { APP_WAIT_FOR_START,
                        APP_START_MEASUREMENT,
                        APP_COLLECT_SAMPLES,
                        APP_STOP_MEASUREMENT,
                        APP_SEND_DATA,                         
                        APP_STATE_RESTART = 255
                      };  

  static uint32_t state = 0;
  
  switch ( state )
  {
    case APP_WAIT_FOR_START:
      if ( digitalRead( BUTTON_START_PIN ) == LOW )
      {
        state++;
      }
      break;
    case APP_START_MEASUREMENT:
      startTimerADC();
      state++;
      break;
    case APP_COLLECT_SAMPLES:
      if ( adcFlag )
      {
        if ( copyData() == 0 )
        {
          state++;
        }
        adcFlag = false;      
      }
      break;
    case APP_STOP_MEASUREMENT:
      stopTimerADC();
      dataBufferIndex = 0;
      state++;
      break;
    case APP_SEND_DATA:
      if ( sendData( DATA_SEND_INTERVAL ) == 0 )
      {
        state++;
      }
      break;
    default:
      dataBufferIndex = 0;
      state = 0;
      break;
  }
}


void Timer3_Handler( void )
{
  adcResult[0] = analogRead( VOLTAGE_L1_PIN );
  adcResult[1] = analogRead( VOLTAGE_L2_PIN );
  adcResult[2] = analogRead( VOLTAGE_L3_PIN );
  adcFlag = true;
}


void initTimerADC()
{
  Timer3.attachInterrupt( Timer3_Handler );
  Timer3.setPeriod( 200 ); // 200μS - 5kHz
}


void startTimerADC()
{
  Timer3.start();
}


void stopTimerADC()
{
  Timer3.stop();
}


void initADC()
{
  analogReadResolution( 12 );
}

int32_t copyData()
{
  dataBuffer[dataBufferIndex] = adcResult[0];
  dataBuffer2[dataBufferIndex] = adcResult[1];
  dataBuffer3[dataBufferIndex] = adcResult[2];
  dataBufferIndex = ( dataBufferIndex + 1 ) % DATA_BUFFER_SIZE;
  return dataBufferIndex;
}


int32_t sendData( uint32_t interval )
{
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis >= interval )
  {
    previousMillis = currentMillis;
    Serial.print( dataBuffer[dataBufferIndex] );
    Serial.print( '\t' );
    Serial.print( dataBuffer2[dataBufferIndex] );
    Serial.print( '\t' );
    Serial.println( dataBuffer3[dataBufferIndex] );
    
    dataBufferIndex = ( dataBufferIndex + 1 ) % DATA_BUFFER_SIZE;
    return dataBufferIndex;
  }
  return -1;
}

I did not have a 3 phase signal, so I used a single 50Hz sine wave with a resistor ladder. So, there is no phase shift but you can see the three signals.

ADC 3CH 50Hz Sampling 5kHz

Thank you.
This is using the timer library function, right? I would like to use the following program to set the timer in the register itself.

// Timer Counter (TC)
void ADC_Timer_Setup(int Frequency){
    pmc_enable_periph_clk (TC_INTERFACE_ID + 0 * 3 + 0) ; // clock the TC0 channel 0

    TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
    // The pointer is used insted of writing: TC0->TC_CHANNEL[0], I can write: t->

    t->TC_CCR = TC_CCR_CLKDIS ;  // disable internal clocking while setup regs         
    t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts                                
    t->TC_SR ;                   // read int status reg to clear pending           

    // In Waveform Mode TIOB is set as output 
    t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK4 |       // use TCLK4 (prescale by 128, = 656.25kHz)
              TC_CMR_WAVE |                    // waveform mode
              TC_CMR_WAVSEL_UP_RC |            // count-up PWM using RC as threshold
              TC_CMR_EEVT_XC0 |              // Set external events from XC0 (this setup TIOB as output)
              TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
              TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;


    t->TC_RC =  0x080; // 0x80 = 5.126kHz
    t->TC_RA =  80;    // setup time for next itteration
    t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;   // set clear and set from RA and RC compares

    // re-enable local clocking and switch to hardware trigger source.
    t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;

void ADC_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;  // ADC power on
  ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC             

  NVIC_EnableIRQ (ADC_IRQn); // enable ADC interrupt vector

  ADC->ADC_IDR = 0xFFFFFFFF; // Disables all interrupts
  ADC->ADC_IER = 0x80; // Enable End-Of-Conversion interrupt on Channel 7 -> A0  

  ADC->ADC_CHDR = 0xFFFFFFFF; // Disables all channels
  ADC->ADC_CHER = 0x80; // Enable channel 7                

  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFF0; // Disable Trigers | 4 lowesr bits of ADC_MR 
  ADC->ADC_MR = ADC->ADC_MR | 0x2; // TRGEN on => External trigger 
  ADC->ADC_MR = ADC->ADC_MR | 0x1; // TIOA Output of the Timer Counter Channel 0 
  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFEF; // 12-bit resolution 
}

An example sketch to trigger ADC conversion (1 channel) at 44.1 KHz:

/*****************************************************************/
/*             ADC conversions at 44.1 KHz                       */
/*****************************************************************/
volatile uint32_t lastConversion;
volatile boolean Flag;
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
  tc_setup();
}

void loop()
{
if (Flag== true)
{
  Flag = false;
  // A new conversion is available
}
}

/*************  Configure ADC function  *******************/
void adc_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;                    // ADC power on
  ADC->ADC_CR = ADC_CR_SWRST;                           // Reset ADC
  ADC->ADC_MR |=  ADC_MR_TRGEN_EN                       // Hardware trigger select
                  | ADC_MR_TRGSEL_ADC_TRIG3             // Trigger by TIOA2
                  | ADC_MR_PRESCAL(1);
  ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                   // For frequencies > 500 KHz

  ADC->ADC_CHER = ADC_CHER_CH7;                        // Enable ADC CH7 = A0
  ADC->ADC_IER = ADC_IER_EOC7;                         // Interrupt on End of conversion
  NVIC_EnableIRQ(ADC_IRQn);                            // Enable ADC interrupt

}
void ADC_Handler()
{
  static uint32_t Count;
  lastConversion = ADC->ADC_CDR[7];
  //lastConversion = ADC->ADC_LCDR;//Do NOT clear EOC7 bit
  
  Flag = true;

  if(Count++ > 44117)
  {
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}
/*************  Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2  ************/
void tc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID29;                      // TC2 power ON : Timer Counter 0 channel 2 IS TC2
  TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2  // MCK/8, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA2 on RA compare match
                              | TC_CMR_ACPC_SET;           // Set TIOA2 on RC compare match


  TC0->TC_CHANNEL[2].TC_RC = 238;  //<*********************  Frequency = (Mck/8)/TC_RC  Hz = 44.117 Hz
  TC0->TC_CHANNEL[2].TC_RA = 40;  //<********************   Any Duty cycle in between 1 and 874

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable

}

If more than 1 ADC channel conversion is needed, enable these channels at this code line:

ADC->ADC_CHER = ADC_CHER_CH7;                        // Enable ADC CH7 = A0

And the uc will automatically adapt the conversion frequency.

Thank you very much. I'll try to use this as a reference.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.