Odd analog readings (part 2)

Oh I was confused a bit because the waveform is spread out in the green trace… so my brain is saying it’s slower! But you are graphing the number of samples you took and apparently are getting more samples because of the faster conversion times. Is this still with DMA? Because if you look at my charts above, you MIGHT be able to get it down to 5us conversion times (plus your for loop time which should easily be less than 500ns).

Sorry I wasn’t very coherent. yes you have corerctly deduced they are plotted against sample number and not time.
And yes they are all with DMA. I think there is a real prospect of getting some ultrasonic samples. I’m being yelled at so bye for now.

1 Like

I spoke too soon. (sad face - but hoping for an embarrasing correction to an obvious mistake I have missed)

This shows measurements from pins A0 and Pin A1. A0 is connected to my 50Hz signal. Pin 1 is grounded. All made with the version of the core software I downloaded yeaterday.

The blue line is measuring A0 pause 50uS and A0 again and pause 50uS in a loop. Very good!
The red line is the same for A1. It runs along the origin and has values of 0 or 1. Still very good

The green line is A0 (the pin with 50Hz) when I measure A0 pause 50uS A1 pause 50uS in a loop
The purple line is A1 measured at the same time. Something is wrong! Purple is grounded Green is 50Hz. I’ve checked many times.

I have checked my software and the readings really are apparently the wrong way round and how about the noise - where did that come from? Increasing the delay in the loop to 250uS makes no difference except to the number of cycles sampled…

Bear in mind there is absolutely no change in the hardware between the good and bad results, purely interleving ADC readings from pins A0 and A1.
The software for the interleved case is below. For the non-interleved just use A0 or A1 for both measurements.

/* 
 * File:   PCtestADC
 * Author: Pete
 * This waits for a ready signal and sends a string to the client
 * Created on 08 February 2014, 11:36
 * Uses Serial
 * 
 */

/* Includes ------------------------------------------------------------------*/

#include "application.h"


/* Function prototypes -------------------------------------------------------*/

/* Variables -----------------------------------------------------------------*/
int LED = D7;
int ADC = A0;

//
//char UDPincomingData[256];
//char UDPoutgoingData[256];
unsigned long lastRead;
unsigned long t1;
unsigned long t2;
unsigned long t3;
unsigned long t4;
int lag;
int netTime;
int i;
int j = 0;
unsigned int myADC0[256];
unsigned int myADC1[256];

/* This function is called once at start up ----------------------------------*/
void setup() {
    pinMode(D7, OUTPUT); // Turn on the D7 led so we know it's time
    pinMode(A0, INPUT);
    digitalWrite(D7, HIGH); // to open the Serial Terminal.
    Serial.begin(9600); // Open serial over USB.
    while (!Serial.available()); // Wait here until the user presses ENTER in the Serial Terminal

    digitalWrite(D7, LOW);
    Serial.println("Ready");
    do{
        myADC0[0] = analogRead(A0);
        
    }
    while ((myADC0[0]<2036) && (myADC0[0]>2060));
    lastRead = millis();
    t1 = millis();
    t2 = millis();
    digitalWrite(D7, HIGH);
    for (int i = 0; i < 256; i++) {
        myADC0[i] = analogRead(A0);
        //digitalWrite(D7, !digitalRead(D7));//delay  
        delayMicroseconds(200);
        myADC1[i] = analogRead(A1);
        delayMicroseconds(200);
    }
    t3 = millis();
    delay(500);
    digitalWrite(D7, HIGH);
        Serial.println(((t3-t2)-(t2-t1)));
        Serial.println(((t3-t2)-(t2-t1))/256.0);
    for (int i = 0; i < 256; i++) {
        Serial.println(myADC0[i]);
        delay(10);
    }
    Serial.println("----------------------------------");    
    for (int i = 0; i < 256; i++) {
        Serial.println(myADC1[i]);
        delay(10);
    }
}

/* This function loops forever --------------------------------------------*/
void loop() {
}

I’m afraid that’s me done for a while. I have a plane to catch.

OK Pin A1 wasn’t INPUT - that doesn’t change the results. RUNNN.

Hi @phec and @BDub plus @zachary and @satishgn

