This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADC multiple Channel Sampling - Erroneous readings

Other Parts Discussed in Thread: TM4C123GH6PM

Hey there.

Firstly just some info on what I am using:

IDE: Keil uVision5
Microcontroller - TM4C123GH6PM

Now onto the problem:

I am trying to get the hang of sampling from 4 ADC channels in a sequence, each of which is connected to the output voltage pin of its own infrared sensor. In order to check that everything works correctly, I use an if statement to turn on the LED connected to port F on the TIVA board if the third sample exceeds a certain trigger voltage. However, the LED turns on whenever I put my hand in front of any of the sensors, not just the one that is supposed to be sampled first.

A note before I give my source code. I am struggling to understand the ADCSequenceDataGet function in the TIVAWARE API. Should the address I put into the third argument of this function be pointing to an array if I want to gather multiple samples in a sequence? And if so, does the ADCSequenceDataGet function just take care of the rest by filling in the elements with the samples? Just looking at it in the adc.c file didn't really help me answer this question as I just saw a lot of stuff I didn't fully understand.

uint32_t resultBuffer[4] = {0};
uint32_t *resultBufferAdd;


int main(void)
{

		//assign pointer used by ADCSequenceDataGet function to resultBuffer array
	
		resultBufferAdd = &resultBuffer[0];
		
		//ENABLE GPIO AND ADC PERIPHERALS
		
		SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	 	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
		SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	
		//Configure port F as output for use of multicolour LED on Tiva Board
	
		GPIOPinTypeGPIOOutput(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
	
		//Configure ADC0 Sampling Sequencer 1 for 4 samples with channel Ain3 being the final channel to sample in the sequence
	
		ADCSequenceConfigure(0x40038000, 1, ADC_TRIGGER_PROCESSOR, 0);
		ADCSequenceStepConfigure(0x40038000, 1, 0, ADC_CTL_CH0);
		ADCSequenceStepConfigure(0x40038000, 1, 1, ADC_CTL_CH1);
		ADCSequenceStepConfigure(0x40038000, 1, 2, ADC_CTL_CH2);
		ADCSequenceStepConfigure(0x40038000, 1, 3, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH3);
		ADCSequenceEnable(0x40038000, 1);
	
		while(1)
		{
				ADCProcessorTrigger(0x40038000, 1); //Begin sampling
				while(!ADCIntStatus(0x40038000, 1, false)){} //wait for sample sequence to finish
				ADCSequenceDataGet(0x40038000, 1, resultBufferAdd); //Get samples and place in resultBuffer array
				if(resultBuffer[2]>1000){GPIO_PORTF_DATA_R=0x04;} //if the 3rd element is over 1000, turn LED on
				else{GPIO_PORTF_DATA_R=0x00;}//if the third element is not over 1000, turn LED off
		}


}

Thank you for taking the time to read.

  • Hi Joshua,

    First, did you know you can use ADC0_BASE instead of writing the base address number? Also for the GPIO you can use GPIO_PORTF_BASE.

    The ADCSequenceDataGet() takes care of loading into the array all the programmed samples. In your case is 4 samples so in resultBuffer[0] holds the first value, resultBuffer[1] the second, and so on.
    So when you are doing resultBuffer[2] you are indeed just checking the 3rd sample.

    Also, you are missing the config for the ADC pins. I don't quite know how to explain the behavior you are experiencing but you have to configure the GPIO as ADC inputs.

    Second, why are you using direct register access to control the LEDs? The GPIO data is not that straight forward and I don't know if the the macro GPIO_PORTF_DATA_R is using the correct address to control all at the same time or the base address for controlling them (it's different).

    Edit:

    How far apart are the sensors? You never know exactly the value they have!
    I advise you to try to monitor the value with the Code Composer Tools. Really handy! A need to learn early.
    I don't quite know if it's the best document to read up that.

    But try this.
    Debug
    Don't start the code
    Set a breakpoint after ADCSequenceDataGet();
    Go to "View"->"Expressions". It should open a window.
    There add "resultBuffer".
    Now it should be possible to see the values of resultBuffer when the breakpoint is reached. Just start the code now.

    Try this to see what is your problem 

  • It's usually faster, easier and more productive to employ a simple voltage divider - and feed that voltage into your ADC. (Your sensor will likely exhibit wide variations due to ambient, temperature & supply voltage - that's non-ideal for ADC checkout)

    A series connection of the same resistor value yields nice, predictable, equally stepped signal levels - which speed & ease your ADC analysis.

    KISS - most always - proves fastest - easiest - simply best...

  • Thank you for your reply Luis. I really appreciate the time you are taking to help me out.

    I was aware of using the base defines but I don't know which file they are stored in, so i haven't yet used them.

    I added in the config function and I now use the TivaWare API to control the LEDs. I prefer to use direct register access because that is how I was first introduced to the Tiva board and I find it easier to simply write "GPIO_PORTF_DATA_R=0x04;" rather than fill in all the arguments for the GPIOPinWrite function. Nevertheless, I removed this element in order to eliminate it as a possible cause for this weird behaviour.

    Here is my new source code:

    uint32_t resultBuffer[4] = {0};
    uint32_t *resultBufferAdd;
    
    
    int main(void)
    {
    
    		//assign pointer used by ADCSequenceDataGet function to resultBuffer array
    	
    		resultBufferAdd = &resultBuffer[0];
    		
    		//ENABLE GPIO AND ADC PERIPHERALS
    		
    		SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	 	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    		SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	
    		//Configure port F as output for use of multicolour LED on Tiva Board
    	
    		GPIOPinTypeGPIOOutput(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    
    		//Configure PE1-3 as ADC inputs
    	
    		GPIOPinTypeADC(0x40024000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    	
    		//Configure ADC0 Sampling Sequencer 1 for 4 samples with channel Ain3 being the final channel to sample in the sequence
    	
    		ADCSequenceConfigure(0x40038000, 1, ADC_TRIGGER_PROCESSOR, 0);
    		ADCSequenceStepConfigure(0x40038000, 1, 0, ADC_CTL_CH0);
    		ADCSequenceStepConfigure(0x40038000, 1, 1, ADC_CTL_CH1);
    		ADCSequenceStepConfigure(0x40038000, 1, 2, ADC_CTL_CH2);
    		ADCSequenceStepConfigure(0x40038000, 1, 3, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH3);
    		ADCSequenceEnable(0x40038000, 1);
    	
    		while(1)
    		{
    				ADCProcessorTrigger(0x40038000, 1); //Begin sampling
    				while(!ADCIntStatus(0x40038000, 1, false)){} //wait for sample sequence to finish
    				ADCSequenceDataGet(0x40038000, 1, resultBufferAdd); //Get samples and place in resultBuffer array
    				if(resultBuffer[2]>1000){GPIOPinWrite(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, 0x4);} //if the 3rd element is over 1000, turn LED on
    				else{GPIOPinWrite(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, 0x0);}//if the third element is not over 1000, turn LED off
    		}
    
    
    }

    When I used your method of monitoring the sample values while stepping through the program, it actually worked as expected. When I put my finger in front of an individual sensor, its corresponding sample would go up, while the adjacent sensor outputs would also increase slightly, as expected as they are very close together. However, when I just run the code and watch the samples, they are all over the place, no matter where I put my hand, they all fluctuate wildly. 

  • Ok I will try using just potentiometers.
  • The values are likely to fluctuate a lot since the voltage output is not exactly stable.
    Note that you are reading at the speed of the code so the smallest change can be detected

    What's the circuit for the sensors?

    PS: When trying to learn how to use the ADC the best approach is the one suggested by cb1. It creates known, very stable values
  • I have attached potentiometers to two of the analogue inputs and connected the other two to ground and I am seeing similar results. If I turn one of the potentiometers, it causes large fluctuations in all of the analogue input readings. This is when I let the code run. When I just step through the code in debug and monitor the samples, it behaves again as expected. I vary the potentiometers and the individual samples increase as one would expect, with no noticeable change in the other readings.
  • Remember to configure the input pins too.

    For you that started with registers (taken from 13.4.1 Module Initialization)
    3. Set the GPIO AFSEL bits for the ADC input pins (see page 671). To determine which GPIOs to
    configure, see Table 23-4 on page 1345.
    4. Configure the AINx signals to be analog inputs by clearing the corresponding DEN bit in the
    GPIO Digital Enable (GPIODEN) register (see page 682).

    And the API:
    void
    GPIOPinTypeADC(uint32_t ui32Port,
    uint8_t ui8Pins)



    Btw the base addresses are in here:
    hw_memmap.h
  • Are you saying I should set those bits using direct register access?
  • It's good to see in the datasheet the initialization. Although there's no need to use direct register access, it's good to understand the initializations described in the datasheet.
    I was referring what's in the datasheet for the ADC in hope you would understand better like that.

    It's best to use GPIOPinTypeADC for configuring the pins for the ADC function.
  • I did actually originally try to do it all using just direct register access, so I am quite well acquainted with this part of the datasheet. I also found a mistake in my code, where I did not initialise the pin corresponding to channel 0 with the  GPIOPinTypeADC function. I also thought I should specifcally make it an input using the GPIOPinTypeInput function. The new source code now has this corrected, but the same problem still remains.

    uint32_t resultBuffer[4] = {0};
    uint32_t *resultBufferAdd;
    
    
    int main(void)
    {
    
    		//assign pointer used by ADCSequenceDataGet function to resultBuffer array
    	
    		resultBufferAdd = &resultBuffer[0];
    		
    		//ENABLE GPIO AND ADC PERIPHERALS
    		
    		SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    	 	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
    		SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
    	
    		//Configure port F as output for use of multicolour LED on Tiva Board
    		
    		GPIOPinTypeGPIOOutput(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    		GPIOPinTypeGPIOInput(0x40024000, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    		//Configure PE1-3 as ADC inputs
    	
    		GPIOPinTypeADC(0x40024000, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3);
    	
    		//Configure ADC0 Sampling Sequencer 1 for 4 samples with channel Ain3 being the final channel to sample in the sequence
    	
    		ADCSequenceConfigure(0x40038000, 1, ADC_TRIGGER_PROCESSOR, 0);
    		ADCSequenceStepConfigure(0x40038000, 1, 0, ADC_CTL_CH0);
    		ADCSequenceStepConfigure(0x40038000, 1, 1, ADC_CTL_CH1);
    		ADCSequenceStepConfigure(0x40038000, 1, 2, ADC_CTL_CH2);
    		ADCSequenceStepConfigure(0x40038000, 1, 3, ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH3);
    		ADCSequenceEnable(0x40038000, 1);
    	
    		while(1)
    		{
    				ADCProcessorTrigger(0x40038000, 1); //Begin sampling
    				while(!ADCIntStatus(0x40038000, 1, false)){} //wait for sample sequence to finish
    				ADCSequenceDataGet(0x40038000, 1, resultBufferAdd); //Get samples and place in resultBuffer array
    				if(resultBuffer[0]>1000){GPIOPinWrite(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, 0x4);} //if the 3rd element is over 1000, turn LED on
    				else{GPIOPinWrite(0x40025000, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3, 0x0);}//if the third element is not over 1000, turn LED off
    		}
    
    
    }

  • Ah. I forgot to put in the breakpoint while trying your method of debugging, instead what i was doing was just stepping through line by line. Now that I am doing this correctly, it is behaving differently.

    Having turned the potentiometer corresponding to sample 1, and then repeatedly clicking run, sample 0 repeatedly alternates between around 0 and around a much larger number which breaks the trigger voltage.
  • Is channel 0 (sample 0 in your case) left floating? Any pin left floating is basically a headache. It can be easily influenced even from you moving closer or further away from the board.

    You can't leave any pin floating. Either connect a voltage divider to each not using a pot, or use a pot for all of them. Or just connect to GND then ones not connected to the pot
  • No, it was connected to a separate potentiometer. :(
  • "Hobby style pots" are not famed for reliability, stability or precision. And - you must often measure the generated voltage.

    Past suggestion - a fixed, series string of 2K2 - 6K8 resistors provides known voltages - saving you countless time/effort.

    We've created such "test fixtures" use them repeatedly - idea is now "copied" by our 9B (USD) client...
  • Agreed on the resistors in an effort to remove pot noise from inexpensive pots.

    Two other possibilities (depending on the signal conditioning you already have in place)
    - Batteries
    - There are DMM Voltage calibrators available that are fairly inexpensive and produce accurate and precise voltages that are very good for checking A/Ds

    Both of these are likely at least as stable as your reference. Remember if you are using a micro w/o a reference input it is likely using the power rail for a reference. The power rail makes a lousy absolute reference. If that's the case you definitely want to use a resistor divider for testing with the divider referenced to the power inputs used by the micro.

    I am wondering though whether the source stability might be a bit of a red herring. Is the signal source impedance sufficiently low and does you input have a large enough capacitor to feed the sampling circuit? You could be getting cross talk.

    Robert