Arduino Forum

Products => Arduino Due => Topic started by: aleshonsa on Dec 16, 2012, 01:05 am

Title: speed of analogRead
Post by: aleshonsa on Dec 16, 2012, 01:05 am
Hello,
I'm interested how long it takes to Arduino Due board to read analog input. In Reference library I found only value 100us that is for Arduino Uno board when using standard analogRead function. And in some book I read that it can be reduced to few tens of us if I will use redefined read analogRead function. But how it is with Arduino Due? How quick is standard analogRead and what time can take read analog input when I will use redefined alanogRead function.

I want to build USB osciloscope and I need time resolution minimally few us. So I'm interested if I'm able to build it based on Arduino Due.

Thanks

Best Regards
Ales
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Dec 16, 2012, 09:57 am
I got an analogue read in about 40uS.
Title: Re: speed of analogRead
Post by: stimmer on Dec 16, 2012, 03:26 pm
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:

Code: [Select]

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");
}
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Dec 16, 2012, 07:26 pm
You might want to read this:-
http://www.atmel.com/Images/doc11106.pdf (http://www.atmel.com/Images/doc11106.pdf)
The fast mode is what is known as a tracking mode. This is useful if the input signal is changing by less than the least significant bit amount between samples. Things like bandwidth limited audio waveforms are good for this.
Title: Re: speed of analogRead
Post by: fat16lib on Dec 16, 2012, 09:28 pm
When measuring a signal, time jitter is as important as amplitude accuracy.

See this http://www.analog.com/library/analogDialogue/archives/42-02/clock_optimization.html (http://www.analog.com/library/analogDialogue/archives/42-02/clock_optimization.html)

Here is an example for 100,000 samples per second with 10-bits of accuracy and a clock jitter of 5 ns.

Quote

SRN of an ideal n-bit ADC:

SNR(dB) = 6.02*n + 1.76


Quote

SNR due clock jitter:

SNR(dB) = -20*log(6.28*f*t)

f is the measurement frequency

t is the time jitter in seconds


For an ideal 10-bit ADC the max SNR is about 62 dB.

The SNR limit for clock jitter is

SNR(jitter) = -20*log(6.28*100000*5e-9) = 50.06

So jitter is the limiting factor.  What is the point of fast measurements if you don't know the time to sufficient accuracy?



Title: Re: speed of analogRead
Post by: PakARD on Feb 28, 2013, 01:49 am

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:

Code: [Select]

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");
}



This piece of code is great!! I'm sampling at 1MHz (one channel) in my Due.
Would the registers setup change for two channels?

Thank you

Title: Re: speed of analogRead
Post by: stimmer on Feb 28, 2013, 02:35 am
It's similar. Basically in free running mode the SAM3X cycles through all enabled channels. So we just enable 2 channels and wait for 2 conversions each time:
Code: [Select]
void setup() {
 Serial.begin(9600);
 int t=analogRead(0);

 ADC->ADC_MR |= 0x80; // these lines set free running mode on adc 7 and adc 6 (pin A0 and A1 - see Due Pinout Diagram thread)
 ADC->ADC_CR=2;
 ADC->ADC_CHER=0xC0; // this is (1<<7) | (1<<6) for adc 7 and adc 6                                    
}

void loop() {
 int t=micros();
 int q0=0,q1=0;
 int a0,a1;
 for(int i=0;i<500000;i++){
    while((ADC->ADC_ISR & 0xC0)!=0xC0);// wait for two conversions (pin A0[7]  and A1[6])
   a0=ADC->ADC_CDR[7];              // read data on A0 pin
   a1=ADC->ADC_CDR[6];              // read data on A1 pin
   q0+=a0;
   q1+=a1;
 }
 t=micros()-t;
 Serial.print("500000 pairs of conversions in ");Serial.print(t);Serial.println(" micros");
 Serial.print("A0 total:");Serial.println(q0);
 Serial.print("A1 total:");Serial.println(q1);
}


Obviously there's only one ADC which is shared by the 2 channels so the effective sample rate halves. This means that you will lose some accuracy, because internally the chip is having to switch between two different input signals 1 million times per second, and that won't be as good as it tracking the same input all the time like in the 1-channel example.
Title: Re: speed of analogRead
Post by: PakARD on Feb 28, 2013, 08:55 pm

It's similar. Basically in free running mode the SAM3X cycles through all enabled channels. So we just enable 2 channels and wait for 2 conversions each time:
Code: [Select]
void setup() {
  Serial.begin(9600);
  int t=analogRead(0);

  ADC->ADC_MR |= 0x80; // these lines set free running mode on adc 7 and adc 6 (pin A0 and A1 - see Due Pinout Diagram thread)
  ADC->ADC_CR=2;
  ADC->ADC_CHER=0xC0; // this is (1<<7) | (1<<6) for adc 7 and adc 6                                     
}

void loop() {
  int t=micros();
  int q0=0,q1=0;
  int a0,a1;
  for(int i=0;i<500000;i++){
    while((ADC->ADC_ISR & 0x80)==0);
    while((ADC->ADC_ISR & 0x80)==0);// wait for two conversions
    a0=ADC->ADC_CDR[7];              // read data on A0 pin
    a1=ADC->ADC_CDR[6];              // read data on A1 pin
    q0+=a0;
    q1+=a1;
  }
  t=micros()-t;
  Serial.print("500000 pairs of conversions in ");Serial.print(t);Serial.println(" micros");
  Serial.print("A0 total:");Serial.println(q0);
  Serial.print("A1 total:");Serial.println(q1);
}


Obviously there's only one ADC which is shared by the 2 channels so the effective sample rate halves. This means that you will lose some accuracy, because internally the chip is having to switch between two different input signals 1 million times per second, and that won't be as good as it tracking the same input all the time like in the 1-channel example.


