EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

Can't understand interrupts

Started by Cristi Neagu January 13, 2012
Using keil tools to install interrupts for LPC1766 is easy, you have to
remember that cortex handles interrupts different to arm7 though.

Usually its simpler to use the CMSIS code to install interrupt handlers, but
it's trivial enough anyway.

If you are compiling as C++ you need to remove name mangling from the
interrupt handler function by declaring it as extern "c"

Then using the keil startup file startup_lpc17xx.s you will see a list of
weak definitions for all of the interrupt handlers, if not defined then the
linker uses the weak definition in the startup file, but if it finds a
matching definition in your code it will use that instead.

So, taking the timer 0 for example you find 2 entries in startup_lpc17xx.s

DCD TIMER16_0_IRQHandler ; 16+16: 16-bit
Counter-Timer 0

And later

EXPORT TIMER16_0_IRQHandler [WEAK]

All you have to do in your code is match the name, cortex does not need
naked functions as ARM does, so in your code just define

extern "C" {

/// \brief very light timer ISR

void TIMER16_0_IRQHandler (void){

.... // your code goes here

}

}

Of course this for C++, if your code is C you don't need the extern "C"
part.

There is no installation of ISR handler required, just enable the interrupt.

For ARM7 you would have to "install" the interrupt handler and declare it as
naked, but not for cortex

Regards

Phil.

From: l... [mailto:l...] On Behalf Of
Cristi Neagu
Sent: 14 January 2012 12:02
To: l...
Subject: [lpc2000] Re: Can't understand interrupts

--- In l... , David
Smead wrote:
>
> At the very least, you need to embed some asm code to return from the
> interrupt in a C interrupt handler. That C isr function also has to be
> declared "naked" so the compiler doesn't do the usual stack manipulations
> or a normally called function.
>
> For me it's just as easy to write the ISR in asm and be done with.
>
> DaveS
>

Would you be willing to post an example of that?

My problem, as I see it, is that I don't know how to tell the
whatever-it-is-that-has-to-know-where-the-interrupt-handler-is where the
interrupt handler is.

I was trying to get software interrupts to work by following the example in
Keil's help files , and I ran in the same problem. The handler function
compiles OK, it is called OK, but everything hangs when it gets to the
vector table. Could you please give me a small example of how the vector
table in startup.s is set up?

Thanks.



An Engineer's Guide to the LPC2100 Series

--- In l..., "Cristi Neagu" wrote:
> I've been trying to get my head around interrupts, and i'm not making any progress. I've been looking over the LPC2119 manual, as well as over "The Insider's Guide for Philips" book, and I can't figure out what i'm doing wrong.
>
> What I'm trying to do is to generate a fast interrupt via EXTINT1 that will turn on a LED (it's inverted, so clearing the pin will turn the LED on).

I can't help with FIQ but I can give you an example of an interrupt driven timer blinking an LED using vectored interrupts. I just posted it as ARM2106BlinkingLED.zip in the files folder of this forum.

