software interrupt

10 Feb 2011

Is there a way to implement an interrupt using software? I know there is one for hardware, InterruptIn, where the interrupt is triggered by the rising or falling edge of the pulse. What I would like to do is create the same behavior, but using a bool stored in memory and having the interrupt handler called when the value is changed from 0 to 1.

If there is not, are there any plans to implement something like this? If there are none, I would be very happy to help start. Thank you.

10 Feb 2011

Hi Thomas,

I think I get what you mean and we haven't got anything like that on the cards, so it'd be great for you to start experimenting. Would love to see what you're thinking, and how it might be used. Simon

10 Feb 2011

Hi Thomas,

Interesting question. My first thought was to use a Ticker to sample the bool and callback on change. But that doesn't seem to fit exactly the bill you are looking for. So, I did come up with something, but it's "cheating".

Looking at the Mbed schematic, there are IO pins that are not connected. I choose P0_22 for my example. Basically, use the Mbed InterruptIn to set it up as a normal InterruptIn. Then with some sneaky cheating macros, switch it to an output. Then you can set and clear the "InterruptIn" and it does indeed fire the .rise() and .fall() callbacks.

Here's my example:-

main.cpp

#include "mbed.h"

#define P0_22_SEL_MASK    ~(3UL << 12)
#define P0_22_SET_MASK    (1UL << 22)
#define P0_22_CLR_MASK    ~(P0_22_SET_MASK)
#define P0_22_AS_OUTPUT   LPC_PINCON->PINSEL1&=P0_22_SEL_MASK;LPC_GPIO0->FIODIR|=P0_22_SET_MASK
#define P0_22_AS_INPUT    LPC_GPIO0->FIOMASK &= P0_22_CLR_MASK; 
#define P0_22_SET         LPC_GPIO0->FIOSET = P0_22_SET_MASK
#define P0_22_CLR         LPC_GPIO0->FIOCLR = P0_22_SET_MASK
#define P0_22_IS_SET      (bool)(LPC_GPIO0->FIOPIN & P0_22_SET_MASK)
#define P0_22_IS_CLR      !(P0_22_IS_SET)
#define P0_22_MODE(x)     LPC_PINCON->PINMODE1&=P0_22_SEL_MASK;LPC_PINCON->PINMODE1|=((x&0x3)<<12)

DigitalOut myled(LED1);
DigitalOut led2(LED2);
InterruptIn mybool(P0_22);

void led2on(void) {
    led2 = 1;
}
 
void led2off(void) {
    led2 = 0;
}   

int main() {

    // Setup InterruptIn as normal.
    mybool.mode(PullUp);
    mybool.rise(&led2on);
    mybool.fall(&led2off);
    
    // Now cheat, manually switch it to an output.
    P0_22_AS_OUTPUT;

    while(1) {
        myled = 1;
        P0_22_SET; // Set the bool and led2on() is called.
        wait(0.2);
        
        myled = 0;
        P0_22_CLR; // Clear the bool and led2off() is called.
        wait(0.2);
    }
}

If you don't need P0_22 in a final design then it maybe the solution you are looking for :)

Import programforums

Used to hold all my \"snippets\" I create in forum support.

11 Feb 2011

whoa, ok that's a bit over my head at the moment, time to hit the books. Thank you very much Andy, an excellent starting point.

I did a basic experiment and discovered I will also need to solve some other issues. ok, time to spill the beans. My project is a serial interface class that uses the serial IRQ to automatically read bytes received by the mbed into a user-defined circular buffer. Pretty basic.

What I'm trying to do now, is allow the user of the class to attach a function to an interrupt that is generated whenever the buffer is full, instead of having the user polling all the time. Since a serial IRQ is already being used, and can't be tied up lest we lose data, I need to be able to "interrupt the interrupt". The serial IRQ must be able to interrupt the user's attached interrupt. Is there a way to make interrupt "layers" or "priority" (I have no idea what the correct term is here)?

My experiment brought me to the conclusion that, no matter what, there can only be one interrupt at a time. If more than one occurs, they must wait turns. And if the same one occurs before it finishes executing, then it is ignored. This cannot happen on my project, the serial IRQ must always execute (or very bad things will happen).

So, is interrupting an interrupt also possible? Or, perhaps, some form of threading?

11 Feb 2011

See /cookbook/MODSERIAL I did. It does all that already :)

[Edit: In answer to your question "So, is interrupting an interrupt also possible?" teh answer is no. But what you do is "chain" them. MODSERIAL does this. Basically you give MODSERIAL a callback function of it's own. When MODSERIAL starts executing it's ISR if the user has defined a callback then MODSERIAL calls it during it's ISR. So you are not interrupting an interrupt, you create a chain call backbacks. Because MODSERIAL inherits from Serial it registers the callback with Serial. You register a callback with MODSERIAL. So you have UART ISR -> Serial -> MODSERIAL -> User app. All from "within" the same original handler. Be aware however that when your application callback is made you are still in interrupt context, the UARTS original ISR in this case. See this post for more details on interrupt context.]

11 Feb 2011

"interrupting an interrupt" is possible, if the new interrupt has higher priority.

11 Feb 2011

Igor, yeah, I know. But is that really a subject you want beginners to dive into at this stage? Probably misleading of me to say that I admit but adding irq priorities to the mix when trying to a grasp of the basics of interrupts isn't something I'd heap on a beginner :) Fwiw, I am working on a followup to my article that goes into priorities, SVC and PendSV that shoudl take beginners to the next level :)

12 Feb 2011

Yes, thank you both for being so helpful. yes, I am a beginner, but I am also fervent and it is useful to have the whole picture before diving in.

Thank you for the article sir, a very informative piece. Yes, I've thought about the chain of callbacks, but I wasn't sure whether or not a new interrupt could start before the callback finished (what if a new packet comes in and overflows the buffer while the callback chain is still executing?). I haven't got to testing it yet (busy at work today), but it already seems like the answer is no. This is because we are still in the IRQ context right?

Unless there is a way to insert back into the user context, it looks like I really need to learn about priority. This is useful! Knowing where to start is half the battle.

Also, I realize it seems odd that I'm so concerned about the user function possibly taking too long since the serial protocol is very slow and the microcontroller is very fast, but, in the system I'm building, redundancy and reliability are paramount.

Could anyone suggest a good reference? Googling now. Again, the help is very appreciated.

12 Feb 2011

It would be useful knowing more about the incoming stream of data you are trying to handle. What baud, format, etc.

Did you read on MODSERIAL that you can deliniate "packets" with it? At the end of the page is an example of how to take a stream of incoming bytes and have MODSERIAL tell you when you have reach a certain point so you can process a byte stream as a char[] buffer. This puts the byte stream into user context, no need to be in interrupt context or multiple chained callbacks. MODSERIAL will call you back if you overrun it's RX input buffer.

At the end of the day it's more about the design of the overall system as opposed to the code and this is something you are obviously grappling with now which it great. But we'ed need to know more about your system to offer help beyond simple coding advice.

12 Feb 2011

ok, I'm going to study this for a while Andy, thank you very much for sharing it with me. This post is to be timely and answer you.

baud = 115200, 8 bits, no parity, 1 stop bit. The aim is to make an efficient and very reliable self-correcting serial interface for an inertial measurement unit (IMU) to be used in an interrupt-driven autopilot (that's a mouthfull). This will be onboard an autonomous UAV helicopter. Redundancy and reliability are very important, otherwise the system will literally crash and burn.

self-correction, resynchronizing, timing have all been sorted; only interrupt driving remains. The idea (in my mind so far) is to create an interrupt that is triggered when a whole packet of (accel, gyro, mag, orient) data is received, and attach code to it. This way, the process using the interface isn't forced to poll (while(! packetAvailable);).

13 Feb 2011

I'm guessing that this is a model and not the real thing with human lives on the line?

115200 is pretty quick. But the real key is how many bytes make a packet and at what frequency do the packets arrive?

When you are looking for straight speed then although MODSERIAL is pretty quick you can go faster by basically writing your own code closer to the hardware and not use generic abstracted libraries. In which case you'll need to start getting familiar with LPC17xx.h. Let me know if you want some help.

13 Feb 2011

Thomas, Do you have a link to your project? Are you modeling your code off of anything at http://diydrones.com/

I am working on a UAV quadcopter and have not chosen the micro yet. I was thinking of using the mbed.

13 Feb 2011

haven't researched enough to continue yet, but here's to give timely responses.

@Andy, yes I've been developing specifically for this hardware, but I've not learned enough let to go to the lower levels, I will be looking at LPC17xx.h. As for the packet size, it varies and there are ways to mix packets together (inject a packet of one data group into a continuous stream of packets of another). The frequency is always 100 hz, the largest packet is 100 bytes. UAV = UNMANNED aerial vehicle, hahaha I wouldn't go near anything built by me.

@Freddie: my experience with DIYdrones is those guys are a bit difficult to understand or to get straightforward answers from. This code is 100% built from scratch by a perfect noob. All I did was read the IMU spec and start experimenting with code. Had to Google some terms and such but it works just like I want it to (except for the function being discussed here). Right now the setup seems to be similar to MODSERIAL, but I haven't had time to look and make sure yet. Sure, here's a link to my project

Import program3DM-GX2

interface class for an inertial measurement unit that uses a serial protocol.

As far as using it for the autopilot, the jury is still out. It is an exceptional development platform though (although it can be a pain if your internet is dodgy). The only problem I foresee is the mbed seems to lack threading support. I could definitely see it being used if there was another microcontroller on board.

13 Feb 2011

Hi Thomas,

I think I can understand what you are talking about. Basically, you are not basically entirely sure if you have CPU power to meet your needs.

So I'll try and give you a real world example of what this little Mbed/LPC1768 can do. Here's an outline of one of my projects:-

SOWB: Artificial Satellite Tracker. Overview: Real time application that tracks satellites with a GOTO telescope mount. Attached to the mount is a low light PAL video camera. The video picture is "timestamped" with a GPS derived clock and the GOTO mount's pointing position.

Here's what the LPC1768 is doing in real time:-

Interrupts:-

  • USB_IRQn:
    • Used to communicate with an XBox360 gamepad, used as the applications main input. Reads the joysticks to move the GOTO mount and the buttons for the OSD menu system. USB is a Host.
  • EINT3_IRQn: (GPIO interrupts, normally associated with InterruptIn)
    • MAX7456 vertical sync IRQ, used to capture the time and update the video signal picture
    • GPS 1PPS, used to align the captured time to the microsecond. I only need 1ms accuracy so the latency isn't an issue for me, it's more than fast enough.
  • UART2_IRQn:
    • Used to communicate with the GOTO mount, 9600baud
  • UART1_IRQn:
    • Used to read the incoming GPS data stream, 9600buad
  • RIT_IRQn:
    • Provides system wide timing and sync.
  • DMA_IRQn:
    • Used to control the flow of data from a two flash devices and an SD card + the MAX7456 OSD
  • SSP0_IRQn:
    • SSP0 bus used for two flash devices and an SD card
  • SSP1_IRQn:
    • Used to handle the MAX7456 OSD

In additional to all that is runs a pretty heavy math library used to calculate the satellites position in real time so it can maintain the variable slew rates of the scope based on my GPS location and time information.

When not actively tracking it runs a list of "targets or interest" decideing when to intercept the next target. It also controls my video recorder to record "samples" which can be reduced to IOD format later. It can also reduce to IOD "instantly" by firing a trigger on the gamepad controller. I am currently adding support for IOD to be sent to my website so that IOD records appear on Twitter, Facebook and via an RSS feed.

And I have other plans for the project.

And I can say so far that the LPC1768 comsumes all this without hardly blinking an eye. I still have plenty of time available in my while(1) loop for it to cook soup!

However, I will say one thing. This project doesn't use Mbed libs at all. It's all in C rather than C++ and was written to directly drive all the peripherals. But that's because it was my first Mbed project. I'm not sure what the results would have been with C++ and libs but I'm sure it would have consumed it all too.

So you can see, it is a pretty good platform to dev off :)

16 Feb 2011

Ok, I think I can ask a more intelligent question now.

How can I find what the IPLs are for the mbed interrupts? (and how to change them)

my experiments indicate that all InterruptIn are on the same one, are the Serial IRQs on this level too? If they are, then I can just implement Andy's code and it will work exactly as I want.

Finally, back to the original purpose, is it possible to access the IRQ pins themselves instead of going through Serial or InterruptIn? My hunch is that it's in LPC17xx.h, which I haven't had time to look at yet (internet down for past 2 days).

I think, after Andy's presentation, my issue will not be performance. But still, I would like to complete this part for my own edification. As always, thank you for the help sir.

20 Feb 2011

ok, I've looked through MODSERIAL and LPC17xx...

if I use MODSERIAL code and cut out the things I don't need for the interface, it seems to me like I will have similar functionality to what I've got now but with more efficiency. But, this is no longer just a quest for a nice library, I need to learn and understand some things.

I see LPC17xx, it's inaccessible to my skill right now but seems to have what I want. I see interrupt numbers and structures, but how do I use them? It seems like I will gain access to the interrupt channels themselves, so I can write code like InterruptIn or Ticker. Is that right? Then, what is the syntax? How do I use the hardware without the abstractions?

Yes Andy, I would like some help if you will still offer it; writing code close to the hardware. Maybe you can help get my foot in the door with a basic example, or a good reference?

20 Feb 2011

Thomas, I'll write a notebook page later today to show some simple examples f how to write "closer to the hardware" that should get you started. I'll post here when it's ready for you to read.

20 Feb 2011

Here's the article. Hope it helps :)

22 Feb 2011

ok, excellent.

I have read the article and am now studying the concepts and syntax introduced. This will probably take a while. So far, looks like I need to learn about the NVIC.

Thank you very much for the help sir.

23 Feb 2011

are the priority levels in the same order as Igor's list? The order seems to make sense to me as Reset is on top.

Just asking so I don't go through the trouble of calling NVIC_GetPriority

Also: is there a place that documents all of the NVIC functions? Can't seem to find it anywhere...

23 Feb 2011

AFAIK all IRQs have the same priority (0) by default. So they're served in the order they happen. The system handlers (Reset, NMI, HardFault) have negative priorities, so they override IRQs.

For NVIC, check the generic Cortex-M3 documentation.

24 Feb 2011

Thank you, I found that one earlier, but SetVector wasn't there. This made me think there was a more complete documentation somewhere else.

Did some more digging, looks like an mbed defined function in cmsis_nvic.h right? Thank you for the information.

New question: not sure where to look for this one... looks like the four EINTx_IRQn are the ones I can use. Now, I might try and use one of the mbed libraries in addition to the interrupt code I am writing. How can I be sure that library will not change the address with another SetVector after I've already put it where I want? How can I be sure not to interfere with mbed's libraries?

Is there a document that describes which interrupts are used by which of mbed's libraries?

25 Feb 2011

I haven't seen any such docs so I did a quick check in the binaries. None of the vectors (besides Reset_Handler) are used by the library initially, all handlers are set dynamically using NVIC_SetVector():

  1. InterruptIn uses EINT3_IRQn.
  2. TimerEvent (used by Ticker and Timeout) uses TIMER3_IRQn.
  3. Serial uses UART0_IRQn to UART3_IRQn, depending on the port used.
26 Feb 2011

I really wish documents like this were accessible/existing... I think I will write a reference for these things when I'm done. Thank you Igor.

ok, I think almost all of the questions are taken care of that I need for this project. Only one left that I can think of:

How do I trigger an interrupt like EINT0?

I know this is done by applying logic levels to the pin on the processor, but how do I access the pin? It looks like there is some sort of multiplexer on the board called a "pin connect block" right? And it seems like I can configure it to allow me to trigger EINT0. Is this correct? I've been researching based on this forum post:

http://mbed.org/forum/mbed/topic/613/

for help on the subject. Is there a good reference for using the pin connect block, or a header file or something I can look at for things like "PINSEL_CFG_Type"?

26 Feb 2011

EINT0 doesn't look like it's available as it's used internally on the Mbed as TGT_SBL_ISP (p2.10 which puts the LPC1768 into ISP mode for flash programming).

What you want to be looking at is EINT3 which can be triggered by any configured port0 or port2 rising or falling edge. If you don't want to use Mbed's InterruptIn (which does this for you) the see this file which shows how to manually configure gpio interrupts.

[Edit: Thomas, it would pay if you spent sometime studing the LPC17xx manual as all the details are there]

06 Mar 2011

Whoa...

my mind has been totally blown 3 times since I last posted. I can't begin to thank you guys enough. You see... I am indeed a beginner, and I have searched for two years to have true understanding of how this stuff works. I search and ask people and they use language too advanced or the explanation is too watered down, and the needed docs are never easy to find, etc. I really wish I could have learned this way from the beginning: start from hardware, build from the ground up.

I now have most of the answers I'm looking for, and have been able to generate an interrupt on EINT1 using only software (so as not to interfere with InterruptIn). Now I'm interested in trying to do what mbed has done with InterruptIn: put several interrupts on the pin. It looks like it uses EINT3 because of the GPIO, and if I wanted to make a library exactly like InterruptIn, I would have to use that pin so that any of mbed's pins could be used to generate an interrupt. Am I on the right track?

As for the software interrupt. I'm going to try something like multiplexing several IRQs on EINT1 by keeping a table of vectors (not sure how I plan to do this just yet).

I really want to express my gratitude for you guys. You really helped a lot, if there's something I can do to help, just let me know.

(EDIT: It occurs to me: do the developers at mbed think it would be a good idea to add members to, say, Serial that will allow the user to change the priority? It doesn't seem too difficult to understand... The user could then at least change priority among serial ports, timers, external interrupts, etc.)

06 Apr 2011

Thomas - are you using CHR-6dm AHRS ? Anyway, your class 3DM-GX2 looks like made for it... May I use it also for my mbed-based multicopter ?

06 Apr 2011

Thomas -

I know this thread is a bit old, but rather than messing around with software interrupts, I think that you could do exactly what you want by using a real-time operating system. For example, scmRTOS is very easy to use and very lightweight. Igor posted a port to mBed last summer (thanks, Igor!), so grab his code and give it a try. His port assumes a 100 MHz clock rather than 96 MHz, but you can easily fix that.

The basic idea is exactly as you described in your original post: Your serial IRQ sets a semaphore when it has a full buffer, and a waiting task takes the semaphore and runs the processing. When it has completed the process, it waits for the next semaphore.

Here's the whole ball o' wax, except for your specific processing:

#include <mbed.h>
#include <scmRTOS.h>

typedef OS::process<OS::pr0, 1000> TProc1;
TProc1 Proc1;
OS::TEventFlag T1_Sem;

void serialIRQ (void) {
    ... Get serial data
    if (I_got_enough_data) {
         T1_Sem.SignalISR();    // Tell Task 1 to wake and process the data
    }
}

int main() {
    ... Initialize, etc. here (including setting up your serial IRQ)
    OS::Run();
}

template<> OS_PROCESS void TProc1::Exec() {
    for (;;) {
        T1_Sem.Wait (0);        // Wait forever for Semaphore from serialIRQ
        ... Process serial data
    }
}

void OS::SystemTimerUserHook() {  // By default, runs at 1 KHz.
}

void OS::IdleProcessUserHook() {
    __WFI();
}

I think that this is a simple and straightforward solution to your original post, and may well be exactly what you were asking for.

Regards,

- Gary

07 Apr 2011

@ Pawel - No multicopters!! Nah, just kidding of course you may use it. I'd imagine that the serial protocols are pretty similar for most IMUs. Let me know if you can suggest improvements.

@ Gary - looks interesting. I'm wrapped up in interrupts at the moment, but I will definitely check the RTOS out soon. Thank you for the heads-up.

08 Apr 2011

I've put some questions (asking for suggestions) in my earlier post in beginners branch, but it may fit better there, so some quote:

Pawel Stepien wrote:

Reading a lot in the meantime, gaining some practical experience with one of leading solution - mikrokopter.de (finally "Gone with the wind" - as you can read in mikrokopter.us forum)and looking at Arducopter project, I decided to go with own solution = mbed + chr6dm. After reading some topics here ("software interrupts" and some more about PPM, PWM reading + IMU reading and analysing) I would like to ask for some suggestions, how to best proceed with following:

  • 1 reading up to 6 RC inputs in PWM mode (0,5 - 1,5 ms pulse with 20ms period, every signal on separate pin and usually, but not always with different timing),
  • 2 reading serial data stream (40-60 bytes packets with 100-300Hz frequency, 115kbauds speed),
  • 3 recalculating both to have desired powers of 4, 6 (now) or up to 12 (in some future) speed controllers (ESC)
  • 4 sending that to - again PWM outputs.

For the beginning (up to 6 PWM controllers) it looks like mbed is sufficient to take and send all signal in parallel without the need for additional hardware (like suggested by Artur above). For the beginning I want to make it flying within as simple hardware setup as possible. Now, for software - as I don't feel comfortable with interrupt right now and hate to dig into bigger libraries (RTOS et c.), I would prefer to organise all soft within one main loop with min 100Hz repetition. Do you think it is possible ? (at least partially...) If not please suggest, how to organise interrupts to avoid problems with relatively long readings of serial packets (2) and often reading of (1). (3) and (4) will obviously go to main loop. What I mostly worry about are mistakes of reading 1 or 2 due to mutual interrupting and I hope experts here simply know how to arrange it best way...

What I am thinking about right now is to use interrupts to read PPM or PWM signals and do the rest, incl. serial stream in main loop, hoping that UART buffer will be sufficient if I check it often... Hope to write some soft for my testing and your review soon.

I want to keep all as simple (even primitive!) as possible ;)