K10 DMA getting ADC value only once.

cancel
Showing results for 
Search instead for 
Did you mean: 

K10 DMA getting ADC value only once.

165 Views
m4l490n
Contributor V

I'm trying something relatively simple but it doesn't work right and I don't know why. I'm trying to create a little sample code with ADC0 measuring the internal chip temp using DMA.

I'm using the config tools for configuring the ADC and DMA. This is the ADC0 configuration in the generated peripherals.c:

 

adc16_channel_config_t adc0_channelsConfig[2] = {
  {
    .channelNumber = 27U,
    .enableDifferentialConversion = false,
    .enableInterruptOnConversionCompleted = true,
  },
  {
    .channelNumber = 26U,
    .enableDifferentialConversion = false,
    .enableInterruptOnConversionCompleted = true,
  }
};
const adc16_config_t adc0_config = {
  .referenceVoltageSource = kADC16_ReferenceVoltageSourceVref,
  .clockSource = kADC16_ClockSourceAlt0,
  .enableAsynchronousClock = true,
  .clockDivider = kADC16_ClockDivider8,
  .resolution = kADC16_ResolutionSE16Bit,
  .longSampleMode = kADC16_LongSampleCycle24,
  .enableHighSpeed = true,
  .enableLowPower = false,
  .enableContinuousConversion = false
};
const adc16_channel_mux_mode_t adc0_muxMode = kADC16_ChannelMuxA;
const adc16_hardware_average_mode_t adc0_hardwareAverageMode = kADC16_HardwareAverageCount32;

static void adc0_init(void) {
  /* Initialize ADC16 converter */
  ADC16_Init(ADC0_PERIPHERAL, &adc0_config);
  /* Make sure, that hardware trigger is used */
  ADC16_EnableHardwareTrigger(ADC0_PERIPHERAL, true);
  /* Configure hardware average mode */
  ADC16_SetHardwareAverage(ADC0_PERIPHERAL, adc0_hardwareAverageMode);
  /* Configure channel multiplexing mode */
  ADC16_SetChannelMuxMode(ADC0_PERIPHERAL, adc0_muxMode);
  /* Perform auto calibration */
  ADC16_DoAutoCalibration(ADC0_PERIPHERAL);
  /* Enable DMA requests from ADC0 peripheral */
  ADC16_EnableDMA(ADC0_PERIPHERAL, true);
  /* Enable interrupt ADC0_IRQn request in the NVIC. */
  EnableIRQ(ADC0_IRQN);
}

 

 

This is the DMA configuration in the generated peripherals.c:

 

const edma_config_t dma_config = {
  .enableContinuousLinkMode = false,
  .enableHaltOnError = false,
  .enableRoundRobinArbitration = false,
  .enableDebugMode = false
};

  /* Channel adc0_coco global variables */
edma_handle_t dma_adc0_coco_Handle;

static void dma_init(void) {

  /* Channel adc0_coco initialization */
  /* Set the source kDmaRequestMux0ADC0 request in the DMAMUX */
  DMAMUX_SetSource(DMA_DMAMUX_BASEADDR, DMA_ADC0_COCO_DMA_CHANNEL, DMA_ADC0_COCO_DMA_REQUEST);
  /* Enable the channel 0 in the DMAMUX */
  DMAMUX_EnableChannel(DMA_DMAMUX_BASEADDR, DMA_ADC0_COCO_DMA_CHANNEL);
  /* Create the eDMA dma_adc0_coco_Handle handle */
  EDMA_CreateHandle(&dma_adc0_coco_Handle, DMA_DMA_BASEADDR, DMA_ADC0_COCO_DMA_CHANNEL);
  EDMA_SetCallback(&dma_adc0_coco_Handle, adc0_val_dma_transfed_done, NULL);
}

 

 

And this is the LPTMR configuration in the generated peripherals.c:

 

const lptmr_config_t lptmr_config = {
  .timerMode = kLPTMR_TimerModeTimeCounter,
  .pinSelect = kLPTMR_PinSelectInput_0,
  .pinPolarity = kLPTMR_PinPolarityActiveHigh,
  .enableFreeRunning = false,
  .bypassPrescaler = true,
  .prescalerClockSource = kLPTMR_PrescalerClock_1,
  .value = kLPTMR_Prescale_Glitch_0
};

static void lptmr_init(void) {
  /* Initialize the LPTMR */
  LPTMR_Init(LPTMR_PERIPHERAL, &lptmr_config);
  /* Set LPTMR period */
  LPTMR_SetTimerPeriod(LPTMR_PERIPHERAL, LPTMR_TICKS);
}

 

 