Broad brush, you are using a VECTORED Interrupt Controller so the IRQ vector in crt.s is branched to every time there is an interrupt and something about the vector code gets the actually handler address from the VIC (which you didn't set, BTW).

The vector often just has code to read the address of the interrupt handler from the VIC and branch accordingly. That is the purpose of this line in crt.s:

ldr PC, [PC,#-0xFF0]

That's a magical concoction that actually results in reading the vector address from the VIC and branching. For this approach, the interrupt handler needs a special prolog and epilog. Interrupts can't be nested because interrupts are disabled unless the handlers get involved with enabling/disabling interrups. Priority interrupting is right out the window.

When the VIC is properly set up, each interrupt is assigned a priority and a handler address.

In the example I posted, interrupt handlers are ordinary C functions without the __irq nonsense. They can also be nested so priority interrupting is back on the table. This is accomplished by some pretty clever wrapper code that somebody else invented. It is in crt.s and you can follow it from the IRQ entry point.

In interrupt.h, you can see the prototype for T0_ISR and how it is a simple C function whereas the FIQ handler has the GCC attributes to cause the prolog and epilog code to be emitted.

All else being equal, you want to use the wrapper code because some compilers don't always emit proper code that works for all conditions.

As I said, I can't help with FIQ but you should be able to get some hints from the code I posted. From what I can gather with about 10 seconds of reading, FIQs are not vectored and the FIQ handler has to sort things out if more than one interrupt source is assigned to FIQ.

Were I you, I would get the standard vectored IRQs well understood before I started writing code for the FIQ.

Richard

--- In l..., "Cristi Neagu" wrote:
>
> Hello. I'm a newb on these forums and on ARM programming, so any help would be appreciated.
>
> I've been trying to get my head around interrupts, and i'm not making any progress. I've been looking over the LPC2119 manual, as well as over "The Insider's Guide for Philips" book, and I can't figure out what i'm doing wrong.
>

You can also use Google and look for 'arm fiq example' and you will find a number of references. Among them:

http://cache.freescale.com/files/32bit/doc/app_note/AN2411.pdf

http://winarm.scienceprog.com/arm-mcu-types/interrupt-system-of-arm-lpc2000-microcontrollers.html

Note that crt.s may not even allocate stack space for FIQ mode. You need to check and see how much stack has been allocated for IRQ and FIQ mode.

Richard

--- In l..., Timo wrote:
>
> On 13.01.2012 22:05, Cristi Neagu wrote:
> > I also tried all of the above with the line
> >
> > FIQ_Addr DCD FIQ_Handler
>
> I think that is close but you also have to write the name in the same
> way as your compiler exports it (with two, one or no '_':s) _and_
> declare it as external in startup.s (you get the idea from how main is
> declared) _and_ make the handler interrupt function (what you have done
> (__irq) may be enough - I don't know Keil).
>
> --
>
> Timo
>
I tried all those things, but thanks for the reply.

--- In l..., "Phil Young" wrote:
>
> Using keil tools to install interrupts for LPC1766 is easy, you have to
> remember that cortex handles interrupts different to arm7 though.
>
>
>
> Usually its simpler to use the CMSIS code to install interrupt handlers, but
> it's trivial enough anyway.
>
>
>
> If you are compiling as C++ you need to remove name mangling from the
> interrupt handler function by declaring it as extern "c"
>
>
>
> Then using the keil startup file startup_lpc17xx.s you will see a list of
> weak definitions for all of the interrupt handlers, if not defined then the
> linker uses the weak definition in the startup file, but if it finds a
> matching definition in your code it will use that instead.
>
>
>
> So, taking the timer 0 for example you find 2 entries in startup_lpc17xx.s
>
> DCD TIMER16_0_IRQHandler ; 16+16: 16-bit
> Counter-Timer 0
>
>
>
> And later
>
> EXPORT TIMER16_0_IRQHandler [WEAK]
>
>
>
> All you have to do in your code is match the name, cortex does not need
> naked functions as ARM does, so in your code just define
>
>
>
> extern "C" {
>
> /// \brief very light timer ISR
>
> void TIMER16_0_IRQHandler (void){
>
> .... // your code goes here
>
> }
>
> }
>
>
>
>
>
> Of course this for C++, if your code is C you don't need the extern "C"
> part.
>
>
>
> There is no installation of ISR handler required, just enable the interrupt.
>
>
>
>
>
> For ARM7 you would have to "install" the interrupt handler and declare it as
> naked, but not for cortex
>
>
>
> Regards
>
>
>
> Phil.
>
>
>
>
>
> From: l... [mailto:l...] On Behalf Of
> Cristi Neagu
> Sent: 14 January 2012 12:02
> To: l...
> Subject: [lpc2000] Re: Can't understand interrupts
>
But I don't have an LPC1766... And the problem arises in the vector table. In your example, it's like the PC would get to:

DCD TIMER16_0_IRQHandler

and it wouldn't find the address for TIMER16_0_IRQHandler. What I want to know is how to enter the FIQ handler's address into the vector table.

--- In l..., "Cristi Neagu" wrote:
>
> Hello. I'm a newb on these forums and on ARM programming, so any help would be appreciated.
>
> I've been trying to get my head around interrupts, and i'm not making any progress. I've been looking over the LPC2119 manual, as well as over "The Insider's Guide for Philips" book, and I can't figure out what i'm doing wrong.
>
> What I'm trying to do is to generate a fast interrupt via EXTINT1 that will turn on a LED (it's inverted, so clearing the pin will turn the LED on).
>
> Here's some code (I use Keil, btw):
>
> Interrupt handler:
>
> void FIQ_Handler(void) __irq
> {
> IOCLR0 = 0x10000; //Set the LED pins
> EXTINT = 0x00000002; //Clear the peripheral interrupt flag
> }
>
> Interrupt initialization:
>
> void initFiq(void)
> {
> IODIR0 = 0x10000; //Set the LED pins as outputs
> PINSEL0 |= 0xC0; //Enable the EXTINT1 interrupt
> VICIntSelect = 0x00008000; //Enable a Vic Channel as FIQ
> VICIntEnable = 0x00008000;
> }
>
> Main program:
>
> int main (void)
> {
> init_serial0(0x30);
> initFiq(); //Initialize the Fast interrupt source
> IOSET0 = 0x10000;
> while(1)
> {
> printf("Waiting for interrupt\n");
> }
> }
>
> And vectors in startup.s
>
> Vectors LDR PC, Reset_Addr
> LDR PC, Undef_Addr
> LDR PC, SWI_Addr
> LDR PC, PAbt_Addr
> LDR PC, DAbt_Addr
> NOP ; Reserved Vector
> ; LDR PC, IRQ_Addr
> LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
> LDR PC, FIQ_Addr
>
> Reset_Addr DCD Reset_Handler
> Undef_Addr DCD Undef_Handler
> SWI_Addr DCD SWI_Handler
> PAbt_Addr DCD PAbt_Handler
> DAbt_Addr DCD DAbt_Handler
> DCD 0 ; Reserved Address
> IRQ_Addr DCD IRQ_Handler
> FIQ_Addr DCD FIQ_Handler
>
> Undef_Handler B Undef_Handler
> SWI_Handler B SWI_Handler
> PAbt_Handler B PAbt_Handler
> DAbt_Handler B DAbt_Handler
> IRQ_Handler B IRQ_Handler
> FIQ_Handler B FIQ_Handler
>

Notice that the FIQ vector entry point branches to FIQ_Handler and that FIQ_Handler branches to itself (loops). Unless FIQ_Handler is defined as a 'weak' symbol, your code will never get called. In fact, I think that if you named your handler as FIQ_Handler, you would get a duplicate definition at link time.

So, write it like this:

FIQ_Handler B myFIQ_Handler

Then rename your handler to 'myFIQ_Handler'.

Your handler MAY have to write VICVectAddr (any value) to tell the VIC that the interrupt has been handled. I have never used FIQ so I'm not sure about this.

VICVectAddr = 0x00000000; // just before leaving the handler

Richard

>
> You can also use Google and look for 'arm fiq example' and you will find a number of references. Among them:
>
> http://cache.freescale.com/files/32bit/doc/app_note/AN2411.pdf
>
> http://winarm.scienceprog.com/arm-mcu-types/interrupt-system-of-arm-lpc2000-microcontrollers.html
>
> Note that crt.s may not even allocate stack space for FIQ mode. You need to check and see how much stack has been allocated for IRQ and FIQ mode.
>
> Richard
>

Thanks for the reply.

I can't open the .pdf, and the other link, while being a good example, doesn't cover my problem. There is nothing in there about setting up the vector table, and that's where the problem is.

My handlers are set up like in the example on the page you indicated, but the program never reaches that point because the vector table isn't set up properly. And before you suggest, I did search Google for how to set up the vector table :P

Hi again,

Sorry I was reading 2 emails and mistakenly thought you were using the
LPC1766.

The same applies though, in keil after compiling double click the project to
open the .map file, in this you can see the names that the compiler
generated, search for your ISR and note the name.

Then go to the startup file and replace the name that it branches to with
the name of your handler.

Then for ARM you will have to declare the function as interrupt which tells
the compiler to modify the entry / exit code so that it is an interrupt
handler.

You may also need to add an import statement in the startup file to import
your handlers symbol, this is the same as the say that the entry point is
imported.

So replace

LDR PC, FIQ_Addr

With

import myFIQ_Handler

LDR PC,myFIQ_Handler

And change your function declaration to

void myFIQ_Handler(void) __irq

Regards

Phil.

From: l... [mailto:l...] On Behalf Of
rtstofer
Sent: 14 January 2012 19:14
To: l...
Subject: [lpc2000] Re: Can't understand interrupts

--- In l... , "Cristi
Neagu" wrote:
>
> Hello. I'm a newb on these forums and on ARM programming, so any help
would be appreciated.
>
> I've been trying to get my head around interrupts, and i'm not making any
progress. I've been looking over the LPC2119 manual, as well as over "The
Insider's Guide for Philips" book, and I can't figure out what i'm doing
wrong.
>
> What I'm trying to do is to generate a fast interrupt via EXTINT1 that
will turn on a LED (it's inverted, so clearing the pin will turn the LED
on).
>
> Here's some code (I use Keil, btw):
>
> Interrupt handler:
>
> void FIQ_Handler(void) __irq
> {
> IOCLR0 = 0x10000; //Set the LED pins
> EXTINT = 0x00000002; //Clear the peripheral interrupt flag
> }
>
> Interrupt initialization:
>
> void initFiq(void)
> {
> IODIR0 = 0x10000; //Set the LED pins as outputs
> PINSEL0 |= 0xC0; //Enable the EXTINT1 interrupt
> VICIntSelect = 0x00008000; //Enable a Vic Channel as FIQ
> VICIntEnable = 0x00008000;
> }
>
> Main program:
>
> int main (void)
> {
> init_serial0(0x30);
> initFiq(); //Initialize the Fast interrupt source
> IOSET0 = 0x10000;
> while(1)
> {
> printf("Waiting for interrupt\n");
> }
> }
>
> And vectors in startup.s
>
> Vectors LDR PC, Reset_Addr
> LDR PC, Undef_Addr
> LDR PC, SWI_Addr
> LDR PC, PAbt_Addr
> LDR PC, DAbt_Addr
> NOP ; Reserved Vector
> ; LDR PC, IRQ_Addr
> LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
> LDR PC, FIQ_Addr
>
> Reset_Addr DCD Reset_Handler
> Undef_Addr DCD Undef_Handler
> SWI_Addr DCD SWI_Handler
> PAbt_Addr DCD PAbt_Handler
> DAbt_Addr DCD DAbt_Handler
> DCD 0 ; Reserved Address
> IRQ_Addr DCD IRQ_Handler
> FIQ_Addr DCD FIQ_Handler
>
> Undef_Handler B Undef_Handler
> SWI_Handler B SWI_Handler
> PAbt_Handler B PAbt_Handler
> DAbt_Handler B DAbt_Handler
> IRQ_Handler B IRQ_Handler
> FIQ_Handler B FIQ_Handler
>

Notice that the FIQ vector entry point branches to FIQ_Handler and that
FIQ_Handler branches to itself (loops). Unless FIQ_Handler is defined as a
'weak' symbol, your code will never get called. In fact, I think that if you
named your handler as FIQ_Handler, you would get a duplicate definition at
link time.

So, write it like this:

FIQ_Handler B myFIQ_Handler

Then rename your handler to 'myFIQ_Handler'.

Your handler MAY have to write VICVectAddr (any value) to tell the VIC that
the interrupt has been handled. I have never used FIQ so I'm not sure about
this.

VICVectAddr = 0x00000000; // just before leaving the handler

Richard



I don't use other peoples project code because you end up with stuff you
don't need, and are forced to fit your own code around their very
generalized approach.

I write my own crt.s file. Somewhere near the end up it I call a
function named "initialize_application". That's where all the I/O is
configured along with thinks like the MAM and RTC. At the bottom of it
is the function init_vic, shown below.

void init_vic( void )
{
typedef void (*func)(void);

/* enabling control slot with irq number 5. */
(*((volatile uint32_t *)(0xfffff200))) = 0x25;

/* setting vector 0 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff100))) = (uint32_t)sys_timr_isr;

/* enabling control slot with irq number 6. */
(*((volatile uint32_t *)(0xfffff204))) = 0x26;

/* setting vector 1 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff104))) = (uint32_t)uart0_isr;

/* enabling control slot with irq number 7. */
(*((volatile uint32_t *)(0xfffff208))) = 0x27;

/* setting vector 2 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff108))) = (uint32_t)uart1_isr;

/* enabling control slot with irq number 14. */
(*((volatile uint32_t *)(0xfffff20c))) = 0x2e;

/* setting vector 3 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff10c))) = (uint32_t)pwr_fail_isr;

/* enabling control slot with irq number 13. */
(*((volatile uint32_t *)(0xfffff210))) = 0x2d;

/* setting vector 4 with interrupt service function name. */
(*((volatile uint32_t *)(0xfffff110))) = (uint32_t)rtc_isr;

/* setting vic_vect_default to service routine (spurious_irq) */
(*((volatile uint32_t *)(0xfffff034))) = (uint32_t)spurious_irq;
} /* end of function init_vic */
That function is actually written by a Python program that I use to
generate the C code from a small table.

Hope this helps.

DaveS

On Sat, Jan 14, 2012 at 4:02 AM, Cristi Neagu wrote:

> **
>
> --- In l..., David Smead wrote:
> >
> > At the very least, you need to embed some asm code to return from the
> > interrupt in a C interrupt handler. That C isr function also has to be
> > declared "naked" so the compiler doesn't do the usual stack manipulations
> > or a normally called function.
> >
> > For me it's just as easy to write the ISR in asm and be done with.
> >
> > DaveS
> > Would you be willing to post an example of that?
>
> My problem, as I see it, is that I don't know how to tell the
> whatever-it-is-that-has-to-know-where-the-interrupt-handler-is where the
> interrupt handler is.
>
> I was trying to get software interrupts to work by following the example
> in Keil's help files , and I ran in the same problem. The handler function
> compiles OK, it is called OK, but everything hangs when it gets to the
> vector table. Could you please give me a small example of how the vector
> table in startup.s is set up?
>
> Thanks.
>
>
>


--- In l..., "Cristi Neagu" wrote:
> I can't open the .pdf, and the other link, while being a good example, doesn't cover my problem. There is nothing in there about setting up the vector table, and that's where the problem is.
>
> My handlers are set up like in the example on the page you indicated, but the program never reaches that point because the vector table isn't set up properly. And before you suggest, I did search Google for how to set up the vector table :P
>

These are the vectors and they are a CONSTANT - Do Not Modify Them

Vectors LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr

Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler

Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler <===== THIS needs to go away!
There's really nothing wrong with your vector table and you shouldn't be looking to change it.

When the VIC sees an FIQ, it causes the hardware to branch to the 8th vector - ALWAYS.

The LDR PC, FIQ_Addr causes the CPU to grab the FIQ_Handler address from the table entry at FIQ_Addr and put it in the PC. This is essentially an indirect jump through the table to the handler.

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.

Richard


The 2024 Embedded Online Conference