RT1064 ADC_ETC with DMA

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

RT1064 ADC_ETC with DMA

Jump to solution
4,816 Views
rost0031b
Contributor II

My goal is to read several (11 or so) ADC channels with the least amount of pertubation to the system. Looking at the reference manual, it looks like it's possible to chain together several ADC reads back to back using ADC External Trigger Controller (ADC_ETC). There is also some discussion about this here: https://community.nxp.com/thread/482078 

What I would like to do is chain together several of these reads and have DMA automatically transfer the data from each ADC channel to some location and get an interrupt once everything is finished.  The reads don't have to be concurrent and the triggering would be about once a second.

I have the example "adc_etc_hardware" working and modified it to only get a single interrupt once the second read occurs. I would like to modify it further to allow DMA and several more channels.

The issue I'm running into is that there is no good documentation on ADC_ETC chains and triggers and how to tie all this together with DMA. First of all, is what I want possible:

1. Several ADC channels read automatically.

2. Data moved to some location with DMA for each channel.

3. A single interrupt once everything is finished.
4. Documentation that explains how these triggers/chains work.

5. Maybe an example of how to tie all this together.

1 Solution
3,896 Views
rost0031b
Contributor II

Ok, an update on my progress on this undocumented feature set:

So I think I have figured out what chains and triggers are: basically, you can setup up to 8 "triggerGroups". Each of these triggerGroups need to be XBARA connected to a trigger and you can control the order in which these triggerGroups go by modifying their priorities.  Within each triggerGroup, you can set up a chain of up to 8 ADC reads. Each of these chains links can have an interrupt or you can just set up an interrupt on the last link in the chain and read all the ADC values at that point. The only real limitation is that you only have 3 interrupts for the entire ADC_ETC.  Below is most of my code that was used to set up 2 trigger groups with chains of 4 and 2 ADC reads. Ignore all the stuff with QS or QK; those are RTOS functions for QPC.

#define DEMO_ADC_BASE ADC1
#define DEMO_ADC_CHANNEL_GROUP0 0U
#define DEMO_ADC_CHANNEL_GROUP1 1U
#define DEMO_ADC_CHANNEL_GROUP2 2U
#define DEMO_ADC_CHANNEL_GROUP3 3U

#define DEMO_ADC_ETC_CHANNEL0 15U
#define DEMO_ADC_ETC_CHANNEL1 0U
#define DEMO_ADC_ETC_CHANNEL2 5U
#define DEMO_ADC_ETC_CHANNEL3 6U

#define ADC_NUM_READS 6

#define DEMO_ADC_ETC_CHANNEL4 7U
#define DEMO_ADC_ETC_CHANNEL5 8U

#define DEMO_ADC_ETC_BASE ADC_ETC
#define ADC_ETC_TRIG_GRP0_CHAIN_LENGTH 3U    /* Chain length 4 */
#define ADC_ETC_TRIG_GRP1_CHAIN_LENGTH 1U    /* Chain length 2 */‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

