speed of analogRead

I think this one is very important bugfix for the original code. With the original code I had glitches on my charts. Here is the picture:

In the begining of each 256-samples block there are three samples "from the future", they are supposed to be 4 blocks ahead. There also was zero samples in the beginning of the chart:

I think what happens here is the buffer is being sent while ADC is writing in it. After I've applied the fix from this comment the problem's gone.

Thank you, @madrang!

madrang:  ADC->ADC_RPR=(uint32_t)buf[0];   // DMA buffer  ADC->ADC_RCR=256;  ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer  ADC->ADC_RNCR=256;  bufn=obufn=1;

I'm not sure, but it look like the Due is using buffer 0 for writing and reserving buffer 1 as the next buffer to be used. If it is the case there is 2 buffer in use.

while(obufn==bufn); // wait for buffer to be full

This line should be

while((obufn + 1)%4==bufn); // wait for buffer to be full

Also why start at buffer 1 ??

bufn=obufn=1;

Would be better to start at 0.

bufn=1;
obufn=0;

Not sure if i understood the code properly, i hope someone can tell me more about this.

Hello! I’m very new in the Arduino world. I’m trying to adapt Stimmer’s code to get data from two channels instead of one, but I’m a little bit confused. Can anyone help me?

#undef HID_ENABLED

// Arduino Due ADC->DMA->USB 1MSPS
// by stimmer
// Input: Analog in A0
// Output: Raw stream of uint16_t in range 0-4095 on Native USB Serial/ACM

// on linux, to stop the OS cooking your data:
// stty -F /dev/ttyACM0 raw -iexten -echo -echoe -echok -echoctl -echoke -onlcr

volatile int bufn,obufn;
uint16_t buf[4][256];   // 4 buffers of 256 readings

void ADC_Handler(){     // move DMA pointers to next buffer
 int f=ADC->ADC_ISR;
 if (f&(1<<27)){
  bufn=(bufn+1)&3;
  ADC->ADC_RNPR=(uint32_t)buf[bufn];
  ADC->ADC_RNCR=256;
 }
}

void setup(){
 SerialUSB.begin(0);
 while(!SerialUSB);
 pmc_enable_periph_clk(ID_ADC);
 adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);
 ADC->ADC_MR |=0x80; // free running

 ADC->ADC_CHER=0x80;

 NVIC_EnableIRQ(ADC_IRQn);
 ADC->ADC_IDR=~(1<<27);
 ADC->ADC_IER=1<<27;
 ADC->ADC_RPR=(uint32_t)buf[0];   // DMA buffer
 ADC->ADC_RCR=256;
 ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer
 ADC->ADC_RNCR=256;
 bufn=obufn=1;
 ADC->ADC_PTCR=1;
 ADC->ADC_CR=2;
}

void loop(){
 while(obufn==bufn); // wait for buffer to be full
 SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
 obufn=(obufn+1)&3;    
}

I’m doing a project where I need at least 200 kHz of sample rate for each channel, but 100 kHz would be alright too.

I'm doing a project where I need at least 200 kHz of sample rate for each channel, but 100 kHz would be alright too.

Forget it, you can't go that fast on two channels.

Grumpy_Mike: Forget it, you can't go that fast on two channels.

What is the maximum sample rate I can achieve using both channels? And where is the bottleneck in the conversion? Thank you in advance!

See http://forum.arduino.cc/index.php?topic=326944.msg2860396#new

I couldn't find anything that answers my questions. Can you be more specific?

I couldn't find anything that answers my questions.

Why not it is all there.

Can you be more specific?

OK. The A/D will not go that fast. If you could get it to go that fast then what are you going to do with the data, there is not enough memory to store very much and you can't get the SD card to read it at that rate. It is too fast to send it out of the serial port.

Specific enough for you?

