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/MSP430FR2355: Issue regarding MSP430FR2355 PWM module

Part Number: MSP430FR2355
Other Parts Discussed in Thread: DRV8323, MSP430F5529, BOOSTXL-DRV8320RS

Tool/software: Code Composer Studio

Hello,

We are developing logic for BLDC motor control using MSP430FR2355. We encountered several issues with PWM module.

The image of the same is shown below. Our desired system is to work at 20kHz switching frequency, two ADC channel for battery monitoring and speed control.

The logic for BLDC is to operate switches in two pulse mode with high side switch working in PWM fashion. 

The image shown below has PWM with few other frequencies and I have kept duty to 50% but that duty is also not visible 


//Header Files//
#include "msp430fr2355.h"
#include "stdint.h"

#define CALTDH0CTL1_256 *((unsigned int *)0x1A36)

/*****************************InstaSPIN**************************************/


#define PWM_PERIOD 600 //PWM Frequency (Hz) = 25MHz/((2*PWM_PERIOD)-1)
#define MAX_DUTYCYCLE 600 //relative to PWM_PERIOD
#define MIN_DUTYCYCLE 5 //relative to PWM_PERIOD
#define ACCEL_RATE 100 //Ramp up time to full scale duty cycle = (Full scale duty cycle) * ACCEL_RATE * PWM_PERIOD/PWM_Frequency
#define DEAD_TIME_MCU 1 // Dead time from MSP430 = DEAD_TIME* 0.0625 uS (for 16MHz clock)
#define Block_Rotor_Duration 800 //Blocked_rotor shut off time (s) = Block_Rotor_Duration*30000/Timer clock frequency


int LPM3_On = 0;
/*************************************program variables**********************************************/
unsigned int DC_BUS_CURRENT = 0;
unsigned int DC_Bus_Voltage = 0;
unsigned int SPEED_REF = 0;
unsigned int Temperature_feedback = 0;
unsigned int start_count = 0;
unsigned int HALL_STATE = 0;
unsigned int softstart_counter = 0;
unsigned int CurrentDutyCycle = 100;
unsigned int DIRECTION = 0 ;
unsigned int FirstADC_flag = 1;
unsigned int ADC_selection_flag = 1;
unsigned int ADC_selection_flag_1 = 1;
unsigned int Block_Rotor_Counter = 0;
unsigned int Block_Rotor_Counter_1 =0;
unsigned int ADC_RESULT = 0;

unsigned int commutation_time = 0;
unsigned int commutation_time_counter = 0;
unsigned int advance_angle_time_counter = 0;
unsigned int advance_angle_time = 0;
unsigned int commutation_done = 0;
unsigned int Previous_State = 0;
unsigned int Present_State = 0;
unsigned int PWM_DUTY = PWM_PERIOD;
unsigned int ADC_counter_flag = 0;
unsigned int BATT_V = 0;

unsigned int i = 0;
unsigned int j = 0;

void Init_Clocks (void);
void init_IO (void);
void init_WDT (void);
void init_ADC (void);
void init_TimerB (void);

void Hall_State_Change_FORWARD(void);
void Hall_State_Change_REVERSE(void);
void A_PWM(void);
void B_PWM(void);
void C_PWM(void);
void A_LOW(void);
void B_LOW(void);
void C_LOW(void);
void A_Z(void);
void B_Z(void);
void C_Z(void);
void A_HIGH(void);
void B_HIGH(void);
void C_HIGH(void);