/******************************************************************************/
void BSP_init( void )
{
    BOARD_BootClockRUN();                          /* Setup and enable clocks */
    CLOCK_EnableClock(kCLOCK_Iomuxc);                 /* Enable the Mux clock */

//    SCB_EnableICache(); /* Enable I-Cache */
//    SCB_EnableDCache(); /* Enable D-Cache */

    if (QS_INIT((void *)0) == 0) { /* initialize the QS software tracing */
        Q_ERROR();
    }
    QS_OBJ_DICTIONARY(&l_SysTick_Handler);
    QS_USR_DICTIONARY(PHILO_STAT);
    QS_USR_DICTIONARY(COMMAND_STAT);

    /* Set up MUX for pins for ADC */
    /* ADC channel 15 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_10_GPIO1_IO26,        /* GPIO_AD_B1_10 is configured as GPIO1_IO26 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_10_GPIO1_IO26,        /* GPIO_AD_B1_10 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 0 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_11_GPIO1_IO27,        /* GPIO_AD_B1_11 is configured as GPIO1_IO27 for ADC1*/
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_11_GPIO1_IO27,            /* GPIO_AD_B1_11 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 5 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_00_GPIO1_IO16,        /* GPIO_AD_B1_00 is configured as GPIO1_IO16 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_00_GPIO1_IO16,        /* GPIO_AD_B1_00 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 6 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_01_GPIO1_IO17,        /* GPIO_AD_B1_01 is configured as GPIO1_IO17 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_01_GPIO1_IO17,        /* GPIO_AD_B1_01 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 7 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,        /* GPIO_AD_B1_02 is configured as GPIO1_IO18 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,        /* GPIO_AD_B1_02 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 8 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_03_GPIO1_IO19,        /* GPIO_AD_B1_03 is configured as GPIO1_IO19 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_03_GPIO1_IO19,        /* GPIO_AD_B1_03 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );


    CLOCK_SetMux(kCLOCK_PerclkMux, 1U);     /* Set PERCLK_CLK source to OSC_CLK*/
    CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U);     /* Set PERCLK_CLK divider to 1 */


    /* Initialize the ADC module. */
    adc_config_t k_adcConfig;
    ADC_GetDefaultConfig( &k_adcConfig );
    ADC_Init( ADC1, &k_adcConfig);
    ADC_EnableHardwareTrigger( ADC1, true);

    adc_channel_config_t adcChannelConfigStruct;
    adcChannelConfigStruct.channelNumber = 16U; /* External channel selection from ADC_ETC. */
    adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
    ADC_SetChannelConfig(ADC1, 0, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 1, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 2, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 3, &adcChannelConfigStruct);

    ADC_SetChannelConfig(ADC1, 4, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 5, &adcChannelConfigStruct);

    /* Do auto hardware calibration. */
    if( kStatus_Success == ADC_DoAutoCalibration( ADC1 ) ) {
        QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC Cal Done"); QS_END()
    } else {
        QS_BEGIN(HAT_ERR, 0)  QS_STR("ADC Cal Failed"); QS_END()
    }

    /* Init xbara module. */
    XBARA_Init(XBARA1);

    /* Configure the XBARA signal connections. */
    XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputPitTrigger0, kXBARA1_OutputAdcEtcXbar0Trig0);
    XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputPitTrigger0, kXBARA1_OutputAdcEtcXbar0Trig1);

    /* Structure of initialize PIT */
    pit_config_t pitConfig;

    /* Init pit module */
    PIT_GetDefaultConfig(&pitConfig);
    PIT_Init(PIT, &pitConfig);

    /* Set timer period for channel 0 */
    PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, USEC_TO_COUNT(1000000U, CLOCK_GetFreq(kCLOCK_OscClk)));

    /* Initialize the ADC_ETC. */
    adc_etc_config_t adcEtcConfig;
    ADC_ETC_GetDefaultConfig(&adcEtcConfig);
    adcEtcConfig.XBARtriggerMask = 3U; /* Enable the external XBAR trigger0 and trigger1. */
    ADC_ETC_Init(ADC_ETC, &adcEtcConfig);

    /* Set the external XBAR trigger0 configuration. */
    adc_etc_trigger_config_t adcEtcTriggerConfig;
    adcEtcTriggerConfig.enableSyncMode = false;
    adcEtcTriggerConfig.enableSWTriggerMode = false;
    adcEtcTriggerConfig.triggerChainLength = ADC_ETC_TRIG_GRP0_CHAIN_LENGTH; /* Chain length is 4. */
    adcEtcTriggerConfig.triggerPriority = 1U;
    adcEtcTriggerConfig.sampleIntervalDelay = 0U;
    adcEtcTriggerConfig.initialDelay = 0U;
    ADC_ETC_SetTriggerConfig(ADC_ETC, 0U, &adcEtcTriggerConfig);

    /* Set the external XBAR trigger1 configuration. */
    adcEtcTriggerConfig.triggerChainLength = ADC_ETC_TRIG_GRP1_CHAIN_LENGTH; /* Chain length is 2. */
    adcEtcTriggerConfig.triggerPriority = 0U;
    ADC_ETC_SetTriggerConfig(ADC_ETC, 1U, &adcEtcTriggerConfig);

    /* Set the external XBAR trigger0 chain configuration. */
    /* ADC1 Trigger group 0 */
    adc_etc_trigger_chain_config_t adcEtcTriggerChainConfig;
    adcEtcTriggerChainConfig.enableB2BMode = true;
    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP0; /* Select ADC_HC0 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL0; /* ADC_HC0 will be triggered to sample Corresponding channel. */
