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.

Tiva C ADC - UART transmit wrong values

Other Parts Discussed in Thread: EK-TM4C129EXL

Hello,

I'm a beginner using the board Tiva C series EK-TM4C129EXL.

I need to use the ADC so in order to check my configuration, I use the UART to send the results.

I have 5 sensors and I need to sample their signal with a sampling frequency of 100kHz. In my code, I try to do it this way:

- Sample from pin PE0 with ADC0, sample sequencer SS2, first.

- Sample from pin PE0 with ADC0, sample sequencer SS2, second.

- Sample from pin PE0 with ADC0, sample sequencer SS2, last.

For the last 2 sensors, I need to synchronize twice of their samples (2 samples for each sensor) so:

- Sample from pin PE3 with ADC0, sample sequencer SS0, sync. with sample from pin PE5 with ADC1, sample sequencer SS0,

- Sample from pin PE3 with ADC0, sample sequencer SS0, a second time sync. with sample from pin PE5 with ADC1, sample sequencer SS0 a second time


For the moment, I haven't got any input (0V) so the UART should send 0. The timer frequency is set to 5 HZ for the UART.

Here is my code (Sorry for the french commentary, I can translate if it's necessary but I think the code is understandable like this)

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "driverlib/adc.h"
#include "drivers/pinout.h"

#include "Custom files/constantes.h"
#include "Custom files/maths.h"
#include "Custom files/fichier.h"

// System clock rate in Hz.
uint32_t g_ui32SysClock;

// The error routine that is called if the driver library encounters an error.
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif

//
// Définition des variables utiles
//

#define TMR0_FREQ 5 //Hz //100000 //Hz
uint8_t LED = 0;
uint32_t ADCbuffer[3], thetaSinBuffer[3], thetaCosBuffer[3];

// Configuration des ADC
void
ConfigureADC(void)
{
	UARTprintf("Configuration des ADC\n");

	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);

	ADCReferenceSet(ADC0_BASE, ADC_REF_INT);
	ADCReferenceSet(ADC1_BASE, ADC_REF_INT);

	GPIOPinTypeADC(GPIO_PORTE_BASE,GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_5);

	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0) && !SysCtlPeripheralReady(SYSCTL_PERIPH_ADC1) && !SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE))
	{}

	UARTprintf("ADC et ports I/O autorisés\n");


	////// Désactivation des ADC (sécurité)
	ADCSequenceDisable(ADC0_BASE,2);
	ADCSequenceDisable(ADC0_BASE,0);
	ADCSequenceDisable(ADC1_BASE,0);

	UARTprintf("Séquenceurs ADC désactivés\n");

	////// Configuration des ADC
	// Séquence courant -> température -> potentiomètre
	ADCSequenceConfigure(ADC0_BASE,2,ADC_TRIGGER_PROCESSOR,0); //ADC0 Sequence 2 déclenchement processeur priorité 0 (Haute)

	UARTprintf("Configuration 1 finie\n");

	//positionSin et positionCos sync
	ADCSequenceConfigure(ADC0_BASE,0,ADC_TRIGGER_PROCESSOR,0);
	ADCSequenceConfigure(ADC1_BASE,0,ADC_TRIGGER_PROCESSOR,0);

	UARTprintf("Configuration des ADC finie\n");

	////// Configuration des séquences
	// Séquence courant -> température -> potentiomètre
	ADCSequenceStepConfigure(ADC0_BASE,2,0,ADC_CTL_CH1); //Courant
	ADCSequenceStepConfigure(ADC0_BASE,2,1,ADC_CTL_CH2); //Température
	ADCSequenceStepConfigure(ADC0_BASE,2,2,ADC_CTL_CH3|ADC_CTL_IE|ADC_CTL_END); //Potentiomètre

	// positionSin et positionCos sync
	// séquence de 2 échantillons pour la dérivée
	ADCSequenceStepConfigure(ADC0_BASE,0,0,ADC_CTL_CH0);
	ADCSequenceStepConfigure(ADC0_BASE,0,1,ADC_CTL_CH0|ADC_CTL_IE|ADC_CTL_END);

	ADCSequenceStepConfigure(ADC1_BASE,0,0,ADC_CTL_CH8);
	ADCSequenceStepConfigure(ADC1_BASE,0,1,ADC_CTL_CH8|ADC_CTL_IE|ADC_CTL_END);

	UARTprintf("Configurations des séquences\n");

	////// Activations des ADC
	ADCSequenceEnable(ADC0_BASE,2);
	ADCSequenceEnable(ADC0_BASE,0);
	ADCSequenceEnable(ADC1_BASE,0);

	UARTprintf("Activation des ADC\n");

	// Reset des flags d'interuption des ADC
	ADCIntClear(ADC0_BASE,2);
	ADCIntClear(ADC0_BASE,0);
	ADCIntClear(ADC1_BASE,0);
}
//
// Fonction d'interuption du timer 0
//
void
Timer0IntHandler(void)
{
	// Reset le flag de l'interuption du timer
	TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

	////// ADC
	// initialise le flag de l'interuption de l'ADC
	ADCIntClear(ADC0_BASE,2);
	// Déclenche la conversion
	ADCProcessorTrigger(ADC0_BASE,2);
	while(!ADCIntStatus(ADC0_BASE,2,false)){} // attend la fin de la conversion

	// Reste le flag de l'interuption de l'ADC
	ADCIntClear(ADC0_BASE,2);

	// Lecture des données
	ADCSequenceDataGet(ADC0_BASE,2,ADCbuffer);

	UARTprintf("Sequence: %d %d %d\n",ADCbuffer[0],ADCbuffer[1],ADCbuffer[2]);


	// position, sync
	ADCIntClear(ADC0_BASE,0);
	ADCIntClear(ADC1_BASE,0);

	// déclenche les conversions synchronisées
	ADCProcessorTrigger(ADC1_BASE,(0|ADC_TRIGGER_WAIT)); // ADC-1 en attente de déclenchement
	ADCProcessorTrigger(ADC0_BASE,(0|ADC_TRIGGER_SIGNAL)); // ADC-0 en déclenchement global
	while(!ADCIntStatus(ADC0_BASE,0,false)){} // attend la fin de la conversion

	// Reset le flag de l'interuption de l'ADC
	ADCIntClear(ADC0_BASE,0);
	ADCIntClear(ADC1_BASE,0);

	// Lecture des données
	ADCSequenceDataGet(ADC0_BASE,0,thetaSinBuffer);
	ADCSequenceDataGet(ADC1_BASE,0,thetaCosBuffer);

	UARTprintf("Sync.: %d %d %d %d\n",thetaSinBuffer[0],thetaSinBuffer[1],thetaCosBuffer[0],thetaCosBuffer[1]);


	//test: la diode s'allume si le timer a le temps d'aller au bout
	HWREGBITW(&LED, 0) ^= 1; // (bit 0 du registre de LED) XOR 1
	GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, LED);
}

