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.

MSP432E401Y: MSP432E401Y sampling 12 analog inputs in sequence

Part Number: MSP432E401Y

I would like some help on setting up the MSP432E401Y to sample 12 analog inputs (Channels 0-11) in sequence and generate

an interrupt when the last channel (channel 11) sample is done.

The maximum sequencer size is 8. I would like to know how to utilize these sequencers if i wanted to 

Currently i can implement this with 8 channels (Channels 0-5, 7 and cpu temperature sensor) using sample sequencer 0 of ADC0 my setup code is as follows

(excluding the MAP_SysCtlPeripheralEnable(..) and MAP_GPIOPinTypeADC(..) portions)

*********************************************

ADCReferenceSet(ADC0_BASE, ADC_REF_INT); // Use internal 3 V as reference

// Configure HW Averaging of 32x

MAP_ADCHardwareOversampleConfigure(ADC0_BASE, 32);

// Configure ADC0 Sequencer 0 (SS0) to sample the analog channels AIN0-AIN7.
//
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH3);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH4);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH5);
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_TS ); // Internal Temperature sensor
// Configure interrupt to be set when this sample is done and tell ADC this is the
// last conversion on the sequencer
MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH7 | ADC_CTL_IE |ADC_CTL_END);

       MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);

        MAP_ADCSequenceEnable(ADC0_BASE, 0);

MAP_ADCIntEnable(ADC0_BASE, 0);
MAP_IntEnable(INT_ADC0SS0); // Interrupt generation from ADC-0 sequencer 0

******************************************************************

 Thanks

