Accurcy ADC

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Accurcy ADC

1,635 Views
Nadia
Contributor III

Good afternoon, I am using a MCU LPC54616, I am using ADC ports, and it measures well, but only detects up to 50mV, if I put a signal that is below it does not measure it, I don't know if anyone knows how to extend the range by software.

Thank you very much.

0 Kudos
14 Replies

1,608 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Nadia,

First of all, I would like to confirm whether your code is correct or not. Now, if you connect a 3.3V voltage to the analog channel, you can get about 0xFFF ADC sample, if you connect GND, you will get about zero. If it is case, the code is correct.

Regarding the 50mV input voltage, if the ADC reference voltage VREFP pin is 3.3V, the ADC result is 4095*0.050V/3.3V=62. If you input 50mV, what is the ADC sample value you get?

If you use the ADC example in SDK package and use ADC channel0, you have to change the code:

/* Use the temperature sensor input to channel 0. */
ADC_EnableTemperatureSensor(DEMO_ADC_BASE, false);

BTW, you have to add the pin assignment so that the pin PIO0_10 becomes the analog channel0.

CLOCK_EnableClock(kCLOCK_Iocon);

IOCON->PIO[0][10] = ((IOCON->PIO[0][10] &
/* Mask bits to zero which are setting */
(~(IOCON_PIO_FUNC_MASK | IOCON_PIO_MODE_MASK | IOCON_PIO_DIGIMODE_MASK)))

/* Selects pin function.
* : PORT010  is configured as ADC0_0. */
| IOCON_PIO_FUNC(PIO010_FUNC_ALT0)

/* Selects function mode (on-chip pull-up/pull-down resistor control).
* : Inactive.
* Inactive (no pull-down/pull-up resistor enabled). */
| IOCON_PIO_MODE(PIO010_MODE_INACTIVE)

/* Select Analog/Digital mode.
* : Analog mode. */
| IOCON_PIO_DIGIMODE(PIO010_DIGIMODE_ANALOG));

Hope it can help you

BR

XiangJun Rong

 

0 Kudos

1,630 Views
frank_m
Senior Contributor III

Check the datasheet (LPC546xx.pdf), section 12.2, 12-bit ADC characteristics.

ADC value is always related to the used reference voltage.

The analog input source must not exceed a maximal output impedance, else the conversion will not work properly, i.e. the values are not correct.

0 Kudos

1,628 Views
Nadia
Contributor III

@frank_m I measure from 3.3V to 50mV.
Below 50 mV it measures zero.

0 Kudos

1,625 Views
frank_m
Senior Contributor III

I think you should try a SDK example project for the ADC. Worked well for my lpcxpresso54628 board.

Or post the relevant code here.

> I measure from 3.3V to 50mV.
> Below 50 mV it measures zero.

This is not very clear. What ADC count do you get for 50mV... 3.3V ?

How stable is the value ?

What is the input source, and it's impedance ?

How is the input source connected, e.g. schematics of buffer amplifiers at the ADC input.

0 Kudos

1,560 Views
Nadia
Contributor III

Good everyone, I would like to know how many analogue input ports I can have running at the same time?

I use two and the readings are very good, but when I add a third port the readings vary and do not take the correct values.

0 Kudos

1,494 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Nadia,

 

You said that "when I add a third port the readings vary and do not take the correct values", can you list your reading for example, the actual voltage you measured with voltage meter, the digital samples, the converted voltage with Vrefp*sample/4096. What is the Vrefp pin voltage?

BR

XiangJun Rong

0 Kudos

1,549 Views
frank_m
Senior Contributor III

As many as the ADC has channels. The channels are multiplexed, and use the same S&H element and converter.

>... but when I add a third port the readings vary and do not take the correct values.

Which readings vary - all, or just the third channel ?

Use a proper low-impedance source to validate the inputs first, like a resistor network.

0 Kudos

1,547 Views
Nadia
Contributor III

Thank you for your response,
I have discovered that channels 5 and 6 can coexist together. And channels 3 and 4 as well. However, if I mix 5 and 6, by that I mean activate them and remove the others, the measurements fail, and I don't know why.

0 Kudos

1,534 Views
frank_m
Senior Contributor III

Are you sure that no interferences on board level exist ?

I have an application on the LPC54628 using 3 ADC channels, and they work fine.

And keep in mind that the S&H element is basically a RC network, and incorrect configuration can cause cross influences as well. The ADC uses the same S&H element with multiplexed inputs. If your attached input circuitry cannot charge/uncharge the S&H capacitor through the input impedance in the configured sampling time, the value will be distorted by the previous channel.

1,532 Views
Nadia
Contributor III

Hello again @frank_m @xiangjun_rong ,
I think it is not a problem of impedances, but of ports, because the signals I send are sent through different ports of the operating system, and unless I send the signal through channel 3 it does not read it well.

I'm attaching the code and the configurations in case you can find any faults.

I only attach one module of the program, there is a call to start the ADC conversion, but it is in another module.

static void _TskConvADC(void)__attribute__((noreturn));
static void _TskConvADC(void)
{
adc_result_info_t ValTen;
ADC_savedat_t Save;
while(true)
{
ulTaskNotifyTake(pdFALSE, portMAX_DELAY);
/* Wait for the converter to be done. */
// ADC_GetChannelConversionResult(ADC0, ADC_DIS, &ValTen);
ADC_GetChannelConversionResult(ADC0, ADCNS, &ValTen);
Save.ADC_NS = ValTen.result;
ADC_GetChannelConversionResult(ADC0, ADCE0, &ValTen);
Save.ADC_EO = ValTen.result;
ADC_GetChannelConversionResult(ADC0, ADC_DIS, &ValTen);
Save.ADC_Dis = ValTen.result;
ADC_GetChannelConversionResult(ADC0, ADCRSSI, &ValTen);
Save.ADC_RSSI = ValTen.result;


//Mandamos los datos al módulo Mod_Operaciones
if(_repDatTensiones != NULL)
_repDatTensiones(&Save,sizeof(Save));
}

} //void _TskConvADC(void)
/******************************************************//**
** Métodos públicos
*********************************************************/

