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.

MSPM0L1306: ADC Result

Part Number: MSPM0L1306
Other Parts Discussed in Thread: SYSCONFIG

Tool/software:

I'm trying to learn and understand more of the MSPM0 boards. I'm attempting to use a potentiometer to adjust the toggle speed of the RGB LED to switch between R, G and B. Currently, the pot is connected to ground, 3.3v and PA25. I've set the ADC12 to channel 2 and conversion memory 0. I've attempted it with two different approaches, one as an ISR caused by a change in the ADC value, another where the ADC value is constantly checked from a called function. When debugged, the LED switches between R, G and B at the initial set delay rate, but adjusting the pot does not result in any change to the toggle speed. Any help on the matter would be greatly appreciated.

ISR code: 

#include "ti_msp_dl_config.h"

/* This results in approximately 1s of delay assuming 32MHz CPU_CLK */
int32_t DELAY = 32000000;

volatile uint16_t gAdcResult = 3000;

int main(void)
{
    SYSCFG_DL_init();
    NVIC_EnableIRQ(ADC12_0_INST_INT_IRQN);

    // Default: all pins off apart from Green LED
    DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);
    DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_RED_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN );

    DL_ADC12_enableConversions(ADC12_0_INST);
    DL_ADC12_startConversion(ADC12_0_INST);

    while (1) {
       
        /* Start our ADC conversion */

        /* Currently switches between RGB LEDs in pattern */

        DELAY =  (32000000*2*gAdcResult) / 4096;
        //delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_RED_LED_1_PIN | GPIO_LEDS_USER_LED_2_PIN);
       
        DELAY =  (32000000*2*gAdcResult) / 4096;
        //delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);

        DELAY =  (32000000*2*gAdcResult) / 4096;
        //delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_RED_LED_1_PIN);
    }
}

//Interrupt signal for new ADC value
void ADC12_0_INST_IRQHandler(void) {
    switch (DL_ADC12_getPendingInterrupt(ADC12_0_INST)) {
        case DL_ADC12_IIDX_MEM0_RESULT_LOADED:
            gAdcResult = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
            break;
        default:
            break;
    }
}
Function Call code:
#include "ti_msp_dl_config.h"

/* This results in approximately 1s of delay assuming 32MHz CPU_CLK */
int32_t DELAY = 32000000;
#define DELAY_MIN (10000000)
#define DELAY_MAX (64000000)
 
volatile uint16_t gAdcResult = 3000;

int32_t get_delay(void) {

    int32_t ADC_RESULT = 0;

    DL_ADC12_startConversion(ADC12_0_INST);

    delay_cycles(1000); //Delay to let ADC update

    ADC_RESULT = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0);
   
    int32_t delay = (ADC_RESULT*DELAY_MAX)/4096 + DELAY_MIN;

    return delay;
}