void main (void)
{

WDTCTL = WDTPW + WDTHOLD; // Stop WDT
_delay_cycles(1000000);
Init_Clocks (); // Initialize clocks for 25 MHz

init_ADC ();
init_TimerB ();
//init_WDT ();
init_IO ();

A_Z();
B_Z();
C_Z();

_delay_cycles(1000000);

/******************************ENABLE for DRV8323*******************************************************/
P4OUT &= ~BIT1;
_delay_cycles(2500); //100 us delay
//P4OUT |= BIT1;
_delay_cycles(250); //10 us delay

// DIRECTION = (P5IN & BIT4);
DIRECTION = 0;
HALL_STATE = ((P3IN & BIT0) + (P3IN & BIT1) + (P3IN & BIT2));

ADC_counter_flag = 0;
while(1)
{
i = 0;

if(softstart_counter >= ACCEL_RATE)
{
softstart_counter = 0;
if (CurrentDutyCycle < SPEED_REF)
{
CurrentDutyCycle ++;
}
else if (CurrentDutyCycle > SPEED_REF)
{
CurrentDutyCycle --;
}

if ( CurrentDutyCycle >= MAX_DUTYCYCLE)
{
CurrentDutyCycle = MAX_DUTYCYCLE;
}
else if (CurrentDutyCycle <= MIN_DUTYCYCLE)
{
CurrentDutyCycle = MIN_DUTYCYCLE;
}
}
if(DIRECTION == 0)
{
Hall_State_Change_FORWARD();
}
else
{
Hall_State_Change_REVERSE();
}


}
//end main
}
//WDT to restart ADC
void init_WDT (void)
{

WDTCTL = WDT_MDLY_32; // WDT 32ms from 1MHz, SMCLK, interval timer
//SFRIE1 |= WDTIE; // Enable WDT interrupt
}


void init_TimerB (void)
{

TB3CTL = TBSSEL__SMCLK | MC_3 | TBCLR; // SMCLK, up_down mode, clear TBR
TB3CCR0 = PWM_PERIOD; // PWM Period
TB3CCR1 = PWM_PERIOD; // CCR1 PWM duty cycle
TB3CCR2 = PWM_PERIOD; // CCR2 PWM duty cycle
TB3CCR3 = PWM_PERIOD; // CCR3 PWM duty cycle
TB3CCR4 = PWM_PERIOD; // CCR4 PWM duty cycle
TB3CCR5 = PWM_PERIOD; // CCR3 PWM duty cycle
TB3CCR6 = PWM_PERIOD; // CCR4 PWM duty cycle

TB3CCTL1 = OUTMOD_6; // CCR1 reset/set
TB3CCTL2 = OUTMOD_2; // CCR2 reset/set
TB3CCTL3 = OUTMOD_6; // CCR3 reset/set
TB3CCTL4 = OUTMOD_2; // CCR4 reset/set
TB3CCTL5 = OUTMOD_6; // CCR4 reset/set
TB3CCTL6 = OUTMOD_2; // CCR4 reset/set
TB3CCTL0 |= CCIE;

__bis_SR_register(GIE); // enable interrupts
__no_operation(); // For debugger

_EINT(); // Enable interrupts

}


// Clocks And Vcore
void Init_Clocks (void)
{

FRCTL0 = FRCTLPW | NWAITS_2;

__bis_SR_register(SCG0); // disable FLL
CSCTL3 |= SELREF__REFOCLK; // Set REFO as FLL reference source
CSCTL0 = 0; // clear DCO and MOD registers
CSCTL1 |= DCORSEL_7; // Set DCO = 24MHz
CSCTL2 = FLLD_0 + 731; // DCOCLKDIV = 24MHz
__delay_cycles(3);
__bic_SR_register(SCG0); // enable FLL
while(CSCTL7 & (FLLUNLOCK0 | FLLUNLOCK1)); // FLL locked

CSCTL4 = SELMS__DCOCLKDIV | SELA__REFOCLK; // set default REFO(~32768Hz) as ACLK source, ACLK = 32768Hz
// default DCOCLKDIV as MCLK and SMCLK source
}