This is also working.
But I have some doubts about it:

- Why on the 
Code: [Select]
while((ADC->ADC_ISR & 0x80)==0);
    while((ADC->ADC_ISR & 0x80)==0);// wait for two conversions

line, you use the same pin of the ADC to compare?

Wouldn't it be:
Code: [Select]
while((ADC->ADC_ISR & 0x80)==0);
    while((ADC->ADC_ISR & 0x40)==0);// wait for two conversions

or better:
Code: [Select]

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

??

On the other hand, to reduce Jitter and free more time for "processing" in the loop function, could it enabling ADC interrupts be a solution?
Do you know how to accomplish this? I have seen ADC_IER register in the datasheet, but I'm pretty new with Arduino Due (just a couple of days) and don't know how to start.  :smiley-red: :smiley-red:

Thank you again

Title: Re: speed of analogRead
Post by: stimmer on Feb 28, 2013, 10:31 pm
Quote

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


Well spotted - what it should actually be is this:
Code: [Select]
    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 :)

Code: [Select]
#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.

Title: Re: speed of analogRead
Post by: PakARD on Mar 01, 2013, 06:22 pm

Quote

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


Well spotted - what it should actually be is this:
Code: [Select]
   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 :)

Code: [Select]
#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.






This code is running smoother and with less jitter.
I am measuring jitter, by setting HIGH and LOW one Digital pin with an oscilloscope and the inline function:
Code: [Select]
inline void digitalWriteDirect(int pin, boolean val){
 if(val) g_APinDescription[pin].pPort -> PIO_SODR = g_APinDescription[pin].ulPin;
 else    g_APinDescription[pin].pPort -> PIO_CODR = g_APinDescription[pin].ulPin;
}





BTW, I am controlling the sample rate by using the PRESCAL register in:
Code: [Select]

ADC->ADC_MR |= 0x2680; // these lines set free running mode on adc 7 (pin A0)  [PRESCAL at  50kHz]

Is there any other (better) way to  do it?

Actually, depending on if I am using one channel or two an the ADC, I need to setup a different PRESCAL, since there is only one ADC for all input pins:
Code: [Select]

if (channels_2)
 {
   ADC->ADC_MR |= 0x1280; // these lines set free running mode on adc 7 (pin A0) and adc 6(pin A1) PRESCAL at 50kHz
 }
 else
 {
   ADC->ADC_MR |= 0x2680; // these lines set free running mode on adc 7 (pin A0) PRESCAL at 50kHz
 }




On the other hand, what would the changes for two channels and 4 buffers? Is it possible to get a buffer for each channel?


Finally, I'm trying to measure  performance (the same way as jitter) and for  50kHz sample rate and 512 points, the time in the interrupt is 1.8us and the free time in the loop is 10.23ms (99.98% free!!).
For 1MHz, it is still around 1.8us against 256us (it is still 99.29% free), however more jitter at the beggining of the loop is appreciated.

I think it is because of the
Code: [Select]
while(obufn==bufn); instruction


It is interesting the
Code: [Select]

SerialUSB.begin(0);
while(!SerialUSB);


Does the computer (or other device) set the speed of the port?



Your spectrogram app looks great, keep up the good work!! Congratulations.

Title: Re: speed of analogRead
Post by: stimmer on Mar 01, 2013, 10:26 pm
The jitter you are measuring is not the same thing as the clock jitter that was mentioned earlier in the thread and will not affect the accuracy of the ADC readings. Clock jitter is caused by the accuracy of the timing and waveform of the master clock oscillator and there is nothing we can do about that.

The line   adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); sets the prescal value - there is also ADC_FREQ_MIN and you can pass any value in between.

It isn't possible to get a buffer per channel as far as I know. There is a tagged mode where the highest 4 bits are written with the channel the sample came from, that is useful when you enable more than one channel.

SerialUSB works as fast as it can, the baud rate is ignored. Since it's USB2.0 the port is faster than the SAM3X can throw data down it. I've had it running at over 50Mbit/sec.

Title: Re: speed of analogRead
Post by: PakARD on Mar 01, 2013, 11:39 pm

The jitter you are measuring is not the same thing as the clock jitter that was mentioned earlier in the thread and will not affect the accuracy of the ADC readings. Clock jitter is caused by the accuracy of the timing and waveform of the master clock oscillator and there is nothing we can do about that.

The line   adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); sets the prescal value - there is also ADC_FREQ_MIN and you can pass any value in between.

It isn't possible to get a buffer per channel as far as I know. There is a tagged mode where the highest 4 bits are written with the channel the sample came from, that is useful when you enable more than one channel.

SerialUSB works as fast as it can, the baud rate is ignored. Since it's USB2.0 the port is faster than the SAM3X can throw data down it. I've had it running at over 50Mbit/sec.




I'm sorry about the jitter misunderstanding, after thinking it I knew it was a different issue.

So, about the ADC sampling in both channels, then I suppose that the samples are alternated in the buffers, right?  Sorry to insist,but I am learning about the system...so, how could I set up a second channel?

Then, about the adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST) function.... Where could I find more information about this macros and functions? They are not the same as in the Arduino UNO, but pretty similar.
Title: Re: speed of analogRead
Post by: stimmer on Mar 02, 2013, 02:05 am
I think that to use a second channel, all you have to do is enable it. You are right, the samples end up alternated in the buffers.

I don't know of any proper documentation for the adc_init function. I just searched through the Arduino directory for files containing adc_init, then looked at the source files.
Title: Re: speed of analogRead
Post by: Gericom on Mar 02, 2013, 10:10 am


The jitter you are measuring is not the same thing as the clock jitter that was mentioned earlier in the thread and will not affect the accuracy of the ADC readings. Clock jitter is caused by the accuracy of the timing and waveform of the master clock oscillator and there is nothing we can do about that.

