Go Down

Topic: speed of analogRead (Read 85431 times) previous topic - next topic

rickrlh

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?

Grumpy_Mike

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.

L4ura

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

Magician

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

clementvanstaen

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


L4ura

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

Magician

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

vffgaston

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

L4ura

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?

Magician

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.

direvius

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!

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.

abonellitoro

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.

Grumpy_Mike

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.

abonellitoro

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!


Go Up