For a project i’m trying to capture the vibrations sensed by an analogue accelerometer. To capture the vibrations correctly, I know I need a rather high sampling speed.
Herein lies the problem. I’m using a MKR1000 board to capture the data, but when I try to verify the sampling speed I average about 1200 samples per second which is slow for an SAMD21 board, if I’m correct? Since the Arduino Uno has a standard (theoretical) sampling speed of ±9600 Hz. This sampling speed of 1200 S/s is really constant as well. I did not change any prescalers, clock speeds or have serial prints in the code. If I change to a MKR NB1500 board, I get the same results. Since both boards output the correct ADC values, just at ‘low’ sampling speeds, I am rather puzzled about what the problem could be?
In attachment you can find the code used to output the sampling speeds. If I forgot about some valuable information, just let me know.
The MKR board's ADC is capable of sampling up to 350k samples/s, however Arduino have set the sample rate for analogRead() to a very conservative 1200 samples/s.
The ADC is clocked at 48MHz from generic clock 0 (GCLK0). This clock is divided down by the ADC prescaler set to 512:
ADC Clock Rate = 48MHz / 512 = 93750Hz
Arduino sets the ADC to single ended, single read mode, gain divide by 2 with 10-bit resolution. According to the SAMD21 datasheet, a conversion takes 7.5 ADC clock cycles to complete:
However, in addition Arduino set the ADC sample control register to 0x3f (63 decimal), this adds a further 64 half clock cycles to the conversion time:
I don't know why Arduino decided to set the sample control register to the maximum value of 0x3f. Increasing the sample time gives the ADC's sample and hold capacitor more time to charge, allowing for a larger source resistance at the analog input.
Here's an example that reads the ADC on A0, using single shot mode, 12-bit resolution and up to 11000 samples/s with register manipulation:
// Set-up the ADC to read analog input on A0 with 12-bit resolution
void setup() {
SerialUSB.begin(115200); // Set-up the native serial port at 115200 bit/s
while(!SerialUSB); // Wait for the console to open
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 | // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
ADC_CTRLB_RESSEL_12BIT; // Set ADC resolution to 12 bits
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->SAMPCTRL.reg = 0x00; // Set max Sampling Time Length to half divided ADC clock pulse (5.33us)
ADC->INPUTCTRL.bit.MUXPOS = 0x0; // Set the analog input to A0
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
ADC->CTRLA.bit.ENABLE = 1; // Enable the ADC
while(ADC->STATUS.bit.SYNCBUSY); // Wait for synchronization
}
void loop() {
ADC->SWTRIG.bit.START = 1; // Initiate a software trigger to start an ADC conversion
while(ADC->STATUS.bit.SYNCBUSY); // Wait for write synchronization
while(!ADC->INTFLAG.bit.RESRDY); // Wait for the conversion to complete
ADC->INTFLAG.bit.RESRDY = 1; // Clear the result ready (RESRDY) interrupt flag
while(ADC->STATUS.bit.SYNCBUSY); // Wait for read synchronization
int result = ADC->RESULT.reg; // Read the ADC result
SerialUSB.println(result); // Output the result
delay(500); // Wait for 500ms
}
Thank you very much for the example code, since this is a bit over my head!
No worries. It's possible to increase the sample rate up to 6250 samples/s and use analogRead(), just by setting the sample control register to zero at the beginning of the setup() portion of your sketch:
ADC->SAMPCTRL.reg = 0x00;
Thereafter just call analogRead() as usual.