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.

TMS320F28335: PPM Decoding using Ecap

Part Number: TMS320F28335


Hello ,

           I am using TMS320F28335 evaluation board and I was hoping to decode the PPM signals using the ecap where it could give me the high time of the signal. I tried the example program of ecap_capture with PWM, it wasn't working. I am not sure why? I connected GPIO0 to GPIO34 as mentioned in the comments. If there is any alternate way to do this, please let me know. Also, do check the code i'm pasting below. it is the same example program but it wouldn't work. Also, you could suggest me if there is any hardware connections that has to be made apart from the GPIO pins.

// TI File $Revision: /main/8 $
// Checkin $Date: April 21, 2008 15:41:29 $
//###########################################################################
//
// FILE: Example_2833xECap_Capture_Pwm.c
//
// TITLE: Capture EPWM3.
//
// ASSUMPTIONS:
//
// This program requires the DSP2833x header files.
//
// Make the following external connection:
// EPWM3 on GPIO4 should be connected to ECAP1 on GPIO24.
//
// As supplied, this project is configured for "boot to SARAM"
// operation. The 2833x Boot Mode table is shown below.
// For information on configuring the boot mode of an eZdsp,
// please refer to the documentation included with the eZdsp,
//
// $Boot_Table:
//
// GPIO87 GPIO86 GPIO85 GPIO84
// XA15 XA14 XA13 XA12
// PU PU PU PU
// ==========================================
// 1 1 1 1 Jump to Flash
// 1 1 1 0 SCI-A boot
// 1 1 0 1 SPI-A boot
// 1 1 0 0 I2C-A boot
// 1 0 1 1 eCAN-A boot
// 1 0 1 0 McBSP-A boot
// 1 0 0 1 Jump to XINTF x16
// 1 0 0 0 Jump to XINTF x32
// 0 1 1 1 Jump to OTP
// 0 1 1 0 Parallel GPIO I/O boot
// 0 1 0 1 Parallel XINTF boot
// 0 1 0 0 Jump to SARAM <- "boot to SARAM"
// 0 0 1 1 Branch to check boot mode
// 0 0 1 0 Boot to flash, bypass ADC cal
// 0 0 0 1 Boot to SARAM, bypass ADC cal
// 0 0 0 0 Boot to SCI-A, bypass ADC cal
// Boot_Table_End$
//
// DESCRIPTION:
//
// This example configures ePWM3A for:
// - Up count
// - Period starts at 2 and goes up to 1000
// - Toggle output on PRD
//
// eCAP1 is configured to capture the time between rising
// and falling edge of the PWM3A output.
//
//###########################################################################
// $TI Release: DSP2833x/DSP2823x Header Files V1.20 $
// $Release Date: August 1, 2008 $
//###########################################################################

#include "DSP28x_Project.h" // Device Headerfile and Examples Include File


// Configure the start/end period for the timer
#define PWM3_TIMER_MIN 10
#define PWM3_TIMER_MAX 8000

// Prototype statements for functions found within this file.
interrupt void ecap1_isr(void);
void InitECapture(void);
void InitEPwmTimer(void);
void Fail(void);

// Global variables used in this example
Uint32 ECap1IntCount;
Uint32 ECap1PassCount;
Uint32 EPwm3TimerDirection;

// To keep track of which way the timer value is moving
#define EPWM_TIMER_UP 1
#define EPWM_TIMER_DOWN 0