// Initialise le port série. Doit être appellée avant UARTprintf().
void
ConfigureUART(void)
{
	//
	// Enable the GPIO Peripheral used by the UART.
	//
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	//
	// Enable UART0.
	//
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

	//
	// Configure GPIO Pins for UART mode.
	//
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinTypeUART(GPIO_PORTA_BASE, CLP_D2_PIN | CLP_D1_PIN);

	//
	// Initialize the UART for console I/O.
	//
	UARTStdioConfig(0, 115200, g_ui32SysClock);
}


//////	Set up, loop forever
int
main(void)
{
	////// Set up

	// Règle l'horloge sur le cristal à 120MHz
	g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ |
			SYSCTL_OSC_MAIN |
			SYSCTL_USE_PLL |
			SYSCTL_CFG_VCO_480), 120000000);


	// Initialisation du port série
	ConfigureUART();

	UARTprintf("GO! \n");

	// Active le port des diodes
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION)){}
	GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0);
	GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, LED); // LED = 0


	// Active le périphérique Timer 0
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);

	// Enable processor interrupts
	IntMasterEnable();

	// Configure the 32-bit periodic timers.
	TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
	TimerLoadSet(TIMER0_BASE, TIMER_A, g_ui32SysClock/TMR0_FREQ);

	// Setup the interrupts for the timer timeouts.
	IntEnable(INT_TIMER0A);
	TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);

	UARTprintf("Set up timers fini\n");

	// Configure les ADC
	ConfigureADC();

	UARTprintf("Configuration ADC finie\n");

	// Enable the timers.
	TimerEnable(TIMER0_BASE, TIMER_A);

	UARTprintf("Set up fini\n");

	// Loop forever while the timers run.
	while(1){}
}