/*! \fn void ADCinit(void)
* \brief Inicializa las conversiones AD.
* \return Nada.
*/
void ADCinit(void)
{


BaseType_t xReturned;
// uint32_t frequency = 0U;
// ** Inicializa variables **
_repDatTensiones = NULL;

// ** Inicializa el driver **
POWER_DisablePD(kPDRUNCFG_PD_VDDA); /* Power on VDDA. */
POWER_DisablePD(kPDRUNCFG_PD_ADC0); /* Power on the ADC converter. */
POWER_DisablePD(kPDRUNCFG_PD_VD2_ANA); /* Power on the analog power supply. */
POWER_DisablePD(kPDRUNCFG_PD_VREFP); /* Power on the reference voltage source. */
ADC_DoSelfCalibration(ADC0);

if(!ModoConfig())
{

xReturned = xTaskCreate((TaskFunction_t)_TskConvADC,"TskConvADC",4 * configMINIMAL_STACK_SIZE, NULL, APP_TSK_HIGH_PRIORITY, &_tskHandleADC);
configASSERT(xReturned);

} //if(!ModoConfig())

} // void ADCinit(void)


/*! \fn void AdcStartConv(void)
* \brief Fuerza la conversión(llamada por un timer).
* \return Nada.
*/
void AdcStartConv(void)
{
// ** Fuerza el arranque de la conversión de la secuencia A **
// ADC_DoSoftwareTriggerConvSeqA(ADC0);
ADC_DoSoftwareTriggerConvSeqA(ADC0);

} // void AdcStartConv(void)

 

/*! \fn void RepDatADC (GenRxCallBack_t callBack)
* \brief Establece la función de call-back para reporte de datos.
* \param[in] callBack Función que establecer.
* \return Nada.
*/
void RepDatADC (GenRxCallBack_t callBack)
{

_repDatTensiones = callBack;

} //void RepDatADC (GenRxCallBack_t callBack)

 


/* ADC0_SEQA_IRQn interrupt handler */

//void ADC0_ADC_SEQ_A_IRQHANDLER(void)
void ADC0_ADC_SEQ_A_IRQHANDLER(void)
{
BaseType_t tskWoken;

if (kADC_ConvSeqAInterruptFlag == (kADC_ConvSeqAInterruptFlag & ADC_GetStatusFlags(ADC0)))
{

ADC_ClearStatusFlags(ADC0, kADC_ConvSeqAInterruptFlag);
vTaskNotifyGiveFromISR(_tskHandleADC, &tskWoken);
portYIELD_FROM_ISR(tskWoken);

} // if (kADC_ConvSeqAInterruptFlag == (kADC_ConvSeqAInterruptFlag & ADC_GetStatusFlags(ADC0)))

/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F
Store immediate overlapping exception return operation might vector to incorrect interrupt. */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
__DSB();
#endif

} // ADC0_ADC_SEQ_A_IRQHANDLER()

 

 

 

 

0 Kudos

1,520 Views
frank_m
Senior Contributor III

Using an OS makes things more obfuscated and difficult ...

My ADC code works differently. First, I set up a sequence with all the channels I want (3 in this case). They need to be handled properly in pinmux.c (or where else you do it).

Second, I use a timer to trigger the ADC, I need a consistent sampling time (audio sampling). Like your code, I configure a sequenceA interrupt. In the interrupt handler, I get the results directly from the conversion result register, I don't use ADC_GetChannelConversionResult. This should not make a difference, though. And I move the results to the final data buffer from the interrupt handler. 

As said, the RT-OS code confuses things - at least for me ;-).

I would suggest to use a debugger, and verify in the interrupt handler that the ADC conversion results are consistent with the signals attached to the pins. Validating over complex and sequental code steps gives you very little hints where things go wrong.

0 Kudos

1,509 Views
Nadia
Contributor III

@frank_m  ok, And what function do you use to read the ports?

0 Kudos

1,501 Views
frank_m
Senior Contributor III

I assume with "ports" you mean the ADC inputs/channels.

I get the conversion result directly from the result registers. In my case, for the three channels:

    uint32_t  data4, data5, data6;

    data4 = ADC0->DAT[4];
    ...
    data5 = ADC0->DAT[5];
    ...
    data6 = ADC0->DAT[6];

    ...

Very similar to the code in ADC_GetChannelConversionResult().

The exact format of the data is specified in UM10912, section 44, at least for my LPC54628.

Since a sequence means the results of all configured channels, I prefer to read them all at once, without longer interruptions. As said, I need sample coherence for my application. I store the results away in my interrupt handler, count the samples, and stop sampling when the planned count is reached. This application is a kind of single-shot event recorder.

Reading hardware registers from a preemptable user task (as in your case) might yield unpredictable results, an incoherent timing.

However, not every use case requires coherence between ADC channels, or a coherent sample rate at all.

0 Kudos

1,487 Views
Nadia
Contributor III

@frank_m  ok, I solved it. I'm still using the same code, and I'm still using the operating system, what I changed was that in the configuration I selected the ADC clock, not the system clock, and I lowered the frequency, so I managed to increase the accuracy.

 

Thank you very much

0 Kudos