Maybe you guys can double check me here because right now, I do think there is something wrong with the ADC in the most recent webIDE. I wrote the following short program:

unsigned int adc0;
unsigned int adc1;
bool sw;

void setup() {
    pinMode(A0,INPUT);
    pinMode(A1,INPUT);
    pinMode(A7,INPUT);
    Serial.begin(9600);
}

void loop() {
    sw = digitalRead(A7);
    adc0 = analogRead(A0);
    if (sw) {
        adc1 = analogRead(A1);
    } else {
        adc1 = 0;
    }
    Serial.print(adc0);
    Serial.print(" ");
    Serial.println(adc1);
    delay(100);
}

So analog read pin A0 and analog read pin A1 only when the A7 is high. I connected some 5% resistors all around 1K ohm like this:

*3V3 resistor to A1
A1 resistor to A0
A0 resistor to GND

I measured 3v3 at 3.304 V, A0 at 0.960 V and A1 at 2.352 V, so the correct values for A0 and A1 would be 1190 and 2915 out of 4095.

When I run this with A7 connected to GND, I get:

1190 0
1190 0
1190 0
...

But when I run with A7 connected to 3V3, I get:

2916 2193
2684 2339
2684 2339
2917 2020
2917 2021
2917 2021
2917 2193
2917 2080
2684 2193

So it looks like there is some mixing of ADC channels and things are bouncing around. Correct output would have been 1190 2915 line after line.

I tried adding 0.01 uF caps from each input to GND but that didn’t change anything. I also tried some larger caps 220uF to the pins with no change.

Any thoughts?

3 Likes

One more thing. If I change the loop delays to be very long (0.5s) like this:

void loop() {
    sw = digitalRead(A7);
    adc0 = analogRead(A0);
    if (sw) {
        delay(500);
        adc1 = analogRead(A1);
    } else {
        adc1 = 0;
    }
    Serial.print(adc0);
    Serial.print(" ");
    Serial.println(adc1);
    delay(500);
}

I get different output. Remember that the correct output would be 1190 2915 on line after line, but I get:

2918 1191
2918 1190
2918 1190
2918 1217
2918 1190
2685 1191
2918 1217

So the data values are closer to the correct values, but A0 and A1 channels are swapped and there are some outliers. If I connect A7 to GND, I still get 1190 0 line after line.

Thanks for the super clear test case @bko. Definitely seems wrong. I’ll take a look over the weekend. @satishgn if you have time to replicate and debug over the weekend please do; otherwise next week is fine.

1 Like

@bko, there seems to be a problem when trying to read multiple analog channels. Working on a fix for this now. Thanks for letting us know about the bug.

1 Like

@bko, can you try again with the latest master fix : https://github.com/spark/core-firmware/commit/178f63bbac45807c416a09443116c9748b3af5fd

1 Like

The git-hub gods are moving very slowly for me tonight–I don’t think I will get to this for some time. Perhaps @braden_s who had the original problem could build and try it out.

I did look at the fix and looks like a timing issue-setting up DMA during init versus per call.

Hi @satishgn

The git-hub gods were moving very slowly for me tonight–it took a while to download a new master.

Bottom-line on top: Your fix addresses the channel mixing (I think) but not the wrong values with more than one channel. Somehow a zero value is being averaged in with the ADC value when more than one channel is in use.

I measured the voltages again tonight and they are 3.304 V, 0.961 V, and 2.354 V, so the perfect answer is 1191 2918.

After the fix, I get:

1131 2773
1131 2773

with the occasional

1130 2773

and

1131 2774

But if I change the switch input pin, I get:

1190 0

or

1191 0

so I can measure one channel to +/- one LSB, but the when I measure two channels I am reading low on both by about 1.5% and 3.5%. That sounds pretty good until you realize the single channel answer is within 0.02%! Plus or minus one LSB is great, but -144 LSBs at 2.354 V is not so good.

Could the two channels still be mixing over time? That would explain the low value for the 2.354 V input, but not the low reading at the 0.961 V input. If the lower value was mixing in data from the other channel, it would raise it since the other channel is always higher.

I think the wrong values are explained by having an off by one error or zero value in the averaging code somewhere. It looks like averaging filter has length 20 and if I work out 19 good values and 1 zero value for each, I get numbers close to the results I am seeing.