The line   adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); sets the prescal value - there is also ADC_FREQ_MIN and you can pass any value in between.

It isn't possible to get a buffer per channel as far as I know. There is a tagged mode where the highest 4 bits are written with the channel the sample came from, that is useful when you enable more than one channel.

SerialUSB works as fast as it can, the baud rate is ignored. Since it's USB2.0 the port is faster than the SAM3X can throw data down it. I've had it running at over 50Mbit/sec.



I'm sorry about the jitter misunderstanding, after thinking it I knew it was a different issue.

So, about the ADC sampling in both channels, then I suppose that the samples are alternated in the buffers, right?  Sorry to insist,but I am learning about the system...so, how could I set up a second channel?

Then, about the adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST) function.... Where could I find more information about this macros and functions? They are not the same as in the Arduino UNO, but pretty similar.


This is a very good site:
http://asf.atmel.com/docs/latest/sam3x/html/group__sam__drivers__adc__group.html
Title: Re: speed of analogRead
Post by: PakARD on Mar 02, 2013, 12:55 pm


This is a very good site:
http://asf.atmel.com/docs/latest/sam3x/html/group__sam__drivers__adc__group.html



Thank you Gericom.
Thank you stimmer.

Title: Re: speed of analogRead
Post by: ssclark88 on Apr 03, 2013, 02:22 am
First of all, Thanks for sharing this. My application requires using 8 analog inputs, while using the digital outputs to switch between banks of 8 sensors(10*8=80 total). It's recording a 20ms event, and I figured that 1 million samples/sec should give me plenty of time resolution(ADC accuracy is not as important). The Due seemed like the logical choice, but this low level programming is intimidating!

I'm trying to piece the code together to understand this all. Where does this function get called?
Code: [Select]

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


I changed this to open up a0-7. Is there anything else needed to read 8 pins?
 ADC->ADC_CHER=0xFF; 

I can't find much information about SerialUSB. What do I use to read data that quickly? The serial monitor only goes up to 115200. Would it be easier/better to write to a SD card?
Title: Re: speed of analogRead
Post by: ralphnev on Apr 03, 2013, 08:45 pm
a few comments
the 1Msps is for one channel under very specific conditions
  if you switch channels there is a lag (you will have to look up the details in the data sheet)

IMO best way is to program the PDC with timing generated by the PWM .. with resonable sample code from atmel 
but that will not help you trying to read 80 samples .....

(i'm running at 500,000 samples / sec on one channel, in bursts of ~2k (roughly 4ms/collection ~ pause for 3ms, repeat )

can't comment on the USB , but writing to a SD card "can" be fast  (i'm writing ~500kb/sec),
you need buffer space for sd card lag
Title: Re: speed of analogRead
Post by: ssclark88 on Apr 04, 2013, 12:54 am
With Stimmer's first bit of code I was able to do 1 Million ADC conversions/second, while switching through 8 analog inputs. When I added in some digitalwrites the speed drop was minimal.  When I tried to read the data with serial.write() everything came to a screeching halt. I need to be able to write 1.5MB/s and SerialUSB.write() looks like it will work. The latency with the SD cards may slow it down too much, but I do like the idea of writing to a csv or txt file.


SerialUSB works as fast as it can, the baud rate is ignored. Since it's USB2.0 the port is faster than the SAM3X can throw data down it. I've had it running at over 50Mbit/sec.



I like his DMA approach much better because is frees up the processor. While the buffer is being filled I can write the data. but I can't find references on how to use SerialUSB.
Title: Re: speed of analogRead
Post by: lcampagn on Jun 08, 2013, 06:19 pm

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 :)

Code: [Select]
#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.


Thanks for this code! I've got my Due pulling 1MS/s as well. I wrote a very simple python script to display data as it is fetched from the serial port. It buffers the data in a separate thread, then plots as quickly as it can (it downsamples 1 second of data to 10k points and plots in realtime). It can also display the power spectrum of the signal by selecting Plot Options->Transform->FFT from the context menu.  Requirements are python, pyserial (http://pyserial.sourceforge.net/ (http://pyserial.sourceforge.net/)), and pyqtgraph (http://pyqtgraph.org/ (http://pyqtgraph.org/)). I'd like to develop this code further to provide display-triggering and controls for configuring the sample rate and channels.

Code: [Select]

import pyqtgraph as pg
import time, threading, sys
import serial
import numpy as np


