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.

Problem syncing two epwm timers and ADC

Other Parts Discussed in Thread: CONTROLSUITE

Hello all. I am new into C2000 family but have some experiences with C6000 DSPs and Tiva MCU. I am working with the LAUNCHXL-F28377S board

In my project I need to sync two epwm timers between them  (epwm7A-epwm8A), with 0 phase delay and use the lower frequency one (epwm8A) to trigger the ADC. The problem I have is that the pwm timer which triggers the ADCs, has to be stopped and then relaunched in sync with the first pwm.

Everything is done in my code, but when I stopped and relaunch my second pwm signal (epwm8A), then I see am irregular delay between them. In Tiva family, this was very easy to do using TimerSynchronize(TIMER0_BASE, TIMER_1A_SYNC | TIMER_1B_SYNC).

Is there any similar function in C2000 Controlsuite?

If not, what changes do I need to do to my code? 

Thank you:

//###########################################################################
// FILE:   ADC_PWM_SYNC.c
// TITLE:  ADC triggering via epwm for F2837xS.
//
//! \addtogroup cpu01_example_list
//! <h1> ADC ePWM Triggering (adc_soc_epwm)</h1>
//!
//! This example sets up the ePWM to periodically trigger the ADC.
//!
//! After the program runs, the memory will contain:\n
//!  A sequence of analog-to-digital conversion samples from
//! pin A0. The time between samples is determined based on the period
//! of the ePWM timer.
//
//###########################################################################
// $TI Release: F2837xS Support Library v191 $
// $Release Date: Fri Mar 11 15:58:35 CST 2016 $
// $Copyright: Copyright (C) 2014-2016 Texas Instruments Incorporated -
//             http://www.ti.com/ ALL RIGHTS RESERVED $
//###########################################################################

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

/////////////////////////////////////////////////////////////////////////////////

//#include "C28x_FPU_FastRTS.h"
#include <math.h>
#include "float.h"
#include "FPU.h"
//#include "fpu_rfft.h"

#include <string.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"

#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"

#define UART_ON 1

#ifdef UART_ON
#include "utils/cmdline.h"
#include "utils/uartstdio.h"
#include "utils/ustdlib.h" 
#endif

/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
static unsigned short indexX = 0;
static unsigned short indexY = 0;

Uint16 DMA_Done = 0;


/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

uint32_t cnt_interr_ADC = 0;
uint32_t cnt_interr_ADC_completo = 0;
int16 cnt_print = 0;

#define RFFT_STAGES  7//128 8-256
#define RFFT_SIZE   (1 << RFFT_STAGES)

#define F_PER_SAMPLE (758000.0L/((float)RFFT_SIZE))       //Assuming sampling rate is 758kHz

RFFT_ADC_F32_STRUCT rfft_adc;
RFFT_F32_STRUCT rfft;

#pragma DATA_SECTION(RFFTin1Buff,"RFFTdata1");
Uint16 RFFTin1Buff[RFFT_SIZE];    //Buffer alignment for the input array,
//RFFT_adc_f32u(optional) RFFT_adc_f32(required)
//Output of FFT overwrites input if
//RFFT_STAGES is ODD
#pragma DATA_SECTION(RFFToutBuff,"RFFTdata2");
float32 RFFToutBuff[RFFT_SIZE];      //Output of FFT here if RFFT_STAGES is EVEN

#pragma DATA_SECTION(RFFTmagBuff,"RFFTdata3");
float32 RFFTmagBuff[RFFT_SIZE / 2 + 1]; //Additional Buffer used in Magnitude calc

#pragma DATA_SECTION(RFFTF32Coef,"RFFTdata4");
float32 RFFTF32Coef[RFFT_SIZE];                 //Twiddle buffer

/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
// variables

#define MAX_SAMPLES  1024

#define ONE_WORD_BURST  0  // 16-bit word per burst
#define TWO_WORD_BURST  1  // 32-bit word per burst

#define SOURCE_NO_ADDRESS_CHANGE  0
#define DESTINATION_INCREMENT_ONE_ADDRESS 1

#define TRANSFER_SIZE_128  127

#define TRANSFER_STEP_SOURCE_NO_CHANGE  0
#define TRANSFER_STEP_DEST_INCREMENT_ONE_ADDRESS 1

