AnsweredAssumed Answered

How to change the sampling of internal ADC

Question asked by Blair Smyth on Jan 13, 2020
Latest reply on Jan 15, 2020 by Petr_H

Hi everyone, 

 

I'm a little new to this processor so bare with me. I have used the MCUXpresso to generate an ADC example with interrupt for the LPCXpresso546/540xx, from there i added a PWM @500Hz and 50% duty cycle. I then save the ADC readings in an array and stop once the array is full. The ADC sampling rate however is too slow running at around 100 sps, from which i have verified by toggling an output at the interrupt service routine. I cant seem to figure out how and where to change the sampling rate of the ADC in this example. Its just a training exercise to get more familiar, but any help would be greatly appreciated. Code is below:

 

/*
* Copyright (c) 2016, Freescale Semiconductor, Inc.
* Copyright 2016-2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "fsl_adc.h"
#include "fsl_clock.h"
#include "fsl_power.h"
#include "fsl_ctimer.h"

#include "pin_mux.h"
#include <stdbool.h>
/*******************************************************************************
* Definitions
******************************************************************************/
#define DEMO_ADC_BASE ADC0
#define DEMO_ADC_SAMPLE_CHANNEL_NUMBER 2U
#define DEMO_ADC_IRQ_ID ADC0_SEQA_IRQn
#define DEMO_ADC_IRQ_HANDLER_FUNC ADC0_SEQA_IRQHandler
#define DEMO_ADC_CLOCK_DIVIDER 4U
static adc_result_info_t gAdcResultInfoStruct;
adc_result_info_t *volatile gAdcResultInfoPtr = &gAdcResultInfoStruct;
volatile bool gAdcConvSeqAIntFlag;
const uint32_t g_Adc_12bitFullRange = 4096U;

#define CTIMER CTIMER1 /* Timer 1 */
#define CTIMER_MAT_OUT kCTIMER_Match_1 /* Match output 1 */
#define CTIMER_CLK_FREQ CLOCK_GetFreq(kCLOCK_BusClk)

#define toggle_PORT 4U
#define toggle_PIN 4U

/*******************************************************************************
* Prototypes
******************************************************************************/
static void ADC_Configuration(void);

/*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t g_pwmPeriod = 0U;
volatile uint32_t g_pulsePeriod = 0U;
uint16_t array_Test[1500] = {0};
uint16_t array_Pointer = 0;

/*******************************************************************************
* Code
******************************************************************************/

status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
/* Calculate PWM period match value */
g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1;

/* Calculate pulse width match value */
if (dutyCyclePercent == 0)
{
g_pulsePeriod = g_pwmPeriod + 1;
}
else
{
g_pulsePeriod = (g_pwmPeriod * (100 - dutyCyclePercent)) / 100;
}
return kStatus_Success;
}

static void ADC_ClockPower_Configuration(void)
{
/* SYSCON power. */
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. */
POWER_DisablePD(kPDRUNCFG_PD_TS); /* Power on the temperature sensor. */

CLOCK_EnableClock(kCLOCK_Adc0); /* SYSCON->AHBCLKCTRL[0] |= SYSCON_AHBCLKCTRL_ADC0_MASK; */
}

/*!
* @brief Main function
*/
int main(void)
{

ctimer_config_t config;
uint32_t srcClock_Hz;
uint32_t timerClock;

/* Initialize board hardware. */
/* attach 12 MHz clock to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);

/* Use 12 MHz clock for some of the Ctimers */
CLOCK_AttachClk(kFRO12M_to_ASYNC_APB);

BOARD_InitPins();
BOARD_BootClockPLL220M();
BOARD_InitDebugConsole();

/* Enable the power and clock for ADC. */
ADC_ClockPower_Configuration();
PRINTF("ADC interrupt example.\r\n");

/* Toggle pin intially set high. */
GPIO_PinWrite(GPIO, toggle_PORT, toggle_PIN, 1);

#if !(defined(FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC) && FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC)
uint32_t frequency = 500U;
/* Calibration after power up. */
#if defined(FSL_FEATURE_ADC_HAS_CALIB_REG) && FSL_FEATURE_ADC_HAS_CALIB_REG
DEMO_ADC_BASE->CTRL |= ADC_CTRL_BYPASSCAL_MASK;
frequency = CLOCK_GetFreq(kCLOCK_BusClk);
if (true == ADC_DoOffsetCalibration(DEMO_ADC_BASE, frequency))
#else
#if defined(SYSCON_ADCCLKDIV_DIV_MASK)
frequency = CLOCK_GetFreq(DEMO_ADC_CLOCK_SOURCE) / CLOCK_GetClkDivider(kCLOCK_DivAdcClk);
#else
frequency = CLOCK_GetFreq(DEMO_ADC_CLOCK_SOURCE);
#endif /* SYSCON_ADCCLKDIV_DIV_MASK */
if (true == ADC_DoSelfCalibration(DEMO_ADC_BASE, frequency))
#endif /* FSL_FEATURE_ADC_HAS_CALIB_REG */
{
PRINTF("ADC Calibration Done.\r\n");
PRINTF("Frequency = %d\r\n", frequency);
}
else
{
PRINTF("ADC Calibration Failed.\r\n");
}
#endif /* FSL_FEATURE_ADC_HAS_NO_CALIB_FUNC */

/* Configure the ADC as basic polling mode. */
ADC_Configuration();

/* Enable the interrupt. */
/* Enable the interrupt the for sequence A done. */
ADC_EnableInterrupts(DEMO_ADC_BASE, kADC_ConvSeqAInterruptEnable);
NVIC_EnableIRQ(DEMO_ADC_IRQ_ID);

PRINTF("Configuration Done.\r\n");

#if defined(FSL_FEATURE_ADC_HAS_CTRL_RESOL) & FSL_FEATURE_ADC_HAS_CTRL_RESOL
PRINTF("ADC Full Range: %d\r\n", g_Adc_12bitFullRange);
#endif /* FSL_FEATURE_ADC_HAS_CTRL_RESOL */

/* CTimer0 counter uses the AHB clock, some CTimer1 modules use the Aysnc clock */
srcClock_Hz = CTIMER_CLK_FREQ;

PRINTF("CTimer example to generate a PWM signal\r\n");

CTIMER_GetDefaultConfig(&config);
timerClock = srcClock_Hz / (config.prescale + 1);

CTIMER_Init(CTIMER, &config);

/* Get the PWM period match value and pulse width match value of 500Hz PWM signal with 50% dutycycle */
CTIMER_GetPwmPeriodValue(500, 50, timerClock);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
CTIMER_StartTimer(CTIMER);


while (1)
{
//GETCHAR();
gAdcConvSeqAIntFlag = false;
ADC_DoSoftwareTriggerConvSeqA(DEMO_ADC_BASE);

while (!gAdcConvSeqAIntFlag)
{
}
PRINTF("gAdcResultInfoStruct.result = %d\r\n", gAdcResultInfoStruct.result);
PRINTF("gAdcResultInfoStruct.channelNumber = %d\r\n", gAdcResultInfoStruct.channelNumber);
PRINTF("gAdcResultInfoStruct.overrunFlag = %d\r\n", gAdcResultInfoStruct.overrunFlag ? 1U : 0U);
PRINTF("\r\n");

if(array_Pointer < 1500)
{
array_Test[array_Pointer] = gAdcResultInfoStruct.result;
array_Pointer++;
}
else
{
while(1);
}
}
}

