I had a DRV8850 EVM module and had modified the code for my application. Basically this turns on and turns the motor CW for ~400ms, then changes the direction to CCW for ~400ms. I left the pot control in to be able to vary the motor speed.
I just built up my prototype based on the DRV8850EVM but I swapped some pins in order to get a better board layout. The changed pins are:
- IN1H was P2.5, now P2.3
- IN1L was P2.1, now P2.4
- IN2H was P2.4, now P2.1
- IN2L was P2.2, also P2.2
- SPD_REF was P1.0, now P1.5
The _v2 program is the original program that works on the EVM and the _Proto is the version I modified for the prototype board. When I debug the code it looks like the ADC is always returning 0 yet if I measure the voltage on the pot pin I can see different voltages.
Hopefully I've just missed something silly in the code!
Thanks in Advance!!
George
#include "msp430g2553.h"
// Port 1 output definitions
#define STATUS_LED BIT7 // P1.7
// Port 2 output definitions
#define IN1H BIT5 // P2.5
#define IN1L BIT1 // P2.1
#define IN2H BIT4 // P2.4
#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 BIT0 // P1.0 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 = BIT2 | BIT1;
TA1CCTL1 = OUTMOD_2;
TA1CCTL2 = OUTMOD_6;
// need to set bits to clear based on direction
if (Direction == FORWARD)
{
P2SEL = BIT5 | BIT1; // PWM IN1 and
P2OUT = BIT2; // SET IN2L high
}
else
{
P2SEL = BIT4 | BIT2; // PWM IN2 and
P2OUT = BIT1; // 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 = BIT4 | BIT1; // Set IN2H and IN1L high
P2OUT &= ~(BIT5 | 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.0
ADC10CTL0 |= ADC10SHT_2 + ADC10ON + ADC10IE; // VR+ = Vcc, VR- =AVSS, 16x sample and conversion, enable ADC and ADC interrupt
ADC10AE0 |= INCH_0; // Enable ADC input on A0
// 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
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
}
#include "msp430g2553.h"
// Port 1 output definitions
#define STATUS_LED BIT7 // P1.7
// Port 2 output definitions
#define IN1H BIT3 // P2.3
#define IN1L BIT4 // P2.4
#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 = BIT3 | 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 = BIT3 | 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 = BIT3 | 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
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
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
}