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.

MSP430FR2675: TA0.1 Produces PWM with Double Expected Period

Part Number: MSP430FR2675
Other Parts Discussed in Thread: MSP430WARE

I'm using the driverlib SDK Timer_A_outputPWM() function to generate a PWM signal on TA0.1.  A PWM signal is produced with the correct duty cycle, but the period is twice what I expect it to be.

By my calculations, the period should be 20ms, but the period of the generated waveform is 40ms.  SMCLK is running at about 2 MHz.  I have verified this using CS_getSMCLK(). I'm dividing the clock signal by 40 down to 50 KHz.  1000 / 50 KHz should be 20ms, or am I missing something?

GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1, GPIO_PIN1, GPIO_SECONDARY_MODULE_FUNCTION);
Timer_A_outputPWMParam TimerParams;
TimerParams.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
TimerParams.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_40;
TimerParams.timerPeriod = 1000 - 1;
TimerParams.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
TimerParams.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
TimerParams.dutyCycle = 750;
Timer_A_outputPWM(TA0_BASE, &TimerParams);
  • I added a wrapper and ran this on an FR2476, which is roughly the same generation. I get a 20ms period.

    Suggestion is that something is going on outside this code. How are you setting up SMCLK? You might try putting SMCLK out on P1.7.

    #include "driverlib.h"
    uint32_t SMCLK_Hz;
    int main(void) {
        WDT_A_hold(WDT_A_BASE);
    
        //  SMCLK=2MHz
        __bis_SR_register(SCG0);                // disable FLL
        CSCTL3 |= SELREF__REFOCLK;              // Set REFO as FLL reference source
        CSCTL1 = DCOFTRIMEN_1 | DCOFTRIM0 | DCOFTRIM1 | DCORSEL_1;// DCOFTRIM=3, DCO Range = 2MHz
        CSCTL2 = FLLD_0 + 60;                  // DCODIV = 2MHz
        __delay_cycles(3);
        __bic_SR_register(SCG0);                // enable FLL
        CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
        SMCLK_Hz = CS_getSMCLK();
    
        //  Run PWM on P1.1 (TA0.1)
        PM5CTL0 &= ~LOCKLPM5;
        GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1, GPIO_PIN1, GPIO_SECONDARY_MODULE_FUNCTION);
        Timer_A_outputPWMParam TimerParams;
        TimerParams.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
        TimerParams.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_40;
        TimerParams.timerPeriod = 1000 - 1;
        TimerParams.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
        TimerParams.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
        TimerParams.dutyCycle = 750;
        Timer_A_outputPWM(TA0_BASE, &TimerParams);
    
        while (1)
        {
            LPM0;
        }
        /*NOTREACHED*/
        return (0);
    }
    

  • Thank you.  I've determined that the returned value (~4 MHz) from CS_getSMCLK() is inconsistent with the measured SMCLK value (~2 MHz).  The measured 2 MHz value is what I expect.  The erroneous value from CS_getSMCLK() is what is throwing off my calculation.  Do you have any ideas as to why CS_getSMCLK() is returning an incorrect value?  A bug in driverlib maybe?

  • Hello, 

    Are you using an external clock/crystal?  I see there is a note for the cs_getSMCLK() that requires CS_setExternalClockSource() api to be called first if using an external clock source.  

    Thanks,

    JD

  • No, only the internal oscillators are being used.  SMCLK is driven by the DCO.

  • I believe getSMCLK works most of the time, so It sounds like you've tripped over a way to fool it. That's why I was curious what calls you made to set it.

    getSMCLK computes the number from "first principles", based on the current CS settings. In my case it got it right (1998848 Hz). There's rather a lot of code there, so knowing what your CS settings actually are would give me a head-start.

    [Edit: I should probably also ask what version of driverlib (MSP430Ware) you're using. I was using 3.80.13.03.]

  • That's interesting.  I believe I'm using an older version of MSP430Ware; it came precompiled with the demo code for the BOOSTXL-CAPKEYPAD.  The version.h file. indicates that I'm using 2.91.12.08.  I could try updating.  However, I think the version.h file may not be kept up to date.  The MSP430Ware installation folder on my machine indicates version 3.80.13.03, but the version file under MSP430Ware_3_80_13_03/driverlib/driverlib/MSP430FR2xx_4xx/inc/version.h indicates 2.21.13.01.

  • I tried updating to MSP430Ware_3_80_13_03, but the problem persists.  My board.c file is attached, which has the clock initialization code.  It's basically lifted from the demo code for the BOOSTXL-CAPKEYPAD with some minor modifications to support printf.

    When the code in the attached file to enable printf is deactivated CS_getSMCLK() returns the correct value.  However, when the code to enable printf is activated CS_getSMCLK() returns the incorrect, doubled value.  In both cases the measured SMCLK frequency is correct.

    /* --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--*/
    //#############################################################################
    //
    //! \file   board.c
    //!
    //! \brief  This is the board file for the BSL keypad
    //
    
    //*****************************************************************************
    // Includes
    //*****************************************************************************
    
    #include "board.h"
    #include "config.h"
    
    //*****************************************************************************
    // Function Implementations
    //*****************************************************************************
    #define USE_CAPTIVATE_CENTER 0
    
    void Board_init(void)
    {
        uint8_t ui8LFOsc = DEFAULT_OSC_SELECTION;
    
        // P1.0: OUTPUT LOW
        // P1.1: IRQ (OPEN DRAIN)
        // P1.2: UCB0 I2C SDA
        // P1.3: UCB0 I2C SCL
        // P1.4: UCA0 UART TXD
        // P1.5: UCA0 UART RXD
        // P1.6: LED2
        // P1.7: LED1
    
    #if USE_CAPTIVATE_CENTER
        P1OUT  = (0);
        P1DIR  = (GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN6 | GPIO_PIN7);
        P1SEL0 = (GPIO_PIN2 | GPIO_PIN3 | GPIO_PIN4 | GPIO_PIN5);
        P1SEL1 = (0);
    #else
        P1OUT  = (0);
        P1DIR  = (GPIO_PIN0 | GPIO_PIN1 | GPIO_PIN4 | GPIO_PIN5 | GPIO_PIN6 | GPIO_PIN7);
        P1SEL0 = (GPIO_PIN2 | GPIO_PIN3);
        P1SEL1 = (0);
    #endif
    
        P2OUT  =  (0);
        P2DIR  =  (GPIO_PIN0 | GPIO_PIN1);
        P2SEL0 =  (0);
        P2SEL1 =  (0);
    
        P3OUT  =  (0);
        P3DIR  =  (GPIO_PIN5 | GPIO_PIN2 | GPIO_PIN6);
        P3SEL0 =  (0);
        P3SEL1 =  (0);
    
        //
        // Clear port lock
        //
        PM5CTL0 &= ~LOCKLPM5;
    	
    	// Configure FRAM wait state (set to 1 to support 16MHz MCLK)
    #if !defined(CONFIG_ENABLE_PRINTF_SUPPORT) || (CONFIG_ENABLE_PRINTF_SUPPORT == 0)
    	FRAMCtl_configureWaitStateControl(FRAMCTL_ACCESS_TIME_CYCLES_1);
    #else
      FRAMCtl_configureWaitStateControl(FRAMCTL_ACCESS_TIME_CYCLES_0);
    #endif
    
    #if (DEFAULT_OSC_SELECTION == CS_XT1CLK_SELECT)
        //
        // Attempt to start the low frequency crystal oscillator
        //
        CS_setExternalClockSource(XT1_OSC_FREQ);
        if (CS_turnOnXT1LFWithTimeout(CS_XT1_DRIVE_0, XT1_OSC_TIMEOUT) == STATUS_FAIL)
        {
            //
            // If a crystal is not present or is failing, switch the LF
            // clock definition to the internal 32kHz reference oscillator.
            //
            ui8LFOsc = CS_REFOCLK_SELECT;
        }
    #endif
    
        //
        // Initialize Clock Signals
        //
        CS_initClockSignal(CS_FLLREF, ui8LFOsc, CS_CLOCK_DIVIDER_1);
        CS_initClockSignal(CS_ACLK, ui8LFOsc, CS_CLOCK_DIVIDER_1);
    #if !defined(CONFIG_ENABLE_PRINTF_SUPPORT) || (CONFIG_ENABLE_PRINTF_SUPPORT == 0)
        CS_initClockSignal(CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_1);
        CS_initClockSignal(CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_8);
    #else
        CS_initClockSignal(CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_2);
        CS_initClockSignal(CS_SMCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_4);
    #endif
    
        //
        // Tune the DCO parameters
        //
        CS_initFLL((MCLK_FREQ/1000), FLL_RATIO);
        CS_clearAllOscFlagsWithTimeout(1000);
    }
    
    int _system_pre_init(void)
    {
        // Disable watchdog timer before CINIT.
        WDTCTL = WDTPW | WDTHOLD;
        return 1;
    }
    

  • It looks to me like a bug in getSMCLK -- I don't see anything that accounts for the fact that the SMCLK divider is not (only) DIVS but rather (DIVS*DIVM). [Ref User Guide (SLAU445I) Fig 3-2 and Table 3-9]

    So any time 

    > CS_initClockSignal(CS_MCLK, CS_DCOCLKDIV_SELECT, CS_CLOCK_DIVIDER_2);

    is called with anything other than CS_CLOCK_DIVIDER_1, it will be off by that divider factor. A possible patch might be to add this to CS_getSMCLK (after cs.c:1040):

    #if 1 || BMC
        uint16_t MCLKSourceDivider =  HWREG16(CS_BASE + OFS_CSCTL5) & DIVM_7;
        SMCLKSourceDivider += (MCLKSourceDivider >> 0); // 2^(DIVM+DIVS) implied
    #endif
    

  • That patch does appear to fix the problem.  What is the significance of BMC?

  • BMC is just Bruce MCKenney. I wanted to mark it somehow, but didn't want to clutter too much.

  • Thanks for the great support,

**Attention** This is a public forum