//IO INITIALISATION//
void init_IO (void)
{
PM5CTL0 &= ~LOCKLPM5;

P2SEL0 &= ~(BIT6); //GPIO-LED3
P2SEL1 &= ~(BIT6);
P2DIR |= (BIT6); //Output-LED3

P1SEL0 |= BIT1; //A0 pin
P1SEL1 |= BIT1;

P1SEL0 |= BIT7; //A0 pin
P1SEL1 |= BIT7;

//PWM outputs
//GPIO-PWM
P6DIR |= (BIT0+BIT1+BIT2+BIT3+BIT4+BIT5); //OutputPWM

//Direction Control
P5SEL0 &= ~(BIT4); //GPIO - DIR
P5SEL1 &= ~(BIT4); //GPIO - DIR
P5DIR |= (BIT4); //Input - DIR

//Enable Gate driver
P4SEL0 &= ~(BIT1); //GPIO - DIR
P4SEL1 &= ~(BIT1); //GPIO - DIR
P4DIR |= (BIT1); //Output - DIR

//Fault input
P4SEL0 &= ~(BIT0); //GPIO - DIR
P4DIR &= ~(BIT0); //Input - DIR


//Hall Sensor inputs

P3SEL0 &= ~(BIT0+BIT1+BIT2); //GPIO - Hall sensors
P3DIR &= ~(BIT0+BIT1+BIT2); //Inputs - Hall sensors

P3IES |= ((BIT0)+(BIT1)+(BIT2)); // change the hall interrupt to falling edge to detect both the edges
P3IE |= (BIT0 | BIT1 | BIT2);

P3IFG &= ~(BIT0| BIT1 | BIT2);

__bis_SR_register(GIE);

}


void init_ADC (void)
{

// Configure ADC12
ADCCTL0 |= ADCSHT_2 | ADCON; // ADCON, S&H=16 ADC clks
ADCCTL1 |= ADCSHP; // ADCCLK = MODOSC; sampling timer
ADCCTL2 &= ~ADCRES; // clear ADCRES in ADCCTL
ADCCTL2 |= ADCRES_2; // 12-bit conversion results
ADCMCTL0 |= ADCINCH_0; // A1 ADC input select; Vref=AVCC
ADCIE |= ADCIE0; // Enable ADC conv complete interrupt
ADCCTL0 |= ADCENC | ADCSC;

}
/**************************************ADC INTERRUPT******************************************/

// ADC interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=ADC_VECTOR
__interrupt void ADC_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC_VECTOR))) ADC_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(ADCIV,ADCIV_ADCIFG))
{
case ADCIV_NONE:
break;
case ADCIV_ADCOVIFG:
break;
case ADCIV_ADCTOVIFG:
break;
case ADCIV_ADCHIIFG:
break;
case ADCIV_ADCLOIFG:
break;
case ADCIV_ADCINIFG:
break;
case ADCIV_ADCIFG:
ADC_RESULT = ADCMEM0;

ADC_counter_flag++;

break;
default:
break;
}
}
/**************************************TIMERD0.1 INTERRUPT********************************************/

// Timer B1 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER3_B0_VECTOR
__interrupt void Timer3_B0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER3_B0_VECTOR))) Timer3_B0_ISR (void)
#else
#error Compiler not supported!
#endif
{

//PWM_DUTY = (PWM_PERIOD - CurrentDutyCycle);
PWM_DUTY = 300;
P5OUT ^= BIT4;

TB3CCR1 = PWM_DUTY; // CCR1 PWM duty cycle
TB3CCR2 = PWM_DUTY;
TB3CCR3 = PWM_DUTY; // CCR1 PWM duty cycle
TB3CCR4 = PWM_DUTY;
TB3CCR5 = PWM_DUTY; // CCR1 PWM duty cycle
TB3CCR6 = PWM_DUTY;

if (ADC_counter_flag == 1)
{
SPEED_REF = ADC_RESULT;
SPEED_REF = (SPEED_REF >>2);
ADCCTL0 &= ~ADCENC; // Disable
ADCMCTL0 = (ADCMCTL0 & ~ADCINCH) | 7;
ADCCTL0 |= ADCENC | ADCSC;
}
else if (ADC_counter_flag == 2)
{
BATT_V = ADC_RESULT;

ADCCTL0 &= ~ADCENC; // Disable
ADCMCTL0 = (ADCMCTL0 & ~ADCINCH) | 0;

ADCCTL0 |= ADCENC | ADCSC;
ADC_counter_flag = 0;
}

softstart_counter ++;

Block_Rotor_Counter_1++;

if(Block_Rotor_Counter_1>=30000)
{
Block_Rotor_Counter++;
}

if (Block_Rotor_Counter > Block_Rotor_Duration)
{
A_Z();
B_Z();
C_Z();
_disable_interrupt ();
//while (1);
}

TB3CCTL0 &= ~CCIFG;

}

