speed of analogRead

It's not an interrupt, the ADC is internally wired to the timer and can use it to trigger a conversion. You can use it with DMA, it works just as you'd expect - for example if you set the timer to 1000Hz and ask the DMA to do 100 conversions you get 100 samples at a sample rate of 1000 samples/sec :slight_smile:

I used this technique in the DueVGA Waterfall example but I haven't properly tidied up the code and commented it yet so it is hard to follow.

stimmer:
It's not an interrupt, the ADC is internally wired to the timer and can use it to trigger a conversion. You can use it with DMA, it works just as you'd expect - for example if you set the timer to 1000Hz and ask the DMA to do 100 conversions you get 100 samples at a sample rate of 1000 samples/sec :slight_smile:

I used this technique in the DueVGA Waterfall example but I haven't properly tidied up the code and commented it yet so it is hard to follow.
https://github.com/stimmer/DueVGA/blob/master/extras/Waterfall/waterfall/waterfall.ino

Thank you again Stimmer. Nice piece of code.

This is working as it should (I tested it at 50kHz). The nice thing about this way is that you don't need to worry about the sampling rate when using more than one channel....just multiply the buffer size by the number of them.

I found some hints to that speed issues in Atmel's doc11106.pdf, Analog-to-Digital Converter in the SAM3S4 (searching for such a document addressing SAM3X was not successful).
They say, that STARTUP is needed for SLEEP mode and Fast Wake Up (FWUP) mode, because the ADC needs this time to get ready for conversation from SLEEP (40 us) and from FWUP (12 us) to normal mode.
As far as I understood, in normal mode there is no need for STARTUP (value may be 0).
There is a formula to calculate the sampling frequency Fs with Fadc = 1/ADC_CLOCK (programmed via PRESCAL).
Fs = Fadc/(n(STARTUP) + Tconv) with Tconv = 20 (ADC clock cycles).
This has to be devided by the number of used channels.
n(STARTUP) is not linear, it is given as a list of values in the data book of SAM3X:
0/0, 1/8, 2/16 .... 10/640 ... 12/768 ... 15/960.

It looks like as other parameters (TRANSFER, SETTLING) happen with a channel while another channel is converted, so there is no influence on the sampling frequency. TRACKTIM is a delay between conversions and may be 0.

I hope, I've read the description in doc11106.pdf correct. If I made mistakes by repeating, someone should tell us the truth.

A correction:
SETTLING and TRANSFER is not overlapping conversion, only the Tracking. Look at Figures 44-2 and 44-3 (page 1333) in the User Guide of SAM3X. Seems, that TRANSFER and SETTLING is inluded in the 20 ADC clock cycles mentioned for conversion time.
But I did not really study all the text, may be, I'll do that later, now I want to make progress with my software.

i use arduino DUE, A0 to GND, A1 to 3V3,
run STIMMER 2 analog in code
and test with arduino 1.5.4 OR arduino 1.5.5. r2 with FIX
see 500kHz OR 333kHz ??

  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.

stimmer:
Well spotted - what it should actually be is this:

    while((ADC->ADC_ISR & 0xC0)!=0xC0);// wait for two conversions (pin A0[7]  and A1[6])

I'll go back and change my code in case anyone else copy/pastes it.

For reducing missed samples and allowing more processing time in the main loop, the biggest win comes through using peripheral DMA. This example uses DMA and interrupts - although if you are new to the Due, don't expect to understand it all at once :slight_smile:

#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;   
}




It reads ADC data at 1 million samples/sec and outputs the data to SerialUSB. (I've only tested it on Linux and most of the time it works, sometimes it is unreliable, I don't know why). Using GNU Radio I was then able to analyse the data stream and receive a long-wave radio signal, with an LC tuned circuit into A0 as an aerial.

Hi Stimmer

I'd like to know , how can I see the Digital signal using SerialUSB, Can I use the Serial Terminal of Arduino IDE?

kllsamui:
i use arduino DUE, A0 to GND, A1 to 3V3,
run STIMMER 2 analog in code
and test with arduino 1.5.4 OR arduino 1.5.5. r2 with FIX
see 500kHz OR 333kHz ??

I'm using Arduino 1.5 and I'm also getting only 333kHz sampling frequency with two channels.
Does anybody know what might be the reason?

Without your code, we can't speculate on the reason. Have you tried the code in reply #18 which claims to achieve 1MHz?

My application will be monitoring two analog channels. I have Arduino Zero which is the same family as the Due. I will need the free-running mode or better yet DMA. I tried copying the code examples but get errors on the compile. Are there library references I need to include to get the compile to run?

My application will be monitoring two analog channels. I have Arduino Zero which is the same family as the Due.

