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.

CCS/EK-TM4C123GXL: Enabling Multiple Timers Is Causing Inaccuracy in Event Capture Timing

Part Number: EK-TM4C123GXL

Tool/software: Code Composer Studio

Hello,

I have been working on a program that needs to read the duty cycle of a PWM input signal and generate an appropriate proportional output signal. The program uses Timer 0 (split into two half-width timers) to capture the rising and falling edges from the PWM signal, and Timer 1 to periodically update the proportional signal. While debugging, I discovered that while Timer 1 is not enabled, the program reads the PWM signal perfectly. When both Timers 0 and 1 are enabled, the capture times for the rising/falling edges aren't lined up as they should for the given PWM signal.

I've pasted the code below. Is there anything I can change that would fix this problem?

#include <stdint.h>
#include <stdbool.h>
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "driverlib/pin_map.h"
#include "driverlib/fpu.h"
#include "driverlib/rom.h"
#include "driverlib/gpio.h"
#include "driverlib/qei.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"
#include "gaitdata/gaitdata.h"
#include "pwmwrite/pwmwrite.h"

volatile uint32_t currentpos = 0;
volatile uint32_t goalpos = 0; //as indicated by index
volatile int32_t error = 0;
volatile uint32_t index = 0; //keeps track of point in gait data
volatile int32_t PIDsignal = 0; //signal that alters goalpwmhigh
volatile uint32_t proportional = 50; //P from PID
volatile uint32_t goalpwmhigh = 2048; //duty value, out of 4096
volatile uint32_t goalduty = 50; //duty in %
volatile float rawgoalpos = 0;

//THIS BLOCK FOR PWMREAD
static volatile uint32_t periodStartTime = 0;
static volatile uint32_t periodStartTimePrevious = 0;
static volatile uint32_t pulseEndTime = 0;
static volatile uint32_t pulseWidth = 0;
static volatile uint32_t periodWidth = 1;
static volatile float dutyCycle = 0;
static volatile uint32_t encoderValue = 0;

void timer1ISR(void)
{
	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
	rawgoalpos = getKneeData(index);
	goalpos = (float)250.0 + (float)2.88 * ((float)rawgoalpos - (float)28.5);
	currentpos = encoderValue;
	error = currentpos - goalpos;
	PIDsignal = proportional * error;
	goalpwmhigh = 2048 + PIDsignal;
	if (goalpwmhigh > 3696)
	{
		goalpwmhigh = 3696;
	}
	else if (goalpwmhigh < 410)
	{
		goalpwmhigh = 410;
	}
	goalduty = (float)100 * ((float)goalpwmhigh	/ (float)4096);

	setPulseWidth(50, goalduty);

	index++;
	if(index == 1000)
	{
		index = 0;
	}
}

void timerARisingISR(void)
{
	//
	// CALCULATE PERIOD WIDTH
	//

	// clear interrupt source
	TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT);

	// retrieve the clock count at event trigger time
	periodStartTime = ((TimerValueGet(TIMER0_BASE, TIMER_A) << 16) >> 16);

	// if timer overflow occurred, calculate period width accordingly assuming 16 bit timer
	if (periodStartTime < periodStartTimePrevious) {
		periodWidth = periodStartTime + (65536 - periodStartTimePrevious);
	}
	// if no timer overflow since last event
	else {
		periodWidth = periodStartTime - periodStartTimePrevious;
	}

	// calculate duty cycle
	dutyCycle = (((float)pulseWidth/(float)periodWidth));

	encoderValue = (dutyCycle*1024);


	UARTprintf("rising\n");
	UARTprintf("periodStartTime: %u\n", periodStartTime);
	UARTprintf("periodStartTimePrevious: %u\n", periodStartTimePrevious);

	// mark current period start time as being old
	periodStartTimePrevious = periodStartTime;
}

void timerBFallingISR(void)
{
	// clear interrupt source
	TimerIntClear(TIMER0_BASE, TIMER_CAPB_EVENT);

	// retrieve the clock count at event trigger time
	pulseEndTime = ((TimerValueGet(TIMER0_BASE, TIMER_B) << 16) >> 16);

	// if 16 bit timer overflowed, calculate pulse width accordingly
	if (pulseEndTime < periodStartTime) {
		pulseWidth = pulseEndTime + (65536 - periodStartTime);
	}
	// if timer did not overflow
	else {
		pulseWidth = pulseEndTime - periodStartTime;
	}

	UARTprintf("falling\n");
	UARTprintf("pulseEndTime: %u\n", pulseEndTime);
}