/**********************************Port 1 interrupt service routine******************************/

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=PORT3_VECTOR
__interrupt void Port_3(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(PORT3_VECTOR))) Port_3 (void)
#else
#error Compiler not supported!
#endif
{
HALL_STATE = ((P3IN & BIT0) + (P3IN & BIT1) + (P3IN & BIT2));

if(DIRECTION == 0)
{
Hall_State_Change_FORWARD();
}
else
{
Hall_State_Change_REVERSE();
}

Block_Rotor_Counter = 0;
Block_Rotor_Counter_1 =0;


P3IES ^= (BIT0)+(BIT1)+(BIT2);
P3IFG &= ~(BIT0| BIT1 | BIT2);

}
/**********************************END OF ADC INTERRUPT***************************************/


// Watchdog Timer interrupt service routine
#pragma vector=WDT_VECTOR
__interrupt void WDT_ISR(void)
{

}

/**************************Commutation sequence Forward***********************************************/
void Hall_State_Change_FORWARD(void)
{

switch (HALL_STATE)
{
case 2:
C_Z();
A_PWM();
B_LOW();


break;

case 6:
B_Z();
A_PWM();
C_LOW();


break;

case 3:
A_Z();
C_PWM();
B_LOW();


break;
case 1:
B_Z();
C_PWM();
A_LOW();


break;

case 4:
A_Z();
B_PWM();
C_LOW();


break;

case 5:
C_Z();
B_PWM();
A_LOW();


break;


default:
A_Z();
B_Z();
C_Z();

break;
}


}

/**************************Commutation sequence Reverse***********************************************/
void Hall_State_Change_REVERSE(void)
{

switch (HALL_STATE)
{
case 2:
C_Z();
B_PWM();
A_LOW();


break;

case 6:
B_Z();
C_PWM();
A_LOW();


break;

case 3:
A_Z();
B_PWM();
C_LOW();


break;
case 1:
B_Z();
A_PWM();
C_LOW();


break;

case 4:
A_Z();
C_PWM();
B_LOW();


break;

case 5:
C_Z();
A_PWM();
B_LOW();


break;


default:
A_Z();
B_Z();
C_Z();

break;
}

}


/**************************Definition of PWM GPIOs***********************************************/

void A_PWM(void)

{
P6SEL0 |= BIT0;
P6SEL0 |= BIT1;

}


void B_PWM(void)

{
P6SEL0 |= BIT2;
P6SEL0 |= BIT3;

}

void C_PWM(void)

{
P6SEL0 |= BIT4;
P6SEL0 |= BIT5;

}

void A_LOW(void)
{
P6SEL0 &= ~BIT0;
P6SEL1 &= ~BIT0;
P6OUT &= ~BIT0;
P6SEL0 &= ~BIT1;
P6SEL1 &= ~BIT1;
P6OUT |= BIT1;
}

void B_LOW(void)
{
P6SEL0 &= ~BIT2;
P6OUT &= ~BIT2;
P6SEL0 &= ~BIT3;
P6OUT |= BIT3;
}