volatile uint16_t *DMA1Source, *DMA1Dest;

                        
interrupt void local_DMACH1_ISR(void);
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////

void ConfigureADC(void);
void ConfigureEPWM(void);
void SetupADCEpwm(Uint16 channel);
interrupt void adca1_isr_con_dma(void);

void ConfigureUART(void);


void Configurar_Serie();    

void DMA_configure(void);

//buffer for storing conversion results
#define RESULTS_BUFFER_SIZE RFFT_SIZE//256
#pragma DATA_SECTION(AdcaResults,"SecureRam0");
Uint16 AdcaResults[RESULTS_BUFFER_SIZE];
Uint16 resultsIndex;
volatile Uint16 bufferFull;

void main(void) {

	Uint16 i, j;
	float32 freq;
	float32 freq2;

	uint16_t cnt_do = 0;
	uint32_t Aux_u32 = 0;
	uint32_t Aux_u32_2 = 0;

	float32 aux_float = 0;
	float32 aux_float2 = 0;


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

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

	InitEPwm7Gpio();
	InitEPwm8Gpio();

	GPIO_SetupPinMux(2, GPIO_MUX_CPU1, 0);
	GPIO_SetupPinOptions(2, GPIO_OUTPUT, GPIO_PUSHPULL);

	GPIO_SetupPinMux(3, GPIO_MUX_CPU1, 0);
	GPIO_SetupPinOptions(3, GPIO_OUTPUT, GPIO_PUSHPULL);

	GPIO_SetupPinMux(4, GPIO_MUX_CPU1, 0);
	GPIO_SetupPinOptions(4, GPIO_OUTPUT, GPIO_PUSHPULL);

// 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 F2837xS_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 F2837xS_DefaultIsr.c.
// This function is found in F2837xS_PieVect.c.
	InitPieVectTable();

//Map ISR functions
	EALLOW;
	PieVectTable.DMA_CH1_INT = &local_DMACH1_ISR; //function for DMA interrupt 1
	PieVectTable.ADCA1_INT = &adca1_isr_con_dma; //function for ADCA interrupt 1

	EDIS;

#ifdef UART_ON
	//
	// Configure UART0 for debug output.
	//
	ConfigureUART();

	// Print hello message to user.
	UARTprintf("\n\nHelloWorld on F28377S - ADC Card Example Program\n");



#endif

//Configure the ADC and power it up
	ConfigureADC();

//Configure the ePWM
	ConfigureEPWM();

//Setup the ADC for ePWM triggered conversions on channel 0
	SetupADCEpwm(0);

	//--- Enable the ADC interrupt
	DMA_configure();

//Enable global Interrupts and higher priority real-time debug events:
	IER |= M_INT1; //Enable group 1 interrupts - Para el ADC1
	IER |= M_INT7; //Enable group 1 interrupts - Para la DMA      
	EINT;
	// Enable Global interrupt INTM
	ERTM;
	// Enable Global realtime interrupt DBGM

//Initialize results buffer
	for (resultsIndex = 0; resultsIndex < RESULTS_BUFFER_SIZE; resultsIndex++) {
		AdcaResults[resultsIndex] = 0;
	}
	resultsIndex = 0;
	bufferFull = 0;

//enable PIE interrupt
	PieCtrlRegs.PIEIER1.bit.INTx1 = 1; //ADC     
	PieCtrlRegs.PIEIER7.bit.INTx1 = 1; //dma

//sync ePWM
	EALLOW;
	CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;
	EDIS;

	/////////////////////////////////////////////////////////////
	//FFT
	//RFFT_F32_STRUCT. Tail pointer of
	//RFFT_ADC_F32_STRUCT is passed to
	//the OutBuf pointer of RFFT_F32_STRUCT




	//////////////////////////////////////////////////////////////

	//start ePWM
	EPwm8Regs.ETSEL.bit.SOCAEN = 1;  //enable SOCA
	EPwm8Regs.TBCTL.bit.CTRMODE = 0; //unfreeze, and enter up count mode

//take conversions indefinitely in loop
	while (1) {

		//wait while ePWM causes ADC conversions, which then cause interrupts,
		//which fill the results buffer, eventually setting the bufferFull
		//flag
		if (DMA_Done == 1)
			DMA_Done = 0;

		while (!bufferFull)
			;
		bufferFull = 0; //clear the buffer full flag

		//stop ePWM
		EPwm8Regs.ETSEL.bit.SOCAEN = 0;  //disable SOCA
		EPwm8Regs.TBCTL.bit.CTRMODE = 3; //freeze counter

		DELAY_US(1000);

		for (i = 0; i < RFFT_SIZE; i++) {
			RFFTin1Buff[i] = AdcaResults[i];			//Clean up output buffer
		}

	
		//////////////////////////////////////////////////////////
	     //Will do some processing
		
		///////////////////////////////////////////////


		EALLOW;

		//start ePWM
		EPwm8Regs.ETSEL.bit.SOCAEN = 1;  //enable SOCA
		EPwm8Regs.TBCTL.bit.CTRMODE = 0; //unfreeze, and enter up count mode

	//	CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
		//EPwm8Regs.TBCTL.bit.SWFSYNC = 1;
		//EPwm7Regs.TBCTL.bit.SWFSYNC = 1;
	//	CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;

		EDIS;

		//at this point, AdcaResults[] contains a sequence of conversions
		//from the selected channel

		//software breakpoint, hit run again to get updated conversions
		//asm("   ESTOP0");
	}
}