//    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done0InterruptEnable; /* Enable the Done0 interrupt. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done0 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP0, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain0. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP1; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL1; /* ADC_HC1 will be triggered to sample Corresponding channel. */
//    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done1InterruptEnable; /* Enable the Done1 interrupt. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP1, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain1. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP2; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL2; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP2, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain2. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP3; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL3; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP3, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */


    /* ADC1 Trigger group 1 */
    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP0; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL4; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 1U, DEMO_ADC_CHANNEL_GROUP0, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */


    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP1; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL5; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done1InterruptEnable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 1U, DEMO_ADC_CHANNEL_GROUP1, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */

    EnableIRQ(ADC_ETC_IRQ0_IRQn);  /* Not really needed here */
    EnableIRQ(ADC_ETC_IRQ1_IRQn);
    EnableIRQ(ADC_ETC_IRQ2_IRQn);  /* Not really needed here */
    EnableIRQ(ADC_ETC_ERROR_IRQ_IRQn);

    /* Start PIT channel0. */
    PIT_StartTimer(PIT, kPIT_Chnl_0);

}



/******************************************************************************/
void BSP_AdcEtcIrq1Handler( void )
{
    QK_ISR_ENTRY();                        /* inform QK about entering an ISR */
    volatile uint32_t intFlags0 = ADC_ETC_GetInterruptStatusFlags( DEMO_ADC_ETC_BASE, kADC_ETC_Trg0TriggerSource);

    if( intFlags0 & kADC_ETC_Done1StatusFlagMask ) {
        ADC_ETC_ClearInterruptStatusFlags( DEMO_ADC_ETC_BASE,
                kADC_ETC_Trg0TriggerSource, kADC_ETC_Done1StatusFlagMask );
    }

    volatile uint32_t intFlags1 = ADC_ETC_GetInterruptStatusFlags( DEMO_ADC_ETC_BASE, kADC_ETC_Trg1TriggerSource);

    if( intFlags1 & kADC_ETC_Done1StatusFlagMask ) {
        ADC_ETC_ClearInterruptStatusFlags( DEMO_ADC_ETC_BASE,
                kADC_ETC_Trg1TriggerSource, kADC_ETC_Done1StatusFlagMask );
    }
    volatile uint32_t adcValues[ADC_NUM_READS] = {0};

//    volatile uint32_t g_AdcValue0 =
    adcValues[0] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 0U ); /* Get trigger0 chain0 result. */
//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain0 val:"); QS_U32( 4, g_AdcValue0 ); QS_END()


//    volatile uint32_t g_AdcValue1 =
    adcValues[1] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 1U ); /* Get trigger0 chain1 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain1 val:"); QS_U32( 4, g_AdcValue1); QS_END()


//    volatile uint32_t g_AdcValue2 =
    adcValues[2] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 2U ); /* Get trigger0 chain2 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain2 val:"); QS_U32( 4, g_AdcValue2); QS_END()

    adcValues[3] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 3U ); /* Get trigger0 chain3 result. */

    adcValues[4] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 1U, 0U ); /* Get trigger1 chain0 result. */

    adcValues[5] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 1U, 1U ); /* Get trigger1 chain1 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain2 val:"); QS_U32( 4, g_AdcValue2); QS_END()

    QS_BEGIN(HAT_DBG, 0)
    for( uint16_t i = 0; i < ADC_NUM_READS; i++ ) {
        QS_STR("ADC"); QS_U32( 1, i); QS_U32( 4, adcValues[i]);
    }
    QS_END()


    QK_ISR_EXIT();                          /* inform QK about exiting an ISR */
}

View solution in original post

7 Replies
3,897 Views
rost0031b
Contributor II

Ok, an update on my progress on this undocumented feature set:

So I think I have figured out what chains and triggers are: basically, you can setup up to 8 "triggerGroups". Each of these triggerGroups need to be XBARA connected to a trigger and you can control the order in which these triggerGroups go by modifying their priorities.  Within each triggerGroup, you can set up a chain of up to 8 ADC reads. Each of these chains links can have an interrupt or you can just set up an interrupt on the last link in the chain and read all the ADC values at that point. The only real limitation is that you only have 3 interrupts for the entire ADC_ETC.  Below is most of my code that was used to set up 2 trigger groups with chains of 4 and 2 ADC reads. Ignore all the stuff with QS or QK; those are RTOS functions for QPC.

#define DEMO_ADC_BASE ADC1
#define DEMO_ADC_CHANNEL_GROUP0 0U
#define DEMO_ADC_CHANNEL_GROUP1 1U
#define DEMO_ADC_CHANNEL_GROUP2 2U
#define DEMO_ADC_CHANNEL_GROUP3 3U