/*
* ISR for ADC conversion sequence A done.
*/
void DEMO_ADC_IRQ_HANDLER_FUNC(void)
{
if (kADC_ConvSeqAInterruptFlag == (kADC_ConvSeqAInterruptFlag & ADC_GetStatusFlags(DEMO_ADC_BASE)))
{
ADC_GetChannelConversionResult(DEMO_ADC_BASE, DEMO_ADC_SAMPLE_CHANNEL_NUMBER, gAdcResultInfoPtr);
ADC_ClearStatusFlags(DEMO_ADC_BASE, kADC_ConvSeqAInterruptFlag);
gAdcConvSeqAIntFlag = true;
GPIO_PortToggle(GPIO, toggle_PORT, 1u << toggle_PIN);
}
/* 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
}

/*
* Configure the ADC as normal converter in polling mode.
*/
void ADC_Configuration(void)
{
adc_config_t adcConfigStruct;
adc_conv_seq_config_t adcConvSeqConfigStruct;

/* Configure the converter. */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE) & FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE
adcConfigStruct.clockMode = kADC_ClockSynchronousMode; /* Using sync clock source. */
#endif /* FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE */
adcConfigStruct.clockDividerNumber = DEMO_ADC_CLOCK_DIVIDER;
#if defined(FSL_FEATURE_ADC_HAS_CTRL_RESOL) & FSL_FEATURE_ADC_HAS_CTRL_RESOL
adcConfigStruct.resolution = kADC_Resolution12bit;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_RESOL */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL) & FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL
adcConfigStruct.enableBypassCalibration = false;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_TSAMP) & FSL_FEATURE_ADC_HAS_CTRL_TSAMP
adcConfigStruct.sampleTimeNumber = 0U;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_TSAMP */
#if defined(FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE) & FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE
adcConfigStruct.enableLowPowerMode = false;
#endif /* FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE */
#if defined(FSL_FEATURE_ADC_HAS_TRIM_REG) & FSL_FEATURE_ADC_HAS_TRIM_REG
adcConfigStruct.voltageRange = kADC_HighVoltageRange;
#endif /* FSL_FEATURE_ADC_HAS_TRIM_REG */
ADC_Init(DEMO_ADC_BASE, &adcConfigStruct);

#if !(defined(FSL_FEATURE_ADC_HAS_NO_INSEL) && FSL_FEATURE_ADC_HAS_NO_INSEL)
/* Use the temperature sensor input to channel 0. */
ADC_EnableTemperatureSensor(DEMO_ADC_BASE, true);
#endif /* FSL_FEATURE_ADC_HAS_NO_INSEL. */

/* Enable channel DEMO_ADC_SAMPLE_CHANNEL_NUMBER's conversion in Sequence A. */
adcConvSeqConfigStruct.channelMask =
(1U << DEMO_ADC_SAMPLE_CHANNEL_NUMBER); /* Includes channel DEMO_ADC_SAMPLE_CHANNEL_NUMBER. */
adcConvSeqConfigStruct.triggerMask = 0U;
adcConvSeqConfigStruct.triggerPolarity = kADC_TriggerPolarityPositiveEdge;
adcConvSeqConfigStruct.enableSingleStep = false;
adcConvSeqConfigStruct.enableSyncBypass = false;
adcConvSeqConfigStruct.interruptMode = kADC_InterruptForEachSequence;
ADC_SetConvSeqAConfig(DEMO_ADC_BASE, &adcConvSeqConfigStruct);
ADC_EnableConvSeqA(DEMO_ADC_BASE, true); /* Enable the conversion sequence A. */
/* Clear the result register. */
ADC_DoSoftwareTriggerConvSeqA(DEMO_ADC_BASE);
while (!ADC_GetChannelConversionResult(DEMO_ADC_BASE, DEMO_ADC_SAMPLE_CHANNEL_NUMBER, &gAdcResultInfoStruct))
{
}
ADC_GetConvSeqAGlobalConversionResult(DEMO_ADC_BASE, &gAdcResultInfoStruct);
}

Outcomes