//Write ADC configurations and power up the ADC for both ADC A and ADC B
void ConfigureADC(void) {
	EALLOW;

	//write configurations   
	AdcaRegs.ADCCTL2.bit.PRESCALE = 6; //set ADCCLK divider to /4


	AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);

	//Set pulse positions to late
	AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;

	//power up the ADC
	AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;

	//delay for 1ms to allow ADC time to power up
	DELAY_US(1000);

	EDIS;
}

void ConfigureEPWM(void) {

	EALLOW;
	// Assumes ePWM clock is already enabled
	EPwm7Regs.ETSEL.bit.SOCAEN = 1;	        // Disable SOC on A group
	EPwm7Regs.ETSEL.bit.SOCASEL = 4;// Select SOC from CTR=PRD (6 for CMPB up count) -orig:Select SOC on up-count
	EPwm7Regs.ETPS.bit.SOCAPRD = 1;	//1		        // Generate pulse on 1st event

	EPwm7Regs.CMPA.bit.CMPA = 0x0040;      // Set compare A value to 64 counts
	EPwm7Regs.TBPRD = 0x0080;			        // Set period to 128 counts


	EPwm7Regs.TBCTL.bit.CTRMODE =  TB_COUNT_UP; //TB_COUNT_UPDOWN; - TB_COUNT_UP or freeze counter 0x03
	//EPwm7Regs.TBCTL.bit.PHSDIR = TB_DOWN;

	EPwm7Regs.TBPHS.all = 0x00000000;
	EPwm7Regs.AQCTLA.bit.PRD = AQ_TOGGLE;      // Toggle on PRD

	//pHASE
	EPwm7Regs.TBPHS.bit.TBPHS = 0;         // Set Phase register to zero, master
	EPwm7Regs.TBCTL.bit.PHSEN = TB_DISABLE;                     // Master module
	EPwm7Regs.TBCTL.bit.SYNCOSEL = TB_CTR_ZERO;  
	EPwm7Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm7Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm7Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO_PRD;      
	EPwm7Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;            // load on CTR=Zero


	EDIS;

	////////////////////////////////////////////
	EALLOW;
	// Assumes ePWM clock is already enabled
	EPwm8Regs.ETSEL.bit.SOCAEN = 0;	        // Disable SOC on A group
	EPwm8Regs.ETSEL.bit.SOCASEL = 4;    // Select SOC from CTR=PRD (6 for CMPB up count) -orig:Select SOC on up-count
	EPwm8Regs.ETPS.bit.SOCAPRD = 1;	//1		        // Generate pulse on 1st event


	EPwm8Regs.CMPA.bit.CMPA = 0x0050; 
	EPwm8Regs.TBPRD = 0x0100; 			  //Double of epwm7



	EPwm8Regs.TBCTL.bit.CTRMODE = 3;//TB_COUNT_UP or freeze counter 0x03
	//EPwm8Regs.TBCTL.bit.PHSDIR = TB_UP;	

	EPwm8Regs.TBPHS.all = 0x00000000;  
	EPwm8Regs.AQCTLA.bit.PRD = AQ_TOGGLE;      // Toggle on PRD


	//pHASE
	EPwm8Regs.TBPHS.bit.TBPHS = 0; //xDEGREES Set Phase register to zero, master
	EPwm8Regs.TBCTL.bit.PHSEN = TB_DISABLE;                     // Master module
	EPwm8Regs.TBCTL.bit.SYNCOSEL = TB_SYNC_IN; //TB_SYNC_IN;   // Select SyncOut to EPWM7SYNC
	EPwm8Regs.CMPCTL.bit.SHDWAMODE = CC_SHADOW;
	EPwm8Regs.CMPCTL.bit.SHDWBMODE = CC_SHADOW;
	EPwm8Regs.CMPCTL.bit.LOADAMODE = CC_CTR_ZERO_PRD;        // load on CTR=Zero
	EPwm8Regs.CMPCTL.bit.LOADBMODE = CC_CTR_ZERO;            // load on CTR=Zero


	EDIS;

}