19*1191/20 = 1131
19*2918/20 = 2772

I don’t see the off by one or extra zero value in the code, but I am pretty sure a stray zero value is ending up in the averaging filter!

2 Likes

I’m actually having a separate issue now of receiving no UDP data so I’ll have to work that out first and then see this fix.

Hi @satishgn

I have a work-around that removes the max and min from the average, but this is just band-aiding (plastering) over the problem. It would be best to find out why there is an extra zero in the average–the answer is almost certainly a bug!

Here’s my code–I could not get the github gods to agree to a pull request tonight. I merged the loops and removed the min and max from the sum and then average 18 samples. This produces correct results for me.

	uint16_t ADC2Value;
	uint16_t ADC1Value;
	uint32_t ADC_SummatedValue = 0;
	uint16_t ADC_AveragedValue = 0;
	uint16_t ADC_MaxValue = 0;
	uint16_t ADC_MinValue = 4095;

	for(uint16_t i = 0 ; i < ADC_DMA_BUFFERSIZE ; i++)
	{
	  ADC2Value = ADC_DualConvertedValues[i] >> 16;
	  ADC1Value = ADC_DualConvertedValues[i] & 0xFFFF;
	  if (ADC2Value > ADC_MaxValue) { ADC_MaxValue = ADC2Value; }
	  if (ADC1Value > ADC_MaxValue) { ADC_MaxValue = ADC1Value; }	  
	  if (ADC2Value < ADC_MinValue) { ADC_MinValue = ADC2Value; }
	  if (ADC1Value < ADC_MinValue) { ADC_MinValue = ADC1Value; }	  
	  ADC_SummatedValue += ADC2Value;
	  ADC_SummatedValue += ADC1Value;
	}

	// remove min and max values
	ADC_SummatedValue -= ADC_MaxValue; 
	ADC_SummatedValue -= ADC_MinValue;
	// average rest of values
	ADC_AveragedValue = (uint16_t)(ADC_SummatedValue / ((ADC_DMA_BUFFERSIZE * 2)-2));

	// Return ADC averaged value
	return ADC_AveragedValue;

2 Likes

@bko, Provided a fix for the extra zero in the converted data by:
1)Changing the DMA mode from “circular” to "normal"
2)Enabling ADC2 external trigger conversion for each analogRead
3)Reset the buffer and DMA counter before starting the ADC-DMA command

Commt : https://github.com/spark/core-firmware/commit/b7ce24a4fb2dfe4f90e597e3a0f568f9ae098cfe

2 Likes

Hi @satishgn

This worked great on my test case and I like that you merged the loops to cut down the overhead.

Nice job and thanks!

2 Likes

Thanks @bkp for verifying the results.

For those interested in STM32’s Advanced ADC Modes, here is an application note from ST:

When do you think this will get pushed out to allow us to build using the WebIDE and not experience the cross-talking issues between analog channels? I know I can use the USB approach, but I’m curious what the timeline is for seeing it in the over-the-air version.

@mbeasley Usually it will be announced in the forum as well.

@satishgn I haven’t read the whole thing yet, but this jumped out at me:

The dual slow interleaved ADC mode is intended for the conversion of one channel. ADC1
and ADC2 convert the selected channel alternately with a period of 14 ADC clock cycles.
The channel is thus converted every 14 clock cycles. Each ADC converts the channel every
28 ADC clock cycles. The conversion can be started by external trigger or by software and
the conversion results of ADC1 and ADC2 are stored into ADC1’s data register (32-bit
format).
The maximum allowed sampling time is 14 ADC clock cycles to avoid any overlap with the
next conversion.
This means that the only allowed sampling times are 1.5, 7.5 and 13.5
cycles.

Yet we still have it set to 41.5.

EDIT: Sounds like the effective sampling rate using two ADCs at 13.5 is still about half as good impedance wise as one ADC set to 41.5. However the sampling rate is higher. I guess for a small percentage of users this would be beneficial. Most would want a rock solid reading.

1 Like

@BDub, good find. This was a big surprise to me as how the sampling time was bumped from the original 1.5 to the current 41.5. Think it happened during the community provided merge. I will update and test the code again to see how the change affects the whole process.
Thanks