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.

How to code second PWM O/P.

Other Parts Discussed in Thread: MSP430F2112, MSP430G2211, MSP430G2231

I need two PWM outputs the are hardware operated so that there is no CPU overhead.

Device: MSP430F2112

Example code shown for P1.2. Not sure why P1.3 required?

The first one works fine:

{

WDTCTL

= WDTPW + WDTHOLD; // Stop WDT

// P1.2 PWM output (Uses timer0_A3)

P1DIR |= 0x0C; // P1.2  and P1.3 outputs
P1SEL |= 0x0C; //P1.2 and P1.3 TA0/1 options

CCR0

= 390-1; // PWM Period (2.63KHz)
CCTL1 = OUTMOD_7; // CCR1 reset/set
CCR1 = 70; // CCR1 PWM duty cycle
TACTL = TASSEL_2 + ID_0 + MC_1; // SMCLK, up mode

// How would I code a PWM output on pin P1.1?

// I assume I have to use timer1_A2?

_BIS_SR(CPUOFF);

// Enter LPM0

}

Thanks.

  • newbie said:

    // How would I code a PWM output on pin P1.1?

    // I assume I have to use timer1_A2?

    In the F21x2 datasheet, pin 22 is designated as: P1.1/TA0.0/TA1.0. In other words, the same pin can be used as the output for the Timer A0 Channel 0 and Timer A1Channel 0. In either case, it can only toggle at 50% duty cycle with the output period being double the timer period.

    The 2xx User Guide (page 392 onwards) covers this concept with a very detailed explanation.

    2xx User Guide (page 403) said:

    The OUTx signal is changed with the rising edge of the timer clock
    for all modes except mode 0. Output modes 2, 3, 6, and 7 are not useful for
    output unit 0, because EQUx = EQU0.

  • newbie said:

    I need two PWM outputs the are hardware operated so that there is no CPU overhead.

    Device: MSP430F2112

    Example code shown for P1.2. Not sure why P1.3 required? ...

    P1.3 is not required.

    newbie said:

    ... The first one works fine:

    {

    WDTCTL

    = WDTPW + WDTHOLD; // Stop WDT

    // P1.2 PWM output (Uses timer0_A3)

    P1DIR |= 0x0C; // P1.2  and P1.3 outputs
    P1SEL |= 0x0C; //P1.2 and P1.3 TA0/1 options ...

    P1.3 can only be used as TA0/2 output.

    newbie said:

    ...

    CCR0

    = 390-1; // PWM Period (2.63KHz)
    CCTL1 = OUTMOD_7; // CCR1 reset/set
    CCR1 = 70; // CCR1 PWM duty cycle
    TACTL = TASSEL_2 + ID_0 + MC_1; // SMCLK, up mode

    // How would I code a PWM output on pin P1.1? ...

    You cannot.

    newbie said:

    ...

    // I assume I have to use timer1_A2? ...

    There is no such thing as timer1_a2. However, timer1_a1 can be used to generate PWM at P3.7

    All the above information are included in the F2112 data-sheet. You need to read the data-sheet. Otherwise you have to wait until someone writes a "sample code" that does exactly what you need.

  • First page of data sheet (MSP430F21x2) shows two timers: Timer0_A3 and Timer1_A2. (I am using device MSP430F2112)

    I need two PWMs one on pin P1.1 and one on P1.2. TI FAE states that this is possible.

    The prototype circuit board has PWM O/Ps conected to P1.1 and P1.2.

  • newbie said:

    First page of data sheet (MSP430F21x2) shows two timers: Timer0_A3 and Timer1_A2. (I am using device MSP430F2112)

    I need two PWMs one on pin P1.1 and one on P1.2. TI FAE states that this is possible.

    The prototype circuit board has PWM O/Ps conected to P1.1 and P1.2.

    Yes, you can use P1.1 as one of the PWM outputs. You can have a cake.

    But you also said:

    I need two PWM outputs the are hardware operated so that there is no CPU overhead.

    Yes, you can do that. You can eat the cake.

    But you can not eat the cake and still have it.

  • newbie said:
    I need two PWMs one on pin P1.1 and one on P1.2. TI FAE states that this is possible.

    GM states that you can drive tehir cars and that certain cars have two or more back seats. This does not imply that you can drive these cars from one of the back seats. You'll need to sit in teh driver seat no matter how many back seats are available.

    You get what you buy, which is not necessarily what you want and maybe even less what you need.

    But to get back on topic: P1.1 is TAx.0, which can be used as frequency output but not for PWM. If will always deliver 50% duty cycle. Either at half the overflow frequency of TAx or at half the PWM frequency set by TAxCCR0 itself.

    PWM outputs, that is, a clock signal with variable duty cycle, is only possible with the CCR1..n outputs. In your case TA0.1, TA0.2 or TA1.1. TA0.x share the same PWM frequency while TA1.1 can have its own.
    These are available at P1.2,P1.3,P1.6,P1.7, P2.3, P2.4 and P3.7.

    On P1.1, you won't get a PWM signal. Period.

    Your posted example code already contains the initialisation of TA0CCR0 for the PWM frequency and TA0CCR1 for the first PWM output. Just copy this part for TA0CCR2 and you're done. On P1.3/P1.7/P2.4. Not on P1.1.

  • I will change the second PWM O/P to P3.7 so that  I can have two PWM O/Ps each with different frequencies and different duty cycles.

    I have read through the user's guides and sample code but I wish I could find more examples and details describing how to achieve the PWM and at what cost (resource wise).

    Even the TI FAE steered me wrong.

    Thanks.

  • There are MANY threads here dealing with the general principles of PWM and even some very specific applications.

    The possible duty cycles of a PWM signal are independent for every CCRx output. Only the frequency is common because the timer round trip frequency is common for all CCR odules in a timer (obviously). If you need different frequencies, then of course you need different timers.

    One final thing: you cannot set the PWM hardware to 0% AND 100% just by altering the CCRx value. Depending on the setup and output modes choosen, the PWM signal will always have a high- or low-spike of one timer tick. If you need both, you must disable the output if you want 0% (or 100%) and set the CCR modules outmode to "out bit" and set this bit properly, so the output signal is fixed to low or high.

  • newbie said:

    I have read through the user's guides and sample code but I wish I could find more examples and details describing how to achieve the PWM and at what cost (resource wise).

    If you visit: ti.com/msp430codeexamples, you will see code examples for several different devices. There are several code examples & several threads on this forum as well which address PWM outputs employing Timers.

    newbie said:

    Even the TI FAE steered me wrong.

    I made it crystal-clear in my post that P1.1 can only toggle at 50% duty cycle with the output period being double the timer period. I also quoted the user's guide to elucidate the point that while P1.1 can toggle, the frequency and duty cycle are fixed. In some applications, it does make sense to have a fixed PWM duty cycle and period. In others, having a variable PWM duty cycle and period is essential to operation. Depends upon the needs of the application.

    So, if in some application, if you need a 50% duty cycle with fixed frequency, setting P1.1 CCTL register to OUTMOD_4 (toggle) can easily do the trick.

     

  • I have been playing with timer A as well; let's see if I can summarize:

    1. Each timer A has at least one Capture Compare Unit (CCR0) that can be used to set the frequency for that timer.  There can be an output pin associated with CCR0, but it will always produce a square wave; CCR0 cannot be used to generate PWM (not without adding software, anyway.)
    2. Each timer A can have additional CC units (up to 6 more; depends on the chip.)  Each of these can be associated with an output pin, and THOSE pins can generate PWM purely in HW, but they will all run at the frequency dictated by the setting of CCR0 on that timer.
    3. So for each timerA "with N Capture Compare Registers", you can get N-1 PWM outputs running at a single controllable frequency.
    4. If you need multiple simultaneous PWM frequencies, you need multiple timers.
    5. PWM outputs appear on pins associated with the CC units, not the timer itself.

    IMHO, it's hardwarilly "clever" that the same hardware block is apparently used for CCR0 and the other CCRs, but they're connected differently and perform significantly different functions, leading to confusion among us firmware types used to other architectures where "one PWM per CCR" is common.

     

  • This summary is quite good, but misleading.

    If the CCR units were for PWM only, they'd be better named PWM0..6. And then you'd be right about  thinking the CCR naming/numbering is confusing.

    But the CCRs are at the first place Capture/Compare units. And many people never use them for PWM at all. PWM is just a special case fo the compare function. And the spacial case of this special case is that the double-action output modes (e.g. set/reset) are triggered by the own compare value and the CCR0 compare value. Which would be identical for CCR0 and therefore useless and is the cause that CCR0 cannot be used for an adjustable duty cycle.
    But if you use the capture function or the compare function without signal output, all CCR units are functionally identical (with the exception that CCR0 has its own interrupt vector because of its special role of controlling the timer runlength.

    Whether something is confusing depends on the viewing angle at which you look at it.

  • Yes, every extra bit of information helps.

    I know they say to look at the code samples but to be honest the code samples just show you how to program without really explaining what is happening.

    What I need is a graphical description of the clock/timer/capture/compare... with examples showing how to ... for each type of function.

    I don't know if there is such documentation? Maybe a textbook that is used for MSP430 archtecture?

    If you know of any please let me know.

    Thanks for all the new info.

  • There are such diagrams in the "MSP430x2xx Family User's Guide" section about timer_a.  For example, figure 12-12 and 12-14 look like they cover the typical PWM cases (for a single PWM output.)  I think they work better as a reminder than as initial explanation, though...

     

  • newbie said:
    the code samples just show you how to program without really explaining what is happening

    That's my main point of criticism. These exaples demonstrate that it is possible to solve this particular problem. They do not explain how or why it is working, nor do they offer any help to solve similar but not 100% identical problems. There are assumptions made and constants used where any real application might require a calculation or different initialisation.
    All in all I consider most of these examples useless unless your exact problem is solved there. And then you're still lost if you have to combine two of them with different (and of course uncommented) initialisations of other system components (such as the clock module).

    Usually, if you're experienced enough to understand what's going on, you won't need the examples at all. And if not, they won't offer any enlightment.

    Best example for this failure of usefulness is a code example where a timer output is used to trigger ADC conversions. On the particular processor where this example has been written for, there is an internal connection between timer and ADC because the small pin count resulted in both modules being connected to the same pin. On other processors with the same tiemr and ADC modules, the code won't work because there the pinout is different and the internal connection is missing. And of course there was no comment in the code that explains why the code is working and that it won't work on other processors of even the same family. This particular code example is worth the predicate 'top of the flops'.

    There are, however, some examples which can be used as starting pint for own investigations and experiments. But it is usually a very tiny point, barely large enough to stand on it as-is.

    During my time here in this forum, I usually tried to no provide code as solution for a quesiton, but to rather tell how it can be done.
    Of course this has the advantage that I don't risk to post faulty code (I cannot test it as I normally don't have the processors or target boards), but it often enough increased the questioners understanding of the matter so he was able to write the code himself. Which is more satisfying for both of us.
    I'd say a more detailed description of how to do a job, mabye with added code snippets where needed, would be more useful than a collection of examples which only cover a few specific problems for very few selected processors and are mostly useless for the majority of users.

  • I totally agree with you.

    It is better to understand fully the architecture of the clock/timmers before copying code from examples.

    I am hoping to find better documentation and maybe a webinar?

    On another note:

    What is the correct wat to turn off the PWM O/P once it is on?

    Here is how it is turned on:

    WDTCTL

    = WDTPW + WDTHOLD; // Stop WDT
    P1DIR |= 0x0D; // P1.0 - input, P1.1 and P1.2 outputs
    P1SEL |= 0x0C; // P1.1 and P1.2 TA1/2 options
    CCR0 = 390-1; // PWM Period (2.63KHz)
    CCTL1 = OUTMOD_7; // CCR1 reset/set
    CCR1 = 70; // CCR1 PWM duty cycle
    TACTL = TASSEL_2 + ID_0 + MC_1; // SMCLK, up mode

  • newbie said:
    What is the correct wat to turn off the PWM O/P once it is on?

    There's no 'correct' way.

    You can set the CCR to outmode 0 (out bit), which is also recommended for 100% or 0% duty cycle (depending on the chosen outmode, only one of these is possible by the CCRx setting, but never both). You can stop the timer (then all PWMs of this timer will freeze in their current state), you can set the CCRx value to >CCR0, in which case no further oputput toggles happen. You can set the port pin to I/O mode again (PxSEL register), set the timer to a clock which does have a frequency of 0, and, and, and...

    The best way (depending on the attached hardware) is to disable the PWM in either the CCRx interrupt function or in the CCR0 interrupt function. If doing so, the PWM is diabled after the last regular clock edge (which interrupt depends on polarity of the PWM signal) and you won't generate any PWM pulse with perhaps a higher frequency than the attached hardware can handle or with a duty cycle that can be misinterpreted. In most cases, this is academic, though.

  • newbie,

        I am just getting into using the MSP430 and have been using the MSP430G2211 that comes with the launchpad. It is a pretty basic chip. The code below may be of help. I present it to you as one newbie to another. It provides 2 PWM outputs and is interrupt driven.

     

    // Two PWM and sec clock example using
    // the MSP430G2211 that comes with
    // the Launchpad.
    // The 2 PWM outputs can not be set to 0 and 100%
    // Uses 32.768 kHz crystal that comes with the Launchpad.
    // Author: Doug Paradis -  2010
    // IAR 5.10.1 used

    #include  "msp430.h"

    int cntr = 0;
    int cntr_trip_pt = 2;                     // change every 1 sec

    // pwm1 is going to increase
    int pwm1_flg = 0;
    int pwm1_period = 10000;
    int pwm1_on = 5;
    int pwm1_chg = 200;
    int pwm1_upper_limit = 9995;
    int pwm1_zero = 5;                      // must be greater than zero

    // pwm2 is going to decrease
    int pwm2_flg = 0;
    int pwm2_period = 10000;
    int pwm2_on = 9995;
    int pwm2_chg = 200;
    int pwm2_upper_limit = 9995;
    int pwm2_zero = 5;                      // must be greater than zero

    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                         // Stop watchdog timer
      BCSCTL1 = RSEL0 + RSEL3;                         // Select MCLK freq range of ~2.3 MHz
      BCSCTL2 = DIVS_2 ;                               // MCLK = DCOCLK, SMCLK = DCOCLK/8
      BCSCTL3 = LFXT1S_0 + XCAP_3;                     // 32.768 kHz crystal, 12.5 pF
      WDTCTL = WDT_ADLY_1000;                          // WDT inv mode, ACK clk,
                                                       // 1000mS interval (assume 32KHz crystal)

      IE1 |= WDTIE;                                    // Enable WDT interrupt
      TACTL = TASSEL_2 + MC_2;                         // TA clock = SMCLK, mode control = continuous
      TACCTL0 = OUTMOD_4 + CCIE;                       // output mode = toggle, interrupt enabled
      TACCTL1 = OUTMOD_1 + CCIE;                       // output mode = set, interrupt enabled
      P1SEL =  BIT5 + BIT6;                            //  P1.5 to TA0.0, P1.6 to TA0.1
      P1DIR =  BIT0 + BIT5 + BIT6;                     // P1.0 (flash led when using Launchpad),
                                                       // P1.5 (PWM1) and P1.6 (PWM2) to output
                                                       // direction
      TACCR0 = pwm1_on;
      TACCR1 = pwm2_on;
      _BIS_SR(LPM0_bits + GIE);                        // Enter LPM0 w/interrupt
    }

    // TimerA CCR0 interrupt service routine
    #pragma vector=TIMERA0_VECTOR
    __interrupt void timer_A0(void)
    {
      if (pwm1_flg == 0)
      {
        TACCR0 = TAR + pwm1_on;
        pwm1_flg = 1;
      }
      else
      {
        TACCR0 = TAR + (pwm1_period - pwm1_on);
        pwm1_flg = 0;
      }
    }

    // TimerA CCR1 interrupt service routine
    #pragma vector=TIMERA1_VECTOR
    __interrupt void timer_A1(void)
    {
      if (pwm2_flg == 0)
      {
        TACCTL1 = OUTMOD_5 + CCIE;                     // output mode = reset, interrupt enabled
        TACCR1 = TAR + pwm2_on;
        pwm2_flg = 1;
      }
      else
      {
        TACCTL1 = OUTMOD_1 + CCIE;                     // output mode = set, interrupt enabled
        TACCR1 = TAR + (pwm2_period - pwm2_on);
        pwm2_flg = 0;
      }
    }

    // Watchdog Timer interrupt service routine
    // interrupt occurs on both leading and falling edge
    #pragma vector=WDT_VECTOR
    __interrupt void watchdog_timer(void)
    {

        P1OUT ^= BIT0;                            // Toggle P1.0 using exclusive-OR
        cntr++;
        if (cntr == cntr_trip_pt)                 // change pwm duty cycle
        {
          pwm1_on += pwm1_chg;                    // increase pwm1
          pwm2_on -= pwm1_chg;                    // decrease pwm2
          cntr = 0;
        }
        if (pwm1_on >= pwm1_upper_limit)          // pwm1 reset
        {
          pwm1_on = pwm1_zero;
        }
        if (pwm2_on <= pwm2_zero)                 // pwm2 reset
        {
          pwm2_on = pwm2_upper_limit;
        }


    }

     

  • Very cool code.  Before I start to work with this, can you tell me if the PWM outputs are independently timed?  Ie. Can you have one at 75% and one at 10%?

    thanks.  KB

  • Ken Bueltmann said:
    can you tell me if the PWM outputs are independently timed?

    Yes. The CCR0 unit is used to set the PWM cycle frequency, while all other PWM units can be used based on this base cycle frequency, to output their signal on different levels. Some MSPs have a B7 timer which has 7 CCR units, which means up to 6 individually settable PWM outputs (which run on the same cycle length). Other MSPs have up to three timers, which means up to three different cycle timings but less CCR units and therefore less PWM outputs per timer.

    Basically, CCR0 determines how many ticks the timer shall count before resettign to 0 (this is the cycle length), while the other CCR units are programmed to toggle their output (set/reset/toggle) when the timer is reset to 0 and when the timer hits a specific count between 0 and the CCR0 limit.

    The higher the 'runlength' of the tiemr cycle (the CCR0 value), the finer you can select the trigger points for the PWM outputs. If CCR0 is set to 999, one PWM cycle will be 1000 timer ticks (whatever this may be, depending on the clocks) and each other CCR units PWM output can be set in 0.1% steps individually. So in the easiest case (reset/set mode) it sets its output high when the timer is reset to 0 and sets it low again when it reaches a certain value from 0 to 1000 for 0 to 100% duty cycle. All PWM outputs will, however, be synchronized to the moment the timer is reset to 0.

     

  • Thanks for the help JMG,

     

    I have been doing much reading about this and it is perplexing me.  I understand the PWM output is a result of the settings for CCR0 and CCR.  According to the SLAS694E table10, signal designations are provided.  Please correct me if i am wrong.  Using timerA, one PxSEL's the appropriate pin, either P1.0, P1.1, P1.2, or P1.6.  The example above uses P1.1 and P1.2, corresponding to TA0 and TA1.  Apparently these are the output signals.  There are then designation options for output pins.   The example uses bits 4 and 5, but P1.4 is not on the output list of the table??.  How do i specify the CCR constraints separately for each ouput? Does nesting them in an interrupt serve this purpose.  

    I appreciate your patience

     

    KB

  • Can anyone understand why I am getting this error from the above code?

    "../lnk_msp430g2231.cmd", line 58: error: run placement fails for object
       ".stack", size 0x32 (page 0).  Available ranges:
       RAM          size: 0x80         unused: 0x2c         max hole: 0x2c     
    error: errors encountered during linking; "msp430.out" not built

     

    /**********************************************
     Chronutator clock project using MSP430G2211
     Copyright: Doug Paradis - 2010
     All rights reserved
     This code may be used for private use, as long
     as, copyright notice retained.

     ACLK  (pin 2) --> crystal osc output (pin labeled P1.0)
                       Use when troubleshooting on Launchpad.
                       Commented out in this listing.
     TA0.0 (pin 3) --> PWM for minute meter (pin labeled P1.1)
     TA0.1 (pin 4) --> PWM for hour meter   (pin labeled P1.2)
     P1.4  (pin 6) --> minute setting button (inc one min per push)
     P1.5  (pin 7) --> hour setting button (inc one hr per push)
     RST   (pin 10) --> reset
     Xin, Xout (pins 13,12) --> 32.768 KHz crystal

     P1.3, P1.6, P1.7 (pins 5,8,9) --> not used

     compiled using IAR Embedded Workbench
    **********************************************/

    #include  "msp430g2211.h"

    unsigned char cntr = 0;                              // sec counter
    unsigned char cntr_trip_pt = 60;                     // change every 1 sec
    unsigned char debounceFlg = 0;                       // flag for debouncing

    // min
    unsigned char mflg = 0;                             // minute flag
    unsigned int m_period = 10000;                      // minute meter PWM period (number of counts of SMCLK)
    unsigned int m_on = 5;                              // minute meter PWM setting
    unsigned char m_cntr = 0;                           // min counter
    unsigned int m_zero = 5;                            // minute meter PWM zero setting  (can't actually be zero)
    //  calibration array for minute meter (set every 5 min mark) -- values should be divisable by 5
    //  min meter             5   10  15  20  25  30  35  40  45  50  55  60
    unsigned int m_cal[12] ={640,690,740,715,655,620,635,625,600,610,620,790}; // divisible by 5
    unsigned char m_index = 0;
    unsigned char m_tick = 0;

    // hour                                             -- see comments above for items below --
    unsigned char hflg = 0;
    unsigned int h_period = 10000;
    unsigned int h_on = 5;                              // hour meter PWM setting 
    unsigned char h_cntr = 0;   
    unsigned int h_zero = 5;                            // (can't actually be zero)
    //  calibration array for hour meter (set for each hour) -- values should be divisable by 12
    //   hr mtr               1   2   3   4   5   6   7   8   9   10  11  12
    unsigned int h_cal[12] ={744,796,708,708,760,720,708,768,684,684,780,732};   // divisible by 12
    unsigned char h_tick = 0;
    unsigned char h_index = 0;
    unsigned int j =0;
    void one_min(void);
    void debounce(char button);

    void main(void)
    {
      WDTCTL = WDTPW + WDTHOLD;                        // Stop watchdog timer LINE58
      BCSCTL1 = RSEL0 + RSEL3;                         // Select DCOCLK freq range of ~2.3 MHz (get rid of meter jitter)
      BCSCTL2 = DIVS_2 ;                               // MCLK = DCOCLK, SMCLK = DCOCLK/8
      // crystal Cload capacitor adjustment - only one line of next four should be uncommented
      // BCSCTL3 = LFXT1S_0 + XCAP_3;                     // 32768KHz crystal, 12.5 pF
      // BCSCTL3 = LFXT1S_0 + XCAP_2;                     // 32768KHz crystal, 10 pF
         BCSCTL3 = LFXT1S_0 + XCAP_1;                     // 32768KHz crystal, 6 pF
      // BCSCTL3 = LFXT1S_0 + XCAP_0;                     // 32768KHz crystal, 1 pF
      WDTCTL = WDTPW + WDTTMSEL + WDTCNTCL + WDTSSEL;  // WDT inv mode, ACK clk,
                                                       // 1000mS interval (assume 32KHz crystal)
                                                       // equivalent to "WDT_ADLY_1000" defined in h file
      IE1 |= WDTIE;                                    // Enable WDT interrupt
      TACTL = TASSEL_2 + MC_2;                         // TA clock = SMCLK, mode control = continuous
      TACCTL0 = OUTMOD_4 + CCIE;                       // output mode = toggle, interrupt enabled
      TACCTL1 = OUTMOD_1 + CCIE;                       // output mode = set, interrupt enabled
      P1SEL =  BIT1 + BIT2;                            //  P1.1 to TA0.0, P1.2 to TA0.1
      P1DIR =  BIT1 + BIT2;                            // P1.1 (min) and P1.2 (hr) to output direction
     
      // P1DIR =  BIT0 + BIT1 + BIT2;                  // P1.0 (flash led when using Launchpad),
                                                       // P1.5 (min) and P1.6 (hr) to output direction
      
      P1IE = BIT4 + BIT5;                              // Enable Port 1 interrupt for P1.4 and P1.5
      //P1IES = BIT4 + BIT5;                            // Interupt edge select, high-low (falling edge)
      P1OUT = BIT4 + BIT5;                             // Time chg buttons set to high
      P1REN = BIT4 + BIT5;                             // pull up resistors enable on time chg button pis

      TACCR0 = m_on;
      TACCR1 = h_on;

      _BIS_SR(LPM0_bits + GIE);                        // Enter LPM0 w/interrupt
    }
    // TimerA CCR0 interrupt service routine
    #pragma vector=TIMERA0_VECTOR
    __interrupt void timer_A0(void)
    {
      if (mflg == 0)
      {
        TACCR0 = TAR + m_on;
        mflg = 1;
      }
      else
      {
        TACCR0 = TAR + (m_period - m_on);
        mflg = 0;
      }
    }
    // TimerA CCR1 interrupt service routine
    #pragma vector=TIMERA1_VECTOR
    __interrupt void timer_A1(void)
    {
      if (hflg == 0)
      {
        TACCTL1 = OUTMOD_5 + CCIE;                     // output mode = reset, interrupt enabled
        TACCR1 = TAR + h_on;
        hflg = 1;
      }
      else
      {
        TACCTL1 = OUTMOD_1 + CCIE;                     // output mode = set, interrupt enabled
        TACCR1 = TAR + (h_period - h_on);
        hflg = 0;
      }
    }
    // Watchdog Timer interrupt service routine
    // interrupt occurs on both leading and falling edge
    #pragma vector=WDT_VECTOR
    __interrupt void watchdog_timer(void)
    {
     
      if (debounceFlg == 0)
      {
        // P1OUT ^= BIT0;                     // Toggle P1.0 using exclusive-OR (Use when monitoring ACLK)
       
        if (cntr == cntr_trip_pt)          // change pwm duty cycle
        {
           one_min(); 
        }
        cntr++;
      }
      else                                  // part of debounce routine
      {
        IFG1 &= ~WDTIFG;                    // clear WDT timer+ interrupt pending (do when changing WDT settings)
        WDTCTL = WDT_ADLY_1000;             // Set Watchdog Timer delay to 1000 mS
        // IE1 |= WDTIE;                      // Enable WDT interrupt
        P1IFG = 0;                          // clear all p1 interrupts
        P1IE |= BIT4 + BIT5;                // Enable Port 1 interrupt for P1.4 and P1.5
        debounceFlg = 0;
      }
    }
    // Port 1 interrupt service routine
    #pragma vector=PORT1_VECTOR
    __interrupt void port_1 (void)
    {
      // P1IFG &= ~BIT0;                      // clear any interrupt on P1.0 (only needed if using Launchpad led)
      switch (P1IFG) {
      case BIT4:                           // P1.4 (increment mins)
        cntr = 0;                          // set seconds to 0
    //    for (int j = 1; j <= 4; j++)      // inc 4 mins  Uncomment these 4 lines when tuning m_cal           
    //    {
    //      one_min();
    //    } 
        one_min();                         // increment 1 min
        debounce(BIT4);
        break;

      case BIT5:                           // P1.5 (increment hrs)
        // cntr = 0;                       // set seconds to 0
        for (j = 1; j <= 60; j++)      // inc 1 hr               
        {
          one_min();
        } 
        debounce(BIT5);                   
        break;
      default:
        P1IFG = 0;                         // clear all p1 interrupts (note: pushing both buttons at same
                                           // time will end up here)
      }
    }

    void one_min (void)
    {
          m_tick++;                                      // subdivsion between 2 - 5 min marks on min meter
          m_cntr++;                                      // number of minutes in this hour
          m_on += m_cal[m_index] / 5;                    // increase 1 min
          if (m_tick == 5)                               // is min meter at a 5 min mark on min meter?
          { 
            m_index++;                                   // increase index in m_cal array
            m_tick = 0;                                  // at a 5 min mark on min meter reset m_tick
            h_tick++;
            h_on += h_cal[h_index] / 12;                 // move hr meter one subdivision (12 per hr) between 2 hr marks
          } 
          if (h_tick == 12)                              // has hr meter reached an hr mark?
          {
            h_index++;                                   // increment the number of hours
            h_tick = 0;                                  // reset subdivisions of min meter
          } 
          cntr = 0;                                      // set second count to 0

            

        if (m_cntr >= 60)                                // reset on the hour (every 60 mins)
        {
          m_on = m_zero;                                 // move to zero on min meter
          m_index = 0;                                   // set number of mins this hr to zero
          m_tick = 0;                                    // set subdivisions of min meter to 0
          m_cntr = 0;                                    // set number of mins this hour to 0
          h_tick = 0;                                    // set subdivisions of hour meter to 0
          h_cntr++;                                      // increment hour count
        }
        if (h_cntr >= 12)                                // reset every 12 hours
        {
          h_on = h_zero;                                 // set hour meter to 0
          // m_index = 0;                                   // reset m_cal array index
          h_index = 0;                                   // reset h_cal array index
          // m_tick = 0;                                    // reset subdivisions of min meter
          // h_tick = 0;                                    // reset subdivisions of hr meter
          h_cntr = 0;                                    // reset hour count
        }


    // debounce routine assmes the use of
    // 47k res to Vcc and .1uF cap to gnd, connected to button
    void debounce(char button)
    {
        debounceFlg = 1;
        P1IFG &= ~(button);                // clear interrupt flag for button pushed (p1.2 or p1.3)
        P1IE &= ~(button);                 // temporarily disable the interrupt
        IFG1 &= ~WDTIFG;                   // clear WDT timer+ interrupt pending (do when changing WDT settings)
        IE1 &= ~WDTIE;                     // disable WDT interrupt
        WDTCTL = WDTPW + WDTHOLD;          // temporarily stop Watchdog Timer
        WDTCTL = WDT_ADLY_250;             // Set Watchdog Timer delay to 250 mS
        IE1 |= WDTIE;                      // Enable WDT interrupt
    }   

  • Ken Bueltmann said:

    Can anyone understand why I am getting this error from the above code?

    "../lnk_msp430g2231.cmd", line 58: error: run placement fails for object
       ".stack", size 0x32 (page 0).  Available ranges:
       RAM          size: 0x80         unused: 0x2c         max hole: 0x2c     

    Simple: You have defined that your stack is 50 bytes (0x32), yet your static/global variables occupy already 0x54 bytes of teh available 0x80 bytes, so only 0x2c = 44 bytes are available. So teh linker complains that the 'defined' stack won't fit.

    Hoewever, teh defined stack size is ONLY used by the linker to generate this error if tehre isn't enough space, and maybe for the debugger to generate a warning. It has no influence on your code. Whether you define teh stack to be 2 bytes or 2kB does not change a single bit in the generated binary. The stack will grow as large as your code flow needs (eventually overwriting your variables and growing until it exceeds the available ram).

    This error is just a (hard) warning to the coder that his ram usage has left the self-defined constraints. It may be that your code will work with a smaller stack, it may also be that your code wouldn't run with these 50 bytes even if they were available.

     

  • Ken Bueltmann said:
    The example uses bits 4 and 5, but P1.4 is not on the output list of the table?

    P1.4 (if I'm right that the used processor is the G2x11) has SMCLK as output, so you can see the signal that's clicking the timer on this pin. P1.5 outputs teh signal from CCR0, which is (in the used toggle mode) toggling at the PWM frequency (so it is a square wave  with 1/2 teh PWM frequency). P1.2 or P1.6 have the PWM output signal from the CCR1 unit.

    Ken Bueltmann said:
    How do i specify the CCR constraints separately for each ouput?

    Not on this processor as it has only CCR0 (used for the PWM frequency) udn CCR1 (used for the actual PWM output). Other MSPs have more than one timer and up to 7 CCR units (so 6 PWM outputs) per timer. There you can set separate duty cycles for CCR1..6 on separate output pins.
    CCR0 is always required for the PWM frequency except for one case:
    If you can live with a PWM frequency that equals 65536 timer ticks, then you don't need CCR0 to limit the timer range. Then CCR0 can be used for its own PWM output with duty cycle. This means for 16MHz clock frequency exactly 244 Hz PWM frequency, but then with a duty cycle resolution of 0.0015% :)

    Ken Bueltmann said:
    Does nesting them in an interrupt serve this purpose

    No. The software is way too slow and unprecise to do anyhing useful. That's why the CCR units are done in hardware. Of course you can do a timer interrupt and toggle port pins maually (as normal I/O). But to get a higher frequency and some resolution, you'll need a really high interrupt frequency that won't leave any room for any useful work (if it can be done at all).

  • Thanks as usual JMG,

     

    Ken Bueltmann said:
    How do i specify the CCR constraints separately for each ouput?
    Not on this processor as it has only CCR0 (used for the PWM frequency) udn CCR1 (used for the actual PWM output). Other MSPs have more than one timer and up to 7 CCR units (so 6 PWM outputs) per timer. There you can set separate duty cycles for CCR1..6 on separate output pins.
    CCR0 is always required for the PWM frequency except for one case:
    If you can live with a PWM frequency that equals 65536 timer ticks, then you don't need CCR0 to limit the timer range. Then CCR0 can be used for its own PWM output with duty cycle. This means for 16MHz clock frequency exactly 244 Hz PWM frequency, but then with a duty cycle resolution of 0.0015% :)

     Which series of MSP offer all these CCRs?

    I like the Idea of not limiting the timer range, in order to use CCR0.  Have you seen any examples of this in action.  I think I can get my idea to work with 2 seperate/definable PWM's.

    Best Regards,  KB

  • Ken Bueltmann said:
    Have you seen any examples of this in action. 

    Basically, this means using the timer in continuous mode instead of up mode. Then CCR0 behaves liek CCR1, while the tiemr will roll over to 0 at its end (65535).

    Ken Bueltmann said:
    Which series of MSP offer all these CCRs?

    The older 1611 has one timerA3 and one TimerB7. Meaning one has 3 CCRs, the other 7 (includign CCR0). TimerB, however, can be limited to 12, 10 or 8 bit which frees up CCR0 for PWM if you can live with a count of 256, 1024 or 4096 ticks.

    The 54xx MSPs have one TimerA3 , one TiemrA5 and one TimerB7. And some others in the 5xxx family have three TimerAx.
    Also, most other MSPs have a TimerAx and a TimerBx.

    The G series is the most limited of all, but also by far the cheapest. (the 54xx costs ~$8) and the easiest to handle (DIP case compared to the SMD cases of msot other MSPs)

**Attention** This is a public forum