Forums

ar91sam7x256 - time between interrupt and pin toggle varies

Started by "d.duric" October 18, 2009
hi,

i am generating video signals with at91sam7x256 for a vga monitor, i
generate the hsync and vsync signal with pwm.
i wait in a function draw() until the pwm->hsync interrupt sets a flag,
after the flag: draw_flag has been set , the function starts to toggle
port pins,
i am trying to generate green and black bars on a monitor..

the problem is the time between the interrupt and the first green pixel
varies, so i don't get straight bars, they look spicky

anyone idea how to prevent the time variation between interrupt and pin
toggle?
here is the code for the PWM ISR and the function draw and the iRQ
handler:
/*
=======================================================================*/
AT91F_Irq_Handler:

/* Manage Exception Entry */
/* Adjust and save LR_irq in IRQ stack */
sub lr, lr, #4
stmfd sp!, {lr}

/* Save r0 and SPSR (need to be saved for nested interrupt) */
mrs r14, SPSR
stmfd sp!, {r0,r14}

/* Write in the IVR to support Protect Mode */
/* No effect in Normal Mode */
/* De-assert the NIRQ and clear the source in Protect Mode */
ldr r14, =AT91C_BASE_AIC
ldr r0 , [r14, #AIC_IVR]
str r14, [r14, #AIC_IVR]

/* Enable Interrupt and Switch in Supervisor Mode */
msr CPSR_c, #ARM_MODE_SVC

/* Save scratch/used registers and LR in User Stack */
stmfd sp!, { r1-r3, r12, r14}

/* Branch to the routine pointed by the AIC_IVR */
mov r14, pc
bx r0

/* Manage Exception Exit */
/* Restore scratch/used registers and LR from User Stack */
ldmia sp!, { r1-r3, r12, r14}

/* Disable Interrupt and switch back in IRQ mode */
msr CPSR_c, #I_BIT | ARM_MODE_IRQ

/* Mark the End of Interrupt on the AIC */
ldr r14, =AT91C_BASE_AIC
str r14, [r14, #AIC_EOICR]

/* Restore SPSR_irq and r0 from IRQ stack */
ldmia sp!, {r0,r14}
msr SPSR_cxsf, r14

/* Restore adjusted LR_irq from IRQ stack directly in the PC */
ldmia sp!, {pc}^
/*
=======================================================================*/

The PWM ISR routine

void ISR_PWM(void){

draw_flag = 1;
//acknowledge interrupt
if ((AT91C_BASE_PWMC->PWMC_ISR & AT91C_PWMC_CHID2) =AT91C_PWMC_CHID2){
}
}

/*
=======================================================================*/

The draw function:

void draw(void){

unsigned int pix_count;
unsigned int delay;

while(1){

while(draw_flag != 1);

for(pix_count=0;pix_count<13;pix_count++){ //38
__asm__ __volatile__( "NOP" );
}

for(pix_count=0;pix_count<8;pix_count++){
for(delay=0;delay<2;delay++){
pPIO_B->PIO_ODSR = 0x78840000;
}
for(delay=0;delay<2;delay++){
pPIO_B->PIO_ODSR = 0x00000000;
}

}
draw_flag = 0;

}

//line_count++;
}
/*
=======================================================================*/
anyone idea how to prevent the time variation between interrupt and pin
toggle

greets damir
I don't know for certain, but I believe there is almost always a variable delay between the interrupt and the servicing of it. There are two ways I can think of to make this work the way you want, but both require that your code be done executing the rest of the code and returns to code that anticipates the sync signal.

One is to change from an interrupt to polled operation. A very short loop can sample the sync input and continue on to the sync service code. You still have the variation in delay equal to the time for cycling around the loop once.

The other is to enable the interrupt and put the processor in a halted state waiting for the interrupt to awaken it. This should give you a better defined delay, but I don't know how long.

Typically interrupt latency is spec'd as a maximum and less often a minimum. Very few processors have a fixed interrupt delay.

One other thought, if your hardware can generate two signals, you could assert one slightly later than the other so that the earlier signal generates the interrupt and in the interrupt routine the later signal is sampled in a loop. That allows the sync signal to be handled asynchronously, but will synchronize it to the sync signal better, maybe. You could use one of the timer blocks to trigger on the same signal as the interrupt and count down to control the fixed delay for synchronization.

This may sound complex, but it really isn't that bad.

Rick
--- In A..., "d.duric" wrote:
>
> hi,
>
> i am generating video signals with at91sam7x256 for a vga monitor, i
> generate the hsync and vsync signal with pwm.
> i wait in a function draw() until the pwm->hsync interrupt sets a flag,
> after the flag: draw_flag has been set , the function starts to toggle
> port pins,
> i am trying to generate green and black bars on a monitor..
>
> the problem is the time between the interrupt and the first green pixel
> varies, so i don't get straight bars, they look spicky
>
> anyone idea how to prevent the time variation between interrupt and pin
> toggle?
> here is the code for the PWM ISR and the function draw and the iRQ
> handler:
> /*
>
Hi,

If you wan t to have a fixed interrupt latency,why don't you try using the
FIQ instead of IRQ.

Regards
TVRPrasad
On Sun, Oct 18, 2009 at 8:57 PM, d.duric wrote:

> hi,
>
> i am generating video signals with at91sam7x256 for a vga monitor, i
> generate the hsync and vsync signal with pwm.
> i wait in a function draw() until the pwm->hsync interrupt sets a flag,
> after the flag: draw_flag has been set , the function starts to toggle
> port pins,
> i am trying to generate green and black bars on a monitor..
>
> the problem is the time between the interrupt and the first green pixel
> varies, so i don't get straight bars, they look spicky
>
> anyone idea how to prevent the time variation between interrupt and pin
> toggle?
> here is the code for the PWM ISR and the function draw and the iRQ handler:
> */*
> ======================================================================== */
> *
> AT91F_Irq_Handler:
>
> /* Manage Exception Entry */
> /* Adjust and save LR_irq in IRQ stack */
> sub lr, lr, #4
> stmfd sp!, {lr}
>
> /* Save r0 and SPSR (need to be saved for nested interrupt) */
> mrs r14, SPSR
> stmfd sp!, {r0,r14}
>
> /* Write in the IVR to support Protect Mode */
> /* No effect in Normal Mode */
> /* De-assert the NIRQ and clear the source in Protect Mode */
> ldr r14, =AT91C_BASE_AIC
> ldr r0 , [r14, #AIC_IVR]
> str r14, [r14, #AIC_IVR]
>
> /* Enable Interrupt and Switch in Supervisor Mode */
> msr CPSR_c, #ARM_MODE_SVC
>
> /* Save scratch/used registers and LR in User Stack */
> stmfd sp!, { r1-r3, r12, r14}
>
> /* Branch to the routine pointed by the AIC_IVR */
> mov r14, pc
> bx r0
>
> /* Manage Exception Exit */
> /* Restore scratch/used registers and LR from User Stack */
> ldmia sp!, { r1-r3, r12, r14}
>
> /* Disable Interrupt and switch back in IRQ mode */
> msr CPSR_c, #I_BIT | ARM_MODE_IRQ
>
> /* Mark the End of Interrupt on the AIC */
> ldr r14, =AT91C_BASE_AIC
> str r14, [r14, #AIC_EOICR]
>
> /* Restore SPSR_irq and r0 from IRQ stack */
> ldmia sp!, {r0,r14}
> msr SPSR_cxsf, r14
>
> /* Restore adjusted LR_irq from IRQ stack directly in the PC */
> ldmia sp!, {pc}^
> */*
> ======================================================================== */
> *
>
> The PWM ISR routine
>
> void ISR_PWM(void){
>
> draw_flag = 1;
> //acknowledge interrupt
> if ((AT91C_BASE_PWMC->PWMC_ISR & AT91C_PWMC_CHID2) == AT91C_PWMC_CHID2){
>
> }
> }
>
> */*
> ======================================================================== */
> *
>
> The draw function:
>
> void draw(void){
>
> unsigned int pix_count;
> unsigned int delay;
>
> while(1){
>
> while(draw_flag != 1);
>
> for(pix_count=0;pix_count<13;pix_count++){ //38
> __asm__ __volatile__( "NOP" );
> }
>
> for(pix_count=0;pix_count<8;pix_count++){
> for(delay=0;delay<2;delay++){
> pPIO_B->PIO_ODSR = 0x78840000;
> }
> for(delay=0;delay<2;delay++){
> pPIO_B->PIO_ODSR = 0x00000000;
> }
>
> }
> draw_flag = 0;
>
> }
>
> //line_count++;
> }
> */*
> ======================================================================== */
> *
> anyone idea how to prevent the time variation between interrupt and pin
> toggle
>
> greets damir
>
>
>
You can't get 100% interrupt timing on ARM7. There are various causes for
this:
1) An instruction has to complete before the the interrupt can get a look in.
stmfd and other multiple register ops can take quite a while.
2) The internal busing of the micro is often asynchronous. That means that pin
flips etc can take a while to propagate.
3) Pipelining can skew timing too.
4) If you get conflicts with other interrupts then the timing can be even more
spikey.

Try using a FIQ. That can potentially fix (4).

You can sometimes fix (1) by using a sequencce of shorter stmfds etc .

I don't think it is possible to fix (2) and (3).
On Monday 19 October 2009 04:27:13 d.duric wrote:
> hi,
>
> i am generating video signals with at91sam7x256 for a vga monitor, i
> generate the hsync and vsync signal with pwm.
> i wait in a function draw() until the pwm->hsync interrupt sets a flag,
> after the flag: draw_flag has been set , the function starts to toggle
> port pins,
> i am trying to generate green and black bars on a monitor..
>
> the problem is the time between the interrupt and the first green pixel
> varies, so i don't get straight bars, they look spicky
>
> anyone idea how to prevent the time variation between interrupt and pin
> toggle?
> here is the code for the PWM ISR and the function draw and the iRQ
> handler:
> /*
>
Most of applications that I know to generate Video signals goes into "wait for an interrupt" mode a few s before the interrupt to synchronize and execute always the same instructions during critical phase.

The name of this instruction varies from one CPU to another one. Check Sleep or Idle modes.

I hope this help.
Eric