#define DEMO_ADC_ETC_CHANNEL0 15U
#define DEMO_ADC_ETC_CHANNEL1 0U
#define DEMO_ADC_ETC_CHANNEL2 5U
#define DEMO_ADC_ETC_CHANNEL3 6U

#define ADC_NUM_READS 6

#define DEMO_ADC_ETC_CHANNEL4 7U
#define DEMO_ADC_ETC_CHANNEL5 8U

#define DEMO_ADC_ETC_BASE ADC_ETC
#define ADC_ETC_TRIG_GRP0_CHAIN_LENGTH 3U    /* Chain length 4 */
#define ADC_ETC_TRIG_GRP1_CHAIN_LENGTH 1U    /* Chain length 2 */‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

/******************************************************************************/
void BSP_init( void )
{
    BOARD_BootClockRUN();                          /* Setup and enable clocks */
    CLOCK_EnableClock(kCLOCK_Iomuxc);                 /* Enable the Mux clock */

//    SCB_EnableICache(); /* Enable I-Cache */
//    SCB_EnableDCache(); /* Enable D-Cache */

    if (QS_INIT((void *)0) == 0) { /* initialize the QS software tracing */
        Q_ERROR();
    }
    QS_OBJ_DICTIONARY(&l_SysTick_Handler);
    QS_USR_DICTIONARY(PHILO_STAT);
    QS_USR_DICTIONARY(COMMAND_STAT);

    /* Set up MUX for pins for ADC */
    /* ADC channel 15 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_10_GPIO1_IO26,        /* GPIO_AD_B1_10 is configured as GPIO1_IO26 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_10_GPIO1_IO26,        /* GPIO_AD_B1_10 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 0 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_11_GPIO1_IO27,        /* GPIO_AD_B1_11 is configured as GPIO1_IO27 for ADC1*/
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_11_GPIO1_IO27,            /* GPIO_AD_B1_11 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 5 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_00_GPIO1_IO16,        /* GPIO_AD_B1_00 is configured as GPIO1_IO16 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_00_GPIO1_IO16,        /* GPIO_AD_B1_00 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 6 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_01_GPIO1_IO17,        /* GPIO_AD_B1_01 is configured as GPIO1_IO17 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_01_GPIO1_IO17,        /* GPIO_AD_B1_01 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 7 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,        /* GPIO_AD_B1_02 is configured as GPIO1_IO18 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_02_GPIO1_IO18,        /* GPIO_AD_B1_02 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );

    /* ADC channel 8 */
    IOMUXC_SetPinMux(
            IOMUXC_GPIO_AD_B1_03_GPIO1_IO19,        /* GPIO_AD_B1_03 is configured as GPIO1_IO19 for ADC1 */
            0U                                      /* Software Input On Field: Input Path is determined by functionality */
    );
    IOMUXC_SetPinConfig(
            IOMUXC_GPIO_AD_B1_03_GPIO1_IO19,        /* GPIO_AD_B1_03 PAD functional properties : */
            IOMUXC_SW_PAD_CTL_PAD_SRE(GPIO_SlowSlewRate)      |   /* Slow slew rate */
            IOMUXC_SW_PAD_CTL_PAD_DSE(GPIO_R0DivBy6)          |   /* Drive strength */
            IOMUXC_SW_PAD_CTL_PAD_SPEED(GPIO_SpeedMed)        |   /* Med speed (100Mhz) */
            IOMUXC_SW_PAD_CTL_PAD_ODE(0)                      |   /* Open drain disabled */
            IOMUXC_SW_PAD_CTL_PAD_PKE(0)                      |   /* Pull/keep disabled */
            IOMUXC_SW_PAD_CTL_PAD_PUE(0)                      |   /* Set to keep */
            IOMUXC_SW_PAD_CTL_PAD_PUS(GPIO_PullDown100Kohm)   |   /* Pull down 100kOhm */
            IOMUXC_SW_PAD_CTL_PAD_HYS(0)                          /* Hysteresis disabled */
    );


    CLOCK_SetMux(kCLOCK_PerclkMux, 1U);     /* Set PERCLK_CLK source to OSC_CLK*/
    CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U);     /* Set PERCLK_CLK divider to 1 */


    /* Initialize the ADC module. */
    adc_config_t k_adcConfig;
    ADC_GetDefaultConfig( &k_adcConfig );
    ADC_Init( ADC1, &k_adcConfig);
    ADC_EnableHardwareTrigger( ADC1, true);

    adc_channel_config_t adcChannelConfigStruct;
    adcChannelConfigStruct.channelNumber = 16U; /* External channel selection from ADC_ETC. */
    adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
    ADC_SetChannelConfig(ADC1, 0, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 1, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 2, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 3, &adcChannelConfigStruct);

    ADC_SetChannelConfig(ADC1, 4, &adcChannelConfigStruct);
    ADC_SetChannelConfig(ADC1, 5, &adcChannelConfigStruct);

    /* Do auto hardware calibration. */
    if( kStatus_Success == ADC_DoAutoCalibration( ADC1 ) ) {
        QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC Cal Done"); QS_END()
    } else {
        QS_BEGIN(HAT_ERR, 0)  QS_STR("ADC Cal Failed"); QS_END()
    }

    /* Init xbara module. */
    XBARA_Init(XBARA1);

    /* Configure the XBARA signal connections. */
    XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputPitTrigger0, kXBARA1_OutputAdcEtcXbar0Trig0);
    XBARA_SetSignalsConnection(XBARA1, kXBARA1_InputPitTrigger0, kXBARA1_OutputAdcEtcXbar0Trig1);

    /* Structure of initialize PIT */
    pit_config_t pitConfig;

    /* Init pit module */
    PIT_GetDefaultConfig(&pitConfig);
    PIT_Init(PIT, &pitConfig);

    /* Set timer period for channel 0 */
    PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, USEC_TO_COUNT(1000000U, CLOCK_GetFreq(kCLOCK_OscClk)));

    /* Initialize the ADC_ETC. */
    adc_etc_config_t adcEtcConfig;
    ADC_ETC_GetDefaultConfig(&adcEtcConfig);
    adcEtcConfig.XBARtriggerMask = 3U; /* Enable the external XBAR trigger0 and trigger1. */
    ADC_ETC_Init(ADC_ETC, &adcEtcConfig);

    /* Set the external XBAR trigger0 configuration. */
    adc_etc_trigger_config_t adcEtcTriggerConfig;
    adcEtcTriggerConfig.enableSyncMode = false;
    adcEtcTriggerConfig.enableSWTriggerMode = false;
    adcEtcTriggerConfig.triggerChainLength = ADC_ETC_TRIG_GRP0_CHAIN_LENGTH; /* Chain length is 4. */
    adcEtcTriggerConfig.triggerPriority = 1U;
    adcEtcTriggerConfig.sampleIntervalDelay = 0U;
    adcEtcTriggerConfig.initialDelay = 0U;
    ADC_ETC_SetTriggerConfig(ADC_ETC, 0U, &adcEtcTriggerConfig);

    /* Set the external XBAR trigger1 configuration. */
    adcEtcTriggerConfig.triggerChainLength = ADC_ETC_TRIG_GRP1_CHAIN_LENGTH; /* Chain length is 2. */
    adcEtcTriggerConfig.triggerPriority = 0U;
    ADC_ETC_SetTriggerConfig(ADC_ETC, 1U, &adcEtcTriggerConfig);

    /* Set the external XBAR trigger0 chain configuration. */
    /* ADC1 Trigger group 0 */
    adc_etc_trigger_chain_config_t adcEtcTriggerChainConfig;
    adcEtcTriggerChainConfig.enableB2BMode = true;
    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP0; /* Select ADC_HC0 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL0; /* ADC_HC0 will be triggered to sample Corresponding channel. */