int main(void)
{
	//set clock to 40 MHz
	SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN);

	//enable UART0
	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);

	//enable GPIOF
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	// TIMER0 peripheral, and GPIO ports B and F
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

	// Wait for the TIMER0 module to be ready
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER0))
	{
	}

	// configure PB6 for timer A capture
	GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6);
	GPIOPinConfigure(GPIO_PB6_T0CCP0);

	// configure PB7 for timer B capture
	GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_7);
	GPIOPinConfigure(GPIO_PB7_T0CCP1);

	// Configure TimerA as a half-width one-shot timer, and TimerB as a
	// half-width edge capture counter.
	TimerConfigure(TIMER0_BASE, (TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_CAP_TIME_UP | TIMER_CFG_B_CAP_TIME_UP));

	// TIMER_CLOCK_SYSTEM: use 40MHz system clock
	// TIMER_CLOCK_PIOSC: use 16MHz precision internal oscillator for timing signals
	// It appears PIOSC might not be an option for the TM4C123GH6PM
	TimerClockSourceSet(TIMER0_BASE, TIMER_CLOCK_SYSTEM);

	// set a prescaler value for timerA and timerB to ensure that overflow occurs more slowly than PWM periods
	// timer frequency currently set to: 10MHz
	// for some reason this isn't working right now and timer is still running at 40MHz
	//TimerPrescaleSet(TIMER0_BASE, TIMER_BOTH, 4);

	// Configure TimerA to mark time of rising edge, configure TimerB to mark time of falling edge
	TimerControlEvent(TIMER0_BASE, TIMER_A, TIMER_EVENT_POS_EDGE);
	TimerControlEvent(TIMER0_BASE, TIMER_B, TIMER_EVENT_NEG_EDGE);

	// clear both interrupts
	TimerIntClear(TIMER0_BASE, TIMER_CAPA_EVENT | TIMER_CAPB_EVENT);

	// register timer interrupt service routines
	//TimerIntRegister(TIMER0_BASE, TIMER_A, *timerARisingISR);
	//TimerIntRegister(TIMER0_BASE, TIMER_B, *timerBFallingISR);

	// enable the interrupts for the two timers, A detects rising edge, B detects falling edge
	TimerIntEnable(TIMER0_BASE, TIMER_CAPA_EVENT | TIMER_CAPB_EVENT);

	// enable interrupts for timerA and timerB
	IntEnable(INT_TIMER0A);
	IntEnable(INT_TIMER0B);

	// enable timer module 1
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);

	// wait for timer module 1 to be ready
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_TIMER1))
	{
	}

	// configure one full width timer, counting down
	TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC);

	// set timer 1 to run off of system clock
	TimerClockSourceSet(TIMER1_BASE, TIMER_CLOCK_SYSTEM);

	// set timer to reset at 10Hz
	TimerLoadSet(TIMER1_BASE, TIMER_A, SysCtlClockGet() / 100);

	// register the timer interrupt service routine
	TimerIntRegister(TIMER1_BASE, TIMER_A, *timer1ISR);

	// clear rollover interrupt and then enable it
	TimerIntClear(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
	TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);

	IntEnable(INT_TIMER1A);

	//configure GPIO Pins for UART
	GPIOPinConfigure(GPIO_PA0_U0RX);
	GPIOPinConfigure(GPIO_PA1_U0TX);
	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

	// Use the internal 16MHz oscillator as the UART clock source.
	UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);

	// Initialize the UART for console I/O.
	UARTStdioConfig(0, 921600, 16000000);


	GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3);
	GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, GPIO_PIN_3);

	IntMasterEnable();

	//synchronize timer A and timer B to start on the same clock cycle
	TimerSynchronize(TIMER0_BASE, TIMER_0A_SYNC | TIMER_0B_SYNC);

	//start the timers
	TimerEnable(TIMER0_BASE, TIMER_BOTH);
	TimerEnable(TIMER1_BASE, TIMER_A);

	while(1)
	{
		//nothing!
	}
}