EmbeddedRelated.com
Forums

Can't understand interrupts

Started by Cristi Neagu January 13, 2012
--- In l..., "rtstofer" wrote:
From a couple of NXP App notes I have read on FIQ interrupts, you don't need to acknowledge the interrupt. You do need to clear the interrupt source.

So, I suspect your handler code will work fine after you fix up crt0.s

Richard

An Engineer's Guide to the LPC2100 Series

Ok... With you'r help, I finally managed to figure out interrupts, and I'll post what I found for others to find.

First of all, I'd like to thanks you all for the help, especially Richard (rtstofer). Your 3 suggestions all worked, mainly:

>So, write it like this:
>
> FIQ_Handler B myFIQ_Handler
>
> Then rename your handler to 'myFIQ_Handler'.

and

>The problem you have is that you should NOT have the code
>
> FIQ_Handler B FIQ_Handler
>
> This is just a tight loop and this is where your CPU will hang.
>
> One solution is to just comment this line and make sure your >handler is named FIQ_Handler. The linker will then insert the >address of this function into the indirection table and all should >link fine.

(with the observation that you need to put in IMPORT FIQ_Handler before the table) and

>From a couple of NXP App notes I have read on FIQ interrupts, you >don't need to acknowledge the interrupt. You do need to clear the >interrupt source.

I'll post what I found in my next message, just to keep things a bit clearer.

Here it is: How to make interrupts work in Keil uVision4.

Software interrupts:

The interrupt function should be declared like this:

void __swi(0) mySWIfunction (void);
void __SWI_0(void)
{
//do something here;
}

And when it's called, you use mySWIfunction(); This is all documented in Keil's help. Just look for __swi in help. But what they don't tell you is that the SWI_table.s file they want you to include doesn't work properly because it lacks a handler. Instead, include in your project the file H:\Programming\Keil\ARM\Boards\Keil\MCB2300\USBCDC\SWI.s and import and declare your functions in that file. Everything should now work. To my untrained eye it doesn't matter if it's meant for another board, the handler works just the same.

Vectored interrupts:

You need to declare the interrupt function like this:

__irq void intr_handler(void)
{
//do something;
//make sure you clear the interrupt and
//write to VICVectAddr before exiting
}

and when you're setting up the VIC:

VICVectAddrn = (unsigned long) intr_handler;

Just replace n with whatever interrupt channel you desire.

Next, go into startup.s and make sure this line is commented out:

; LDR PC, IRQ_Addr

Everything should work.

Fast interrupts:

There are 2 ways of doing this:

1. declare your function like this:

__irq void FIQ_Handler(void)
{
//do something
//and remember to clear the interrupt before exit
}

then go in startup.s and add this line before the DCD part of the table:

IMPORT FIQ_Handler

and comment out this line:

;FIQ_Handler B FIQ_Handler

2. declare your function with whatever name:

__irq void my_FIQ_Handler(void)
{
//do something
//and remember to clear the interrupt before exit
}

then import your function before the table in statup.s:

IMPORT my_FIQ_handler

and change this line:

FIQ_Handler B FIQ_Handler

to

FIQ_Handler B my_FIQ_Handler

either of these methods work, but I prefer the first.

That should be it.

Two more things to add:
1. I don't know why these things work, but they do. Maybe someone can explain further.
2. I still haven't got around non-vectored interrupts, but I think they should be easy to figure out.

Thanks again for all the help, guys.

--- In l..., "Cristi Neagu" wrote:
> Two more things to add:
> 1. I don't know why these things work, but they do. Maybe someone can explain further.

They work because the VIC is pretty smart. For FIQs there is only one non-vectored vector address and the handler has to separate multiple FIQ interrupt sources. FIQ was really intended for ONE very fast interrupt. The vector is placed in such a way that the FIQ handler could be placed at the end of the vector table with no branching required. The VIC causes the CPU to branch to the FIQ entry point, the code does whatever as fast as it can (no branching around) and returns.

Vectored interrupts are different. Each interrupt source can be assigned a priority and a handler address. When an interrupt occurs, the code at the IRQ vector reads the handler address from the VIC. For vectored interrupts you have to clear the interrupt source and write something (it doesn't matter what) to the VICVectAddr registers to acknowledge the interrupt.

For non-vectored interrupts, the VIC will provide the default handler address and the handler will have to sort out the sources. The interrupt source must be cleared and the interrupt acknowledged by writing something to VICVectAddr. NOTE: I'm pretty sure this is true but I have never used non-vectored interrupts. Seems pretty useless to me!

Grab that Blinking LED code before I delete it. I don't leave code lying around that I might have to support. The reason you want it is that it shows how to use a wrapper around vectored interrupts such that nesting will function properly and interrupts based on priority can actually work. BTW, I would increase the stack size for the IRQ and FIQ stacks. Looking at it, I'm pretty sure the stacks are only working by magic.

> 2. I still haven't got around non-vectored interrupts, but I think they should be easy to figure out.

See above. I don't know why you would want to use non-vectored interrupts.

[QUOTE] -- from LPC2148 User Manual

The VIC ORs the requests from all the vectored and non-vectored IRQs to produce the IRQ signal to the ARM processor. The IRQ service routine can start by reading a register from the VIC and jumping there. If any of the vectored IRQs are requesting, the VIC provides the address of the highest-priority requesting IRQs service routine, otherwise it provides the address of a default routine that is shared by all the non-vectored IRQs. The default routine can read another VIC register to see what IRQs are active.

[/QUOTE]

Richard

Thanks for the explanations, Richard.

About that project, a couple of questions:

1. What development chain is it written in?
2. If i understand it correctly, all I need to do is add IRQ_Wrapper
to my startup.s (or crt.s), and point IRQ_Addr to the wrapper, and
interrupts should work correctly and be nested?

Also, with regard to non-vectored interrupts, I agree. I made them
work pretty much like normal interrupts. But unless you plan on using
more than 16 interrupts in your project... they are pretty useless.

Thanks again for all the help, and for the example you provided.
--
Cristi Neagu
--- In l..., Cristi Neagu wrote:
>
> Thanks for the explanations, Richard.
>
> About that project, a couple of questions:
>
> 1. What development chain is it written in?
Any GCC toolchain should work. That would include a Windows package like YAGARTO.

The Makefile indicates to me that I wrote that on a Linux box with GNUARM. You would need to change the paths to the tools. To use the lastest version of YAGARTO, all references to 'arm-elf-' need to be changed to 'arm-none-eabi-'. The YAGARTO tools would need to be on the path or fully specified in the Makefile.
> 2. If i understand it correctly, all I need to do is add IRQ_Wrapper
> to my startup.s (or crt.s), and point IRQ_Addr to the wrapper, and
> interrupts should work correctly and be nested?

Yes, you will see where the wrapper reads the VIC and gets the vector address. It reenables interrupts before branching to the handler address and this allow the VIC to interrupt again if a higher priority interrupt should occur.

Having all of this wrapper code, the need for the prolog and epilog code created by the __irq spec is no longer needed and all handler functions are ordinary C functions. Of course this is one of the advantages of the Cortex-M3 devices. Everything can be written in C.

Good luck and enjoy! Several years ago I was working through the very same issues.

Richard