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!
}
}