class SerialReader(threading.Thread):
    """ Defines a thread for reading and buffering serial data.
    By default, about 5MSamples are stored in the buffer.
    Data can be retrieved from the buffer by calling get(N)"""
    def __init__(self, port, chunkSize=1024, chunks=5000):
        threading.Thread.__init__(self)
        # circular buffer for storing serial data until it is
        # fetched by the GUI
        self.buffer = np.zeros(chunks*chunkSize, dtype=np.uint16)
       
        self.chunks = chunks        # number of chunks to store in the buffer
        self.chunkSize = chunkSize  # size of a single chunk (items, not bytes)
        self.ptr = 0                # pointer to most (recently collected buffer index) + 1
        self.port = port            # serial port handle
        self.sps = 0.0              # holds the average sample acquisition rate
        self.exitFlag = False
        self.exitMutex = threading.Lock()
        self.dataMutex = threading.Lock()
       
       
    def run(self):
        exitMutex = self.exitMutex
        dataMutex = self.dataMutex
        buffer = self.buffer
        port = self.port
        count = 0
        sps = None
        lastUpdate = pg.ptime.time()
       
        while True:
            # see whether an exit was requested
            with exitMutex:
                if self.exitFlag:
                    break
           
            # read one full chunk from the serial port
            data = port.read(self.chunkSize*2)
            # convert data to 16bit int numpy array
            data = np.fromstring(data, dtype=np.uint16)
           
            # keep track of the acquisition rate in samples-per-second
            count += self.chunkSize
            now = pg.ptime.time()
            dt = now-lastUpdate
            if dt > 1.0:
                # sps is an exponential average of the running sample rate measurement
                if sps is None:
                    sps = count / dt
                else:
                    sps = sps * 0.9 + (count / dt) * 0.1
                count = 0
                lastUpdate = now
               
            # write the new chunk into the circular buffer
            # and update the buffer pointer
            with dataMutex:
                buffer[self.ptr:self.ptr+self.chunkSize] = data
                self.ptr = (self.ptr + self.chunkSize) % buffer.shape[0]
                if sps is not None:
                    self.sps = sps
               
               
    def get(self, num, downsample=1):
        """ Return a tuple (time_values, voltage_values, rate)
          - voltage_values will contain the *num* most recently-collected samples
            as a 32bit float array.
          - time_values assumes samples are collected at 1MS/s
          - rate is the running average sample rate.
        If *downsample* is > 1, then the number of values returned will be
        reduced by averaging that number of consecutive samples together. In
        this case, the voltage array will be returned as 32bit float.
        """
        with self.dataMutex:  # lock the buffer and copy the requested data out
            ptr = self.ptr
            if ptr-num < 0:
                data = np.empty(num, dtype=np.uint16)
                data[:num-ptr] = self.buffer[ptr-num:]
                data[num-ptr:] = self.buffer[:ptr]
            else:
                data = self.buffer[self.ptr-num:self.ptr].copy()
            rate = self.sps
       
        # Convert array to float and rescale to voltage.
        # Assume 3.3V / 12bits
        # (we need calibration data to do a better job on this)
        data = data.astype(np.float32) * (3.3 / 2**12)
        if downsample > 1:  # if downsampling is requested, average N samples together
            data = data.reshape(num/downsample,downsample).mean(axis=1)
            num = data.shape[0]
            return np.linspace(0, (num-1)*1e-6*downsample, num), data, rate
        else:
            return np.linspace(0, (num-1)*1e-6, num), data, rate
   
    def exit(self):
        """ Instruct the serial thread to exit."""
        with self.exitMutex:
            self.exitFlag = True


# Get handle to serial port
# (your port string may vary; windows users need 'COMn')
s = serial.Serial('/dev/ttyACM0')

# Create the GUI
app = pg.mkQApp()
plt = pg.plot()
plt.setLabels(left=('ADC Signal', 'V'), bottom=('Time', 's'))
plt.setYRange(0.0, 3.3)
           
# Create thread to read and buffer serial data.
thread = SerialReader(s)
thread.start()

# Calling update() will request a copy of the most recently-acquired
# samples and plot them.
def update():
    global plt, thread
    t,v,r = thread.get(1000*1024, downsample=100)
    plt.plot(t, v, clear=True)
    plt.setTitle('Sample Rate: %0.2f'%r)
   
    if not plt.isVisible():
        thread.exit()
        timer.stop()

# Set up a timer with 0 interval so Qt will call update()
# as rapidly as it can handle.
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

# Start Qt event loop.   
if sys.flags.interactive == 0:
    app.exec_()

Title: Re: speed of analogRead
Post by: PakARD on Jun 08, 2013, 07:06 pm


The line   adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); sets the prescal value - there is also ADC_FREQ_MIN and you can pass any value in between.




There is something I am missing.... if I want to set up an effective 20kHz or 50 KHz sampling rate using this method, what should I write?

The line   adc_init(ADC, SystemCoreClock, 50000, ADC_STARTUP_FAST); feels wrong since ADC_FREQ_MIN is 1000000


Thank you
Title: Re: speed of analogRead
Post by: stimmer on Jun 08, 2013, 11:02 pm
ADC_FREQ_MAX/MIN is the frequency of the ADC clock, not the sample rate. The ADC takes about 20 clocks to do a conversion (maybe exactly 20 clocks, I can't remember), so the conversion rate by this method is 50000-1000000 samples per second. To get a slower sample rate using free-running mode you could sample some channels you don't need to slow things down. But the proper way to do it is to use a timer triggered mode instead of free-running mode.
Title: Re: speed of analogRead
Post by: PakARD on Jun 09, 2013, 02:36 am

ADC_FREQ_MAX/MIN is the frequency of the ADC clock, not the sample rate. The ADC takes about 20 clocks to do a conversion (maybe exactly 20 clocks, I can't remember), so the conversion rate by this method is 50000-1000000 samples per second. To get a slower sample rate using free-running mode you could sample some channels you don't need to slow things down. But the proper way to do it is to use a timer triggered mode instead of free-running mode.


I see, but then is every analogread sampled in the timer interrupt? How does DMA Work with the timer?
I don't get it.

Thank you again.
Title: Re: speed of analogRead
Post by: stimmer on Jun 09, 2013, 02:59 am
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 :)

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
Title: Re: speed of analogRead
Post by: PakARD on Jun 09, 2013, 02:07 pm

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 :)

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.

Title: Re: speed of analogRead
Post by: DrRobertPatzke on Jun 28, 2013, 07:56 pm
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.
Title: Re: speed of analogRead
Post by: kllsamui on Jan 20, 2014, 08:46 am
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 ??

Title: Re: speed of analogRead
Post by: madrang on Jan 22, 2014, 06:29 pm
Code: [Select]
 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.

Code: [Select]
while(obufn==bufn); // wait for buffer to be full

This line should be

Code: [Select]
while((obufn + 1)%4==bufn); // wait for buffer to be full

Also why start at buffer 1  ??
Code: [Select]
bufn=obufn=1;

Would be better to start at 0.
Code: [Select]
bufn=1;
obufn=0;


