2 MSP430's on my bench, both are MSP430FR5739 dev boards. Both are using an external TCXO as the clock source. MCLK = 16MHz on both here. ACLK = 500kHz, ACLK/8 is used as clock source for Timer_A0.
I've got MSP430 #1 "Jabba" outputting 91Hz @ 10% duty cycle ( 1ms high time, 10ms low time ) from a GPIO pin. It does this by manipulation of CCR0 in a TimerA ISR. Time intervals are known at compile time, stored in an array. Everything fine here as far as pulse generation & timing is concerned.
I've got MSP430 #2 "Leia" configured to wait around in a polling loop, waiting for P2.3 to go high.
As soon as possible after P2.3 goes high, I want to immediately start timerA0 on Leia to start outputting the same pulses as Jabba is sending. Leia can do this because the same time intervals are known at compile time, stored in an array. Basically, the pulse train intervals are known and shared ahead of time between Leia and Jabba through the software.
I am not certain what is achievable when I say "as soon as possible", but I cannot tolerate the large delay I am seeing between P2.3 going high and TimerA finally starting. Here's a scope shot showing a
latency of 1.975ms between P2.3 on Leia going high, and P1.0's first pulse from the Timer_A0 module:
I need to get this latency down to 1us or less. I am not sure why TimerA takes so long to start up ( approx. 31,600 MCLK cycles ). By the way, using an ISR to read P2.3 instead of polling results in the same latency ( I experimented ) I realize polling a GPIO in this manner is generally bad practice but for sake of this thread, take it as it is. Code below:
#include <msp430fr5739.h>
// Prototypes
void initCS(void);
void initGPIO(void);
void initTA0(void);
void enableConfig(void);
// Pulse-sending pin is P1.0
// Pulse-reading pin is P2.3
//**************************************************************//
//************ GLOBALS GLOBALS GLOBALS GLOBALS *****************//
//**************************************************************//
// Timer A sourced from ACLK @ 500kHz
// Timer A clk source / 8 for 62.5kHz tick or 16us/tick
// 0.992ms = 62 ticks
// 9.92ms = 620 ticks
// 99.2ms = 6200 ticks
// 992ms = 62000 ticks
unsigned int pulseTiming[2] = {62, 620};
volatile unsigned int numEdges = 2;
volatile unsigned int pulseIdx = 0;
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
initCS();
initGPIO();
enableConfig();
__bis_SR_register(GIE); // Enable global interrupts
// We are in MSP Leia's code here only.
// MSP Jabba is off sending pulses already.
// Endlessly poll P2.3 ...
while(1)
{
if( P2IN & BIT3 ) // If P2.3 goes high ...
{
initTA0(); // ... start timerA right away
}
}//while(1)
return 0;
}//main()
void initCS(void)
{
PJSEL0 |= BIT4; // Set PJ.4 as XIN, digital square wave input
PJSEL1 &= ~BIT4;
CSCTL0_H = 0xA5; // Unlock password for updating CS
//CSCTL1 |= // Don't care about this register, not using DCO
CSCTL2 = SELM__XT1CLK + SELS__XT1CLK + SELA__XT1CLK; // MCLK source = XT1CLK; SMCLK source = XT1CLK;
// ACLK source = XT1CLK
CSCTL3 = DIVM__1 + DIVS__2 + DIVA__32; // MCLK/1; SMCLK/2; ACLK/32
CSCTL4 |= XT1BYPASS | XTS | XT2OFF; // Select Bypass mode operation
// for XT1; select high-freq. mode;
// Don't need XT2 so turn it off
do
{
CSCTL5 &= ~XT1OFFG; // Clear XT1 fault flag
SFRIFG1 &= ~OFIFG;
__delay_cycles(10000); // Wait around a little while before next check
} while (SFRIFG1&OFIFG); // Test oscillator fault flag
PJDIR |= BIT5; // Set PJ.5 as a GPIO output ( don't need XOUT )
PJDIR |= BIT0; // Set PJ.0 as SMCLK output
PJSEL0 |= BIT0;
PJSEL1 &= ~BIT0;
PJDIR |= BIT1; // Set PJ.1 as MCLK output
PJSEL0 |= BIT1;
PJSEL1 &= ~BIT1;
PJDIR |= BIT2; // Set PJ.2 as ACLK output
PJSEL0 |= BIT2;
PJSEL1 &= ~BIT2;
}//initCS()
void initTA0(void)
{
TA0CTL = TACLR; // Reset everything first
TA0CCTL0 = CCIE; // CCR0 interrupt enabled
TA0CCR0 = pulseTiming[0]; // Initialize CCR0
TA0EX0 = TAIDEX_0; // Expansion divider: /1
TA0CTL |= TASSEL_1 | MC__CONTINUOUS | ID__8; // TA0 clock source = ACLK;
// Continuous mode
// Input divider: /8 = 62.5kHz or 16us/tick
}//initTA0
void enableConfig(void)
{
PM5CTL0 &= ~LOCKLPM5; // Disable the GPIO power-on default high-impedance mode
// to activate previously configured port settings
}
void initGPIO(void)
{
// Pulse output pin P1.0
P1DIR |= BIT0; // Set P1.0 as an output pin
P1OUT &= ~BIT0; // Initialize P1.0 to low
// Sync sensing pin P2.3
P2DIR &= ~BIT3; // Set P2.3 as an input pin
P2REN |= BIT3; // Enable pu or pd on pin
P2OUT &= ~BIT3; // Select pull-down resistor on pin
__delay_cycles(5); // Give time for pullup to settle
}//initGPIO()
// Timer A0 ISR
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer0_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
P1OUT ^= BIT0; // Flip pulse output pin
TA0CCR0 += pulseTiming[pulseIdx];
if(pulseIdx < numEdges - 1) { pulseIdx++; }
else { pulseIdx = 0; }
}//TimerA0 ISR