Hello, i have a problem. There are allways difficulties in programms with IRQ und FIQ. So i wrote this code to investigate where the problem is.
I use two Timer, Timer0 and Timer1 to generate interrupts.
Timer0 generates a periodic normal IRQ every 2000 cycles.
Inside the IRQ routine there is a for(i=80;i;i--) loop with port toggling. So i can watch on my 2-channel storage oszilloscope when this toggling is interrupted by the fast interrupt.
Timer1 generates a periodic fast interrupt FIQ every 1000 cycles.
I interprete the microcontroller datasheet that FIQ has the higher pririty and can interrupt IRQ. Am i right???
Unfortunaltely i can not post a oszilloscope screenshot in this forum.
So i try to describe the result: On channel 1 there is a train of 40 pulses. Duration is about 33us. You can see, after pulse 31 there a little gap. This is the moment FIQ is started and returns to IRQ. Then the left 9 pulses are there. Than there is a low level for about 17us. This looks all the way i expected things to take place.
On channel 2 there is a port toggling every 25us. Sometimes the port toggling is replaced by a single needle pulse.
Whats going on there. Unfortunaltely i have no idea where the problem occurs. I expanded FIQ-stack-size from 4 to 80. (startup.s)
//************************************************************************* #include <ADuC7026.h> //************************************************************************* void Port_init(void); void PLL_init(void); void Timer_init(void); void interrupt_init(void); //************************************************************************* int main (void) { Port_init(); PLL_init(); Timer_init(); interrupt_init(); while(1) { GP4DAT ^= 0x00040000; // P4.2 toggling => program is in main loop } } //************************************************************************* void IRQ_Handler()__irq __ram // Timer 0 => Interrupt { unsigned int i; T0CLRI = 0; for(i=80;i;i--) GP4DAT ^= 0x00010000; // P4.0 shows's Interrupt } //************************************************************************* void FIQ_Handler()__fiq __ram // Timer 1 => Fast Interrupt { T1CLRI = 0; GP4DAT ^= 0x00020000; // P4.1 show's Fast Interrupt } //************************************************************************* void interrupt_init(void) { IRQEN = RTOS_TIMER_BIT; // Timer 0 => Interrupt FIQEN = GP_TIMER_BIT; // Timer 1 => Fast Interrupt } //************************************************************************* void Timer_init(void) { T0LD = 2000 - 1; // Timer 0 => periodic Interrupt 2000 cycles T1LD = 1000 - 1; // Timer 1 => periodic Fast Interrupt 1000 cycles T0CON = 0xC0; // Enabled,Periodic,CLK/1 T1CON = 0xC0; // Enabled,Periodic,Binary and CLK/1 } //************************************************************************* void PLL_init(void) { PLLKEY1 = 0xAA; PLLCON = 0x01; // clear Bit5 OSEL => External 32.768kHz crystal PLLKEY2 = 0x55; while(~IRQSIG & PLL_LOCK_BIT){} // waiting for PLL lock } //************************************************************************* void Port_init(void) { GP4DAT |= 0x01000000; // P4.0 is output GP4DAT |= 0x02000000; // P4.1 is output GP4DAT |= 0x04000000; // P4.2 is output } //*************************************************************************
Hi, there's another solution for Port-Toggling, so it is not necessary to have an atomic toggle instruction.
(GP4DAT & 0x00010000) ? GP4CLR = 0x00010000 : GP4SET = 0x00010000;
Depends on your ARM7-derivate, mine is ADuC7026
There are some Cortex-3-derivate which have an atomic Port/Bit-Toggling instruction.
Sorry Georg but HOW is that atomic ?
The set register can only affect the bits that you set a one in.
The clear register can only affect the bits that you set a one in.
In short, one "setreg = bitmasp" assign can not affect other bits. If the FIQ handles one bit, IRQ another and main a third, it is impossible for the three locations to affect the wrong bit.
When you instead read the pin register and make a change, all three locations will read and write the state for all pins in the port, which is the reason why lower-priority code may create glitches by overwriting a toggle made by the IRQ and/or the FIQ.
Hi Per,
Your explanation is clear but Georg's code is not (as written) an atomic operation. It could still be interrupted, resulting in glitches (or worse).
In the very specific case you highlight, where each access is dealing with a unique bit (or bits), you may be safe, but as a general solution, it is not applicable.
Correct. The Set/Cler registers are not a way to get atomic operation.
It is a way to be able to isolate arbitrary sets of bits on a port, and have different tasks/ISR own different bits and work on them without having their changes overwritten by another task/ISR.
In your case, you don't need any atomic updates - you just need to make sure that one task/ISR doesn't overwrite bits owned by another task/ISR. You will get task isolation without the additional burden of creating critical sections, i.e. with a minimum of overhead.
If you look at Georg Bunter text a bit more closely, you'll notice that he says "so it is not necessary to have an atomic toggle instruction." There is no claim that you will get an atomic access - just that this is a good workaround to remove your need of atomic access.
Fair enough - I misinterpreted his text as claiming that his code was a viable alternative to an atomic toggle instruction !
In the specific case under discussion, this is a more efficient solution. However, "specific cases" often have a nasty habit of subsequently being used where they're not appropriate :-)
David.