Then I have the following in main.c:

 

#define ADCR_VDD               (65535U) /* Maximum value when use 16b resolution */
#define STANDARD_TEMP          (25U)
#define V_BG                   (1000U)  /* BANDGAP voltage in mV (from datasheet p13) */
#define V_TEMP25               (716U)   /* Typical VTEMP25 in mV (from the datasheet p45) */
#define M                      (1620U)  /* Typical slope: (mV x 1000)/oC (from the datasheet p45) */

volatile bool adc0_val_dma_transfer_rdy = false;

volatile static uint32_t adc0_val = 0;      /*! ADC value from DMA */
volatile static uint32_t adc0_val_int = 0;  /*! ADC value from ADC int */
volatile static bool adc0_val_rdy = false; /*! Conversion completed Flag */
volatile static uint32_t adcrTemp25 = 0;    /*! Calibrated ADCR_TEMP25 */
volatile static uint32_t adcr100m = 0;

/*!
 * @brief ADC0 Interrupt handler
 *
 * Get current ADC value and set adc0_conv_rdy flag.
 */
void ADC0_IRQHandler(void)
{
    /* Get current ADC value */
    adc0_val_int = ADC16_GetChannelConversionValue(ADC0_PERIPHERAL, ADC0_CH0_CONTROL_GROUP);
    /* Set conversionCompleted flag. This prevents an wrong conversion in main function */
    adc0_val_rdy = true;
}

/* eDMA callback function for the 0 channel.*/
void adc0_val_dma_transfed_done(edma_handle_t *handle, void *param, bool transferDone, uint32_t tcds)
{
    if (transferDone)
    {
        adc0_val_dma_transfer_rdy = true;
        EDMA_StartTransfer(&dma_adc0_coco_Handle);
    }
}

static int32_t adc_get_chip_temp(void)
{
    if (adc0_val_dma_transfer_rdy)
    {
        adc0_val_dma_transfer_rdy = false;
        adc0_val_rdy = false;
        return (int32_t)(STANDARD_TEMP - ((int32_t)adc0_val - (int32_t)adcrTemp25) * 100 / (int32_t)adcr100m);
    }

    return -1000;
}

int main(void)
{
    int32_t die_temp = 0;

    /* Init board hardware. */
    BOARD_InitBootPins ();
    BOARD_InitBootClocks ();
    BOARD_InitBootPeripherals ();

    PRINTF("\n\n\n");
    PRINTF("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n");
    PRINTF("┃      MK10DN512VLQ10 Started      ┃\n");
    PRINTF("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n");

    if (kRCM_SourceWdog == RCM_GetPreviousResetSources(RCM))
    {
        PRINTF("\n\e[3;5;93mThe processor has been reset %u times by the WDOG\e[0m\n\n",
               WDOG_GetResetCount(WATCHDOG_PERIPHERAL));
    }

    /*********************************************************************
     *                      Configure the DMA
     *********************************************************************/
    edma_transfer_config_t transferConfig_ch0;

    EDMA_PrepareTransfer(&transferConfig_ch0,       /* Prepare TCD for CH0 */
                         (uint32_t*) (ADC0->R),     /* Source Address (ADC0_RA) */
                         sizeof(uint32_t),          /* Source width (2 bytes) */
                         (uint32_t*)&adc0_val,      /* Destination Address (Internal buffer)*/
                         sizeof(uint32_t),          /* Destination width */
                         sizeof(uint32_t),          /* Bytes to transfer each minor loop */
                         sizeof(uint32_t),          /* Total of bytes to transfer */
                         kEDMA_PeripheralToMemory); /* From ADC to Memory */

    /* Push TCD for CH1 into hardware TCD Register */
    EDMA_SubmitTransfer(&dma_adc0_coco_Handle, &transferConfig_ch0);

    EDMA_StartTransfer(&dma_adc0_coco_Handle);

    /*********************************************************************
     *            Start the LPTimer to start ADC measurements.
     *********************************************************************/
    LPTMR_StartTimer(LPTMR_PERIPHERAL);

    /*********************************************************************
     *           Read bandgap for internal temp measurements.
     *********************************************************************/
    uint32_t adcr_bg = 0; /*! ADC value of BANDGAP */
    uint32_t vdd = 0;     /*! VDD in mV */
    pmc_bandgap_buffer_config_t pmcBandgapConfig;

    pmcBandgapConfig.enable = true;
    PMC_ConfigureBandgapBuffer(PMC, &pmcBandgapConfig);

    /* Set the channel to convert by ADC0 to bandgap */
    ADC16_SetChannelConfig(ADC0_PERIPHERAL, ADC0_CH0_CONTROL_GROUP, &adc0_channelsConfig[0]);

    /* Wait for the conversion to be done */
    //WDOG_Refresh(WATCHDOG_PERIPHERAL);
    while (!adc0_val_dma_transfer_rdy);

    /* Clear the flag */
    adc0_val_dma_transfer_rdy = false;
    adc0_val_rdy = false;

    /* Get current ADC BANDGAP value */
    adcr_bg = adc0_val;

    /* Get VREFH value measured in mV: VREFH = (ADCR_VDD x V_BG) / ADCR_BG
     * (Eqn. 3, AN3031, p9) */
    vdd = ADCR_VDD * V_BG / adcr_bg;

    /* Calibrate ADCR_TEMP25: ADCR_TEMP25 = ADCR_VDD x V_TEMP25 / VDD
     * (AN3031, @bottom of p10) */
    adcrTemp25 = ADCR_VDD * V_TEMP25 / vdd;

    /* ADCR_100M = ADCR_VDD x M x 100 / VDD
     * (Eqn.7, AN3031, p11) */
    adcr100m = (ADCR_VDD * M) / (vdd * 10);

    /* Disable BANDGAP reference voltage */
    pmcBandgapConfig.enable = false;
    PMC_ConfigureBandgapBuffer(PMC, &pmcBandgapConfig);

    /* Now set the channel to convert by AC0 to internal temp sensor */
    ADC16_SetChannelConfig(ADC0_PERIPHERAL, ADC0_CH0_CONTROL_GROUP, &adc0_channelsConfig[1]);

    //WDOG_Refresh(WATCHDOG_PERIPHERAL);
    while (1)
    {
        if ((die_temp = adc_get_chip_temp()) != -1000)
        {
            PRINTF("\e[92mDie Temperature = %ld °C\e[0m\n", die_temp);
        }

        PRINTF("\e[1F");

        //WDOG_Refresh(WATCHDOG_PERIPHERAL);
    }

    return 0;
}

 

 

