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.

CCS/MSP430F5529: Generating a PWM waveform with particular specification using interrupt driven Timer Library.

Part Number: MSP430F5529

Tool/software: Code Composer Studio

Original Thread:-e2e.ti.com/.../737345

#include "driverlib.h"

#define TIMER_PERIOD 13105
#define DUTY_CYCLE 82

void main (void)
{
//Stop WDT
WDT_A_hold(WDT_A_BASE);

//P2.0 as PWM output
GPIO_setAsPeripheralModuleFunctionOutputPin(
GPIO_PORT_P2,
GPIO_PIN0
);
//GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0);
//Generate PWM - Timer runs in Up mode
Timer_A_outputPWMParam param = {0};
param.clockSource = TIMER_A_CLOCKSOURCE_ACLK;
param.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;
param.timerPeriod = TIMER_PERIOD;
param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
param.dutyCycle = DUTY_CYCLE;
Timer_A_outputPWM(TIMER_A1_BASE, &param);

//Enter LPM0
__bis_SR_register(LPM0_bits);

//For debugger
__no_operation();
}

I have written this OutputPWM based MSPware code, which generates the waveform, I desire. I was hoping if I could get the same thing going where I have control over when I toggle the output and perhaps do some processing. From what I know, this can only be done using an ISR, and MSPware Timer library provides the Timer_up/down/upDown along with Capture and compare. However, I am unable to get it to work with the required specifications[4s total period,~25ms ON time]. Could someone help me out? 

  • Why aren't you configuring or enabling interrupts? That's most likely one of the reasons your code is not working. Please review our timer PWM DriverLib code examples and compare them to what you're doing.

    timer_a_ex5_pwmMultipleUp.c

    /* --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--*/
    //*******************************************************************************
    //!  This program generates two PWM outputs on P2.0,P2.1 using
    //!  Timer1_A configured for up mode. The value in CCR0, 512-1, defines the PWM
    //!  period and the values in CCR1 and CCR2 the PWM duty cycles. Using ~1.045MHz
    //!  SMCLK as TACLK, the timer period is ~500us with a 75% duty cycle on P2.2
    //!  and 25% on P2.3.
    //!  ACLK = n/a, SMCLK = MCLK = TACLK = default DCO ~1.045MHz.
    //!
    //!  Tested On: MSP430F5529
    //!            -------------------
    //!        /|\|                   |
    //!         | |                   |
    //!         --|RST                |
    //!           |                   |
    //!           |         P2.0/TA1.1|--> CCR1 - 75% PWM
    //!           |         P2.1/TA1.2|--> CCR2 - 25% PWM
    //!
    //! This example uses the following peripherals and I/O signals.  You must
    //! review these and change as needed for your own board:
    //! - Timer peripheral
    //! - GPIO Port peripheral
    //!
    //! This example uses the following interrupt handlers.  To use this example
    //! in your own application you must add these interrupt handlers to your
    //! vector table.
    //! - NONE
    //!
    //*****************************************************************************
    
    #include "driverlib.h"
    
    #define TIMER_PERIOD 511
    #define DUTY_CYCLE1  384
    #define DUTY_CYCLE2  128
    
    void main (void)
    {
        //Stop WDT
        WDT_A_hold(WDT_A_BASE);
    
        //P2.0 and P2.1 output
        //P2.0 and P2.1 options select
        GPIO_setAsPeripheralModuleFunctionOutputPin(
            GPIO_PORT_P2,
            GPIO_PIN0 + GPIO_PIN1
            );
    
        //Start timer
        Timer_A_initUpModeParam initUpParam = {0};
        initUpParam.clockSource = TIMER_A_CLOCKSOURCE_SMCLK;
        initUpParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_1;
        initUpParam.timerPeriod = TIMER_PERIOD;
        initUpParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
        initUpParam.captureCompareInterruptEnable_CCR0_CCIE =
            TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE;
        initUpParam.timerClear = TIMER_A_DO_CLEAR;
        initUpParam.startTimer = false;
        Timer_A_initUpMode(TIMER_A1_BASE, &initUpParam);
    
        Timer_A_startCounter(TIMER_A1_BASE,
        		TIMER_A_UP_MODE
        		);
    
        //Initialize compare mode to generate PWM1
        Timer_A_initCompareModeParam initComp1Param = {0};
        initComp1Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
        initComp1Param.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
        initComp1Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
        initComp1Param.compareValue = DUTY_CYCLE1;
        Timer_A_initCompareMode(TIMER_A1_BASE, &initComp1Param);
    
        //Initialize compare mode to generate PWM2
        Timer_A_initCompareModeParam initComp2Param = {0};
        initComp2Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
        initComp2Param.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_DISABLE;
        initComp2Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
        initComp2Param.compareValue = DUTY_CYCLE2;
        Timer_A_initCompareMode(TIMER_A1_BASE, &initComp2Param);
    
        //Enter LPM0
        __bis_SR_register(LPM0_bits);
    
        //For debugger
        __no_operation();
    }

    Regards,

    James

  • Hi James, Yeah I have seen this example and I will post my code in just a while as well. But the example you posted doesn't use an ISR. I excplictly want an ISR because I want to set a flag when my pulse is in ON period and reset once ISR is finished. I don't suppose I could do that with this code. Also, I wanted to find out that in this code on P2.0 there is a pulse which has 75% duty cycle and on P2.1 there is another?
  • Here is my code, while the control goes in the ISR, I don't get the required specification pulse. I am unsure about which ISR I should toggle my pin. And I ma unable to get to trigge the ISR

    #include "driverlib.h"

    #define TIMER_PERIOD 13105
    #define DUTY_CYCLE1 82
    #define DUTY_CYCLE2 128

    void main (void)
    {
    //Stop WDT
    WDT_A_hold(WDT_A_BASE);

    //P2.0 and P2.1 output
    //P2.0 and P2.1 options select
    GPIO_setAsPeripheralModuleFunctionOutputPin(
    GPIO_PORT_P2,
    GPIO_PIN0 + GPIO_PIN2
    );

    //Start timer
    Timer_A_initUpModeParam initUpParam = {0};
    initUpParam.clockSource = TIMER_A_CLOCKSOURCE_ACLK;
    initUpParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;
    initUpParam.timerPeriod = TIMER_PERIOD;
    initUpParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_ENABLE;
    initUpParam.captureCompareInterruptEnable_CCR0_CCIE =
    TIMER_A_CCIE_CCR0_INTERRUPT_ENABLE;
    initUpParam.timerClear = TIMER_A_DO_CLEAR;
    initUpParam.startTimer = false;
    Timer_A_initUpMode(TIMER_A1_BASE, &initUpParam);

    Timer_A_startCounter(TIMER_A1_BASE,
    TIMER_A_UP_MODE
    );

    //Initialize compare mode to generate PWM1
    Timer_A_initCompareModeParam initComp1Param = {0};
    initComp1Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    initComp1Param.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initComp1Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    initComp1Param.compareValue = DUTY_CYCLE1;
    Timer_A_initCompareMode(TIMER_A1_BASE, &initComp1Param);

    //Initialize compare mode to generate PWM2
    Timer_A_initCompareModeParam initComp2Param = {0};
    initComp2Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
    initComp2Param.compareInterruptEnable = TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initComp2Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    initComp2Param.compareValue = DUTY_CYCLE1;
    Timer_A_initCompareMode(TIMER_A1_BASE, &initComp2Param);

    //Enter LPM0
    __bis_SR_register(LPM0_bits);

    //For debugger
    __no_operation();
    }

    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=TIMER1_A1_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(TIMER1_A1_VECTOR)))
    #endif
    void TIMER1_A0_ISR (void)
    {
    uint16_t compVal = Timer_A_getCaptureCompareCount(TIMER_A1_BASE,
    TIMER_A_CAPTURECOMPARE_REGISTER_0)
    + DUTY_CYCLE1;

    //Toggle P1.0
    GPIO_toggleOutputOnPin(
    GPIO_PORT_P2,
    GPIO_PIN0
    );

    //Add Offset to CCR0
    Timer_A_setCompareValue(TIMER_A1_BASE,
    TIMER_A_CAPTURECOMPARE_REGISTER_0,
    compVal
    );
    }

  • > GPIO_setAsPeripheralModuleFunctionOutputPin( GPIO_PORT_P2, GPIO_PIN0 + GPIO_PIN2 );
    Typo alert: I think you meant:
    > GPIO_setAsPeripheralModuleFunctionOutputPin( GPIO_PORT_P2, GPIO_PIN0 + GPIO_PIN1 ); // TA1.1, TA1.2
    -----------------
    You aren't getting an interrupt since there's nothing that enables interrupts globally (GIE). Replace:
    > __bis_SR_register(LPM0_bits);
    with
    > __bis_SR_register(LPM0_bits | GIE);
    -----------------
    Since you're running in Up mode, you shouldn't do this:
    > Timer_A_setCompareValue(TIMER_A1_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_0,compVal);
    since it will just make the period longer and longer until it wraps and becomes very short. Just remove this line.
    -----------------
    If you want an in-memory flag(s) to mirror the current setting of the PWM: You know when it has gone low (Reset/Set) in your TIMER1_A0_VECTOR. Knowing when it goes high requires defining a TIMER1_A1_VECTOR to catch (and reset) the CCR1 and CCR2 CCIFGs.

    Alternatively: Consider just checking P2IN directly, since at any moment it will reflect the state of the pin(s). [Ref pin schematic in Data Sheet (SLAS590N) Fig. 6-3. I think the User Guide also mentions this, but I forget where.]
  • 1. I used Pin 0 and Pin 2, because those are the pins available to me on the board as breakout pins.

    2. Yeah, made a mistake in not enabling in the interrupts.

    3. Yep, made changes to my code and here is my new code. Essentially I want something like the waveform attached in the message. During the high time, I want to turn on the ADC Channel A0. Is my thinking[as per the new code in the right direction]:

    Snippets and relevant part of the code:

    void configureTimerForSensorPulse()
    {
    Timer_A_initUpDownModeParam initUpDownParam = { 0 };
    initUpDownParam.clockSource = TIMER_A_CLOCKSOURCE_ACLK;
    initUpDownParam.clockSourceDivider = TIMER_A_CLOCKSOURCE_DIVIDER_10;
    initUpDownParam.timerPeriod = TIMER_PERIOD;
    initUpDownParam.timerInterruptEnable_TAIE = TIMER_A_TAIE_INTERRUPT_DISABLE;
    initUpDownParam.captureCompareInterruptEnable_CCR0_CCIE =
    TIMER_A_CCIE_CCR0_INTERRUPT_DISABLE;
    initUpDownParam.timerClear = TIMER_A_DO_CLEAR;
    initUpDownParam.startTimer = false;
    Timer_A_initUpDownMode(TIMER_A0_BASE, &initUpDownParam);

    //Initialze compare registers to generate PWM1
    Timer_A_initCompareModeParam initComp1Param = { 0 };
    initComp1Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_1;
    initComp1Param.compareInterruptEnable =
    TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initComp1Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    initComp1Param.compareValue = DUTY_CYCLE;
    Timer_A_initCompareMode(TIMER_A0_BASE, &initComp1Param);

    //Initialze compare registers to generate PWM2
    Timer_A_initCompareModeParam initComp2Param = { 0 };
    initComp2Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_2;
    initComp2Param.compareInterruptEnable =
    TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initComp2Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    initComp2Param.compareValue = DUTY_CYCLE;
    Timer_A_initCompareMode(TIMER_A0_BASE, &initComp2Param);

    Timer_A_initCompareModeParam initComp3Param = { 0 };
    initComp3Param.compareRegister = TIMER_A_CAPTURECOMPARE_REGISTER_3;
    initComp3Param.compareInterruptEnable =
    TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE;
    initComp3Param.compareOutputMode = TIMER_A_OUTPUTMODE_RESET_SET;
    initComp3Param.compareValue = DUTY_CYCLE;
    Timer_A_initCompareMode(TIMER_A0_BASE, &initComp3Param);
    }

    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=TIMER0_A1_VECTOR
    __interrupt
    #elif defined(__GNUC__)
    __attribute__((interrupt(TIMER0_A1_VECTOR)))
    #endif
    void TIMER1_A1_ISR(void)
    {
    switch (__even_in_range(TA0IV, 10))
    {
    case 0x00:
    break;
    case 0x02:
    adcSensor1Enable = 0;
    break;
    case 0x04:
    adcSensor2Enable = 0;
    break;
    case 0x06:
    adcSensor3Enable = 0;
    break; // CCR3 IFG
    case 0x08:
    break; // CCR4 IFG
    case 0x0A:
    break; // CCR5 IFG
    case 0x0C:
    break; // CCR6 IFG
    case 0x0E: // TA0IFG
    break;
    default:
    break;

    }
    __bic_SR_register_on_exit(LPM4_bits);
    //__no_operation();

    }

    void main(void)
    {

    //These flags are global variables:

    adcSensor1Enable == 1;
    adcSensor2Enable == 1;
    adcSensor3Enable == 1;

    //Stop WDT
    WDT_A_hold(WDT_A_BASE);

    configureTimerForSensorPulse();

    configureADC();

    __enable_interrupt();

    startADC();

    while (1)
    {

    //If any of the flag is low it means ADC capture should start
    if (adcSensor1Enable == 0 || adcSensor2Enable == 0
    || adcSensor3Enable == 0)
    {
    ADC12_A_startConversion(ADC12_A_BASE,
    ADC12_A_MEMORY_0,
    ADC12_A_SEQOFCHANNELS);
    }

    //Stop ADC capture when all are low
    if (adcSensor1Enable == 0 && adcSensor2Enable == 0
    && adcSensor3Enable == 0)
    {

    adcSensor1Enable == 1;
    adcSensor2Enable == 1;
    adcSensor3Enable == 1;

    }
    //Enter LPM4, Enable interrupts
    __bis_SR_register(LPM4_bits + GIE);
    //For debugger
    __no_operation();
    }

    }

  • Did you catch the part about just looking at the PWM value directly? i.e., treating the PWM output as an input and reading it? That should solve your problem, no interrupt needed.
  • No actually,I din't understand that part. How would I do that?
  • To answer the question: I was just mentioning that at any time (P1IN&BIT2) will tell you whether the TA0.1 (CCR1) output is high or low. This provides the indication you asked about earlier, but you can't clear it, as your new code evidently wants to do.

    More generally: Your question has changed significantly. I can't think of a way to generate your 3 different asymmetric (alternatively: Dead-Time) waveforms with a single timer, even in Up/Down mode. Maybe one of the Wizards here knows better. There may be a DMA trick.

    At this point I'd be looking at software PWM. With a 25msec pulse, a few usec of latency/jitter probably won't be noticeable. There is a variant of software PWM which, rather than wiggling the P1OUT bits directly, manipulates the CCR(s) on the fly in preparation for the Next timer event; this is about the same amount of work as simple software PWM, but since the hardware is generating the edges it avoids latency/jitter.

    It might even be less code: The CCR1 interrupt turns TA0.1 off and TA0.2 on (and starts the ADC), the CCR2 interrupt turns TA0.2 off and TA0.3 on, and so on.
  • MY current code actually generates 3 pulses each of which has a 4s period and 25ms ON time. However, the thing I most desire to the turn on the ADC during the ON period. I have somehow found a way to make my downstream circuit such that I don't need to generate asymmetric waveforms. And I don't think my current code is working correctly in that regards. 

    Assuming that I don't even need 3 waveforms, how can I turn on the ADC during the ON time. I can't think of anything other than interrupt when the PWM is its ON time. Is my thinking correct?   

        

  • As I read this code, it will generate three simultaneous pulses, and will start the ADC when they (simultaneously) go active (low). If you want to start the ADC when they go high, you'll want Set/Reset rather than Reset/Set.

    I'm supposing that you've configured the pins and the ADC appropriately.

    Is that what you were thinking of? I guess I'm progressively less clear what your goal is.

**Attention** This is a public forum