About S12XE Input Capture Interrupt

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

About S12XE Input Capture Interrupt

615 Views
赵子成
Contributor IV

Hi All,

Now I Use IOC7 Pin to capture the PWM. In order to calculate the PWM frequency and duty cycle, I set to Capture on any edge (rising or falling) and will lead to interruption.

The input PWM is 1K frequency and 50% duty cycle, everything is good.

But if the duty cycle is larger than  95%, I found sometime the  rising edge won't trigger interrupt(Maybe the period between  falling edge and following rising edge is too short). That leads to the wrong result of calculation.

My init code is this:

void IOC_Init(void)
{
ECT_TIOS_IOS7 = 0; //The corresponding channel acts as an input capture
ECT_TIOS_IOS4 = 0; //The corresponding channel acts as an input capture
ECT_TIOS_IOS1 = 0; //The corresponding channel acts as an input capture
ECT_TSCR1_TFFCA = 1; //A read from an input capture causes the corresponding channel flag, CxF, to be cleared in the TFLG1 register
ECT_TCTL3_EDG7x = 3; //Capture on any edge (rising or falling)
ECT_TCTL3_EDG4x = 3;
ECT_TCTL4_EDG1x = 3;
ECT_TSCR2_PR = 7; //Prescale Factor=32, capture clock is 48M/128=375K
ECT_TIE_C7I = 1; //The corresponding flag(C7F) is enabled to cause an interrupt.
ECT_TIE_C4I = 1; //The corresponding flag(C4F) is enabled to cause an interrupt.
ECT_TIE_C1I = 1; //The corresponding flag(C1F) is enabled to cause an interrupt.

ECT_TSCR1_TEN = 1; //Allows the timer to function normally
}

And the interrupt code is like this:

#pragma CODE_SEG __NEAR_SEG NON_BANKED
interrupt void ISR_IC7(void)
{
static UINT32 u32ICU_RiseTick;
static UINT32 u32ICU_FallDownTick;
static UINT32 u32ICU_HighLevelTick;
static UINT32 u32ICU_LowLevelTick;
UINT16 u16Fre, u16DutyCycle;


if(I_D_CP == HIGH_LEVEL)   // IOC7 Pin is high level
{
u32ICU_RiseTick = ECT_TC7;   // read the counter
if(u32ICU_RiseTick > u32ICU_FallDownTick)
{
u32ICU_LowLevelTick = u32ICU_RiseTick - u32ICU_FallDownTick;
}
else
{
u32ICU_LowLevelTick = u32ICU_RiseTick + ICU_TICK_SCALE - u32ICU_FallDownTick;
}
}
else          // IOC7 Pin is low level
{
u32ICU_FallDownTick = ECT_TC7;    // read the counter
if(u32ICU_FallDownTick > u32ICU_RiseTick)
{
u32ICU_HighLevelTick = u32ICU_FallDownTick - u32ICU_RiseTick;
}
else
{
u32ICU_HighLevelTick = u32ICU_FallDownTick + ICU_TICK_SCALE - u32ICU_RiseTick;
}
if((u32ICU_LowLevelTick + u32ICU_HighLevelTick) > 0)
{
u16DutyCycle = u32ICU_HighLevelTick * 1000 / (u32ICU_LowLevelTick + u32ICU_HighLevelTick);
u16Fre = ICU_TICK_FRE * 10 / (u32ICU_LowLevelTick + u32ICU_HighLevelTick);
}
else
{
u16DutyCycle = 0;
u16Fre = 0;
}
g_stCpPwmVar.u16Fre = u16Fre;
g_stCpPwmVar.u16DutyCycle = u16DutyCycle;
g_stCpPwmVar.u32UpdateTick = STMR_GetTickCount();
}
}

Can someone tell me why?

0 Kudos
5 Replies

491 Views
kef2
Senior Contributor IV

Is this the only interrupt in your design? 1/1kHz * (1-0.95) = 50us. Isn't interrupt latency due to other interrupts comparable to this figure?

0 Kudos

491 Views
kef2
Senior Contributor IV

Final frequency and period calculation involving slow 32/32bit divide should be moved away from ISR to foreground task.

Edward

0 Kudos

491 Views
赵子成
Contributor IV

Hi ,

 

I have removed some cods from interrupt function, but the problem still exist:

interrupt void ISR_IC7(void)
{
if(I_D_CP == HIGH_LEVEL)
{
g_stCpPwmVar.bRise = TRUE;
g_stCpPwmVar.u32ICU_RiseTick = ECT_TC7;
}
else
{
g_stCpPwmVar.bRise = FALSE;
g_stCpPwmVar.u32ICU_FallDownTick = ECT_TC7;
g_stCpPwmVar.u32UpdateTick = STMR_GetTickCount();
}
}

 

It seems that not the code execute time issue.

0 Kudos

491 Views
lama
NXP TechSupport
NXP TechSupport

Hi,

you use 32bit variables in a 16bit system which causes longer calculations plus C code. I do not want to investigate processing length (also depends on used busclk) but what I suggest you is to investigate your routines in different way.

I would suggest you to toggle some bit when you start interrupt and when you leave it. This will show you whether you are able to process each duty cycle.

The better precision you want the faster calculation must be done. It look like you are loosing edge.

If you do not need to get frequency for each period then you could only store values in interrupt, then disable interrupt, calculate frequency and after finished perform another measurement.

Finally, you need 2*busclk time to be able to catch one edge. Moreover, what about timer prescaller which decreases measurement precision.

I have also attached a simple code for frequency measurement I made in past. I am sending it only as a inspiration for different approach. It measures frequency only. Duty is not calculated.

I was thinking about possible solution and one of the ways is to split measure between two channel where one of them is catching rising edges only and the other one falling edges only. Then calculation is started only once in period for example on rising edges. Of course, values of Tcx should be buffered to be not changed before calculation of previous period is in progress. Moreover, if the processing time is too long I suggest to do not calculate each period. ECT should only store values in some buffer and you calculate frequency when necessary with last buffered values.

Best regards,

Ladislav

0 Kudos

491 Views
赵子成
Contributor IV

Hi Lama,

I have removed some cods from interrupt function, but the problem still exist:

interrupt void ISR_IC7(void)
{
if(I_D_CP == HIGH_LEVEL)
{
g_stCpPwmVar.bRise = TRUE;
g_stCpPwmVar.u32ICU_RiseTick = ECT_TC7;
}
else
{
g_stCpPwmVar.bRise = FALSE;
g_stCpPwmVar.u32ICU_FallDownTick = ECT_TC7;
g_stCpPwmVar.u32UpdateTick = STMR_GetTickCount();
}
}

It seems that not the code execute time issue.

0 Kudos