You’re wrong, DUE can do 1MSPS (http://www.atmel.com/ru/ru/Images/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf)

And you can get the data through USB as demonstrated with the code posted before in this topic.

Hi @direvius, I saw you also look at this code:

https://gist.github.com/pklaus/5921022

And I want to ask you if you know how to get data from two channels instead of one. I want to have the max speed achievable, if it's 100 khz per channel it would be great, but it would be useful too to have this code that connects to the PC and Python for 2 channels :)

I find this information quite useful! Thank you! I tried sampling the input voltages to pins A0 and A8 of my Arduino DUE, and I needed 3 us to get both samples. Which is good enough for my application (> 300 kSa/s on 2 pins). Now, the next I need to do is to sample 2 pins (A1, A9) just once, and then continuosly sample A0 and A8.

I tried two ways 1) I use analogRead(A1) , analogRead(A9), however, the sampling rate of A0 and A8 decreases (which I dont fully understand why) 2) I enable also A1 and A9 ADC->ADC_MR |= 0x80; //set free running mode on ADC ADC->ADC_CHER = 0xcc0; //enable ADC on pins A0,1,8,9 However, of course also the sampling rate will decrease.

My question: Is there a way to "disable" pins A1 and A9 after reading them once? Or do you have a better idea to keep above 300 kSa/s when reading both A0,A8? I thouhg DUE

The simplest way to read only once 2 channels and repeatedly 2 others is to :

First setup ADC controller with 4 channels enable Enable interrupts at the end of each conversion Disable 2 channels Inside the interrupt Handler

Try the code below with channels 4 and 5 (A3 and A2) disabled after one conversion, and channels 6 and 7 ( A1 and A0) repeatedly read:

volatile uint32_t Channel, LastConversion;

void setup() {
  adc_setup();
}

void loop() {

  // Do whatever you want in loop with LastConversion value
  Channel = LastConversion >> 12;
  LastConversion = LastConversion & 0xFFF;
}

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_FREERUN_ON;                    // ADC in free running mode

  ADC->ADC_EMR = ADC_EMR_TAG;                          // ADC_LCDR contains the last channel number

  ADC->ADC_CHER = ADC_CHER_CH4                          // A3
                  | ADC_CHER_CH5                        // A2
                  | ADC_CHER_CH6                        // A1
                  | ADC_CHER_CH7;                       // Enable Channel 7 = A0

  ADC->ADC_IER = ADC_IER_EOC4                          // Interrupt on End of conversion
                 | ADC_IER_EOC5                        // for channels 4,5,6 and 7
                 | ADC_IER_EOC6
                 | ADC_IER_EOC7;

  NVIC_EnableIRQ(ADC_IRQn);                            // Enable ADC interrupt
}

void ADC_Handler() {

  uint32_t status;
  LastConversion = ADC->ADC_LCDR;
  
  status = ADC->ADC_ISR;                            // Read and clear status register
  // Stop reading channels 4 then 5
  if (status && ADC_ISR_EOC4) ADC->ADC_CHDR = ADC_CHDR_CH4;  // Disable channel 4
  if (status && ADC_ISR_EOC5) ADC->ADC_CHDR = ADC_CHDR_CH5;  // Disable channel 5

  // Else keep enabled channels 6 and 7 
}

You could probably speed the ADC beyond 1MHz by :

analogReadResolution(10); // Set the AD to 10 bits of resolution instead of 12

adc_configure_timing (ADC, 1, ADC_SETTLING_TIME_3, 0); // Not (1) - 3 cycles instead of 5 (you have the choice !)

Hello,

I’m an Arduino beginner and I would need your help with the code.
Could you please tell me how to get timestamps to the voltage values and send them over to the python script? as a bundle?

like
1ms , 0V
2ms, 0.1V

also I’m getting a sample rate of 660kHz only. Do you know how to speed it up?

Thank you in advance!

Post the sketch you have already done (between code tags).

Thank you for your reply.

Before that, I have a different problem. With stimmer’s original code, I am only receiving zeros on python with a frequency generator. And that problem hasn’t been there before. First it was all fine.

The line

ADC->ADC_PTCR=1

seems to let the Arduino crash. (I know that because when it is commented then the loop runs.)

The problem is, I don’t receive any data from the frequency generator that I have connected to pin A0 of the due.
I have the following slightly altered code (for debugging purposes):

#undef HID_ENABLED

// Arduino Due ADC->DMA->USB 1MSPS
// by stimmer
// from http://forum.arduino.cc/index.php?topic=137635.msg1136315#msg1136315
// Input: Analog in A0
// Output: Raw stream of uint16_t in range 0-4095 on Native USB Serial/ACM


