ADC DMA readings out of order

Hello,

I am using the Argon for development and planning on switching to the Boron for production when ready. I am using the ADCDMAGen3_RK code (found at GitHub - rickkas7/ADCDMAGen3_RK: Analog to digital conversion using DMA for Particle Gen 3 devices (Argon, Boron, Xenon)) to read the ADC across four different pins using DMA. When I am looking at the readings come out of the device, it looks like that readings are shifting over time.

	if (bufferReady) 
	{
		adc.stop();
		int16_t *samples = (int16_t *)bufferReady;
		bufferReady = 0;

		//Serial.printlnf("ADC buffer ready");

		// Do something with the samples here
		// For the default 12-bit sampling, each index in samples will be 0 - 4095 (inclusive)

		ADC_Value1 = 0;
		ADC_Value2 = 0;
		ADC_Value3 = 0;
		ADC_Value4 = 0;

		for (int i = 0; i < ADC_BUFFER_LENGTH; i++)
		{
			ADC_Value1 += samples[i];
			i++;
			ADC_Value2 += samples[i];
			i++;
			ADC_Value3 += samples[i];
			i++; 
			ADC_Value4 += samples[i];
		}

		ADC_Value1 /= (ADC_BUFFER_LENGTH/4);  
		ADC_Value2 /= (ADC_BUFFER_LENGTH/4);
		ADC_Value3 /= (ADC_BUFFER_LENGTH/4);
		ADC_Value4 /= (ADC_BUFFER_LENGTH/4);
}

When I am looking at the readings it seems like the readings should be

ADC_Value1 = readings_from_ADC1
ADC_Value2 = readings_from_ADC2
ADC_Value3 = readings_from_ADC3
ADC_Value4 = readings_from_ADC4

But I seem to find reading as follows:

ADC_Value1 = readings_from_ADC4
ADC_Value2 = readings_from_ADC1
ADC_Value3 = readings_from_ADC2
ADC_Value4 = readings_from_ADC3

And after time,

ADC_Value1 = readings_from_ADC2
ADC_Value2 = readings_from_ADC3
ADC_Value3 = readings_from_ADC4
ADC_Value4 = readings_from_ADC1

I am not sure the best way to check that the ADC readings are not being shifted around. If anyone has suggestions, I would love to hear them.

Thanks

Can you also show us how you set the library up to read the four GPIOs?

Just for brevity you could write you loop like this too

		for (int i = 0; i < ADC_BUFFER_LENGTH;)
		{
			ADC_Value1 += samples[i++];
			ADC_Value2 += samples[i++];
			ADC_Value3 += samples[i++];
			ADC_Value4 += samples[i++];
		}

and I’d rather use float ADC_Value[4] instead of four independent variables.

To debug the issue, you may want to keep track of the actual address you get from bufferReady to see whether this stays the same over time (IIRC, with double buffering it should flip flop between two constant values).

Thanks for the response.

I realized I should have provided the initialization as well. Here is what I have:

#define ADC_Pin_1       A2
#define ADC_Pin_2       A3
#define ADC_Pin_3       A4
#define ADC_Pin_4       A5

// Works threaded or not
SYSTEM_THREAD(ENABLED);

const size_t SAMPLE_FREQ = 48000; // Hz
#define ADC_BUFFER_LENGTH (256)	
const size_t SAMPLES_IN_BUFFER = ADC_BUFFER_LENGTH; 

// This is where the samples are stored
static nrf_saadc_value_t buffer0[SAMPLES_IN_BUFFER];
static nrf_saadc_value_t buffer1[SAMPLES_IN_BUFFER];

//static 
nrf_saadc_value_t *bufferReady = 0;

ADCDMAGen3 adc;

void myBufferCallback(nrf_saadc_value_t *buf, size_t size);

  pinMode(ADC_Pin_1, INPUT);
  pinMode(ADC_Pin_2, INPUT);
  pinMode(ADC_Pin_3, INPUT);
  pinMode(ADC_Pin_4, INPUT);

  setADCSampleTime(100);
  ret_code_t err = adc
  .withSampleFreqHz(SAMPLE_FREQ)
  .withResolution(NRF_SAADC_RESOLUTION_12BIT)
  .withAcqTime(NRF_SAADC_ACQTIME_5US)
  .withDoubleBuffer(SAMPLES_IN_BUFFER, buffer0, buffer1)
  .withSampleMultiplePins({ ADC_Pin_1, ADC_Pin_2, ADC_Pin_3, ADC_Pin_4})
  .withBufferCallback(myBufferCallback)
  .init();
   
   Log.info("adc.init %lu", err);
   adc.start();

I’ve never seen that happen, but I don’t regularly use multiple pin mode. Once multiple pins are enabled the nRF52 hardware takes over so there isn’t really any code that I can think of that would cause that to happen. Everything is done at the buffer level in the library, not at the per-sample level, so I’m not sure how it could get out of sync due to a library error.

However, if I recall correctly, the ADCs are just sampled into sequential DMA locations until the buffer is full, so if one was missed or added, then it does seem like the samples would end up being shifted by a position after that. But as far as I know, that shouldn’t happen.

So I did some investigating.
So I forgot that I was using the BLE UART Service. I am using the BLE UART that Ricckas7 posted (Thanks) to try interact with my device via BLE UART from my phone and instead of having to connect to device via a USB/UART Terminal.
https://github.com/rickkas7/BleSerialPeripheralRK

I disabled that and the ADC readings seem to work fine. I turned the BLE UART service back on and then the the readings were all messed up again.

I looked through the documentation and it states that a Hardware Timer needs to be used. By default it should be using NFC. I am not trying to use NFC in my application.
I tried to select different timers with .withHardwareTimer(4), .withHardwareTimer(NRF_TIMER4), .withHardwareTimer(NFC) and nothing seems to work.

Is there a way to use the BLE UART service and ADC DMA on multiple pins?
If anyone has suggestions, please let me know.
Thanks.

That’s a good clue. The BLE radio runs at a high interrupt priority, and i’d guess it has to do with interrupts being delayed for long enough to drop a sample. I’m not sure how to fix that, but that would certainly explain that sort of behavior. It might be as simple as adjusting interrupt priority for the ADC DMA, but that’s just a guess. And it might not be that at all, but it’s certainly plausible.

I have been thinking. Could I just use the small method on one particular pin and then when the device is supposed to process the captured reading set it up for a different pin and run the ADC again? I could just cycle back through the IO one at a time. I will have to try it.

I did work on this and I was able to use the DMA method so that the device will sample on just one ADC pin, and then once the callback occurs, another ADC pin will be initialized and used for the next required sampling period. This seems to work well and I can get the BLE UART service to operate as well.

3 Likes

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.