Not sure if i understood the code properly, i hope someone can tell me more about this.
Title: Re: speed of analogRead
Post by: Adonisleal on Nov 20, 2014, 02:32 am
Well spotted - what it should actually be is this:
Code: [Select]
    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 :)

Code: [Select]
#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?
Title: Re: speed of analogRead
Post by: BubbleGum on Jan 18, 2015, 06:50 am
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?
Title: Re: speed of analogRead
Post by: MorganS on Jan 18, 2015, 06:14 pm
Without your code, we can't speculate on the reason. Have you tried the code in reply #18 which claims to achieve 1MHz?
Title: Re: speed of analogRead
Post by: rickrlh on Aug 12, 2015, 04:52 am
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?
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Aug 12, 2015, 07:30 am
Quote
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.
Title: Re: speed of analogRead
Post by: L4ura on Dec 10, 2015, 02:22 pm
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
Title: Re: speed of analogRead
Post by: Magician on Dec 10, 2015, 06:43 pm
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
Title: Re: speed of analogRead
Post by: clementvanstaen on Dec 15, 2015, 10:36 am
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

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:

Code: [Select]

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");
}

Title: Re: speed of analogRead
Post by: L4ura on Dec 15, 2015, 06:41 pm
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
Title: Re: speed of analogRead
Post by: Magician on Dec 15, 2015, 07:10 pm
Quote
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:

Quote
• 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
Title: Re: speed of analogRead
Post by: vffgaston on Dec 16, 2015, 10:05 am
Quote
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
Title: Re: speed of analogRead
Post by: L4ura on Dec 18, 2015, 09:52 am
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?
Title: Re: speed of analogRead
Post by: Magician on Dec 18, 2015, 12:14 pm
Quote
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.
Title: Re: speed of analogRead
Post by: direvius on Oct 18, 2016, 11:11 am
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:

(http://image.prntscr.com/image/81e63c7585fe4d0d93d475c15385ab8a.png)

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:

(http://image.prntscr.com/image/8c1e88dcb62a403d8b83510f3499c56a.png)

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!

Code: [Select]
 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.

Code: [Select]
while(obufn==bufn); // wait for buffer to be full

This line should be

Code: [Select]
while((obufn + 1)%4==bufn); // wait for buffer to be full

Also why start at buffer 1  ??
Code: [Select]
bufn=obufn=1;

Would be better to start at 0.
Code: [Select]
bufn=1;
obufn=0;


Not sure if i understood the code properly, i hope someone can tell me more about this.
Title: Re: speed of analogRead
Post by: abonellitoro on Dec 23, 2016, 02:24 pm
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?



Code: [Select]
#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.
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Dec 24, 2016, 05:57 pm
Quote
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.
Title: Re: speed of analogRead
Post by: abonellitoro on Dec 25, 2016, 05:26 pm
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!
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Dec 25, 2016, 08:28 pm
See
http://forum.arduino.cc/index.php?topic=326944.msg2860396#new (http://forum.arduino.cc/index.php?topic=326944.msg2860396#new)
Title: Re: speed of analogRead
Post by: abonellitoro on Dec 25, 2016, 09:59 pm
I couldn't find anything that answers my questions. Can you be more specific?
Title: Re: speed of analogRead
Post by: Grumpy_Mike on Dec 26, 2016, 07:59 pm
Quote
I couldn't find anything that answers my questions.
Why not it is all there.

Quote
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?
Title: Re: speed of analogRead
Post by: direvius on Jan 09, 2017, 02:40 pm
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.
Title: Re: speed of analogRead
Post by: abonellitoro on Jan 17, 2017, 03:16 pm
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 :)
Title: Re: speed of analogRead
Post by: pmarin on Jul 13, 2017, 03:13 pm
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
Title: Re: speed of analogRead
Post by: ard_newbie on Jul 13, 2017, 04:11 pm
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:

Code: [Select]

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
}






Title: Re: speed of analogRead
Post by: jlsilicon on May 17, 2018, 05:22 pm
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 !)
Title: Re: speed of analogRead
Post by: adrianka on May 25, 2018, 02:30 pm
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!
Title: Re: speed of analogRead
Post by: ard_newbie on May 25, 2018, 08:32 pm

Post the sketch you have already done (between code tags).
Title: Re: speed of analogRead
Post by: adrianka on Jun 08, 2018, 08:49 pm
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

Code: [Select]
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):

Code: [Select]
#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:

Code: [Select]
Start inter

and just hangs there forever.

Does anyone have an idea?
Title: Re: speed of analogRead
Post by: ard_newbie on Jun 09, 2018, 02:43 pm

Serial is much too slow compared to the speed of ADC samplings in Free Running mode (~ 1 MHz = 1000000 *  uint16_t per second).
Title: Re: speed of analogRead
Post by: adrianka on Jun 09, 2018, 02:47 pm
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.
Title: Re: speed of analogRead
Post by: ard_newbie on Jun 09, 2018, 03:25 pm
...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 ?
Title: Re: speed of analogRead
Post by: adrianka on Jun 09, 2018, 03:45 pm
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.
Title: Re: speed of analogRead
Post by: ard_newbie on Jun 09, 2018, 04:26 pm
reply #55
Title: Re: speed of analogRead
Post by: adrianka on Jun 09, 2018, 05:10 pm
So is speed critical if I just want to find out what lets my arduino crash?
I don't mind if some measurements get tossed away.

Why can't I switch from SerialUSB to Serial if I just want to debug?
Title: Re: speed of analogRead
Post by: MorganS on Jun 09, 2018, 06:52 pm
What is wrong with:
Code: [Select]
void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


?

Does this run fast enough for you? How fast do you actually need for your application?
Title: Re: speed of analogRead
Post by: adrianka on Jun 13, 2018, 03:36 pm
What is wrong with:
Code: [Select]
void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


?