//    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done0InterruptEnable; /* Enable the Done0 interrupt. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done0 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP0, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain0. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP1; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL1; /* ADC_HC1 will be triggered to sample Corresponding channel. */
//    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done1InterruptEnable; /* Enable the Done1 interrupt. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP1, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain1. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP2; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL2; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Disable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP2, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain2. */

    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP3; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL3; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, DEMO_ADC_CHANNEL_GROUP3, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */


    /* ADC1 Trigger group 1 */
    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP0; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL4; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_InterruptDisable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 1U, DEMO_ADC_CHANNEL_GROUP0, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */


    adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << DEMO_ADC_CHANNEL_GROUP1; /* Select ADC_HC1 register to trigger. */
    adcEtcTriggerChainConfig.ADCChannelSelect = DEMO_ADC_ETC_CHANNEL5; /* ADC_HC1 will be triggered to sample Corresponding channel. */
    adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done1InterruptEnable; /* Enable the Done1 interrupt. */
    ADC_ETC_SetTriggerChainConfig(ADC_ETC, 1U, DEMO_ADC_CHANNEL_GROUP1, &adcEtcTriggerChainConfig); /* Configure the trigger0 chain3. */

    EnableIRQ(ADC_ETC_IRQ0_IRQn);  /* Not really needed here */
    EnableIRQ(ADC_ETC_IRQ1_IRQn);
    EnableIRQ(ADC_ETC_IRQ2_IRQn);  /* Not really needed here */
    EnableIRQ(ADC_ETC_ERROR_IRQ_IRQn);

    /* Start PIT channel0. */
    PIT_StartTimer(PIT, kPIT_Chnl_0);

}