void C_LOW(void)
{
P6SEL0 &= ~BIT4;
P6OUT &= ~BIT4;
P6SEL0 &= ~BIT5;
P6OUT |= BIT5;
}

void A_Z(void)
{
P6SEL0 &= ~BIT0;
P6OUT &= ~BIT0;
P6SEL0 &= ~BIT1;
P6OUT &= ~BIT1;
}

void B_Z(void)
{
P6SEL0 &= ~BIT2;
P6OUT &= ~BIT2;
P6SEL0 &= ~BIT3;
P6OUT &= ~BIT3;
}

void C_Z(void)
{
P6SEL0 &= ~BIT4;
P6OUT &= ~BIT4;
P6SEL0 &= ~BIT5;
P6OUT &= ~BIT5;
}

void A_HIGH(void)
{
P6SEL0 &= ~BIT0;
P6OUT |= BIT0;
P6SEL0 &= ~BIT1;
P6OUT &= ~BIT1;
}

void B_HIGH(void)
{
P6SEL0 &= ~BIT2;
P6OUT |= BIT2;
P6SEL0 &= ~BIT3;
P6OUT &= ~BIT3;
}

void C_HIGH(void)
{
P6SEL0 &= ~BIT4;
P6OUT |= BIT4;
P6SEL0 &= ~BIT5;
P6OUT &= ~BIT5;
}


/**************************End****************************************************************/

  • When I run this code on a Launchpad and pause it at some fortuitous spot, then explicitly set P6SEL0=0x3F, I get a perfect 20kHz square wave at 50% duty (as expected).

    What I observed was that the state machine is frequently enabling/disabling the PWM (at P6SEL0). Without necessarily following your logic, I suspect these interactions are what is throwing your duty cycle off.

  • Hi Jeet,

    I was digging through some example code that I found on the Software-Files for the BOOSTXL-DRV8320RS. This code was originally used for MSP430F5529 Launchpad. 

    It looks that for each state, they do the following:

    - Reset the LS only for the LOW phase (x_LOW of your code), then select I/O

    - Reset the HS and LS of the Hi-Z phase (x_Z of your code), then select I/O

    - Select PWM(s) of the PWM phase (x_PWM of your code), then set LOW phase low

    I copied the U-->V example of the example code to compare to your Case 5 (A_PWM, B_LOW, C_Z). Try modifying your functions so that it matches the order of the steps as listed above and shown in the code below:

    case 2:          /* U-V */
    
                /* Reset switches for phase V (LOW-HIGH)*/
                P1OUT &= ~(BIT5);                                    /* Reset bits P1.4 , P1.5  */
                P1SEL &= ~(BIT5);                                    /* Select P1.4 , P1.5 as I/O Function for Phase V*/
                /* Reset switches for phase W (LOW-HIGH)*/
    
                P1OUT &= ~(BIT2 | BIT3);                                    /* Reset bits P1.2 , P1.3  */
                P1SEL &= ~(BIT2 | BIT3);                                    /* Select P1.2 , P1.3 as I/O Function for Phase W*/
    
            	P2SEL |= BIT4 | BIT5;                                  /* Select Synchronous PWM for U phase*/
                P1OUT |= BIT4;                                         /* Set Low side of V phase */
                break;

  • Hey,

    Thank you for replying.

    The issue with the code was:

    1. The code was stuck in Block rotor condition and thus the PWMs when in to the High impedance mode as specified in the code.  And all the interrupts were disabled as specified in the  "if "loop.

    My queries is regarding high impedance state, why in high impedance state does the PWM comes in the above mentioned image way.

  • >void B_Z(void) {
    >P6SEL0 &= ~BIT2;
    >P6OUT &= ~BIT2;
    >P6SEL0 &= ~BIT3;
    >P6OUT &= ~BIT3;

    I'll just mention here that this doesn't put the pin(s) into Hi-Z state, rather output-low. For that you need to add:

    >P6DIR &= ~(BIT2|BIT3);

**Attention** This is a public forum