Does this run fast enough for you? How fast do you actually need for your application?
Hi MorganS,

thanks for your reply!
Unfortunately, this is too slow. I'd be happy to get a sample rate of 1 Msps, 250ksps being the absolute minimum.

The good thing is, I am reading values by analogRead. I still couldn't figure out why SerialUSB stopped working.
Any ideas?

Any help is very much appreciated!

Now, I tried this under a windows machine, just to test the serial connection:

Code: [Select]
#!/usr/bin/python
import serial
# import syslog
import time

#The following line is for serial over GPIO
usb_ports = ('COM3')
s = serial.Serial(usb_ports)

print("starting while")
while 1:
    try:
        # Serial read section
        print("opening file")
        file = open("debugging.txt", "w")
        print("reading line")
        msg = str(s.readline())
        print("writing to file")
        file.write(msg + "\n")
        print(msg + "\n")
        print("closing file")
        file.close()
    except KeyboardInterrupt:
        file = open("debugging.txt", "w")
        print("closing file and exiting program")
        file.close()
        exit()


Arduino code:
Code: [Select]
void setup() {
  SerialUSB.begin(0);
  while(!SerialUSB);
}

void loop() {
  SerialUSB.print(micros());
  SerialUSB.print(',');
  SerialUSB.println(analogRead(A0));
}


result in the shell:
Code: [Select]
starting while
opening file
reading line



Again: The code that stimmer posted delivers no output anymore. It says voltage constantly 0, rate 0. (At the beginning it worked, now I'm looking for a solution to this problem but can't find it.)
Title: Re: speed of analogRead
Post by: MorganS on Jun 20, 2018, 01:21 am
Quote
Unfortunately, this is too slow.
How much slower? I'm not going to do the measurement myself.

If it's close then perhaps there's some simple things that can be done to increase the speed.

If it's a long way away then we have make major changes, such as maybe not using any Arduino.

Quote
Again: The code that stimmer posted delivers no output anymore. It says voltage constantly 0, rate 0. (At the beginning it worked, now I'm looking for a solution to this problem but can't find it.)
What did you change?
Title: Re: speed of analogRead
Post by: adrianka on Jun 25, 2018, 06:30 pm
Thanks for the help. The problem was in the patches posted on github that I haven't read at that point.


Code: [Select]
// patch 1.1 start
  while((obufn + 1)%4==bufn);// wait for buffer to be full
  // patch 1.1 end


Code: [Select]
  // patch 1.2 start
  //bufn=obufn=1;
  bufn=1;
  obufn=0;
  // patch 1.2 end


and

Code: [Select]
   // patch 2 start
   //ADC->ADC_RNPR=(uint32_t)buf[bufn];
   ADC->ADC_RNPR=(uint32_t)buf[(bufn+1)&3];
   // patch 2 end


it's running fine now. apart from the digital write. Which has no effect.

Code: [Select]
#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

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

// source: https://gist.github.com/pklaus/5921022
// applied patch 1 from: http://forum.arduino.cc/index.php?topic=137635.msg2526475#msg2526475
// applied patch 2 from:  https://gist.github.com/pklaus/5921022 comment on Apr 2, 2017 from borisbarbour

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;
   // patch 2 start
   //ADC->ADC_RNPR=(uint32_t)buf[bufn];
   ADC->ADC_RNPR=(uint32_t)buf[(bufn+1)&3];
   // patch 2 end
   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;
  // patch 1.2 start
  //bufn=obufn=1;
  bufn=1;
  obufn=0;
  // patch 1.2 end
  ADC->ADC_PTCR=1;
  ADC->ADC_CR=2;

  // this has no effect:
  pinMode(52, OUTPUT);
  digitalWrite(52, HIGH);
}

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


does somebody know why or can offer any help?

It is highly appreciated.

Thanks in advance and kind wishes!
Title: Re: speed of analogRead
Post by: adrianka on Jun 25, 2018, 08:10 pm
Arduino has been waiting for serial connection...  :smiley-roll-sweat:
Title: Re: speed of analogRead
Post by: adrianka on Jun 29, 2018, 02:07 pm
Hello!

So now I want to read from two channels. I this change to the Arduino code only:

Code: [Select]
ADC->ADC_CHER=0xC0; // 0x80 for pin a0, 0xC0 for pin a0 and a1

It is running and sending data to the PC but I can't make any sense of the data.
After reading this thread I thought data is being written to the buffer alternately. Is this correct? I can't seem to get the correct data out of it.

My python code is this:

Code: [Select]
#!/usr/bin/env python

# from http://forum.arduino.cc/index.php?topic=137635.msg1270996#msg1270996

import pyqtgraph as pg
import time, threading, sys
import serial
import numpy as np

# script takes 2 arguments.
# Argument 1: duration; determines for how long values shall be gathered
# value must be in seconds
# Argument 2: procedure; determines which procedure is going to be called and to which file data is saved
# possible procedures are:
# - startup
# - idle
# - readseq
# - readrnd
# - writeseq
# - writernd

# voltage references
aref1 = 1.0 # for 5V measurements
aref2 = 2.5 # for 12V measurements
arefDue = 3.3

resolution = 12 #resolution in bits