int main(void)
{
    SYSCFG_DL_init();
    DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);
    DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_RED_LED_1_PIN | GPIO_LEDS_USER_TEST_PIN );

    DL_ADC12_enableConversions(ADC12_0_INST);
    DL_ADC12_startConversion(ADC12_0_INST);

    while (1) {

        /* Currently switches between RGB LEDs in pattern */

        DELAY = get_delay();
        delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_RED_LED_1_PIN | GPIO_LEDS_USER_LED_2_PIN);
       
        DELAY = get_delay();
        delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);

        DELAY = get_delay();
        delay_cycles(DELAY);
        DL_GPIO_togglePins(GPIO_LEDS_PORT,
            GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_RED_LED_1_PIN);
    }
}
  • Hello Carwyn, 

    Thank you for contacting us. Moreover, for your ADC configuration, can you access SysConfig and take screenshots of your ADC's conversion mem 0 configuration, advanced configuration, and interrupt configuration? Also, it would be helpful if you can attach the ti_msp_dl_config.c file. 

    Regards, Jojo

  • I'm not completely sure I understand how this is supposed to work, but some observations:

    1) My calculator says that this line

       int32_t delay = (ADC_RESULT*DELAY_MAX)/4096 + DELAY_MIN;

    is prone to overflow. Try changing:

    #define DELAY_MAX (64000000)

    to:
    #define DELAY_MAX (64000000ULL)
    2) A common problem with using a potentiometer is that the input impedance can easily get very high, requiring a fairly long sample/hold time. Hardware Development Guide (SLAAE76B) Sec 6.1 [this is for the G series, but I think the ADCs are the same] has a formula, but try extending the sampling time in Sysconfig (start with maybe 10x, since your code isn't in a big hurry). 
  • Here is ti_msp_dl_config.c file:

    /*
     * Copyright (c) 2023, 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.
     */
    
    /*
     *  ============ ti_msp_dl_config.c =============
     *  Configured MSPM0 DriverLib module definitions
     *
     *  DO NOT EDIT - This file is generated for the MSPM0L130X
     *  by the SysConfig tool.
     */
    
    #include "ti_msp_dl_config.h"
    
    /*
     *  ======== SYSCFG_DL_init ========
     *  Perform any initialization needed before using any board APIs
     */
    SYSCONFIG_WEAK void SYSCFG_DL_init(void)
    {
        SYSCFG_DL_initPower();
        SYSCFG_DL_GPIO_init();
        /* Module-Specific Initializations*/
        SYSCFG_DL_SYSCTL_init();
        SYSCFG_DL_TIMER_0_init();
        SYSCFG_DL_ADC12_0_init();
    }
    
    SYSCONFIG_WEAK void SYSCFG_DL_initPower(void)
    {
        DL_GPIO_reset(GPIOA);
        DL_TimerG_reset(TIMER_0_INST);
        DL_ADC12_reset(ADC12_0_INST);
    
        DL_GPIO_enablePower(GPIOA);
        DL_TimerG_enablePower(TIMER_0_INST);
        DL_ADC12_enablePower(ADC12_0_INST);
        delay_cycles(POWER_STARTUP_DELAY);
    }
    
    SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void)
    {
    
        
    	DL_GPIO_setAnalogInternalResistor(GPIO_ADC12_0_IOMUX_C2, DL_GPIO_RESISTOR_NONE);
    
        DL_GPIO_initDigitalOutput(GPIO_LEDS_RED_LED_1_IOMUX);
    
        DL_GPIO_initDigitalOutput(GPIO_LEDS_USER_LED_2_IOMUX);
    
        DL_GPIO_initDigitalOutput(GPIO_LEDS_USER_LED_3_IOMUX);
    
        DL_GPIO_initDigitalOutput(GPIO_LEDS_USER_TEST_IOMUX);
    
        DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_RED_LED_1_PIN |
    		GPIO_LEDS_USER_LED_2_PIN |
    		GPIO_LEDS_USER_LED_3_PIN |
    		GPIO_LEDS_USER_TEST_PIN);
        DL_GPIO_enableOutput(GPIO_LEDS_PORT, GPIO_LEDS_RED_LED_1_PIN |
    		GPIO_LEDS_USER_LED_2_PIN |
    		GPIO_LEDS_USER_LED_3_PIN |
    		GPIO_LEDS_USER_TEST_PIN);
    
    }
    
    
    
    SYSCONFIG_WEAK void SYSCFG_DL_SYSCTL_init(void)
    {
    
    	//Low Power Mode is configured to be SLEEP0
        DL_SYSCTL_setBORThreshold(DL_SYSCTL_BOR_THRESHOLD_LEVEL_0);
    
        
    	DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE);
    
    }
    
    
    
    /*
     * Timer clock configuration to be sourced by LFCLK /  (32768 Hz)
     * timerClkFreq = (timerClkSrc / (timerClkDivRatio * (timerClkPrescale + 1)))
     *   32768 Hz = 32768 Hz / (1 * (0 + 1))
     */
    static const DL_TimerG_ClockConfig gTIMER_0ClockConfig = {
        .clockSel    = DL_TIMER_CLOCK_LFCLK,
        .divideRatio = DL_TIMER_CLOCK_DIVIDE_1,
        .prescale    = 0U,
    };
    
    /*
     * Timer load value (where the counter starts from) is calculated as (timerPeriod * timerClockFreq) - 1
     * TIMER_0_INST_LOAD_VALUE = (0 ms * 32768 Hz) - 1
     */
    static const DL_TimerG_TimerConfig gTIMER_0TimerConfig = {
        .period     = TIMER_0_INST_LOAD_VALUE,
        .timerMode  = DL_TIMER_TIMER_MODE_ONE_SHOT,
        .startTimer = DL_TIMER_STOP,
    };
    
    SYSCONFIG_WEAK void SYSCFG_DL_TIMER_0_init(void) {
    
        DL_TimerG_setClockConfig(TIMER_0_INST,
            (DL_TimerG_ClockConfig *) &gTIMER_0ClockConfig);
    
        DL_TimerG_initTimerMode(TIMER_0_INST,
            (DL_TimerG_TimerConfig *) &gTIMER_0TimerConfig);
        DL_TimerG_enableClock(TIMER_0_INST);
    
    
    
    
    
    }
    
    
    /* ADC12_0 Initialization */
    static const DL_ADC12_ClockConfig gADC12_0ClockConfig = {
        .clockSel       = DL_ADC12_CLOCK_SYSOSC,
        .divideRatio    = DL_ADC12_CLOCK_DIVIDE_1,
        .freqRange      = DL_ADC12_CLOCK_FREQ_RANGE_24_TO_32,
    };
    SYSCONFIG_WEAK void SYSCFG_DL_ADC12_0_init(void)
    {
        DL_ADC12_setClockConfig(ADC12_0_INST, (DL_ADC12_ClockConfig *) &gADC12_0ClockConfig);
        DL_ADC12_initSingleSample(ADC12_0_INST,
            DL_ADC12_REPEAT_MODE_ENABLED, DL_ADC12_SAMPLING_SOURCE_AUTO, DL_ADC12_TRIG_SRC_SOFTWARE,
            DL_ADC12_SAMP_CONV_RES_12_BIT, DL_ADC12_SAMP_CONV_DATA_FORMAT_UNSIGNED);
        DL_ADC12_configConversionMem(ADC12_0_INST, ADC12_0_ADCMEM_0,
            DL_ADC12_INPUT_CHAN_2, DL_ADC12_REFERENCE_VOLTAGE_VDDA, DL_ADC12_SAMPLE_TIMER_SOURCE_SCOMP0, DL_ADC12_AVERAGING_MODE_DISABLED,
            DL_ADC12_BURN_OUT_SOURCE_DISABLED, DL_ADC12_TRIGGER_MODE_AUTO_NEXT, DL_ADC12_WINDOWS_COMP_MODE_DISABLED);
        DL_ADC12_enableFIFO(ADC12_0_INST);
        DL_ADC12_setSampleTime0(ADC12_0_INST,32000);
        /* Enable ADC12 interrupt */
        DL_ADC12_clearInterruptStatus(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM0_RESULT_LOADED));
        DL_ADC12_enableInterrupt(ADC12_0_INST,(DL_ADC12_INTERRUPT_MEM0_RESULT_LOADED));
        DL_ADC12_enableConversions(ADC12_0_INST);
    }
    
    

    I can't provide screen shots, but this covers the information required:

    • ADC Conversion Memory Configurations
    Active Memory Control Blocks 
    ADC Conversion Memory 0
    • ADC Conversion Memory 0 Configuration
    Name 
    0
    Input Channel 
    Channel 2
    Device Pin Name 
    PA25
    Reference Voltage 
    VDDA
    VDDA 
    3.3V
    Sample Period Source 
    Sampling Timer 0
    ADC Conversion Period  1ms
    • Advanced Configuration
    Total Conversion Frequency 
    999.47Hz
    Conversion Resolution 
    12 bits
    Enable FIFO Mode 
    Enabled
    Power Down Mode 
    Auto
    Desired Sample Time 0 
    1ms
    Actual Sample Time 0 
    1ms
    Desired Sample Time 1 
    0ms
    Actual Sample Time 1  0ms
    • Interrupt Configuration
    Enable Interrupts 
    MEM0 result loaded interrupt
    Interrupt Priority 
    Default

  • Thanks for submitting this information. Have you tested with 10x the sampling time as indicated by Bruce? Did that by chance make any difference?

  • 1ms is actually a pretty long sample/hold time.

    I (still) recommend fixing the overflow thing [(1) above].

  • I've fixed the overflow, and yet the issue persists. When debugging,  ADC_RESULT always remains 0, even after  ADC_RESULT = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0). Currently the LEDs switch between R, G and B fine, at the set speed of MIN_DELAY.

  • I just noticed:

    > DL_ADC12_enableFIFO(ADC12_0_INST);

    I suggest you not enable the FIFO, since you're reading using getMemResult(). TRM (SLAU847D) Sec 14.2.12.2 says "Single conversion mode with FIFO enabled is not recommended for CPU or DMA based operation." I expect this is because "MEMCTLx is NOT correlated to MEMRESx." since "MEMRES0,1,2,….N [is] (organized as a FIFO)". In any case, the FIFO just complicates things if you don't need it.

    To disable the FIFO, un-check the Sysconfig box at "ANALOG->ADC12->ADC12_0->Advanced->Enable FIFO Mode".

  • FIFO mode has been turned off, yet I still cannot get an ADC reading. This is my current code in main.c: 

    #include "ti_msp_dl_config.h"
    
    int32_t DELAY;
    int32_t ADC_RESULT;
    #define DELAY_MIN (3000000) // roughly 0.3s
    #define DELAY_MAX (64000000ULL) // roughly 2s
    
    uint32_t get_delay(void) {
    
        DL_ADC12_startConversion(ADC12_0_INST);
    
        delay_cycles(1000); //allow ADC to update
    
        ADC_RESULT = DL_ADC12_getMemResult(ADC12_0_INST, DL_ADC12_MEM_IDX_0); //ADC result
        
        int32_t delay = ((ADC_RESULT*(DELAY_MAX-DELAY_MIN))/4096) + DELAY_MIN; //calculate delay value
    
        return delay;
    }
    
    int main(void)
    {
        /* Power on GPIO, initialize pins as digital outputs */
        SYSCFG_DL_init();
    
        // Default: all pins off apart from red LED of RGB LED
        DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);
        DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_TEST_PIN | GPIO_LEDS_USER_LED_1_PIN );
    
        DL_ADC12_startConversion(ADC12_0_INST);
    
        while (1) {
    
            // Currently switches between RGB LEDs in sequence
    
            DELAY = get_delay();
            delay_cycles(DELAY);
            DL_GPIO_togglePins(GPIO_LEDS_PORT,
                 GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_LED_2_PIN);
            
            DELAY = get_delay();
            delay_cycles(DELAY);
            DL_GPIO_togglePins(GPIO_LEDS_PORT,
                GPIO_LEDS_USER_LED_2_PIN | GPIO_LEDS_USER_LED_3_PIN);
    
            DELAY = get_delay();
            delay_cycles(DELAY);
            DL_GPIO_togglePins(GPIO_LEDS_PORT,
                GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_USER_LED_1_PIN);
        }
    }

    I have connected the pot to PA25, ground, and 3.3v provided by the J1 pin header. From comparison to other ADC files in the SDK, i cannot see a clear blunder.

  • Are you using the Launchpad? The schematics [Ref Launchpad UG (SLAU869E) Sec 5] show PA25 also connected to J4. Have you removed that jumper?

  • Yes I am using the Launchpad, I know nothing of a jumper between J1 and J4. There is no physical connection I can see.

  • J4 connects PA25 to GND (also to the Photodiode) if there is a jumper block installed on it. I think that jumper block is installed when you get it. It should be removed.

    [Disclaimer: I'm pretty sure there were two versions of the L1306 Launchpad. Rev E (but not Rev D) of the Launchpad User Guide matches the (later) board revision I got.]

  • There is no jumper block on PA25

  • What does a voltmeter show if you connect it to the high side of J4? (Probably the pin further from the board edge.)

    I have connected the pot to PA25, ground, and 3.3v provided by the J1 pin header. 

    I don't see 3.3v nor GND on J1. I see PA25, PA15, and a thermistor circuit. Now I wonder if we're looking at the same board (Rev).

    [Edit: I think I see now: The Booster Pack headers are also named J1-J4, i.e. the designators are duplicated (!). I'm thinking of J4 down at the bottom of the board (alongside J5/J6).]

  • Hello Carwyn, 

    Hope you are having a good day. Nevertheless, wanted to verify if there is any update on this based on Bruce's last comment. If not, kindly let me know as well. 

    Regards, Jojo

  • Hello,

    A new project was created in CCS and a similar code was created. This newer version works completely fine, with no errors. I am unsure of what the previous problem was.

    Thank you both for your help.