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.