void main(void)
{

// Step 1. Initialize System Control:
// PLL, WatchDog, enable Peripheral Clocks
// This example function is found in the DSP2833x_SysCtrl.c file.
InitSysCtrl();

// Step 2. Initalize GPIO:
// This example function is found in the DSP2833x_Gpio.c file and
// illustrates how to set the GPIO to it's default state.
// InitGpio(); // Skipped for this example

InitEPwm3Gpio();
InitECap1Gpio();

// Step 3. Clear all interrupts and initialize PIE vector table:
// Disable CPU interrupts
DINT;

// Initialize the PIE control registers to their default state.
// The default state is all PIE interrupts disabled and flags
// are cleared.
// This function is found in the DSP2833x_PieCtrl.c file.
InitPieCtrl();

// Disable CPU interrupts and clear all CPU interrupt flags:
IER = 0x0000;
IFR = 0x0000;

// Initialize the PIE vector table with pointers to the shell Interrupt
// Service Routines (ISR).
// This will populate the entire table, even if the interrupt
// is not used in this example. This is useful for debug purposes.
// The shell ISR routines are found in DSP2833x_DefaultIsr.c.
// This function is found in DSP2833x_PieVect.c.
InitPieVectTable();

// Interrupts that are used in this example are re-mapped to
// ISR functions found within this file.
EALLOW; // This is needed to write to EALLOW protected registers
PieVectTable.ECAP1_INT = &ecap1_isr;
EDIS; // This is needed to disable write to EALLOW protected registers

// Step 4. Initialize all the Device Peripherals:
// This function is found in DSP2833x_InitPeripherals.c
// InitPeripherals(); // Not required for this example
InitEPwmTimer(); // For this example, only initialize the ePWM Timers
InitECapture();


// Step 5. User specific code, enable interrupts:

// Initalize counters:
ECap1IntCount = 0;
ECap1PassCount = 0;

// Enable CPU INT4 which is connected to ECAP1-4 INT:
IER |= M_INT4;

// Enable eCAP INTn in the PIE: Group 3 interrupt 1-6
PieCtrlRegs.PIEIER4.bit.INTx1 = 1;

// Enable global Interrupts and higher priority real-time debug events:
EINT; // Enable Global interrupt INTM
ERTM; // Enable Global realtime interrupt DBGM

// Step 6. IDLE loop. Just sit and loop forever (optional):
for(;;)
{
asm(" NOP");
}

}


void InitEPwmTimer()
{

EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 0;
EDIS;

EPwm3Regs.TBCTL.bit.CTRMODE = TB_COUNT_UP; // Count up
EPwm3Regs.TBPRD = PWM3_TIMER_MIN;
EPwm3Regs.TBPHS.all = 0x00000000;
EPwm3Regs.AQCTLA.bit.PRD = AQ_TOGGLE; // Toggle on PRD

// TBCLK = SYSCLKOUT
EPwm3Regs.TBCTL.bit.HSPCLKDIV = 1;
EPwm3Regs.TBCTL.bit.CLKDIV = 0;


EPwm3TimerDirection = EPWM_TIMER_UP;

EALLOW;
SysCtrlRegs.PCLKCR0.bit.TBCLKSYNC = 1;
EDIS;

}

void InitECapture()
{
ECap1Regs.ECEINT.all = 0x0000; // Disable all capture interrupts
ECap1Regs.ECCLR.all = 0xFFFF; // Clear all CAP interrupt flags
ECap1Regs.ECCTL1.bit.CAPLDEN = 0; // Disable CAP1-CAP4 register loads
ECap1Regs.ECCTL2.bit.TSCTRSTOP = 0; // Make sure the counter is stopped

// Configure peripheral registers
ECap1Regs.ECCTL2.bit.CONT_ONESHT = 1; // One-shot
ECap1Regs.ECCTL2.bit.STOP_WRAP = 3; // Stop at 4 events
ECap1Regs.ECCTL1.bit.CAP1POL = 1; // Falling edge
ECap1Regs.ECCTL1.bit.CAP2POL = 0; // Rising edge
ECap1Regs.ECCTL1.bit.CAP3POL = 1; // Falling edge
ECap1Regs.ECCTL1.bit.CAP4POL = 0; // Rising edge
ECap1Regs.ECCTL1.bit.CTRRST1 = 1; // Difference operation
ECap1Regs.ECCTL1.bit.CTRRST2 = 1; // Difference operation
ECap1Regs.ECCTL1.bit.CTRRST3 = 1; // Difference operation
ECap1Regs.ECCTL1.bit.CTRRST4 = 1; // Difference operation
ECap1Regs.ECCTL2.bit.SYNCI_EN = 1; // Enable sync in
ECap1Regs.ECCTL2.bit.SYNCO_SEL = 0; // Pass through
ECap1Regs.ECCTL1.bit.CAPLDEN = 1; // Enable capture units


ECap1Regs.ECCTL2.bit.TSCTRSTOP = 1; // Start Counter
ECap1Regs.ECCTL2.bit.REARM = 1; // arm one-shot
ECap1Regs.ECCTL1.bit.CAPLDEN = 1; // Enable CAP1-CAP4 register loads
ECap1Regs.ECEINT.bit.CEVT4 = 1; // 4 events = interrupt

}