class SerialReader(threading.Thread):
    """ Defines a thread for reading and buffering serial data.
    By default, about 5MSamples are stored in the buffer.
    Data can be retrieved from the buffer by calling get(N)"""

    #dataStorage = list()

    def __init__(self, port, chunkSize=1024, chunks=5000):
        threading.Thread.__init__(self)
        # circular buffer for storing serial data until it is
        # fetched by the GUI
        self.buffer = np.zeros(chunks*chunkSize, dtype=np.uint16)
       
        self.chunks = chunks        # number of chunks to store in the buffer
        self.chunkSize = chunkSize  # size of a single chunk (items, not bytes)
        self.ptr = 0                # pointer to most (recently collected buffer index) + 1
        self.port = port            # serial port handle
        self.sps = 0.0              # holds the average sample acquisition rate
        self.exitFlag = False
        self.exitMutex = threading.Lock()
        self.dataMutex = threading.Lock()
        #self.countLoop = 0
        self.dataStorage = list()
       
       

    def run(self):
        exitMutex = self.exitMutex
        dataMutex = self.dataMutex
        buffer = self.buffer
        port = self.port
        count = 0
        sps = None
        lastUpdate = pg.ptime.time()
       
        while True:
            # see whether an exit was requested
            with exitMutex:
                if self.exitFlag:
                    break
           
            # read one full chunk from the serial port
            data = port.read(self.chunkSize*2)
            # convert data to 16bit int numpy array
            data = np.fromstring(data, dtype=np.uint16)
           
            # write data to file
            data = data.astype(np.float32) * (arefDue / 2**resolution)
           
            self.dataStorage.extend(data)

            # keep track of the acquisition rate in samples-per-second
            count += self.chunkSize
            now = pg.ptime.time()
            dt = now-lastUpdate
            if dt > 1.0:
                # sps is an exponential average of the running sample rate measurement
                if sps is None:
                    sps = count / dt
                else:
                    sps = sps * 0.9 + (count / dt) * 0.1
                count = 0
                lastUpdate = now
               
            # write the new chunk into the circular buffer
            # and update the buffer pointer
            with dataMutex:
                buffer[self.ptr:self.ptr+self.chunkSize] = data
                self.ptr = (self.ptr + self.chunkSize) % buffer.shape[0]
                if sps is not None:
                    self.sps = sps

    def getData(self):
        return self.dataStorage

    def exit(self):
        """ Instruct the serial thread to exit."""
        with self.exitMutex:
            self.exitFlag = True

# Calling update() will request a copy of the most recently-acquired
# samples and plot them.
def update():
   
    if timer2.elapsed() >= int(sys.argv[1])*1000:
        timer.stop()
        dataList = thread.getData()
        thread.exit()
        prepfile(dataList)
        exit()

# Get handle to serial port
# (your port string may vary; windows users need 'COMn')

def prepfile(dataList):
    title = sys.argv[2]
    file0 = open('dual_' + title + '_pin0' + ".txt", "w")
    file1 = open('dual_' + title + '_pin1' + ".txt", 'w')
    i = 0
    while(i<len(dataList)):
        file0.write(str(dataList[i]) + "\n")
        i+=1
        file1.write(str(dataList[i]) + "\n")
        i+=1
    file0.close()
    file1.close()

print("running " + sys.argv[2] + " for " + sys.argv[1] + " seconds")

usb_ports = ('/dev/ttyACM0', '/dev/ttyACM1')
for usb in usb_ports:
    try:
        s = serial.Serial(usb)
        break
    except serial.SerialException:
        print('Arduino not found at ' + usb)
        pass

# check if number of arguments matches
if len(sys.argv) != 3:
    print("Wrong number of arguments. Make sure to pass \'duration\' and \'procedure\'")
    exit()

if not sys.argv[1].isdigit():
    print("Wrong duration. Please pass number of seconds.")
    exit()
   
# Create the GUI
app = pg.mkQApp()


# Set up a timer with 0 interval so Qt will call update()
# as rapidly as it can handle.

# Create thread to read and buffer serial data.

thread = SerialReader(s)
thread.start()
timer2 = pg.QtCore.QElapsedTimer()
timer2.start()

timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

# Start Qt event loop.   
if sys.flags.interactive == 0:
    app.exec_()


I don't plot the data with the script but write it to a file directly.

I'm having a sinus with 228kHz that goes into the Arduino. When I'm only reading from pinA0 python receives the sinus correctly. However, when I use pinA0 and A1 the sinus is gone.

I am referring to this post:
I think that to use a second channel, all you have to do is enable it. You are right, the samples end up alternated in the buffers.
Where is my mistake?
Title: Re: speed of analogRead
Post by: ard_newbie on Jun 29, 2018, 02:29 pm

1 MHz is the maximum sampling ADC frequency (with ADC_PRESCAL(1)) in Free Running Mode (and with a PDC DMA). when you enable n channels, the ADC peripheral multiplies the selected frequency by n to be able to sample each channel at the selected frequency. For 1 channel, it's OK, BUT for more than 1 channel, the ADC peripheral can't sample at 2 MHz (for 2 channels), 3 MHz (for 3 channels), .... You see the issue ?  

Conclusion: you can sample several channels (with ADC_CHER) and simply select the frequency you want for each channel (with a Timer Counter or a PWM Event Line) as long as the selected frequency times the number of channels do not exceed 1 MHz.

Title: Re: speed of analogRead
Post by: Sadoo2010 on Jul 04, 2018, 10:42 am
Code: [Select]
[quote author=PakARD link=msg=1138713 date=1362158574]

if (channels_2)
  {
    ADC->ADC_MR |= 0x1280; // these lines set free running mode on adc 7 (pin A0) and adc 6(pin A1) PRESCAL at 50kHz
  }
  else
  {
    ADC->ADC_MR |= 0x2680; // these lines set free running mode on adc 7 (pin A0) PRESCAL at 50kHz
  }





[/quote]



HI
I'm confused

 Why do you say 50 Hz FOR PRESCAL:
According to Data Sheet that says:
ADCClock = MCK / ( (PRESCAL+1) * 2 )
THEN assuming 0X26=0b00100110 THEN  ADCCLOCK= 84000000 / ((32+6)+1)*2)=1,076,923HZ
Even with the assumption that we put 26 in the equation:
ADCClock = MCK / ( (PRESCAL+1) * 2 )
THEN ADCCLOCK= 84000000 / ((26+1)*2)=1,555,555hz
not 50 hz!!!!!!

