I'm more of a hardware guy and am having trouble coming up with a work around on a program change. I have a modified program from the DRV8850 EVM which uses TA1.1 and TA1.2. I didn't review the code closely enough before doing our board layout and I modified the connections between the DRV8850 and the MSP430G2553.
I now see that they separated the signals going to the DRV8850 so the high side signals (IN1H/IN2H) were connected to TA1.2 and the timer register was set to using OUTMOD6 (toggle/set mode). The low side signals (IN1L/IN2L) were connected to TA1.1 and the timer register was set to using OUTMOD2 (toggle/reset).
The way I routed my board was with these connections and an easy PCB fix is also proposed in the last column.
Signal Name |
DRV8850EVM |
Proto |
Easy PCB Change |
IN1H |
P2.5 (TA1.2) |
P2.3 (TA1.0) |
P2.5 (TA1.2) |
IN1L |
P2.1 (TA1.1) |
P2.4 (TA1.2) |
P2.4 (TA1.2) |
IN2H |
P2.4 (TA1.2) |
P2.1 (TA1.1) |
P2.1 (TA1.1) |
IN2L |
P2.2 (TA1.1) |
P2.2 (TA1.1) |
P2.2 (TA1.1) |
I think if I was a better programmer I could come up with a fix but I'm not having any luck so I thought I'd put up a post and see if someone can think of a clever way to modify the code to match the board layout.
Attached is the code.
Thanks,
George
#include "msp430g2553.h" // Port 1 output definitions #define STATUS_LED BIT7 // P1.7 // Port 2 output definitions #define IN1H BIT3 // P2.3 #define IN1L BIT5 // P2.5 #define IN2H BIT1 // P2.1 #define IN2L BIT2 // P2.2 // Port 1 REN definitions (set unused to REN) #define UNUSED BIT4 | BIT 3 // P1.4 and P1.3 are unused // #define SPEED_REF BIT5 // P1.5 is ADC read location // Port 2 inputs #define DIR BIT7 // P2.7 is direction // TIMER defines // use 8 #define DUTY_CYCLE_TIMER_MAX 127 // max timer count value -- total is 256 for up/down #define DEADTIME 1 // software deadtime loop // set to 0 to use internal deadtime #define MIN_DC 4 // sets the minimum duty cycle threshold to spin the motor // 1 - 50% // 2 - 25% // 3 - 12.5% // 4 - 6.25% // 5 - 3.125% #define MAX_DC 5 // sets the maximum duty cycle threshold to spin the motor // 1 - 50% // 2 - 75% // 3 - 87.5% // 4 - 93.75% // 5 - 96.875% #define DC_TOLERANCE 1 // Masked ADC value must change by DC_TOLERANCE + 1 before an update will occur // Value to change is ((DC_TOLERANCE + 1) * Vcc)/1024/8) Volts // direction #define FORWARD 0 #define ALL_LOW 0x00 #define ALL_HIGH 0xFF volatile unsigned int LEDcounter=0; // Initialize LED timing counter unsigned int loop_counter; unsigned int Direction, PriorDirection; signed int DutyCycle, PriorDutyCycle; unsigned int delayloop1; unsigned int delayloop2; /// SUBROUTINES // this routine is a simple method of using duty cycle // it is calculating the duty cycle by using the DUTY_CYCLE_TIMER_MAX number as // a multiple of the number of bits (the number of bits is 6 (64 bins), and the // the multiple is MULT_FACTOR) // by using a multiple of the number of bits, the duty cycle is a math calculation // OUTMOD0 is static, uses state of BIT2 to set low or high // OUTMOD2 is toggle/reset -- use on low side // OUTMOD6 is toggle/set -- use on high side void UpdateSpeed(unsigned int dc) { // calculate the next high side and low side values, then set them to minimize time // the truth table for using this section is shown below // Dir/Speed IN1L IN1H IN2L IN2H // 0 H L H L // FWD < 100% PWM PMW H L // FWD - 100% L H H L // REV < 100% H L PWM PWM // REV - 100% H L L H // set up for slow decay mode // Stop and reset the timers // TA1CCTL1 &= ~CCIE; // disable the interrupt TA1CTL |= TACLR; // CLR the timer; if (dc < (DUTY_CYCLE_TIMER_MAX >> MIN_DC)) // do not start spinning until 12.5% { // clear IN1H and IN2H // set IN1L and IN2L high to stop motor TA1CCTL1 = OUTMOD_0 | BIT2; TA1CCTL2 = OUTMOD_0; P2SEL = BIT5 | BIT4 | BIT2 | BIT1; } else if (dc < DUTY_CYCLE_TIMER_MAX - (DUTY_CYCLE_TIMER_MAX >> MAX_DC)) // use this up to 88% { // enable interrupts // TACTL |= TAIE; TA1CCR1 = DUTY_CYCLE_TIMER_MAX - dc; TA1CCR2 = DUTY_CYCLE_TIMER_MAX + DEADTIME - dc; // TA1CCTL1 |= CCIE; // clear IN1H and IN2H // set IN1L and IN2L high to stop motor P2SEL = BIT4 | BIT2; TA1CCTL1 = OUTMOD_2; TA1CCTL2 = OUTMOD_6; // need to set bits to clear based on direction if (Direction == FORWARD) { P2SEL = BIT5 | BIT4; // PWM IN1 and P2OUT = BIT2; // SET IN2L high } else { P2SEL = BIT1 | BIT2; // PWM IN2 and P2OUT = BIT4; // SET IN1L high } // restart the timer TA1CTL = TASSEL_2 | ID_0 | MC_3 | TACLR; // Timer Clock = SMCLK; } else // 100% { TA1CCTL1 = OUTMOD_0 | BIT2; TA1CCTL2 = OUTMOD_0 | BIT2; if (Direction == 0) { P2SEL = BIT5 | BIT2; // Set IN1H and IN2L high P2OUT &= ~(BIT4 | BIT1); // Set IN1L and IN2H low } else { P2SEL = BIT1 | BIT4; // Set IN2H and IN1L high P2OUT &= ~(BIT3 | BIT2); // Set IN1H and IN2L low } } } // MAIN Routine int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer // Initialization -- First set all GPIO as inputs with pulldown P1REN = BIT7 + BIT6 + BIT5 + BIT4 + BIT3 + BIT2 + BIT1 + BIT0; P2REN = BIT7 + BIT6 + BIT5 + BIT4 + BIT3 + BIT2 + BIT1 + BIT0; P3REN = BIT7 + BIT6 + BIT5 + BIT4 + BIT3 + BIT2 + BIT1 + BIT0; P1OUT = ALL_LOW; P2OUT = ALL_LOW; P3OUT = ALL_LOW; // Now re-configure Port Directions and Peripherals as required P1DIR = STATUS_LED; // Set Port 1 GPIO to output on P1.7 P1REN &= ~STATUS_LED; // Disable Resistor // must clear P2.7 prior to using it as an input P2SEL = ALL_LOW; // Now re-configure Port Directions and Peripherals as required P2DIR = IN1H | IN1L | IN2H | IN2L; // Set Port 1 GPIO to output on P1.1 and P1.2 P2REN &= ~(IN1H | IN1L | IN2H | IN2L); // Disable resistors // // Configure ADC10 to supply reference voltage and read value of voltage divider on P1.5 ADC10CTL0 |= ADC10SHT_2 + ADC10ON + ADC10IE; // VR+ = Vcc, VR- =AVSS, 16x sample and conversion, enable ADC and ADC interrupt ADC10CTL1 |= INCH_5; // Select ADC input on A5 ADC10AE0 |= INCH_5; // Enable ADC input on A5 // BCSCTL3 = LFXT1S_3; // Register value that must be set to allow TAI functionality on XIN pin // Timer count value needs to be load only once TA1CCR0 = DUTY_CYCLE_TIMER_MAX; // set time TA1CCTL1 |= OUTMOD_2; // set mode to toggle/set TA1CCTL2 |= OUTMOD_6; // set mode to toggle/reset // delay for some time prior to speeding the clock, approximately 250ms per outer loop for (delayloop2 = 2; delayloop2 > 0; delayloop2 --) { for (delayloop1 = 20000; delayloop1 > 0; delayloop1--) {__no_operation(); } } // Set the DCO to calibrated 12MHz, be sure to have MCLK/2 and SMCLK/2 set prior to these commands. BCSCTL2 |= DIVM_1 | DIVS_1; // MCLK is DCO/2, SMCLK is DCO/2 DCOCTL = ALL_LOW; // clear the DCO then read the values BCSCTL1 = CALBC1_12MHZ; DCOCTL = CALDCO_12MHZ; // Now set clock frequency at 6MHz //// End of initialization // Main Body of Code _BIS_SR(GIE); // Global interrupt enable // set values to force compare PriorDirection = 12000; PriorDutyCycle = 12000; // initial setup of direction to avoid time delay //Direction = ((P2IN & DIR) >> 7); // read direction if 0 -- forward else reverse Direction = 0; // No need to read a direction if (Direction != PriorDirection) { UpdateSpeed(0); // stop the motor first then restart in new direction //Set Direction PriorDirection = Direction; } // end of setup, beginning of main loop TA1CCTL1 = OUTMOD_0 | BIT2; TA1CCTL2 = OUTMOD_0 | BIT2; // UpdateSpeed(DutyCycle); //Check Speed Wheel once before looping for (;;) { //UpdateSpeed(0); //Stop motor before changing Direction Direction = 0; ADC10CTL0 |= ENC + ADC10SC; // Read Speed value from ADC -- Sampling and conversion start UpdateSpeed(DutyCycle); //Check Speed Wheel // Delay for 30ms per outer loop or 420ms for (delayloop2 = 14; delayloop2 > 0; delayloop2 --) { for (delayloop1 = 24000; delayloop1 > 0; delayloop1--) {__no_operation(); } } //UpdateSpeed(0); //Stop motor before changing Direction Direction = 1; ADC10CTL0 |= ENC + ADC10SC; // Read Speed value from ADC -- Sampling and conversion start UpdateSpeed(DutyCycle); //Check Speed Wheel // Delay for 30ms per outer loop or 420ms for (delayloop2 = 14; delayloop2 > 0; delayloop2 --) { for (delayloop1 = 24000; delayloop1 > 0; delayloop1--) {__no_operation(); } } } } ///////////////////////////////// Interrupt service routines //////////////////////// // ADC10 interrupt service routine #pragma vector=ADC10_VECTOR __interrupt void ADC10_ISR(void) { DutyCycle = (ADC10MEM & 0x3f8) >> 3; // discard the last 3 bits ~25mV/bit and // shift right one to match 511 max P1OUT ^= STATUS_LED; // Toggle LED } #pragma vector=PORT1_VECTOR, PORT2_VECTOR, USCIAB0TX_VECTOR, USCIAB0RX_VECTOR, \ TIMER0_A1_VECTOR, TIMER0_A0_VECTOR, WDT_VECTOR, COMPARATORA_VECTOR, \ TIMER1_A1_VECTOR, TIMER1_A0_VECTOR, NMI_VECTOR __interrupt void Trap_ISR(void) { // this is a trap ISR - check for the interrupt cause here by // checking the interrupt flags, if necessary also clear the interrupt // flag }