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.

PWM + Phase shift

Other Parts Discussed in Thread: MSP430FR5739

Hello, I need some help using my MSP430FFG4618/F2013 Experimenter's Board.

1. I want to generate 6 square waves each one shifted by a constant phase from the one before it. 

I was thinking of using a 50% duty cycle PWM signal but i don't know how to enter the phase delay between the differant signals.

2. Once I manage to send out the requested signals i want to hook them up to a device I'm using. 

How do i know which pins can be connected using the board , and not directly to the chip?

I'll apreciate any help. 

Yanai

  • Hi Yanai,

    the schematics are available in the users guide (MSP430FG4618/F2013 Experimenter’s Board (Rev. A)) or the design files MSP-EXP430FG4618 Design Files (Gerbers and CAD Layout) (Rev. A)

    You should use Timer_B in up/down mode; refer to chapter-16 for details http://focus.ti.com/lit/ug/slau056j/slau056j.pdf

    Rgds
    aBUGSworstnightmare 

  • Yanai Barr said:

    I was thinking of using a 50% duty cycle PWM signal but i don't know how to enter the phase delay between the differant signals.

    I know 'Nightmare suggested up-down mode, but I'm not sure how that would work.  One way I thought of would be to use continuous mode and use ISRs to add to the TBCCRn register.  For example, just after Timer B toggled the output pin for TBCCR5, then Timer B would give you an interrupt.  In your ISR, you could add 500 (or whatever) to TBCCR5.  I think you would probably configure the output channel for TOGGLE.

    Using that approach, you could initialize TBCCRn to have appropriate phase shifts from eachother.  Then the ISRs would handle the rest.  Yes, there would be lots of ISRs running quite a lot, but the ISRs would be very short and fast.  As long as you don't need the outputs to have high frequency, this approach should work.  Unfortunately, it's not ideal since it requires lots of CPU intervention (the ISRs) to keep it running.

    Jeff

  • If you really only need 50% duty cycle, this one is easy.

    Set up the timerB in up mode and configure CCR0 so it will overflow with twice the required PWm frequency. Then program the 6 other CCR registers for toggle mode.
    the value you give the six CCRs define their phase shift relative to the timer B overflow.

    The only thing that might be problematic is that you'll need to be careful when you change the phase, or you'll experience a sudden 180° phase shift (output toggled twice or not at all during one cycle)

  • JMG -- Very nice solution!

     

  • Jens-Michael Gross said:

    Set up the timerB in up mode and configure CCR0 so it will overflow with twice the required PWm frequency. Then program the 6 other CCR registers for toggle mode.
    the value you give the six CCRs define their phase shift relative to the timer B overflow.

    I'm having issues with this kind of problem too. I need 4 phases : 0deg, 90deg, 180deg and 270deg. All these signals are 50% duty cycle, but I want it to be adjustable.

    So, let's say I use a value of 511 for my CCR0, and all my other CCRs are set to "Toggle".

    To obtain 0deg dephase, I put 0 in the associated CCR. To obtain 90deg, I put 255. Where I'm having difficulties is to obtain 180deg and 270deg. The only way I could think to solve this it is to set initial values for all my signals (ex: 1,0,0,1), put 0 for the first two, and 255 for the other two. This way, each signal with the same CCR value is dephased of 180deg from the other, and there is a 90deg dephase between the first and the third signal.

    Am I thinking correctly, and if so, what do I need to do to put an initial value before activating the Timer?

  • Jens-Michael Gross said:
    ... The only thing that might be problematic is that you'll need to be careful when you change the phase, or you'll experience a sudden 180° phase shift (output toggled twice or not at all during one cycle)

    First the easy part -- to set up different phases initially. You noticed that CCR settings for 0 to 179 degrees are the same as 180 to 359 degrees. The only difference is that you need to pre-set OUT to 0 for 0 to 179 degrees and pre-set OUT to 1 for 180 to 359 degrees.

    To change the phase while it is already in motion is a little more difficult. But TimerB has Double-buffered CCR and you can update CCR synchronously with TBCR overflow. Not a problem!

  • Yes, I'm actually trying to force an initial value for the port I'm using, but it doesn't seem to affect the behavior of the timer. Here is my code :

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #include "msp430x54x.h"

    void main(void)
    {
     
      WDTCTL = WDTPW+WDTHOLD;                   // Stop WDT

      P4SEL |= 0x7E;                             // P4 option select
      P4DIR |= 0x7E;                              // P4 outputs

    //PWM for Timer B
      TBCCR0 = 512-1;                          // PWM Period
     
      TBCCTL1 = OUTMOD_4;               // CCR1 toggle
      TBCCR1 = 255;                             // CCR1 PWM Duty Cycle = 50%
                                                           // Phase = 0deg

      TBCCTL2 = OUTMOD_4;               // CCR2 toggle
      TBCCR2 = 255;                             // CCR2 PWM duty cycle = 50%
                                                           // Phase = 180deg
      
      TBCCTL3 = OUTMOD_4;              // CCR3 toggle
      TBCCR3 = 0;                                // CCR3 PWM duty cycle = 50%
                                                          // Phase = 90deg
     
      TBCCTL5 = OUTMOD_4;              // CCR5 toggle
      TBCCR5 = 0;                                // CCR5 PWM duty cycle = 50%
                                                          // Phase = 270deg

      TBCTL = TBSSEL_2 + MC_1 + TBCLR;          // SMCLK, upmode, clear TBR

      P4OUT |= 0x22;                               // Set 4.1 and 4.5                                                    <==These lines don't work
      P4OUT &= ~0x0C;                           // Reset 4.2 and 4.3                                                <==

    //LMP0
      __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, enable interrupts
      __no_operation();                                     // For debugger 
     
    }        //end main;

     

    Is there a setting that I'm misusing? Excuse the simplicity of the question, I'm new with this chip.

  • Yes, I'm actually trying to force an initial value for the port I'm using, but it doesn't seem to affect the behavior of the timer. Here is my code :

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    #include "msp430x54x.h"

    void main(void)
    {
     
      WDTCTL = WDTPW+WDTHOLD;                   // Stop WDT

      P4SEL |= 0x7E;                             // P4 option select
      P4DIR |= 0x7E;                              // P4 outputs

    //PWM for Timer B
      TBCCR0 = 512-1;                          // PWM Period
     
      TBCCTL1 = OUTMOD_4;               // CCR1 toggle
      TBCCR1 = 255;                             // CCR1 PWM Duty Cycle = 50%
                                                           // Phase = 0deg

      TBCCTL2 = OUTMOD_4;               // CCR2 toggle
      TBCCR2 = 255;                             // CCR2 PWM duty cycle = 50%
                                                           // Phase = 180deg
      
      TBCCTL3 = OUTMOD_4;              // CCR3 toggle
      TBCCR3 = 0;                                // CCR3 PWM duty cycle = 50%
                                                          // Phase = 90deg
     
      TBCCTL5 = OUTMOD_4;              // CCR5 toggle
      TBCCR5 = 0;                                // CCR5 PWM duty cycle = 50%
                                                          // Phase = 270deg

      TBCTL = TBSSEL_2 + MC_1 + TBCLR;          // SMCLK, upmode, clear TBR

      P4OUT |= 0x22;                               // Set 4.1 and 4.5                                                    <==These lines don't work
      P4OUT &= ~0x0C;                           // Reset 4.2 and 4.3                                                <==

    //LMP0
      __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0, enable interrupts
      __no_operation();                                     // For debugger 
     
    }        //end main;

     

    CCR1 and CCR2 and in phase, and CCR3 and CCR5 are dephased of 90deg with CCR1. Is there a setting that I'm misusing? Excuse the simplicity of the question, I'm new with this chip.

  • You are barking at the wrong trees.

    The pins of interests are controlled by the TBCCTLn, not by the P4OUT any more. (Because you have set the corresponding P2SEL and P2DIR.)

    Before you do TBCCTLn = OUTMOD_4; you can do either TBCCTLn = 0; or TBCCTLn = OUT; to clear or set the OUT respectively. (Note that BIT2 of TBCCTn is called OUT and while OUTMOD is 00, the corresponding output pin follows OUT.)

  • It works greatly! Thank you very much for your help, it is much appreciated

  • How do you go about changing the phase so that you do not get the sudden 180 shift?

    I am using TB1 and my two pulses are outputs TB1.1 and TB1.2. I am using up mode with output mode toggle to keep a fixed 50% duty cycle. I set CLLD = 01 so that "New data is transferred from TBxCCRn to TBxCLn when TBxR counts to zero". I keep TB1CCR1 = 0 and only vary TB1CCR2 to change the phase relationship.

    So far it seems like I was able to get rid of the sudden shift when the new value for TB1CCR2 is an increase compared to the current one. For TB1CCR2 values less than the current one, sometimes I am getting that problematic sudden shift.

    Some extra info:

    - For my application, I only care about knowing the phase difference percentage. As in 0% <= prcnt_phase_diff <= 100%. That range can be adjusted a bit to accommodate a solution that does not involve shutting off the timer in order to set the proper phase relationship.

    - I am using the MSP430FR5739

  • Please check if the following example makes sense.

     

    Declare global static variables.

    unsigned Phase_1 = 0;

    boolean Q34_1 = false;

    unsigned Phase_2 = 0;

    boolean Q34_2 = false;

     

    Set up

    TB1.0, TB1.1, and TB1.1 to show up on Port pins.

    TB1CCR0 = 179; // for example

    TB1CCTL0 = OUTMOD_4; // toggle

    TB1CCR1 = 0;

    TB1CCTL1 = OUTMOD_4 | CCIE;

    TB1CCR2 = 0;

    TB1CCTL2 = OUTMOD_4 | CCIE;

    TB1CTL = TBSSEL_x | MC_1 | TBCLR; // upmode

     

    The ISR for TB1CCR1 and TB1CCR2 are very similar and do the following:

    Q34_n = !Q34_n;

    If (Phase_n < 360) {

        If (Phase_n < 180) TB1CCRn = Phase_n;

                      else TB1CCRn = Phase_n - 180;

        Phase_n = 360; // means do not change

    }

    Initially, TB1.1, and TB1.2 are in phase with TB1.0. But the main() can set Phase_n to anywhere between 0 and 359 (inclusive) while setting them to 360 or larger means keep the current phase. The ISR will set the phase accordingly and change Phase_n to 360 to indicate that it is done.

  • old_cow_yellow said:

    The ISR for TB1CCR1 and TB1CCR2 are very similar and do the following:

    Q34_n = !Q34_n;

    If (Phase_n < 360) {

        If (Phase_n < 180) TB1CCRn = Phase_n;

                      else TB1CCRn = Phase_n - 180;

        Phase_n = 360; // means do not change

    }

    That is incorrect. Update to: 

    The ISR for TB1CCR1 and TB1CCR2 are very similar and do the following:

    Q34_n = !Q34_n;

    If (Phase_n < 360) {

        If (Phase_n < 180) {if (!Q34) TB1CCRn = Phase_n };

                      else { if (Q34) TB1CCRn = Phase_n - 180; }

        Phase_n = 360; // means do not change

    }

  • Sorry, still incorrect.

    Q34_n = !Q34_n;

    If (Phase_n < 360) {

        If (Phase_n < 180) {if (!Q34) { TB1CCRn = Phase_n; Phase_n = 360; } };

                      else { if (Q34) {TB1CCRn = Phase_n - 180; Phase_n = 360; } }

    }

  • I went over what you provided and I rearranged it and added comments just to help me understand it better. Please let me know if any of my comments or what I think is wrong.

    Declaring the global variables is pretty straightforward. 

    // Set up example you provided /////////////////////////
    TB1.0, TB1.1, and TB1.1 to show up on Port pins.
    TB1CCR0 = 179;                  // for example
    TB1CCTL0 = OUTMOD_4; // toggle
    TB1CCR1 = 0;
    TB1CCTL1 = OUTMOD_4 | CCIE;
    TB1CCR2 = 0;
    TB1CCTL2 = OUTMOD_4 | CCIE;
    TB1CTL = TBSSEL_x | MC_1 | TBCLR; // upmode
    // My set up ///////////////////////////////////////////
    Select timer module and set TB1.1, TB1.2 to show up on port pins
    TB1CCR0 = tb1_period - 1;                 // TB1 Period = 600 - 1;
    TB1CCR1 = 0;                                        // Init CCR1 to 0
    TB1CCR2 = 0;                                        // Init CCR2 0% out of phase (relative to TB1.1)
    TB1CCTL1 = CLLD_1 + OUTMOD_4 + CCIE; // Load TBxCLn when TBxR counts to 0 + toggle mode + int enable
    TB1CCTL2 = CLLD_1 + OUTMOD_4 + CCIE; // Load TBxCLn when TBxR counts to 0 + toggle mode + int enable
    TB1CTL = TBSSEL_2 + MC_1 + TBCLR;  		  // SMCLK (=24 MHz), Up Mode, clear TBR
    ////////////////////////////////////////////////////////

    The set up example you provided and mine are petty similar. I did not output TB1.0 on any port pins nor select an output mode for it because I am only using TB1CCR0 to set the period of my waveform. I will comment out CLLD_x in my set up to get it to match what you have better before I test it.

    // Code for TB1CCR1 & TB1CCR2 ISR //////////////////////
    Q34_n = !Q34_n;	// Record/Update current state of output
    
    if (Phase_n < 360) 
    {
    	if (Phase_n < 180) 
    	{
    		if (!Q34_n) // If TB1.n output was recently toggled low
    		{
    			TB1CCRn = Phase_n; 
    			Phase_n = 360; 
    		} 
    	}
    	else // If phase phase is >= 180 but less than 360
    	{ 
    		if (Q34_n)	// if TB1.n ouput was recently toggled high
    		{
    			TB1CCRn = Phase_n - 180; 
    			Phase_n = 360; 
    		} 
    	}
    }

    The outer if statement that checks to make sure that the phase is less than 360 (because 360 means a value was just set) is implemented elsewhere in my framework so I can get rid of it in the ISR. Actually, my code elsewhere only allows the phase to vary from 0% (aka in phase) to 100% (aka completely out of phase or 180 degrees).

    The inner if else statement I am not 100% sure I understand yet.

    Since my code limits phase from 0 to 180, the else part will (or should) never be entered. So removing the else should not affect the behavior we are aiming for right?

    My code will enter the "if (Phase_n < 180)" but it seems like you only set the new phase when the output is low. What's the rationale behind that?

    I didn't get a chance to test this yet but I will let you know when I do.

  • I just tested a similar version of the solution you suggested and I saw the sudden 180 phase shift on the first command I sent. The most relevant pieces of code that I tested are below.

    unsigned int Phase_1 = 0;
    unsigned int Phase_2 = 0;
    bool  Q34_1 = 0; 
    bool  Q34_2 = 0; 
    
    void main()
    {
       ...
       ...
       ...
    }
    
    // TB1 set-up
    {
    Select timer module and set TB1.1, TB1.2 to show up on port pins
    TB1CCR0 = tb1_period - 1;           // TB1 Period = 600 - 1;
    TB1CCR1 = 0;                        // Init CCR1 to 0
    TB1CCR2 = 0;                        // Init CCR2 0% out of phase (relative to TB1.1)
    TB1CCTL1 = OUTMOD_4;				// Toggle mode
    TB1CCTL2 = OUTMOD_4 + CCIE;			// Toggle mode + int enable
    TB1CTL = TBSSEL_2 + MC_1 + TBCLR;	// SMCLK (=24 MHz), Up Mode, clear TBR
    }
    
    //TB1 ISR
    #pragma vector=TIMER1_B1_VECTOR
    __interrupt void TIMER1_B1_ISR(void)
    {
      switch(__even_in_range(TB1IV,0x0E))
      {
      case 0x00: break;                             // No interrupt pending
      case 0x02:                                    // TB1CCR1
        break;
      case 0x04:                                    // TB1CCR2
        Q34_2 = !Q34_2;	// Record/Update current state of output
        if (Phase_2 < 600) 
        {
                if (!Q34_2) // If TB1.2 output was recently toggled low
                {
                        TB1CCR2 = Phase_2; 
                        Phase_2 = 600; 
                } 
        }
        break;
      case 0x06: break;                             // TB1CCR3
      case 0x08: break;                             // TB1CCR4
      case 0x0A: break;                             // TB1CCR5
      case 0x0C: break;                             // TB1CCR6
      case 0x0E: break;                             // TB0CTL
      default: break;
      }
    }

    Sine I am not changing Phase_1, because Phase_2 is calculated relative to Phase_1, I do not activate the interrupt for TB1CCR1. In what I have, Phase_2 represents the number of timer counts of TB1CCR2; not the phase in the traditional sense.

    The last big change I made from what you showed is how I restructured the code in TB1CCR2 ISR.

    I assumed that my changes wouldn't affect the behavior we are aiming for, but I could have missed something.

**Attention** This is a public forum