Please guide me how to calculate the ADC CLOCK
I need this number
Thank you,
sadoo2010@gmail.com
Title: Re: speed of analogRead
Post by: maria90 on Jul 23, 2019, 03:09 pm
Thanks for this code! I've got my Due pulling 1MS/s as well. I wrote a very simple python script to display data as it is fetched from the serial port. It buffers the data in a separate thread, then plots as quickly as it can (it downsamples 1 second of data to 10k points and plots in realtime). It can also display the power spectrum of the signal by selecting Plot Options->Transform->FFT from the context menu.  Requirements are python, pyserial (http://pyserial.sourceforge.net/ (http://pyserial.sourceforge.net/)), and pyqtgraph (http://pyqtgraph.org/ (http://pyqtgraph.org/)). I'd like to develop this code further to provide display-triggering and controls for configuring the sample rate and channels.

Code: [Select]

import pyqtgraph as pg
import time, threading, sys
import serial
import numpy as np


class SerialReader(threading.Thread):
    """ Defines a thread for reading and buffering serial data.
    By default, about 5MSamples are stored in the buffer.
    Data can be retrieved from the buffer by calling get(N)"""
    def __init__(self, port, chunkSize=1024, chunks=5000):
        threading.Thread.__init__(self)
        # circular buffer for storing serial data until it is
        # fetched by the GUI
        self.buffer = np.zeros(chunks*chunkSize, dtype=np.uint16)
       
        self.chunks = chunks        # number of chunks to store in the buffer
        self.chunkSize = chunkSize  # size of a single chunk (items, not bytes)
        self.ptr = 0                # pointer to most (recently collected buffer index) + 1
        self.port = port            # serial port handle
        self.sps = 0.0              # holds the average sample acquisition rate
        self.exitFlag = False
        self.exitMutex = threading.Lock()
        self.dataMutex = threading.Lock()
       
       
    def run(self):
        exitMutex = self.exitMutex
        dataMutex = self.dataMutex
        buffer = self.buffer
        port = self.port
        count = 0
        sps = None
        lastUpdate = pg.ptime.time()
       
        while True:
            # see whether an exit was requested
            with exitMutex:
                if self.exitFlag:
                    break
           
            # read one full chunk from the serial port
            data = port.read(self.chunkSize*2)
            # convert data to 16bit int numpy array
            data = np.fromstring(data, dtype=np.uint16)
           
            # keep track of the acquisition rate in samples-per-second
            count += self.chunkSize
            now = pg.ptime.time()
            dt = now-lastUpdate
            if dt > 1.0:
                # sps is an exponential average of the running sample rate measurement
                if sps is None:
                    sps = count / dt
                else:
                    sps = sps * 0.9 + (count / dt) * 0.1
                count = 0
                lastUpdate = now
               
            # write the new chunk into the circular buffer
            # and update the buffer pointer
            with dataMutex:
                buffer[self.ptr:self.ptr+self.chunkSize] = data
                self.ptr = (self.ptr + self.chunkSize) % buffer.shape[0]
                if sps is not None:
                    self.sps = sps
               
               
    def get(self, num, downsample=1):
        """ Return a tuple (time_values, voltage_values, rate)
          - voltage_values will contain the *num* most recently-collected samples
            as a 32bit float array.
          - time_values assumes samples are collected at 1MS/s
          - rate is the running average sample rate.
        If *downsample* is > 1, then the number of values returned will be
        reduced by averaging that number of consecutive samples together. In
        this case, the voltage array will be returned as 32bit float.
        """
        with self.dataMutex:  # lock the buffer and copy the requested data out
            ptr = self.ptr
            if ptr-num < 0:
                data = np.empty(num, dtype=np.uint16)
                data[:num-ptr] = self.buffer[ptr-num:]
                data[num-ptr:] = self.buffer[:ptr]
            else:
                data = self.buffer[self.ptr-num:self.ptr].copy()
            rate = self.sps
       
        # Convert array to float and rescale to voltage.
        # Assume 3.3V / 12bits
        # (we need calibration data to do a better job on this)
        data = data.astype(np.float32) * (3.3 / 2**12)
        if downsample > 1:  # if downsampling is requested, average N samples together
            data = data.reshape(num/downsample,downsample).mean(axis=1)
            num = data.shape[0]
            return np.linspace(0, (num-1)*1e-6*downsample, num), data, rate
        else:
            return np.linspace(0, (num-1)*1e-6, num), data, rate
   
    def exit(self):
        """ Instruct the serial thread to exit."""
        with self.exitMutex:
            self.exitFlag = True


# Get handle to serial port
# (your port string may vary; windows users need 'COMn')
s = serial.Serial('/dev/ttyACM0')

# Create the GUI
app = pg.mkQApp()
plt = pg.plot()
plt.setLabels(left=('ADC Signal', 'V'), bottom=('Time', 's'))
plt.setYRange(0.0, 3.3)
           
# Create thread to read and buffer serial data.
thread = SerialReader(s)
thread.start()

# Calling update() will request a copy of the most recently-acquired
# samples and plot them.
def update():
    global plt, thread
    t,v,r = thread.get(1000*1024, downsample=100)
    plt.plot(t, v, clear=True)
    plt.setTitle('Sample Rate: %0.2f'%r)
   
    if not plt.isVisible():
        thread.exit()
        timer.stop()

# Set up a timer with 0 interval so Qt will call update()
# as rapidly as it can handle.
timer = pg.QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)

# Start Qt event loop.   
if sys.flags.interactive == 0:
    app.exec_()


data = data.reshape(num/downsample,downsample).mean(axis=1)
            num = data.shape[0]
"float object cannot be interpreted as an integer "
I'm getting this error.
does anyone know why?