void SetupADCEpwm(Uint16 channel) {
	Uint16 acqps;

	//determine minimum acquisition window (in SYSCLKS) based on resolution
	if (ADC_RESOLUTION_12BIT == AdcaRegs.ADCCTL2.bit.RESOLUTION) {
		acqps = 14; //75ns
	} else { //resolution is 16-bit
		acqps = 63; //320ns
	}

	//Select the channels to convert and end of conversion flag
	EALLOW;
	AdcaRegs.ADCSOC0CTL.bit.CHSEL = channel;  //SOC0 will convert pin A0
	AdcaRegs.ADCSOC0CTL.bit.ACQPS = acqps; //sample window is 15-100 SYSCLK cycles
	AdcaRegs.ADCSOC0CTL.bit.TRIGSEL = 0X13; //trigger on ePWM8 SOCA/C

	AdcaRegs.ADCINTSEL1N2.bit.INT1SEL = 0; //end of SOC0 will set INT1 flag
	AdcaRegs.ADCINTSEL1N2.bit.INT1E = 1;   //enable INT1 flag
	AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //make sure INT1 flag is cleared
	EDIS;

}

uint16_t valor_LED = 0;


interrupt void adca1_isr_con_dma(void) {
	//AdcaResults[resultsIndex++] = AdcaResultRegs.ADCRESULT0;
	cnt_interr_ADC++;
	

	AdcaRegs.ADCINTFLGCLR.bit.ADCINT1 = 1; //clear INT1 flag
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

void DMA_configure(void) {
	EALLOW;
	CpuSysRegs.SECMSEL.bit.PF1SEL = 1;
	EDIS;

	// refer to dma.c for the descriptions of the following functions.

	

	//Initialize DMA
	DMAInitialize();

	DMA1Source = &AdcaResultRegs.ADCRESULT0;
	DMA1Dest = &AdcaResults[0]; //DMA destination;

	// configure DMACH5  for TX
	DMACH1AddrConfig(DMA1Dest, DMA1Source);
	//DMACH5BurstConfig(BURST,1,0);			// Burst size, src step, dest step
	DMACH1BurstConfig(ONE_WORD_BURST, SOURCE_NO_ADDRESS_CHANGE,
			DESTINATION_INCREMENT_ONE_ADDRESS);	// transfer size, src step, dest step

	//DMACH5TransferConfig(TRANSFER,1,0);		// transfer size, src step, dest step
	DMACH1TransferConfig(TRANSFER_SIZE_128, TRANSFER_STEP_SOURCE_NO_CHANGE,
			TRANSFER_STEP_DEST_INCREMENT_ONE_ADDRESS);// transfer size, src step, dest step

	DMACH1WrapConfig(0, 0, 128, 128);

	DMACH1ModeConfig(DMA_ADCAINT1, PERINT_ENABLE, ONESHOT_DISABLE, CONT_DISABLE,
			SYNC_DISABLE, SYNC_SRC, OVRFLOW_DISABLE, SIXTEEN_BIT, CHINT_END,
			CHINT_ENABLE);

	StartDMACH1();

	/*	// configure DMA CH2 for RX
	 DMACH6AddrConfig(DMADest,&SpiaRegs.SPIRXBUF);
	 DMACH6BurstConfig(BURST,0,1);
	 DMACH6TransferConfig(TRANSFER,0,1);
	 DMACH6ModeConfig(DMA_SPIARX,PERINT_ENABLE,ONESHOT_DISABLE,CONT_DISABLE,
	 SYNC_DISABLE,SYNC_SRC,OVRFLOW_DISABLE,SIXTEEN_BIT,CHINT_END,CHINT_ENABLE);/**/
}

interrupt void local_DMACH1_ISR(void) {
	DMA_Done = 0x0001;
	EALLOW;
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;
	DmaRegs.CH1.CONTROL.bit.TRANSFERSTS = 1;
	DmaRegs.CH1.CONTROL.bit.RUN = 1;
	EDIS;

	resultsIndex = 0;
	bufferFull = 1;
	cnt_interr_ADC_completo++;
//    ESTOP0;
}

#ifdef UART_ON
//*****************************************************************************
//
// Configure the UART and its pins.  This must be called before UARTprintf().
//
//*****************************************************************************
void ConfigureUART(void) {

	//
	// Enable UART0
	//
	SysCtlPeripheralEnable(SYSCTL_PERIPH_SCI1);

	//
	// Configure GPIO Pins for UART mode.
	//
	/*  EALLOW;
	 GpioCtrlRegs.GPAMUX2.bit.GPIO28 = 1;
	 GpioCtrlRegs.GPAPUD.bit.GPIO28 = 0;
	 GpioCtrlRegs.GPAQSEL2.bit.GPIO28 = 3;
	 GpioCtrlRegs.GPADIR.bit.GPIO28 = 0;

	 GpioCtrlRegs.GPAMUX2.bit.GPIO29 = 1;
	 GpioCtrlRegs.GPAPUD.bit.GPIO29 = 0;
	 GpioCtrlRegs.GPADIR.bit.GPIO29 = 1;
	 EDIS;/**/

	// For this example, only init the pins for the SCI-A port.
	EALLOW;
	GpioCtrlRegs.GPCMUX2.bit.GPIO84 = 1;
	GpioCtrlRegs.GPCMUX2.bit.GPIO85 = 1;

	GpioCtrlRegs.GPCGMUX2.bit.GPIO84 = 1;
	GpioCtrlRegs.GPCGMUX2.bit.GPIO85 = 1;

	EDIS;

	//
	// Initialize the UART for console I/O.
	//
	UARTStdioConfig(0, 115200, SysCtlLowSpeedClockGet(SYSTEM_CLOCK_SPEED) / 2);

}
#endif

  • Hello, ins't there an example for this?
    Regards
  • Hi PAk,

    You may be able to reuse the following PWM configuration file:
    \controlSUITE\libs\app_libs\digital_power\f28x7x_v1.0\CNF\

    Note that to use it you'll need to define a struct in main like the following:
    volatile struct EPWM_REGS *ePWM[] = { &EPwm1Regs, &EPwm1Regs, &EPwm2Regs, &EPwm3Regs, ... }        //intentionally, ePWM[0] not to be used

    The guide describing why ePWM registers are set the way they are is in the ePWM section of this device's Technical Reference Manual (TRM).

    ===

    Synchronization in C2000 devices is generally done done downstream.  As a result, it will be best to have ePWM7 generate the slower frequency PWM while ePWM8 generates the faster PWM.

    ePWM7 can then generate the Start of Conversion (SoC) pulses for an ADC channel.

    Hopefully this helps.


    Thank you,
    Brett

  • Thank you Brett.

    Sorry, but I have spotted that in the definition of:
    volatile struct EPWM_REGS *ePWM[] = { &EPwm1Regs, &EPwm1Regs, &EPwm2Regs, &EPwm3Regs, ... }

    the &EPwm1Regs is present twice....is this correct?
  • Hi PAk,

    This was a merely a choice. 

    Since the C2000 ePWM modules are named ePWM1 through ePWMn (no ePWM0), we decided to have the software match the same naming philosophy as the datasheet.  If you wish to make the more memory efficient choice, you could.

    As a result, ePWM[2]'s registers are EPwm2Regs in the strategy we've taken in the Digital Power library. And ePWM[0] should not be used.

    Hopefully this helps.


    Thank you,
    Brett