When I debug the project and place a breakpoint on the adc0_val_dma_transfer_rdy = false; line right below the while (!adc0_val_dma_transfer_rdy); line, I see that the correct value for the bandgap is present in adc0_val. But then when I get to the adc_get_chip_temp() call, I see that the value for adc0_val is the same that the one for the bandgap channel despite having changed the channel to the temp sensor.

If fact, this value doesn't change at all even when the EDMA transfer done interrupt is being generated. In other words, the adc0_val_dma_transfed_done callback is being called correctly but the value in adc0_val is not updated at all. It keeps the very first value obtained the very first time adc0_val_dma_transfed_done is called.

What's happening? Isn't the value supposed to be updated every time the DMA transfer done callback (adc0_val_dma_transfed_done) is called?

 

EDIT:

I found out that if I run this:

    edma_transfer_config_t transferConfig_ch0;

    EDMA_PrepareTransfer(&transferConfig_ch0,       /* Prepare TCD for CH0 */
                         (uint32_t*) (ADC0->R),     /* Source Address (ADC0_RA) */
                         sizeof(uint32_t),          /* Source width (2 bytes) */
                         (uint32_t*)&adc0_val,      /* Destination Address (Internal buffer)*/
                         sizeof(uint32_t),          /* Destination width (2 bytes) */
                         sizeof(uint32_t),          /* Bytes to transfer each minor loop (2 bytes) */
                         sizeof(uint32_t),          /* Total of bytes to transfer (12*2 bytes) */
                         kEDMA_PeripheralToMemory); /* From ADC to Memory */

    /* Push TCD for CH1 into hardware TCD Register */
    EDMA_SubmitTransfer(&dma_adc0_coco_Handle, &transferConfig_ch0);

    EDMA_StartTransfer(&dma_adc0_coco_Handle);

 

Every time a DMA transfer is complete everything works fine. But this seems highly inefficient and unnecessary. Or is that how it works? do I have to "Prepare the Transfer", "Submit the Transfer", and "Start the Transfer" every time a DMA transfer is complete? That seems awful!

Of what am I doing wrong?

0 Kudos
1 Reply

117 Views
danielchen
NXP TechSupport
NXP TechSupport

Please refer to frdmk64f_adc16_continuous_edma demo.  

 

Regards

Daniel

0 Kudos