The UART send this:

GO!
Set up timers fini
Configuration des ADC
ADC et ports I/O autorisés
Séquenceurs ADC désactivés
Configuration 1 finie
Configuration des ADC finie
Configurations des séquences
Activation des ADC
Configuration ADC finie
Set up fini
Sequence: 1545 1811 1466
Sync.: 1173 1151 1637 1622
Sequence: 1350 1641 1436
Sync.: 989 986 1437 1410
Sequence: 1237 1525 1394
Sync.: 911 888 1320 1286
Sequence: 1141 1434 1340
Sync.: 843 849 1230 1221
Sequence: 1102 1367 1296
Sync.: 816 800 1192 1166
Sequence: 1073 1325 1265
Sync.: 781 773 1145 1141
Sequence: 1051 1294 1238
Sync.: 780 769 1143 1133
Sequence: 1049 1278 1239
Sync.: 785 763 1150 1131
Sequence: 1047 1277 1234
Sync.: 771 754 1131 1114
Sequence: 1041 1265 1226
Sync.: 771 759 1135 1127
Sequence: 1060 1272 1227
Sync.: 773 755 1140 1125
Sequence: 1037 1259 1224
Sync.: 766 760 1130 1109
Sequence: 1037 1265 1223
Sync.: 766 773 1126 1115
Sequence: 1038 1253 1219
Sync.: 765 768 1127 1112

etc...


But if there is no input, it should be: 

...
Sequence: 0 0 0

Sync.: 0 0 0 0
Sequence: 0 0 0
Sync.: 0 0 0 0

etc...


So, what am I doing wrong? 

Also, considering the frequency (100kHz) that I need to get the data andmake calculations, should I configure it as in the data sheet or the driverlib/adc is ok?

If you have any question, please ask me,

Thank you

  • But if there is no input, it should be: ...

    Sure ?

    Letting ADC inputs float is not the best idea. For a zero result, tie them to ground (perhaps with a resistor). Otherwise, you get EMI dependent conversion values.

  • Yes, it works. (I was searching for code mistakes)

    Thank you!

    Also, do you think the use of the driverlib increase the calculus time?
  • Not sure what you mean with "calculus time" - I don't see "serious" calculations in the presented code which seem to have an impact on runtime and performance. I guess you application is (currently) spending most of it's runtime in UARTprintf().
  • I haven't written it yet, because I wanted to try the ADC, but for each call of the timer interuption, I have to:
    - Save current and previous input (up to 4 previous inputs)
    - Calculate atan() twice
    - Make multiple calculations, essentially muliplications and additions

    The UARTprintf() will be revoved, it was just for the test. I'm afraid of the atan() runtime. I have my own algorithm for a better runtime but less resolution.

    I think i'm going to try this way and see
  • I don't have much experience with the driverlib code, so I don't feel confident to comment on performance.
    Measuring the runtime for different implementations/algorithms sounds good.
    But I agree - the atan() function will have the greatest impact on performance. The M4 FPU does not implement any trigonometric function in hardware, so using a (math) library code will pull in an emulation. A table-based interpolation, tailored to your precision needs, might save you a lot of cycles. You might even get away with integer based implementation here. Used this quite often on low-performing 8-bit controllers.
    Floating point multiplications and additions have FPU hardware support, and have a very small individual impact.