Other Parts Discussed in Thread: DRV8838, DRV8838EVM
Hi,
I have a question about MSP430G2131 timer. This link 'MSP430 Multiple Time Bases on Single Timer.pdf' mentions “the number of frequencies and duty cycles that can be simultaneously produced on a particular MSP430 device is dependent on the total number of all TxCCRx registers on the device” (for multiple time base method). We are using MSP430G2131 on the DRV8838 EVM. It has 2 TxCCRx registers and hence we assume it can create 2 different frequencies and duty cycles.
For our project we need to generate:
- One PWM with frequency = 20 Hz and Duty Cycle = 20%. We can generate this one correctly.
- Another PWM (to drive the motor) with a set frequency preferable in KHz and a varying duty cycle depending on the input from the user.
We are not using the potentiometer on the DRV8838EVM to change the duty cycle. We have SPI communication established between MSP430G2131 and a CH341 chip. So, the duty cycle is changed via a software on PC (sent to the MSP430G2131 via CH341 using SPI).
We are facing a problem generating the PWM to drive the motor. For each frequency, there are few duty cycle values for which the PWM will be generated fine (the duty cycle changes according to the input from the PC). However, for the remaining duty cycle values the PWM will either not be stable with big gaps in between or it will not produce right PWM signal. We have noted these effects in the attached excel file. However, we are not able to understand the reason behind this.
Attached is also our CCS code.
#include <msp430.h> #include "msp430g2131.h" //------------------------------------------------------------------------------ // Definitions //------------------------------------------------------------------------------ #define LED BIT1 // P1.1 #define PHASE BIT2 // P1.2 #define DIRECTION BIT3 // P1.3 #define ADC_VREF BIT4 // P1.4 #define ENABLE BIT6 // P2.6 //------------------------------------------------------------------------------ // Global variables //------------------------------------------------------------------------------ unsigned int i, m_cnt, counter, flag = 0; unsigned int DutyCycle = 0; // PWM duty cycle unsigned int high, low = 0; unsigned int master_data[12] ; // array to store the 12B data from the master unsigned int slave_data[12] = {0x7E, 0x5D, 0x78, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x00}; // array to store the 12B data from the slave unsigned int *pslaveArray = slave_data; // pointer pointing to slave_data array //------------------------------------------------------------------------------ // Declare functions //------------------------------------------------------------------------------ /* * Function to calculate checksum */ unsigned int checksum(unsigned int array[], int size) { unsigned int sum = 0; unsigned int j = size; for( ; j > 0; j--) { sum += array[j-1]; } return sum; } /* * Function to check the master array */ void master_check(unsigned int master_array[12]) { // Combine both the duty cycle (DC) values from the master as integer values to compare them later unsigned int asked_DC = master_array[3] + master_array[2]; unsigned const int safe_DC = 0x00 + 0x6D; // check if the asked DC from the master is less than the limit DC. // if(asked_DC <= safe_DC) // Yes then change that value into the slave data array accordingly // { DutyCycle = asked_DC; high = DutyCycle; low = 250 - high; /** } else // No then put the limit DC value into the slave data array accordingly { DutyCycle = safe_DC; high = DutyCycle; low = 250 - high; } **/ } /* * Function to initialize values */ void init(void) { IE1 |= WDTIE; // Enable WDT interrupt BCSCTL1 = CALBC1_1MHZ; // Set DCO to 1MHz DCOCTL = CALDCO_1MHZ; P1SEL = LED + PHASE; P1OUT = 0x00; // P1.x Reset P1DIR = 0x56; CCTL0 = CCIS_0 + OUTMOD_4 + CCIE; // CCR0 toggle, interrupt enabled, CCI0A input on CCI CCTL1 = CCIS_0 + OUTMOD_4 + CCIE; TACTL = TASSEL_2 + MC_2 + TAIE; // SMCLK source, continuous mode, int enabled ADC10CTL0 = ADC10SHT_2 + ADC10ON; //+ MSC + ADC10IE; // VCC ref ADC10AE0 |= INCH_0; // P1.0 ADC option select USICTL0 |= USIPE7 + USIPE6 + USIPE5 + USIOE; // Port, SPI slave USICTL1 |= USIIE; // Counter interrupt, flag remains set USICTL0 &= ~USISWRST; // USI released for operation USISRL = 0xCD; // init-load data into the lower byte of the serial register USISRH = 0xEF; // init-load data into the higher byte of the serial register USICNT = USI16B + 16; // init-load counter + enable 16-bit shift register mode } //------------------------------------------------------------------------------ // Main function //------------------------------------------------------------------------------ int main(void) { WDTCTL = WDT_MDLY_8; // Set Watchdog Timer interval to ~30ms // __bis_SR_register(LPM0_bits + GIE); // Enter LPM0 w/ interrupt init(); while(1) { _BIS_SR(LPM0_bits + GIE); // Enter LPM0 with interrupts P1OUT |= ADC_VREF; // Enable poti divider ADC10CTL0 |= ENC + ADC10SC; // Sampling and conversion start while (ADC10IFG & ADC10CTL0); // Wait for conversion complete ADC10CTL0 &= ~ADC10IFG; // Clear converstion flag P1OUT &= ~ADC_VREF; // Disable poti divider // DutyCycle = 63 - (ADC10MEM>>4); // Mask upper 6-bits // LED_Blink = 63 - (ADC10MEM>>4); // Mask upper 6-bits // CCR1 = DutyCycle; // PWM duty cycle to DRV /** if (DIRECTION & P1IN) // Forward direction? { P1OUT |= PHASE; } else // Reverse { P1OUT &= ~PHASE; } **/ } } // Watchdog Timer interrupt service routine #pragma vector=WDT_VECTOR __interrupt void watchdog_timer(void) { _BIC_SR_IRQ(LPM3_bits); // Clear LPM0 bits from 0(SR) } // USI interrupt service routine #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=USI_VECTOR __interrupt void universal_serial_interface(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(USI_VECTOR))) universal_serial_interface (void) #else #error Compiler not supported! #endif { if(flag == 0 || ((USISRH == 0x7E) && (USISRL == 0x5D))) { flag = 1; m_cnt = 0; } if(flag == 1) { master_data[m_cnt] = USISRH; master_data[m_cnt+1] = USISRL; m_cnt = m_cnt + 2; } if (m_cnt >= 10) { flag = 0; if((master_data[0] == 0x7E) && (master_data[1] == 0x5D)) //Check if the master header is right master_check(master_data); } if(counter >= 12) { counter = 0; slave_data[3] = ADC10MEM; //sampling lower 8 bytes first slave_data[4] = ADC10MEM >> 8; //shift the upper 8 bytes to the left to sample them // slave_data[5] = DutyCycle; // slave_data[6] = DutyCycle >> 8; slave_data[5] = CCR1; slave_data[6] = CCR1 >> 8; } pslaveArray = slave_data + counter; USISRL = *(pslaveArray+1); USISRH = *(pslaveArray); USICNT = USI16B + 16; counter = counter + 2; } // Timer A0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) // Generate 20Hz frequency with 20% constant duty cycle { if(CCTL0 & CCI) // If output currently high { CCR0 += 10000; // 20% high } else { CCR0 += 40000; // 80% low } } // Timer A1 Interrupt Vector (TA0IV) handler #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A1(void) // Generate 4KHz frequency for DRV motor { switch( TA0IV ) { case 2: if(CCTL1 & CCI) // If output currently high { //CCR1 += 50; // 20% high //CCR1 += DutyCycle; CCR1 += high; } else { //CCR1 += 200; // 80% low //CCR1 += (250 - DutyCycle); CCR1 += low; } break; default: break; } }
Any help is appreciated.