Hi,
I am looking to implementing a multi-channel (maximum 3 channels) pulse width measurement using Timer_A (16-bit mode) are there any drivelib examples i could use as a starting point?
Thanks
David Nyarko
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
Hi,
I am looking to implementing a multi-channel (maximum 3 channels) pulse width measurement using Timer_A (16-bit mode) are there any drivelib examples i could use as a starting point?
Thanks
David Nyarko
Hi David,
Thank you for reaching out, another team member will reply by the end of the day or first thing Monday morning.
Have you checked out this thread? It uses Timer A Capture mode - which is used to calculate inputted signals i.e. PWM measurements. https://dev.ti.com/tirex/explore/node?node=AG7JOJNuvDb6UGmBsWvn2Q__z-lQYNj__LATEST
The code example for Timer A capture mode for the MSP432P401R may be helpful: https://dev.ti.com/tirex/explore/node?node=AG7JOJNuvDb6UGmBsWvn2Q__z-lQYNj__LATEST
There's also this thread:
https://e2e.ti.com/support/microcontrollers/msp430/f/166/p/861680/3188741
It discusses the HC-SR04, but the code has the pieces you'll need.
Hi,
I have looked at the examples and came up with the following code snippet.
Wondering if my ISR approach to each Compare register is the correct approach to :
a) Determine which CCR generated the edge interrupt
b) Clearing the specific CCR which generated the interrupt
c) Getting the correct count value.
*****************************************************
#define PULSEWIDTH_TIMER_DEFAULT_RELOAD 0xFFFF // Default reload value
volatile uint16_t CurrentTimerA1_CC1_CaptureValue, CurrentTimerA1_CC2_CaptureValue, CurrentTimerA1_CC3_CaptureValue;
volatile uint16_t LastTimerA1_CC1_CaptureValue, LastTimerA1_CC2_CaptureValue, LastTimerA1_CC3_CaptureValue;
volatile uint16_t TimerA1_CC1_delta, TimerA1_CC2_delta, TimerA1_CC3_delta;
//Timer_A Continuous Mode Configuration Parameter
Timer_A_ContinuousModeConfig continuousModeConfig =
{
TIMER_A_CLOCKSOURCE_SMCLK, //SMCLK Clock Source
TIMER_A_CLOCKSOURCE_DIVIDER_1, //SMCLK/1 = 48MHz/1 = 48Mhz
TIMER_A_TAIE_INTERRUPT_DISABLE, //Disable Timer ISR
TIMER_A_SKIP_CLEAR //Skip Clear Counter
};
//Timer_A Capture Mode Configuration Parameter
Timer_A_CaptureModeConfig captureModeConfig1 =
{
TIMER_A_CAPTURECOMPARE_REGISTER_1, // CC2 Register 1
TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE, //Rising and Falling Edges
TIMER_A_CAPTURE_INPUTSELECT_CCIxA, //CCIxA Input Select - Will use P7.7 which is TA1 CCR1 capture input CCI1A
TIMER_A_CAPTURE_SYNCHRONOUS, //Synchronized Capture
TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE, //Enable interrupt
TIMER_A_OUTPUTMODE_OUTBITVALUE //Output bit value
};
Timer_A_CaptureModeConfig captureModeConfig2 =
{
TIMER_A_CAPTURECOMPARE_REGISTER_2, // CC2 Register 2
TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE, //Rising and Falling Edges
TIMER_A_CAPTURE_INPUTSELECT_CCIxA, //CCIxA Input Select - Will use P7.6 which is TA1 CCR2 capture input CCI2A
TIMER_A_CAPTURE_SYNCHRONOUS, //Synchronized Capture
TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE, //Enable interrupt
TIMER_A_OUTPUTMODE_OUTBITVALUE //Output bit value
};
Timer_A_CaptureModeConfig captureModeConfig3 =
{
TIMER_A_CAPTURECOMPARE_REGISTER_3, // CC2 Register 3
TIMER_A_CAPTUREMODE_RISING_AND_FALLING_EDGE, //Rising and Falling Edges
TIMER_A_CAPTURE_INPUTSELECT_CCIxA, //CCIxA Input Select - Will use P7.5 which is TA1 CCR3 capture input CCI3A
TIMER_A_CAPTURE_SYNCHRONOUS, //Synchronized Capture
TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE, //Enable interrupt
TIMER_A_OUTPUTMODE_OUTBITVALUE //Output bit value
};
int main(void)
{
// Configuring Capture Mode
MAP_Timer_A_initCapture(TIMER_A1_BASE, &captureModeConfig1);
MAP_Timer_A_initCapture(TIMER_A1_BASE, &captureModeConfig2);
MAP_Timer_A_initCapture(TIMER_A1_BASE, &captureModeConfig3);
// Configuring Timer_A Continuous Mode
MAP_Timer_A_configureContinuousMode(TIMER_A1_BASE, &continuousModeConfig);
// Enable Interrupts
MAP_Timer_A_enableCaptureCompareInterrupt(TIMER_A1_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_1);
MAP_Timer_A_enableCaptureCompareInterrupt(TIMER_A1_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_2);
MAP_Timer_A_enableCaptureCompareInterrupt(TIMER_A1_BASE,TIMER_A_CAPTURECOMPARE_REGISTER_3);
MAP_Interrupt_enableInterrupt(INT_TA1_N);
MAP_Interrupt_enableMaster();
MAP_Timer_A_startCounter(TIMER_A1_BASE, TIMER_A_CONTINUOUS_MODE);
}
while(1)
{
// Other stuff
}
//******************************************************************************
//
//This is the TIMERA_1 interrupt vector service routine for capture register > 0.
//
//******************************************************************************
void TA1_N_IRQHandler(void)
{
uint32_t cc1, cc2, cc3;
cc1 = MAP_Timer_A_getCaptureCompareInterruptStatus( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1, TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG);
cc2 = MAP_Timer_A_getCaptureCompareInterruptStatus( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2, TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG);
cc3 = MAP_Timer_A_getCaptureCompareInterruptStatus( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3, TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG);
if ( cc1 == TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG)
{
// TIMER_A_CAPTURECOMPARE_REGISTER_1 caused interrupt
MAP_Timer_A_clearCaptureCompareInterrupt( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1);
CurrentTimerA1_CC1_CaptureValue = MAP_Timer_A_getCaptureCompareCount (TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1 );
if(CurrentTimerA1_CC1_CaptureValue >= LastTimerA1_CC1_CaptureValue)
{
TimerA1_CC1_delta = CurrentTimerA1_CC1_CaptureValue - LastTimerA1_CC1_CaptureValue ;
}
else // A counter reload occurred
{
TimerA1_CC1_delta = CurrentTimerA1_CC1_CaptureValue + (PULSEWIDTH_TIMER_DEFAULT_RELOAD - LastTimerA1_CC1_CaptureValue ); // 0xFFFF -last reading
}
LastTimerA1_CC1_CaptureValue = CurrentTimerA1_CC1_CaptureValue; // Update Capture value for next interrupt
}
if ( cc2 == TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG)
{
// TIMER_A_CAPTURECOMPARE_REGISTER_2 caused interrupt
MAP_Timer_A_clearCaptureCompareInterrupt( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2);
CurrentTimerA1_CC2_CaptureValue = MAP_Timer_A_getCaptureCompareCount (TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_2 );
if(CurrentTimerA1_CC2_CaptureValue >= LastTimerA1_CC2_CaptureValue)
{
TimerA1_CC2_delta = CurrentTimerA1_CC2_CaptureValue - LastTimerA1_CC2_CaptureValue ;
}
else // A counter reload occurred
{
TimerA1_CC2_delta = CurrentTimerA1_CC2_CaptureValue + (PULSEWIDTH_TIMER_DEFAULT_RELOAD - LastTimerA1_CC2_CaptureValue ); // 0xFFFF -last reading
}
LastTimerA1_CC2_CaptureValue = CurrentTimerA1_CC2_CaptureValue; // Update Capture value for next interrupt
}
if ( cc3 == TIMER_A_CAPTURECOMPARE_INTERRUPT_FLAG)
{
// TIMER_A_CAPTURECOMPARE_REGISTER_3 caused interrupt
MAP_Timer_A_clearCaptureCompareInterrupt( TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3);
CurrentTimerA1_CC3_CaptureValue = MAP_Timer_A_getCaptureCompareCount (TIMER_A1_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_3 );
if(CurrentTimerA1_CC3_CaptureValue >= LastTimerA1_CC3_CaptureValue)
{
TimerA1_CC3_delta = CurrentTimerA1_CC3_CaptureValue - LastTimerA1_CC3_CaptureValue ;
}
else // A counter reload occurred
{
TimerA1_CC3_delta = CurrentTimerA1_CC3_CaptureValue + (PULSEWIDTH_TIMER_DEFAULT_RELOAD - LastTimerA1_CC3_CaptureValue ); // 0xFFFF -last reading
}
LastTimerA1_CC3_CaptureValue = CurrentTimerA1_CC3_CaptureValue; // Update Capture value for next interrupt
}
}
This looks about right. Some observations:
1) Since you're doing uint16_t arithmetic, there's no reason to check whether (prev > curr), since unsigned-underflow will do the right thing. I.e. something like
uint16_t prev, curr, diff; curr = CCRx; diff = curr - prev; prev = curr;
2) You're getting an interrupt for each rising or falling edge. Eventually you'll need to keep track of which edge you're working with, since you're probably interested in either the high time or the low time. You would do this by checking the CCI bit. Glancing through the driverlib source, I don't see a function that returns the CCI bit, so you'll want something like:
if (TIMER_A1->CCTL[1] & TIMER_A_CCTLN_CCI); // high so it was a rising edge // (Keep the Forum code formatter at bay)
This checks the current state of the pin, so there is a race here.
3) If you haven't already, do some arithmetic on the back of an envelope based on your anticipated input frequency range and the speed of your timer (currently SMCLK/1=3MHz). The goal is to have the range of your timer (64K ticks) match as closely as possible with your minimum input frequency (max resolution) without going over (timer wrap)..
Bruce McKenney47378 said:This looks about right. Some observations:
1) Since you're doing uint16_t arithmetic, there's no reason to check whether (prev > curr), since unsigned-underflow will do the right thing. I.e. something like
1234uint16_t prev, curr, diff;curr = CCRx;diff = curr - prev;prev = curr;2) You're getting an interrupt for each rising or falling edge. Eventually you'll need to keep track of which edge you're working with, since you're probably interested in either the high time or the low time. You would do this by checking the CCI bit. Glancing through the driverlib source, I don't see a function that returns the CCI bit, so you'll want something like:
12if(TIMER_A1->CCTL[1] & TIMER_A_CCTLN_CCI);// high so it was a rising edge// (Keep the Forum code formatter at bay)This checks the current state of the pin, so there is a race here.
3) If you haven't already, do some arithmetic on the back of an envelope based on your anticipated input frequency range and the speed of your timer (currently SMCLK/1=3MHz). The goal is to have the range of your timer (64K ticks) match as closely as possible with your minimum input frequency (max resolution) without going over (timer wrap)..
Thanks for the response