Yes but it is not the same.
I suggest you start your own thread in the appropriate section of the forum.

As I am trying to do something similiar, I tried to follow along your code with the datasheet of the SAM3X and I could not find some of the registers you use, such as ADC->ADC_RPR. From your code I guess that a pointer towards memory is stored there and the ADC uses this address to store the conversion result there using DMA. As I do want to send out the PIOC Register via USB instead of an ADC conversion result I would be happy to know where to find the registers you use to fully understand whats happening.

Thanks a lot,

Laura

RPR registers described in section 28.5
Peripheral DMA Controller (PDC) User Interface

Have to say, that non of the Parallel Input/Output Controller (PIO) .... among the list of periferial devices supported by PDC. Direct DMA controller also doesn't support PIO. So, your idea of fast reading digital port over DMA is not possible.
I would look for alternative, for example SPI is supported by PDC, so you can add parallel to serial external register IC, depends on bit's width 8-16-32 etc. SPI driven with 42MHz clock may provide over 5 mega read/sec for 8- bits register

Hello,

if I got it right, by using the code underneath, I'll be using the maximum sampling rate possible (the quickest). Is there a way to define a precise sampling rate? (i need 44,1kHz).

Thanks in advance,
Clément

stimmer:
I'm working on a similar project at the moment. If analogRead is too slow for you it is possible to put the ADC in 'free-running' mode and read the registers directly - doing this I am getting conversions in 1uS (although I have not tested for accuracy yet). This is the code I was using to test speed:

void setup() {

Serial.begin(9600);
 int t=analogRead(0);

ADC->ADC_MR |= 0x80; // these lines set free running mode on adc 7 (pin A0)
 ADC->ADC_CR=2;
 ADC->ADC_CHER=0x80;
}

void loop() {
 int t=micros();
 int q=0;
 int a0;
 for(int i=0;i<1000000;i++){
   while((ADC->ADC_ISR & 0x80)==0); // wait for conversion
   a0=ADC->ADC_CDR[7];              // read data
   q+=a0;
 }
 t=micros()-t;
 Serial.print("1 million conversions in ");Serial.print(t);Serial.println(" micros");
}

Magician:
I would look for alternative, for example SPI is supported by PDC, so you can add parallel to serial external register IC, depends on bit's width 8-16-32 etc. SPI driven with 42MHz clock may provide over 5 mega read/sec for 8- bits register

Thank you very much, that helped. In fact, my ADC also is sending out the data as serial data clocked at around 28Mhz, 18 bit and each bit valid at rising clock edge and without any delay between each 18 bit transfers. Clock is generated on the ADC board fixed. So it would be a two wire setup.
Do you think one could abuse the Due as a SPI slave to sample that kind of data or am I missing some obvious restriction? I would also be happy with not sending out the data but logging it to a SD Card using the SD Fat library which supports SPI with DMA on Due but i have the feeling there will be more complications.

Thanks for your time,

Laura

Do you think one could abuse the Due as a SPI slave to sample that kind of data or am I missing some obvious restriction?

AFAIK, DUE doesn't support 18-bits. Section 33.2 in SAM3X datasheet:

• Master or Slave Serial Peripheral Bus Interface
– 8- to 16-bit Programmable Data Length Per Chip Select
– Programmable Phase and Polarity Per Chip Select
– Programmable Transfer Delay Between Consecutive Transfers and Delay Before SPI
Clock per Chip Select
– Programmable Delay Between Chip Selects
– Selectable Mode Fault Detection
• Connection to DMA Channel Capabilities Optimizes Data Transfers
– One channel for the Receiver, One Channel for the Transmitter

if I got it right, by using the code underneath, I'll be using the maximum sampling rate possible (the quickest). Is there a way to define a precise sampling rate? (i need 44,1kHz).

As far as I know, as you have to make a reading every 22,67 us and the only time reference you have is the micros(), that is an integer, the answer is "no".

Regards

Magician:
AFAIK, DUE doesn't support 18-bits. Section 33.2 in SAM3X datasheet:

Thank you, I am aware of that. But as the bitflow is continous, I would just save the bytes one after another and let Matlab figure out which bits belong to which full 18bit ADC read.

The missing piece of information for me is:

Would the SAM3X be fine with continous reads with only two lines - Clock and Data - while the Arduinos outputline would just be pulled to ground and chipselect on a fixed potential too?

Would the SAM3X be fine with continous reads with only two lines - Clock and Data - while the Arduinos outputline would just be pulled to ground and chipselect on a fixed potential too?

No, SPI is 3-wire interface.
Your idea is non-sense, no way to shift in 18-bits on 16-bits interface.
You better to post ADC part name, link to datasheet, explain why not 16 or 24 bits is important, and better in another thread, as it has nothing to do with 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.