//fast@fast:~/Documents/IP$ nautilus ~/.arduino15/packages/arduino/hardware/sam/1.6.11/cores/arduino


// on linux, to stop the OS cooking your data: 
// stty -F /dev/ttyACM0 raw -iexten -echo -echoe -echok -echoctl -echoke -onlcr
// screen /dev/ttyACM0^C

// changes and prints terminal line settings 
volatile int bufn,obufn;
uint16_t buf[4][256];     // 4 buffers of 256 readings


void ADC_Handler(){       // move DMA pointers to next buffer
  Serial.println("Start interrupt handler");
  int f=ADC->ADC_ISR;     // read the interrupt status register
  if (f&(1<<27)){         // check the bit "endrx" in the status register, if endrx bit == 1 then execute body
                          // 1 = the receive counter register has reached 0 since the last write in ADC_RCR or ADC_RNCR
  /// set up the "next pointer register"
   bufn=(bufn+1)&3;       // wht does this line do?
   ADC->ADC_RNPR=(uint32_t)buf[bufn]; // RPNR = Receive Next Pointer Register points to  
   ADC->ADC_RNCR=256;      // next count register is set to 256
  } 
}

void setup(){
  pinMode(52, OUTPUT);
  digitalWrite(52,HIGH);
  Serial.begin(115200);     // open a serialUSB connection
  //Serial.begin(115200);
  Serial.println("wait for SerialUSB");
  while(!Serial);      // wait for serialUSB, works only when connected to the native port

  pmc_enable_periph_clk(ID_ADC);  
  adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST);

  ADC->ADC_MR |=0x80; // free running, set bit 8 to 1 in mode register

  ADC->ADC_CHER=0x80; //channel enable register, set bit 8 to 1, rest 0, meaning enable pin A0
  
  NVIC_EnableIRQ(ADC_IRQn);         // interrupt controller set to enable adc.
  ADC->ADC_IDR=~(1<<27);            // interrupt disable register, disables all interrupts but ENDRX
  ADC->ADC_IER=1<<27;               // interrupt enable register, enables only ENDRX
  ADC->ADC_RPR=(uint32_t)buf[0];    // DMA buffer
  ADC->ADC_RCR=256;
  ADC->ADC_RNPR=(uint32_t)buf[1];   // next DMA buffer
  ADC->ADC_RNCR=256;                // and next counter is set to 256
  bufn=obufn=1;
  ADC->ADC_PTCR=1;  // transfer control register for the DMA is set to enable receiver channel requests
  // now that all things are set up, it is safe to start the ADC.....
  
  ADC->ADC_CR=2;
  
}

void loop(){ 
  
  
  while(obufn==bufn)
  {
    Serial.println("wait for buffer to be full");// wait for buffer to be full
  }
  Serial.println("buffer full");   
  for(int i=0; i<256;i++)
  {
    Serial.println(buf[obufn][i]);
  }
  //SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
  Serial.println("buffer sent");
  //Serial.write((uint8_t *)buf[obufn],512);
  //obufn=(obufn+1)&3;  
}

I wanted to check if the buffer contains no values or if the serialusb interface is failing.
When I checked the Serial Monitor it only showed:

Start inter

and just hangs there forever.

Does anyone have an idea?

Serial is much too slow compared to the speed of ADC samplings in Free Running mode (~ 1 MHz = 1000000 * uint16_t per second).

ard_newbie: Serial is much too slow compared to the speed of ADC samplings in Free Running mode (~ 1 MHz = 1000000 * uint16_t per second).

This is correct. I'm using serial monitor only for simpler debugging. In debugging speed doesn't matter to me. When the problem is solved I'll go back to SerialUSB.

adrianka: ...debugging speed doesn't matter to me. When the problem is solved I'll go back to SerialUSB.

Then you don't understand the code snippet you are using.

First off, can you write a python code to receive a SerialUSB flow from the DUE ?

ard_newbie: Then you don't understand the code snippet you are using.

First off, can you write a python code to receive a SerialUSB flow from the DUE ?

Ok thank you for the reply. Can you tell me, what is wrong with the code?

I'm using stimmer's python code. Which worked fine at the beginning.

reply #55