David

  • Hello,

        Have you looked at this example?

    It would need to be modified for the number and channels you are needing, but conceptually I believe it is similar.

    Regards,

    Chris

  • Hi Chris,

    Thanks. This should work.

    David

  • Hi Chris,

    The example implements a polling mode. I want to implement an interrupt mode, however my  ISR is never entered.

    I setup my 12 channels to use ADC0 sequencer 0 for channels 0-7 and sequencer 1 for channels 8-11.

    as below

    ***************

    ADCReferenceSet(ADC0_BASE, ADC_REF_INT); // Use internal 3 V as reference

    // Configure HW Averaging of 32x
    MAP_ADCHardwareOversampleConfigure(ADC0_BASE, 32);

    // Configure ADC0 Sequencer 0 (SS0) to sample the analog channels AIN0-AIN7.
    // end of conversion and interrupt generation is set to AIN7
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 3, ADC_CTL_CH3);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 4, ADC_CTL_CH4);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 5, ADC_CTL_CH5);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 6, ADC_CTL_TS ); // Internal Temperature sensor
    // Configure interrupt to be set when this sample is done and tell ADC this is the
    // last conversion on the sequencer
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 7, ADC_CTL_CH7 | ADC_CTL_IE |ADC_CTL_END);

    // Configure ADC0 Sequencer 1 (SS1) to sample the analog channels AIN8-AIN11.
    // end of conversion and interrupt generation is set to AIN11
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH8);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH9);
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_CH10);
    // Configure interrupt to be set when this sample is done and tell ADC this is the
    // last conversion on the sequencer
    MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 3, ADC_CTL_CH11 | ADC_CTL_IE |ADC_CTL_END);

    // Enable sample sequences 0-1 with a processor signal trigger. Sequencers
    // will do a single sample when the processor sends a signal to start the
    // conversion. Also make sure that the priority is set with SS0 being the
    // highest and SS1 being the lowest


    // Enable repeat sample sequence capture (so long as there is not a higher priority source active).
    // on ADC0 sequencer 0 (SS0)
    MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
    MAP_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_ALWAYS, 1);

    // Since ADC0 sequencers 0 (SS0) and 1 (SS1) are now configured, they must be enabled.

    MAP_ADCSequenceEnable(ADC0_BASE, 0);
    MAP_ADCSequenceEnable(ADC0_BASE, 1);


    // Clear the interrupt status flags of ADC0 sequencers 0 (SS0) and 1 (SS1) .
    // This is done to make sure the interrupt flag is cleared before we sample.
    MAP_ADCIntClear(ADC0_BASE, 0);
    MAP_ADCIntClear(ADC0_BASE, 1);

    **************************************************

    In the main routine i have 

    // Set Interrupt priorities ( Uses only top 3 bits of priority setting
    MAP_IntPrioritySet(INT_GPIOH, 0x00); // Set the interrupts priority to the highest
    MAP_IntPrioritySet(INT_ADC0SS1, 0x40); // Set the interrupts priority

    // Enable Interrupts
    MAP_IntEnable(INT_GPIOH); // For IGBT trigger signals table
    MAP_IntEnable(INT_GPIOJ); // Launchpad User switches


    MAP_ADCIntEnable(ADC0_BASE, 1);
    MAP_IntEnable(INT_ADC0SS1); // Interrupt generation from ADC-0 sequencer 1

    // Trigger the ADC conversion for sequencers 0 and 1 simultaneously.
    // The ADC shall take care of performing conversion based on the
    // priority assigned to each sequencer.
    ADC0->PSSI = 0x3;

    However my ISR is never entered.

    void ADC0SS1_IRQHandler(void){

    .....

    }

    When in addition i  include an ISR for SS0 i get that being executed but not for SS1.

  • I cannot see an immediate reason why. You might try the API MAP_ADCIntEnableEx() instead. I see that used in the examples.

    Regards,
    Chris
  • David,
    Any progress on this issue? Do you have anything that you can share?

    Thanks,
    Chris
  • Hi Chris,
    I did not get it working. I am on holidays now but my plan was to investigate it further in the New Year. I am using the original MSP432E401Y launchpad ( I notice the version being sold now is about double the price and i dont know if it has a new chip version). I am wondering if somehow the SS1 interrupt is not correctly setup. Are there any files i should into ( e.g. a startup file ). I could try later using the non-rom ( 'MAP....') versions of the driverlib functions . Presently to get my 12 ADC channels working in interrupt mode, I have ADC0 set up with SS0 (I read all 8 channels) and also using ADC1 SS0 . I read only the 1st 4 channels for ADC1 SS0 so i get my total 12 ADC channels.
  • /* --COPYRIGHT--,BSD
     * Copyright (c) 2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * --/COPYRIGHT--*/
    /******************************************************************************
     * MSP432E4 Example project for ADC with multiple channel and multiple
     * sequencers
     *
     * Description: In this application example the ADC0 is configured for multiple
     * sequencers sampling 7 channels in single ended mode. The data is then
     * displayed on the serial console.
     *
     *                MSP432E401Y
     *             ------------------
     *         /|\|               PE3|<-- AIN0
     *          | |               PE2|<-- AIN1
     *          --|RST            PE1|<-- AIN2
     *            |               PE0|<-- AIN3
     *            |               PD7|<-- AIN4
     *            |               PD6|<-- AIN5
     *            |               PD5|<-- AIN6
     *            |                  |
     *            |               PA0|<--U0RX
     *            |               PA1|-->U0TX
     * Author: Amit Ashara
    *******************************************************************************/
    /* DriverLib Includes */
    #include <ti/devices/msp432e4/driverlib/driverlib.h>
    
    /* Standard Includes */
    #include <stdint.h>
    #include <stdbool.h>
    
    /* Display Include via console */
    #include "uartstdio.h"
    
    bool ADCNotFinished;
    
    void ConfigureUART(uint32_t systemClock)
    {
        /* Enable the clock to GPIO port A and UART 0 */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    
        /* Configure the GPIO Port A for UART 0 */
        MAP_GPIOPinConfigure(GPIO_PA0_U0RX);
        MAP_GPIOPinConfigure(GPIO_PA1_U0TX);
        MAP_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    
        /* Configure the UART for 115200 bps 8-N-1 format */
        UARTStdioConfig(0, 115200, systemClock);
    }
    
    int main(void)
    {
        uint32_t getADCValue[7];
        uint32_t systemClock;
    
        /* Configure the system clock for 120 MHz */
        systemClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN |
                                              SYSCTL_USE_PLL | SYSCTL_CFG_VCO_480),
                                              120000000);
    
        /* Initialize serial console */
        ConfigureUART(systemClock);
    
        /* Enable the clock to GPIO Port E and wait for it to be ready */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
        while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOE)))
        {
        }
    
        /* Enable the clock to GPIO Port D and wait for it to be ready */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
        while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOD)))
        {
        }
    
        /* Configure PE0-PE3 as ADC input channel */
        MAP_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
        MAP_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2);
        MAP_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_1);
        MAP_GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0);
        MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_7);
        MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_6);
        MAP_GPIOPinTypeADC(GPIO_PORTD_BASE, GPIO_PIN_5);
    
        /* Enable the clock to ADC-0 and wait for it to be ready */
        MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
        while(!(MAP_SysCtlPeripheralReady(SYSCTL_PERIPH_ADC0)))
        {
        }
    
        /* Configure Sequencer 0 to sample the analog channel : AIN0-AIN2. The
         * end of conversion is set for AIN2 */
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 0, ADC_CTL_CH0);
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 1, ADC_CTL_CH1);
    //    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_IE |
    //                                 ADC_CTL_END);
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_END);
    
        /* Configure Sequencer 1 to sample the analog channel : AIN3-AIN4. The
         * end of conversion is set for AIN4 */
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_CH3);
    //    MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH4 | ADC_CTL_IE |
    //                                 ADC_CTL_END);
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_CH4 | ADC_CTL_END);
    
        /* Configure Sequencer 2 to sample the analog channel : AIN5-AIN6. The
         * end of conversion and interrupt generation is set for AIN6 */
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 2, 0, ADC_CTL_CH5);
        MAP_ADCSequenceStepConfigure(ADC0_BASE, 2, 1, ADC_CTL_CH6 | ADC_CTL_IE |
                                     ADC_CTL_END);
    
        /* Enable sample sequence 0-2 with a processor signal trigger.  Sequencers
         * will do a single sample when the processor sends a signal to start the
         * conversion. Also make sure that the priority is set with SS0 being the
         * highest and SS2 being the lowest */
        MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
        MAP_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 1);
        MAP_ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, 2);
    
        /* Since sample sequence 0-2 is now configured, it must be enabled. */
        MAP_ADCSequenceEnable(ADC0_BASE, 0);
        MAP_ADCSequenceEnable(ADC0_BASE, 1);
        MAP_ADCSequenceEnable(ADC0_BASE, 2);
    
        /* Clear the interrupt status flag.  This is done to make sure the
         * interrupt flag is cleared before we sample. */
        MAP_ADCIntClear(ADC0_BASE, 0);
        MAP_ADCIntClear(ADC0_BASE, 1);
        MAP_ADCIntClear(ADC0_BASE, 2);
    
        MAP_ADCIntEnable(ADC0_BASE, 2);
        MAP_IntEnable(INT_ADC0SS2);
    
        /* Sample AIN0 forever.  Display the value on the console. */
        while(1)
        {
            ADCNotFinished = true;
            /* Trigger the ADC conversion for all sequencers simultaneously.
             * The ADC shall take care of performing conversion based on the
             * priority assigned to each sequencer. */
            ADC0->PSSI = 0x7;
    
            /* Wait for conversion to be completed for Sequencer 2 as it has
             * the lowest priority. If the priority changes then change the
             * sequencer number which has the lowest priority. */
    //        while(!MAP_ADCIntStatus(ADC0_BASE, 2, false))
            while(ADCNotFinished);
    
            /* Read ADC Value. */
            MAP_ADCSequenceDataGet(ADC0_BASE, 0, &getADCValue[0]);
            MAP_ADCSequenceDataGet(ADC0_BASE, 1, &getADCValue[3]);
            MAP_ADCSequenceDataGet(ADC0_BASE, 2, &getADCValue[5]);
    
            /* Display the AIN0-AIN06 digital value on the console. */
            UARTprintf("\033[2J\033[H");
            UARTprintf("Sequencer-0 AIN0-2 = %4d %4d %4d\n", getADCValue[0],
                       getADCValue[1], getADCValue[2]);
            UARTprintf("Sequencer-1 AIN3-4 = %4d %4d\n", getADCValue[3],
                       getADCValue[4]);
            UARTprintf("Sequencer-2 AIN5-6 = %4d %4d\n", getADCValue[5],
                       getADCValue[6]);
    
            /* Delay the next sampling */
            MAP_SysCtlDelay(systemClock / 3);
        }
    }
    
    void ADC0SS2_IRQHandler(void)
    {
        /* Clear the ADC interrupt flag. */
        MAP_ADCIntClear(ADC0_BASE, 2);
        ADCNotFinished = false;
    }
    

    You can use ADC0 and ADC1 and potentially measure channels simultaneously depending upon the configuration.  I modified the example to interrupt after sequence 2 is complete.  Please find the attached.

    Regards,

    Chris

  • Hi Chris,

    Your code run fine. I set a breakpoint in the 'ADC0SS2_IRQHandler(void)' IRQ handler and it works well

    However when i modified your code to simulate my sampling process by replacing:

    MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_PROCESSOR, 0);
    MAP_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 1);
    MAP_ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, 2);

    with

    MAP_ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_ALWAYS, 0);
    MAP_ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_ALWAYS, 1);
    MAP_ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_ALWAYS, 2);

    The IRQ handler was no longer entered.

    The code does not go past :

            while(ADCNotFinished);

    Still using the 'ADC_TRIGGER_ALWAYS'  i replaced:

    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_END);

    with 

    MAP_ADCSequenceStepConfigure(ADC0_BASE, 0, 2, ADC_CTL_CH2 | ADC_CTL_IE | ADC_CTL_END);

    and set up an IRQ handler 

    ADC0SS0_IRQHandler(void)

    This ISR was entered correctly.

    Somehow it seems only the ADC0SS IRQ is working in the 'ADC_TRIGGER_ALWAYS' mode.

    I must be missing something

    Thanks

    David Nyarko

  • David,
    What is the reasoning behind setting the trigger to always? Because you are only using one ADC, the sequencers take turns based upon the priority in the sequence configuration. It is my understanding that if you keep triggering sequencer 0 then sequencers 1 and 2 will never have an opportunity because sequencer 0 has the highest priority.

    Regards,
    Chris
  • Hi Chris,
    I now understand why i have the problem.
    Actually what i want to do is the following;
    - Sample up to 12 ADC channels continuously sampling their inputs and implementing an interrupt mode.
    This will allow me to quickly respond to sensors connected to the ADC inputs which exhibit out-of-range values.


    From your explanation, that means i can do a maximum of 8 channels ( since SS0 has the maximum fifo size) using either ADC0 or ADC1.
    So the only solution might be what i have tested to work, which is:
    using ADC0 ( SS0) and ADC1 (SS0) both set to priority 0 but ADC1 has a lower interrupt priority than ADC0.
    ADC0 is assigned to Channels 0-7, and ADC1 to channels 8-11.

    Does this appear to be the best approach?
    Thanks
    David Nyarko
  • David,
    if you use the ADC_TRIGGER_ALWAYS will you have enough time to process the information before the next interrupt?

    Chris
  • Somehow your response does not show up in this ongoing thread:

    e2e.ti.com/.../2802925

    I just set a flag when in the ISR when i go over a threshold. This flag is used in the main program to stop my gate trigger pulses. An initial test appeared to work.

    I will perform further testing to confirm as the system is further developed.

  • David,
    Has any progress been made on this?

    Thanks,
    Chris
  • Hi Chris,

    I have been working on urgent improvements to an existing system so i have not had time to continue with this project.

    It seems in an interrupt mode, i might  be limited to using the ADC0 SS0 and ADC1 SS0 which will serve my needs since i need 12 channels

    since that can provide a maximum of 16 (8+8) channels.

  • David,
    I will close this but feel free to post or ask a related question if additional support is needed.

    Chris

**Attention** This is a public forum