10 years, 2 months ago.

How to disable specific interrupts?

I have a system that must react, in a specific order, to interrupts from Timers and pin-changes. One example is that I would like to *not* react to a pin-change interrupt within a time "window".

I can attach a function to an interrupt in the usual way, e.g. "NoEcho.attach(&DeadTime, 0.00018);" and "PinInt.fall(&PinFall);" but once I have set up the PinInt interrupt it stays set up.

I would like to enable the interrupt-on-pin-fall, wait for activity, then disable interrupt-on-pin-fall for a fixed time set by a Timer (which needs an interrupt to do this), then enable interrupt-on-pin-fall again. Clearly "disable_irq()" is not helpful.

Before I can re-enable an interrupt, I would have to clear the flag that says there is an interrupt pending, so that would have to be part of the solution.

I can bodge all this with an "ignore" flag, so the interrupt-on-pin-fall interrupt does happen, but does nothing if the "ignore" flag is set, but that's really not good as the interrupt is still serviced, and takes time.

My background is solidly in writing complex assembler programs that are locked to time-critical hardware, so I'm not used to have a compiler between me and the hardware!

Martin

1 Answer

10 years, 2 months ago.

First of all, which is not an answer directly to your question, but might be relevant, you can change the priority of interrupts with for example:

NVIC_SetPriority(TIMER3_IRQn, 1); 

Where '0' is default, and highest priority.

Now for your specific question. In general the way to disable an mbed interrupt is to attach a NULL pointer, so:

PinInt.fall(NULL); 

This actually disables the interrupt, it doesn't just stop calling your user function, it is disabled in hardware. As a bit faster option, see: http://mbed.org/users/mbed_official/code/mbed/docs/824293ae5e43//classmbed_1_1InterruptIn.html#aac52723c749b32fe1750006adf23370b. There is also a disable/enable_irq function for InterruptIn. This disabled the entire interruptvector of the interruptin, and what that does exactly depends on the device (contrary to attaching a NULL pointer, which always does the same). On the LPC1768 I see you have it disables all pin interrupts.

Erik,

Now that's useful information - thanks!! Shouldn't that be in the manual somewhere? Or is it?!!

I like the system in your weblink. To disable the interrupt I would just give the command "PinInt.disable_irq();" and to re-enable it I would say "PinInt.enable_irq();"?

Would these commands dis/enable *only* the interrupt "PinInt", which I would have connected to a real pin with something like "DigitalIn Pin(p21); InterruptIn PinInt(p21);" at the top?

Would the dis/enable happen quickly (fractions of a us)??

If you re-enable the interrupt, is the associated interrupt flag cleared, or would you immediately go off and service a pending interrupt? In other words, must you explicitly clear an interrupt-pending flag?

Can you disable a timer/ticker/timeout interrupt in a similar manner? Sometimes I wake up early and want to turn the alarm off manually - that sort of thing. Would "detach" do this?

Martin

posted by Martin Reekie 08 Feb 2014

AND (sorry, forgot), with respect to re-enabling an interrupt, I would like to enable the interrupt caused by a rising edge, but not a falling edge, and then, later, the reverse, so to re-enable an interrupt caused by a falling edge, but not a rising edge.

I am working with a noisy, rather unpredictable, input waveform, so I really want to nail things down as far as possible.

How can that be done?

Martin

posted by Martin Reekie 08 Feb 2014

Martin,

That weblink is from the handbook ;). I agree it could be clearer what attaching a NULL pointer does, probably will edit it in in the coming days. The pinint.enable/disable_irq are device specific what they do, on the LPC1768 they disable ALL interruptins. If you want to disable a specific one, attach a NULL pointer. This is however slower. The enable/disable functions should do it in a fraction of a us. IIRC attaching NULL pointer is something like 2-3 times slower, but don't pin me on that.

What happens when you re-enable also depends on the method. enable/disable just disables the interrupt vector, I assume the interrupt is still set, so once you re-enable it, it will probably call the old one. Attaching NULL pointer (in general preferred, but a bit slower) will disable the interrupt from ever being generated, so once you re-enable it an old one won't be called.

For Tickers there is indeed detach (same as attaching NULL pointer, but explicit function for it). You can also disable it by disabling the entire interrupt vector (Timer3_IRQn iirc). But again, just detaching it is generally the way to go.

Finally your last one: Simply use .rise(&yourFunction) to attach your rising edge interrupt, when you want to switch it around, use .rise(NULL) to disable it, and .fall(&yourFallFunction) to enable falling edge interrupt.

That said, I don't know you application and requirements, but if it is quite fast you might need to go directly to the hardware.

posted by Erik - 08 Feb 2014

Erik,

Once again, thanks for such a complete answer. I don't know how you can see that I am using an LPC1768 but you are, of course, correct.

I tried to just slip into the code a line "EchoInt.disable_irq ();", without worrying about the actual effect, and it wouldn't compile.

In the handbook I see that "fall", "mode", "enable_irq" and "disable_irq" come under a sub-heading "template<typename T >". There is another sub-heading, with exactly the same title, that includes "rise" and "fall", just above it. What are these? I get the strong impression that I am not using them as I should, and that is why the "disable_irq" thing is not compiling. Is there a short example program that could show me what I am supposed to do here?

I am currently only attaching an interrupt to a single pin, but I am sure that will not always be the case. Suppose I have two interrupt pins, as in the code below:

  1. include "mbed.h"

DigitalIn Pin(p21); InterruptIn PinInt(p21); DigitalIn Wake(p22); InterruptIn WakeInt(p22); Timer TimeIt; Timeout TooLong;

Lots of void functions called by interrupts

int main() {

Random stuff

PinInt.fall(&PinFall); PinInt.rise(&PinRise); WakeInt.fall(&WakeFall); WakeInt.rise(&WakeRise);

More random stuff

If, after the above, I give the command "PinInt.disable_irq();" will that have any effect on WakeInt? I think you are saying that it will.

If, instead, I give the command "PinInt.fall(NULL);" will that have any effect on WakeInt? I think you are saying that it will not, but it'll be slower.

If, after the command "PinInt.fall(NULL);" I get a *rising* edge on Pin, will I get an interrupt? I think you are saying that I will, as "PinInt.rise(&PinRise);" will still be valid.

If I use the "PinInt.fall(NULL);" form, then a falling edge arrives on Pin, followed by a rising edge, and then I give the command "PinInt.fall(&PinFall);" I would NOT expect to get an immediate interrupt, but I would get one on the *next* falling edge. Correct?

This would be so easy in assembler! "bcf PIE, n" disables, and "bcf PIF, n" followed by "bsf PIE, n" enables, and both would happen in less than 0.2us. I quite liked having absolute control! But it was *so* easy to make mistakes.

Thanks so much for helping me with this. If it's any consolation, this is going to a whole class of students, so you're not just educating one person!

Martin

posted by Martin Reekie 08 Feb 2014

Hey,

First of all, use <<code>> and <</code>> (on seperate lines) to make code better readable. Those enable/disable irq functions are relative new (few months ago added). So make sure your mbed lib is updated (click on it, then there is an update button if you don't have the latest version).

I know you have an LPC1768 because if you click on someones name, at the right side it shows the platforms he has activated :). Regarding PinInt/WakeInt, yes that is correct, NULL attaching won't have an effect, disable_irq will have. If you disable falling edge interrupt, rising edge one should not be affected and still valid. Next one about getting immediate interrupt is also correct (or should be correct).

You can if you directly write to the hardware also still set everything manually, but it is alot more work. If it isn't time critical I wouldn't advice it :). And at least now you don't have to worry about storing status register, internal pointers, etc :).

This is the source code for the LPC1768 InterruptIn: http://mbed.org/users/mbed_official/code/mbed-src/file/49e45cb70de1/targets/hal/TARGET_NXP/TARGET_LPC176X/gpio_irq_api.c (there is a C++ layer somewhere else, so it doesn't map one on one on the functions you use here). There you have in C what is happening, for example enabling a falling edge interrupt on port 0:

LPC_GPIOINT->IO0IntEnF |= 1 << obj->pin;

Direct assembly can also be included, but it generally isn't done in the mbed source. If you need a specific assembly function they are available also in C. Of course not the same as actually writing in assembly, but for example from the interruptin source code:

 bitloc = 31 - __CLZ(rise0);

clz is count leading zeros, no standard C function to do something like that, so it is called this way.

posted by Erik - 08 Feb 2014

Erik,

Yes, I thought of the <<code>> thing right after I saw what had happened to my code formatting! Next time.

Thanks again for all the information - think I'm nearly there. The only remaining thing is this "template" stuff - the third paragraph of my essay above. How does that work?

Martin

posted by Martin Reekie 08 Feb 2014

Ah yeah, forgot that one :)

It is the same as the regular one, but used to attach a member function instead of a regular function. For example here it is used in a quadrature encoder library which uses InterruptIns:

channelA_.rise(this, &QEI::encode);

The first argument points to the object (in this case itself, since it is used in a library here, but it can also point to another object), and the second one is the function it needs to call (encode, of the QEI class, and 'this' is an object of the QEI class).

One of those things that just work, don't need to bother figuring out how it works exactly :P. I know it allows for some things to be undefined until it is actually used in the code, then the compiler figures it out. But I only used templates once myself, and that was very basic:

template<typename Type>
void source(Type* pointer, bool autoinc, int size = sizeof(Type) * 8) {

The first argument of that function is a pointer of type 'Type'. But what 'Type' actually is, is only defined once the function is actually used.

posted by Erik - 08 Feb 2014

Erik,

No, I can't pretend I understand that! Perhaps as I only have a rudimentary knowledge of "C", and nothing of C++ or later, this is not surprising.

We are getting further and further from the assembler I understand and, more importantly, the assembler the LPC1768 will eventually have to understand. Should we not code more simply, in order to ease the task of the compiler, and hence get more efficient code?

In any case, what you have told me has been an enormous help, and I've got stuff going the way I wanted. Thank you very much indeed.

All the best,

Martin

posted by Martin Reekie 09 Feb 2014

Actually apparantly the template stuff can be really efficiently done by the compiler, since part of it is already done at compile time instead of runtime. In general you are right you don't want a very abstract programming language, especially on a microcontroller. So a function with very high timing demands is probably already better in C instead of C++. But C++ still isn't too much more abstract than C.

Memory wise it helps to not use too many of the standard libs. Printf is fine, cout takes a lot of memory. Same for for example C++'s vector lib, an array meanwhile has no overhead.

posted by Erik - 10 Feb 2014