interrupt void ecap1_isr(void)
{

// Cap input is syc'ed to SYSCLKOUT so there may be
// a +/- 1 cycle variation

if(ECap1Regs.CAP2 > EPwm3Regs.TBPRD*2+1 || ECap1Regs.CAP2 < EPwm3Regs.TBPRD*2-1)
{
Fail();
}

if(ECap1Regs.CAP3 > EPwm3Regs.TBPRD*2+1 || ECap1Regs.CAP3 < EPwm3Regs.TBPRD*2-1)
{
Fail();
}

if(ECap1Regs.CAP4 > EPwm3Regs.TBPRD*2+1 || ECap1Regs.CAP4 < EPwm3Regs.TBPRD*2-1)
{
Fail();
}


ECap1IntCount++;

if(EPwm3TimerDirection == EPWM_TIMER_UP)
{
if(EPwm3Regs.TBPRD < PWM3_TIMER_MAX)
{
EPwm3Regs.TBPRD++;
}
else
{
EPwm3TimerDirection = EPWM_TIMER_DOWN;
EPwm3Regs.TBPRD--;
}
}
else
{
if(EPwm3Regs.TBPRD > PWM3_TIMER_MIN)
{
EPwm3Regs.TBPRD--;
}
else
{
EPwm3TimerDirection = EPWM_TIMER_UP;
EPwm3Regs.TBPRD++;
}
}

ECap1PassCount++;

ECap1Regs.ECCLR.bit.CEVT4 = 1;
ECap1Regs.ECCLR.bit.INT = 1;
ECap1Regs.ECCTL2.bit.REARM = 1;

// Acknowledge this interrupt to receive more interrupts from group 4
PieCtrlRegs.PIEACK.all = PIEACK_GROUP4;
}


void Fail()
{
asm(" ESTOP0");
}

//===========================================================================
// No more.
//===========================================================================

Thank you,

Akhil Pillai

  • I'm not sure where you're getting GPIO0 and GPIO34? The example says GPIO4 and GPIO24. Or did you change the code to use 0 and 34?

    If switching your GPIOs doesn't solve the issue, can you elaborate on how it's failing? Are you hitting the ESTOP0 in the Fail() function? Are you not getting any interrupts? Can you see the ePWM signal coming out on the pin if you look at it with an oscilloscope?

    Thanks,
    Whitney
  • Hi Whitney, I used GPIO34 as its one of the function was ecap1 that's why and in evaluation board, there was no GPIO24. I checked again and I guess GPIO24 is routed through GPIO22. And yes, the PWM is generated and i could see that in the oscilloscope but I am not getting any interrupts. I'll try again connecting it to GPIO22 and see if it works. If you have any other suggestions please let me know. Also, instead of pwm, if it's a ppm, will ECAP serve the purpose?

    --
    With Warm Regards,
    Akhil Pillai
  • Hello Whitney,

    Now its working but I am not sure as to how I'll get the high time of the pulse? Should i capture the result of CAP1,2,3,4 and then subtract it from each other to get the high time of the PWM? Could you please help me out here?
  • Glad you got it working. Yes, you should be able to use this for a PPM if my understanding of the term is correct--it's still a square wave input, correct?

    The ECAP Reference Guide has some helpful examples (both with code and diagrams) in chapter 7 (Application of the ECAP Module) that should help you decide what configuration is most appropriate for your use case. Example 4 is probably closest. The document can be found here:
    www.ti.com/.../sprufg4

    Whitney

  • Hello Whitney,

    Thanks for the document. That example has code to find the Period and i only need the duty ratio and that too only when it is high. So i guess I can configure it in the registers to capture the high time and I'll be able to know the time(cycles) from the CAP1,2,3,4 registers? Also, would it capture in a sequential manner? Say, first high time will be captured in Cap1, second in cap2 and so on?

    Thanks,
    Akhil
  • The CAP events correspond to edges/transitions, so you'll need to use two CAP events in combination to detect a single high time. So if you use the configuration in Example 4 in that document:

    CAP1 detects that the signal went high and resets the counter
    CAP2 detects that the signal went low and captures the counter value (which is the first high time)
    CAP3 detects that the signal went high and resets the counter
    CAP4 detects that the signal went low and captures the counter value (which is the second high time)

    So after the CAP4 event, CAP2 and CAP4 contain the cycles spent high for two consecutive high times. Values in CAP1 and CAP3 contain the low times--if you don't need to know the low times, you don't have to bother reading them.

    Whitney
  • Hey Whitney,

    Thanks a ton to help me out here. Really a great help! This works!

    With Warm Regards,
    Akhil