/******************************************************************************/
void BSP_AdcEtcIrq1Handler( void )
{
    QK_ISR_ENTRY();                        /* inform QK about entering an ISR */
    volatile uint32_t intFlags0 = ADC_ETC_GetInterruptStatusFlags( DEMO_ADC_ETC_BASE, kADC_ETC_Trg0TriggerSource);

    if( intFlags0 & kADC_ETC_Done1StatusFlagMask ) {
        ADC_ETC_ClearInterruptStatusFlags( DEMO_ADC_ETC_BASE,
                kADC_ETC_Trg0TriggerSource, kADC_ETC_Done1StatusFlagMask );
    }

    volatile uint32_t intFlags1 = ADC_ETC_GetInterruptStatusFlags( DEMO_ADC_ETC_BASE, kADC_ETC_Trg1TriggerSource);

    if( intFlags1 & kADC_ETC_Done1StatusFlagMask ) {
        ADC_ETC_ClearInterruptStatusFlags( DEMO_ADC_ETC_BASE,
                kADC_ETC_Trg1TriggerSource, kADC_ETC_Done1StatusFlagMask );
    }
    volatile uint32_t adcValues[ADC_NUM_READS] = {0};

//    volatile uint32_t g_AdcValue0 =
    adcValues[0] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 0U ); /* Get trigger0 chain0 result. */
//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain0 val:"); QS_U32( 4, g_AdcValue0 ); QS_END()


//    volatile uint32_t g_AdcValue1 =
    adcValues[1] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 1U ); /* Get trigger0 chain1 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain1 val:"); QS_U32( 4, g_AdcValue1); QS_END()


//    volatile uint32_t g_AdcValue2 =
    adcValues[2] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 2U ); /* Get trigger0 chain2 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain2 val:"); QS_U32( 4, g_AdcValue2); QS_END()

    adcValues[3] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 0U, 3U ); /* Get trigger0 chain3 result. */

    adcValues[4] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 1U, 0U ); /* Get trigger1 chain0 result. */

    adcValues[5] = ADC_ETC_GetADCConversionValue(
            DEMO_ADC_ETC_BASE, 1U, 1U ); /* Get trigger1 chain1 result. */

//    QS_BEGIN(HAT_DBG, 0)  QS_STR("ADC chain2 val:"); QS_U32( 4, g_AdcValue2); QS_END()

    QS_BEGIN(HAT_DBG, 0)
    for( uint16_t i = 0; i < ADC_NUM_READS; i++ ) {
        QS_STR("ADC"); QS_U32( 1, i); QS_U32( 4, adcValues[i]);
    }
    QS_END()


    QK_ISR_EXIT();                          /* inform QK about exiting an ISR */
}
3,896 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,

Thank you for your interest in NXP Semiconductor products and for the opportunity to serve you.
The RT1064 is definitely to achieve the application as you expect.
These're no application note or similar document about this application, so it needs the developer to develop it by integrating the SDK demos.
If you have any questions about it, please feel free to contact me.

Have a great day,
TIC

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
3,896 Views
rogerc1894
Contributor II

I have similar goals and questions about using the ADC External Trigger Controller (ADC_ETC), Enhanced Direct Memory Access (eDMA), and trigger chaining. Ideally I would like to cycle through about 10 ADC channel reads in a predetermined order, using Direct Memory Access (DMA) to set a specific configuration in the ADC before each channel is read, and have the Direct Memory Access store the results for each conversion into a circular buffer, and then generate an interrupt upon completion of each completed cycle. Looking at the i.mx RT1050 reference manual, it looks like this is all possible, but as rost0031b has mentioned the documentation for the ADC_ETC leaves much to be desired in the original post:

"The issue I'm running into is that there is no good documentation on ADC_ETC chains and triggers and how to tie all this together with DMA. First of all, is what I want possible:

1. Several ADC channels read automatically.

2. Data moved to some location with DMA for each channel.

3. A single interrupt once everything is finished.
4. Documentation that explains how these triggers/chains work.

5. Maybe an example of how to tie all this together."

It is insufficient to say the developer needs to develop it by integrating the SDK demos. The SDK demos are great coding examples of a specific use case for the individual peripheral, they are not an adequate explanation of the interrelationship between these peripherals, nor do they explain the order in which they should be configured and interactions between them in a coherent way.

The hardware in these processors seem very capable of handling the desired functionality, however, with the rich features and complexity of these peripheral's, more information than what is offered for them is needed before its potential can be fully realized by the programmer. Specifically, the reference manual seems to be incomplete in its treatment of the ADC_ETC module. This entire subject is an excellent candidate for an Application Note along with a reference design.

Thank you to rost031b for your post!

0 Kudos
3,896 Views
rost0031b
Contributor II

I'm not really sure how to contact you but maybe I can ask some questions about what I don't understand.

1. What does "chain length" mean?  From the adc_etc_hardware example, there's a section where the chain length is being set to 2

/* Set the external XBAR trigger0 configuration. */
    adc_etc_trigger_config_t adcEtcTriggerConfig;
    adcEtcTriggerConfig.enableSyncMode = false;
    adcEtcTriggerConfig.enableSWTriggerMode = false;
    adcEtcTriggerConfig.triggerChainLength = DEMO_ADC_ETC_CHAIN_LENGTH; /* Chain length is 2. */
    adcEtcTriggerConfig.triggerPriority = 0U;
    adcEtcTriggerConfig.sampleIntervalDelay = 0U;
    adcEtcTriggerConfig.initialDelay = 0U;
    ADC_ETC_SetTriggerConfig(ADC_ETC, 0U, &adcEtcTriggerConfig);

I don't understand the chain length. It seems to set the the value into register ETC_TRIGn, field ChainLength. The description of that field is simply "TRIG chain length to the ADC. 0: Trig length is 1; ... 7: Trig length is 8".  Nowhere else is the chain length mentioned in the reference manual.  What exactly is this field controlling?

2. It looks like I can do a maximum of 8 triggers per ADC. But there seem to be multiple chains per trigger... Does that mean that I have to use more than ADC1 to do 11 or 12 reads or can ADC1 handle all of that using some combination of triggers and chains?

3. Looking at the example code it seems like there are 8 trigger groups and each of those has 8 chains?  Is that correct and if so, what does that mean?  Does that mean I can hook up a total of 64 reads per ADC_ETC on a single ADC?

0 Kudos
3,896 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,
Thanks for your reply.
1) The chain length corresponds to the number of response to the external trigger input.
2) 2. It looks like I can do a maximum of 8 triggers per ADC. But there seem to be multiple chains per trigger... Does that mean that I have to use more than ADC1 to do 11 or 12 reads or can ADC1 handle all of that using some combination of triggers and chains?
-- Yes.
3) Does that mean I can hook up a total of 64 reads per ADC_ETC on a single ADC?
-- Yes.

Have a great day,
TIC

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
3,896 Views
rost0031b
Contributor II

1) The chain length corresponds to the number of response to the external trigger input.

Ok, but what does that mean?  What is the "number of response to external trigger input" mean? I am clearly not seeing some very basic concept here...

2) 2. It looks like I can do a maximum of 8 triggers per ADC. But there seem to be multiple chains per trigger... Does that mean that I have to use more than ADC1 to do 11 or 12 reads or can ADC1 handle all of that using some combination of triggers and chains?

-- Yes.

"Yes, I have to use ADC1 and ADC2 for 11 to 12 reads" or "Yes, ADC1 can handle all 11 or 12 reads"

Are there any application notes for any of the RT10xx series processors that describe this in detail? The reference manual is extremely vague.

3,896 Views
jeremyzhou
NXP Employee
NXP Employee

Hi,
Thanks for your reply, let me make it clear.
1. It means that sequential incoming external trigger whose number is equal to the chain length will make the ADC complete the same amount of conversion.
2.ADC1 can handle all of that using some combination of triggers and chains.

3. Until now, there's no application note or similar document about ADC application.

Have a great day,
TIC

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos