Hey just asking for some opinions on the benefits of interrupts, or the benefits of polling. What are some specific advantages and disadvantages of each in FIRST Robotics, especially from a programming standpoint? Is there an advantage using one over the other when using timed code or limit switches?
Taken from: http://www.ni.com/white-paper/5423/en
"Polling vs. Interrupts
A key consideration when designing a LabVIEW real-time application is whether the system needs to concurrently perform its time-critical function along with other non-time-critical operations, such as local disk logging or communication with the host. This decision dictates the basic architecture of the real-time application.
Polling can be used for I/O mode in the case where the system has no non-time-critical responsibilities, or, more realistically, the system uses a state machine to schedule time-critical and non-time-critical tasks to operate sequentially. For most I/O drivers, polling mode is faster than interrupt mode.
Although slower than polling mode, interrupt mode is the more common for real-time applications as most applications contain a mix of time-critical and non-time-critical functions occurring simultaneously. Interrupts allow the I/O portion of a diagram to suspend its operation and allow other code, such as communication and logging, to run while the hardware is in the process of acquiring data. Once the hardware has finished its acquisition, it raises an interrupt to notify the software that it should resume with it’s time-critical I/O processing.
As noted in the table, we ran the single-point tests using the appropriate I/O mode to highlight the differences between these two modes of operation."
The benefit of interrupts is that you maximize the amount of time your program does “doing work” rather than checking whether something has happened, and you minimize the latency in responding to the event (if you only check for a sensor value every 100ms, and the value changes 1ms into your waiting period, you are wasting 99ms).
The downside of interrupts is that they can happen at any time…if you have “real-time” code such as a PID loop, being interrupted at the wrong time can cause bad things to happen. It also means you need to be careful that your interrupt handler doesn’t cause data hazards…it shouldn’t mess with any critical state that could cause your program to go off the rails when it returns to normal execution. Good interrupt handlers should also be very fast.
As a general rule of thumb, in FRC I usually poll my data (since it is simpler)…unless polling is too slow. For example, on the sensor we used to detect a ball ready to fire, polling in the main teleoperated loop (nominally 50Hz) is just fine. The ball moved at about 3 feet per second through our elevator, so if we’re 1/50s late in responding to the sensor, the ball has only moved an additional 3/4"…not a big deal since the shooter wheels are several more inches away. When we used encoders with the old IFI control system, we used interrupts because missing a count would be disastrous (the cRIO does this with its FPGA, so now we simply poll the FPGA for the encoder count when we need it).
Excellent question. A good way to introduce someone new to the interrupt vs. polling concept is to offer an analogy. Bear with me; it’s a bit long. Suppose you were at home one Saturday morning all alone. You had plans to go somewhere with your friends later that day so you wanted to spend the morning getting some of your homework done. Before your parents left they gave you some household chores that they wanted you to do before you were allowed to leave with your friends. Specifically, they wanted you to do several loads of laundry, put the dishes from breakfast in the dishwasher, run the dishwasher and put the dishes away after they’re clean. They were also expecting a package requiring a delivery signature to be delivered that morning and they needed you to sign for it when it arrived.
What would your morning look like if you used interrupts? You might begin by putting the dishes in the dishwasher, add some detergent and turn it on. The dishwasher doesn’t have a buzzer to indicate when the clean/dry cycle is complete so, knowing it takes 2 hours for a cycle you set the timer on the stove for 120 minutes. Then you might put the first load of laundry in the washing machine and finally settle into some homework. When you hear the washing machine’s buzzer you’d take a break from your home work, put the washed clothes in the dryer, and a new load in the washer then turn them both on and return to your homework. When the timer alerts you that the dishes are clean you get up again and put away the dishes. Finally, when you hear the doorbell, you again get up from doing your homework a third time, sign for the package and quickly return to your homework.
Now, suppose that that for some reason you were incapable of hearing any buzzers, bells, or knocks on the door. Your might plan your morning something like this: The delivery person can be reasonably be expected to wait a maximum of about 30 seconds. Therefore, I’ll need to go to the door every 30 seconds to see whether a delivery person is standing there. If I do this any less often it is possible that I’ll miss the delivery and that’s not acceptable.
Loading the dishwasher takes more than 30 seconds, so I will have to sub-divide the chore into smaller sub-chores each taking no longer than 30 seconds. I will check the door between sub-chores. After 240 delivery checks I’ll put the away the clean dishes, dividing this into less than 30-second sub-chores and continue checking the door for deliveries. In the mean time I’ll get started on the laundry.
As with the dish washing chore, from the time I collect clothes from hampers until I turn on the washing machine I will have to check the door, so I need to break this chore up into sub-chores that are less than 30 seconds each and check the door between sub-chores. A load of laundry takes about 40 minutes so after 80 delivery checks I’ll put the clothes in the dryer and more clothes in the washer, dividing this into less than 30-second sub-chores and continue checking the door for deliveries. In the mean time I’ll do my homework in 30 second segments and continue checking the door every 30 seconds.
Should the 240th delivery check after starting the dishwasher coincide with some later chore of washing more clothes, I’ll delay the start of washing more clothes until I’m done putting the dishes away, since the damp clothes from the previous load can sit for a little while in the washer and the new laundry load can wait a little while to be washed. However, under no circumstances will I delay checking the door for a deliver since that might result in my missing the delivery.
Oh yeah, if the package delivery arrives before the dishes and laundry are completed, then I can re-calculate the timing of my sub-chores and of the time blocks I allot myself for studying.
Sounds kind of crazy, doesn’t it? Yet this is somewhat analogous to the software for the driver-controlled competition segments of many teams.
Although a software design base on interrupts is conceptually easier to think about and describe, it does require a bit deeper programming skill. The level of necessary additional skill may vary among the FRC programming choices: LabVIEW, C++ and Java. However, the basic point is this: when an interrupt occurs, whatever the computer had been doing is suspended and the “interrupt service routine”, or ISR, executes instead. The code that had been interrupted by the ISR resumes when the ISR function has finished. The ISR is just a function that you have somehow registered with the operating system (details will vary) to execute upon some condition being met, such as a switch being closed (or opened). Here, there are several things to consider.
First, in general, switches don’t open and close cleanly. They bounce a lot between the open and closed states before settling down to one or the other. The bouncing happens real fast and you usually don’t notice it but with interrupts it can register as many switch closures. I won’t go into the details here as the details can vary (edge vs. level triggering; some switches might have “debounce” circuitry built in (most don’t)) but if you’re programming at a low level, you’ll need to consider whether you need a debounce strategy.
Second, the longer the ISR takes to do its thing, the longer you have suspended whatever the robot had been doing. Suppose the main code (by main code, I simply mean whatever code had been running that was interrupted by the ISR) had just turned on the drive motors at full power and was supposed to wait for 1 second and then turn the motors off. Also suppose that an interrupt occurred during that one second wait and that your ISR takes 5 seconds to finish (just for illustration purposes; 5 seconds is an exceedingly long time for an ISR). You now have an out-of-control robot for 5 seconds. In general, make your ISRs short and fast (very few milliseconds), and design them to execute in a reasonably predictable amount of time.
Third, remember that the main code can’t predict when your ISR will run. It could be between any two lines of code in the main code. In fact, it could be within any single line of code. If your ISR starts monkeying carelessly with variables used by your main code, you could introduce errors into your main code while it is running, or it could even fail altogether. Making your code safe from corruption by an ISR might be easy or difficult depending upon the situation and the code, but it takes a little experience to get the hang of it.
This could be considered off topicish, but does anybody have any documentation of Java ISRs? There is some stuff in the WPILibJ javadocs, but not much more than method signatures.
Thanks everyone for the help. That makes a lot more sense now. I joined FRC this year but I know our bot last year had a problem with registering our basketballs to plunge into the shooting position because we were just checking every sub-second to see if the ball was in the right place to use the plunger, instead of using interrupts, which I know they didn’t figure out until competition.
Thanks to all who answered Adam’s question! I’d like to ask a follow on if I could -
I think we understand the difference between Polling and Interrupt, but HOW are these two methods manifested with the program?? We lost our programming mentor and the previous lead was a senior, so we are really having a tough time getting up to speed with troubleshooting last years bot and developing new code.
Adam’s initial question comes from assumptions we made in a team meeting about why a motor with a limit switch sometimes behaves properly, sometimes it ignores the limit witch and keeps going. This motor drives a plunger that pushes a ball into the spinner shooter wheels. Sometimes it makes one cycle and stops when the limit switch is activated, sometimes it keeps going on its cycle and doesn’t react to the limit switch. We want to try slowing down the cycle to see that makes a difference, and also want to look at the time the switch is polled. Where would that be within the program??
Thanks again for the help!
Thanks to all who answered Adam’s question! I’d like to ask a follow on if I could -
I think we understand the difference between Polling and Interrupt, but HOW are these two methods manifested with the program?? We lost our programming mentor and the previous lead was a senior, so we are really having a tough time getting up to speed with troubleshooting last years bot and developing new code.
Adam’s initial question comes from assumptions we made in a team meeting about why a motor with a limit switch sometimes behaves properly, sometimes it ignores the limit witch and keeps going. This motor drives a plunger that pushes a ball into the spinner shooter wheels. Sometimes it makes one cycle and stops when the limit switch is activated, sometimes it keeps going on its cycle and doesn’t react to the limit switch. We want to try slowing down the cycle to see that makes a difference, and also want to look at the time the switch is polled. Where would that be within the program??
Thanks again for the help!
As far as I am aware, there is no interrupt system directly available to programmers in FRC. Interrupts are typically a very low-level construct used by firmware, drivers, or the operating system. If you’re working primarily through WPILib, there may be some interrupts making things happen behind the scenes, if only to facilitate FPGA functionality and threading, but almost everything else is polling-based.
For your problem, to find the offending code, I recommend doing a ctrl+shift+F search of your whole project to see when the limit switch is read. From your description of the bug, it sounds like the limit switch is either being checked at the wrong times or not being checked frequently enough. It may only be checking it once, which would explain why it could continue while ignoring the limit switch.
You can get an interrupt like behavior if you use the new C++ or Java Command-based template in the WPILib.
This program structure handles all the complicated timing, scheduling and notifications. You simply set up input-to-command relationships. When a button, joystick, limit switch, or other input device changes state, a scheduler automatically calls your command. For example, we had a limit switch for the elevator that when a ball came in and depressed the switch our Elevator command was called. This command turned on the elevator motor until the recently ingested ball depressed another limit switch which signaled it to stop. Essentially the WPILib functions are handling the input state polling for you.
Each command is associated with various subsystems (elevator, shooter, drivetrain, etc) so the scheduler can determine if it needs to stop a currently executing command before starting another command that shares the same subsystem. You can set up sequential groups of commands to force things to happen in a predictable order or parallel groups of commands for things to happen simultaneously.
There are also teleop and autonomous methods called each robot iteration where you can place polling functions.
It’s a little more work to setup up at first, but I found it to be quite elegant and easy to teach kids once we got going. It also distributes the code well so multiple people can work on the code base without stepping on each other’s toes.
If you are interested, the best place to start is the WPILib Robot Programming Cookbook:
While similar, it may be worth distinguishing interrupts and events. The command-based templates is dispatching events and not implementing interrupt handlers.
WPI does expose an interrupt or IRQ class, at least in LabVIEW, I’m not sure about Java or C++. These are raised by the FPGA based on analog triggers or digital state. More documentation on the detail is here.
Greg McKaskle
Greg is correct on this point and I meant to imply that the command-based template provides a high-level “interrupt like behavior” to FRC programmers (post updated). Under the hood, the scheduler uses polling to determine if the input button state has changed. In the limit switch example I gave in my previous post, we created a custom “DigitalIOButton” class that used a DigitalInput class to read the current state of the limit switch from the FPGA.
I thought I’d follow up to my original post, where I came off sounding very binary, suggesting that solutions can either use polling or interrupts but not discussing any other hybrid approaches. In fact, a hybrid approach is probably a very good solution.
To go back to my analogy about doing homework while also completing Saturday morning chores, there are other options. You could ask your little brother to check the door for a delivery every 30 seconds and tell you when the delivery has arrived or – even better – just accept the package and sign for it himself without ever having to bother you. Taking this approach, your dishwashing and laundry chores will be greatly simplified. You’ll also be able to apply yourself to your homework much more productively. Getting another person to “poll” for you rather than doing it yourself is much like creating a software task that runs independently of the main code and handles the polling.
If the task has no need to communicate (i.e., share data) with your main code then things are pretty simple. However, when the task needs to send information to the main code (or to any other task(s) that you may have created), or vice-versa, then you need to worry about the potential for the data to become corrupted in the hand-off. These can be really nasty bugs to hunt down because sometimes everything works perfectly and sometimes the code seems to go berserk, where “sometimes” could mean anything from rarely to occasionally to usually to almost always.
There are several different approaches that you can take in FRC for multi-tasking, which is one of a handful of umbrella terms encapsulating a number of related issues. First, beginning in the 2012 season, the WPILib was enhanced to include some new classes that permit you you define sub-systems and commands. As has already been mentioned, the “WPILib Robot
Programming Cookbook” can help instruct you on how to design and implement a software architecture consisting of a set of cooperating subsystems. This document can be downloaded from the http://firstforge.wpi.edu/sf/sfmain/do/home website (go to the WPILib project).
Second, you can create your own tasks, again using the WPILib. Refer to the “WPI Robotics Library
User’s Guide” PDF document, available from the same place as the cookbook. These tasks can do anything you want. You could, for example, create a task that periodically polls an input for changes in values. You are essentially checking the value of an input inside an infinite loop. Just be sure to insert a wait instruction; otherwise this task will suck up a lot of the CRIO CPU and starve other tasks of CPU time. You could also (or instead) create a task and schedule it to run periodically. This should have the same effect as the infinite loop approach, but is arguably cleaner since the task just contains the code to check the input, does away with the infinite loop and instead lets an operating system timer repeatedly call the task at a regular time interval.
Third, you can define and register interrupt handlers for analog outputs and for digital inputs and outputs. I have no experience with these but you can check the WPILib API (I’m looking at the C++ API available at http://www.virtualroadside.com/WPILib/index.html).
Fourth, just as you can create WPI tasks (above), you can also go straight to the VxWorks operating system (applies to C++; not sure about Java) and create VxWorks OS tasks. Consult the “WindRiver VxWorks Kernel Programmer’s Guide” for details on how to setup and use such tasks. VxWorks documentation can be difficult to come by but I have seen it on the web. Failing that, perhaps if you explain that you are a FRC team programmer, you might be able to email the good folks at WindRiver’s support web site and see whether they might help you out with some VxWorks documents for the OS version applicable to FRC.
Finally, you might also be able to write code that connects to interrupts at the the operating system level. I’m not sure about this, as this might be “firewalled” (for lack of a better term) by the way the WPI CRIO image routes inputs and outputs through the FPGA. I’m sure there are a few people out there that can answer this.
Reading all the documentation and blog posts about how to program your robot is daunting. And it can often be confusing trying to see the big picture when people are talking online about so many various techniques. Sometimes you just need explained that the online cacophony represents a witch’s brew of alternative solutions rather than pieces of the one-and-only way to do it.