![]() |
Interrupts, Interrupts, and more Interrupts!
1 Attachment(s)
There seems to be a lot of questions as well as misinformation about PIC18F interrupts and the robot code/devices. I thought a separate thread to ask more general questions would be worthwhile.
The purpose of this thread is share technical information at a detail level that most team members and even programmers won't care about. That's because building a solid device interrupt service routine only needs to be crafted once and then used multiple times. So if you're one of those crazies that want to understand how to craft interrupt service routines then welcome and have at it! The are a couple documents that serve well for information: DS31008A 33023a.pdf PICmicro™Mid-Range MCU Family Reference Manual DS39646B 39646b.pdf PIC18F8722 Family Data Sheet DS00097D MPLAB® C18 C COMPILER USER’S GUIDE These documents, and more, are available from the Microchip website. For example, there are chip errata sheets that explain specific flaws that appear in specific chip models and how to get around them. There may be newer versions of the above, but these are the ones I currently have downloaded and use for references. To answer a question, PLEASE DONT GUESS! Look it up and post the answer and reference if there is one. For example, someone asked about interrupt latency in another thread. The chip latency is defined in DS31008A above as: Quote:
Code:
#pragma code InterruptVectorReset = LOW_INT_VECTORIf curious, you an see some of this information in the attached image in MPLAB's View->Program Memory window. |
Re: Interrupts, Interrupts, and more Interrupts!
Will too many interupts will cause the red-light-of-death?
------------------------------------------------------- It is possible, but is just as easily avoided. One of the other common misconceptions is that having too high of an interrupt load will starve off user code from executing and result in the red-light-of-death. This is true of the standard IFI code framework but not true of either MBLAB/WPILIB or EasyC environments. Why? Because in these frameworks the Getdata/Putdata calls are performed at interrupt level and not at user level... so as long an one interrupt service routine doesn't take forever to run, the appropriate get/put data will occur and prevent bad things from happening by doing all this in the background. Its also why the main loops look kinda of funky in that they loop forever and don't care how long they take to run. But I'll tackle that one in another reply. Code:
Anyway, doing these operations in the background at interrupt time frees the user code from having to worry about taking too long between updates causing the red-light-of-death. |
Re: Interrupts, Interrupts, and more Interrupts!
How can the autonomous routine loop and loop and loop and never
(apparently) return and still work under MPLAB/WPILIB or EasyC? ---------------------------------------------------------------- I've done something similar before. Again, in the system clock routine check to determine when autonomous mode is ending (I was in autonomous last clock tick, but this time I'm not -- so autonomous period just ended). When you detect this, reset the variable stack managed by the C compiler and reset the hardware stack location (STKPTR) to point to the return to main(). For example, in the standard IFI code if: _entry (reset) jumps to _startup _startup then calls main() main() then calls Autonomous() Code:
main()Code:
0000Yeah, we could do the same thing between iterations through Autonomous(), but this way again we don't really care how long the code path through autonomous is, it will get yanked immediately into the Operator() code when autonomous ends. |
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
If I hadn't already decided that easyC wasn't for me, this alone would make me worry about what other "gotchas" were lurking behind the pretty face. |
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
|
Re: Interrupts, Interrupts, and more Interrupts!
I agree. If you are hiding when txdata gets sent, then you hide the whole structure behind SendPWM() and similar calls that interface to it. That way your not tempted to see it as a handy temporary data storage location.
Just a paradigm shift of how you think about the data structure or not since it gets hidden away. |
Re: Interrupts, Interrupts, and more Interrupts!
Bud,
Since you can do a better job of explaining these things than I can, how about you discuss why C18 3.0+ does a much better job of handling interrrupts than 2.4 does? -Kevin |
Re: Interrupts, Interrupts, and more Interrupts!
In what way? The ability to use pragma to avoid some context save/restore? Or something else?
|
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
-Kevin |
Re: Interrupts, Interrupts, and more Interrupts!
That was the specific feature I was thinking about. I *don't* like the fact that all of the compiler managed resources are saved unless you do a nosave to exclude them... kinda of the opposite of 2.4 where you had to specify them in order to save them. I also really wish there was a "use a return instead of a RETIE" option - that would really help. But I do like that a different .tmpdata section is created for the interrupt service routines.
I'll give this some thought, because it gets confusing since it deals with SFRs of the PIC and how the compiler uses them (and when). Honestly, other than reading the docs I haven't used the 3.0+ compiler yet. I'm hoping to get it installed on my laptop this weekend. I have played a lot of "games" with context in V2.4. One is to setup the main interrupt service routine with minimal context save: just WREG, BSR, STATUS, FSR0 and PRODL/H. The FSR0 is used by the compiler to hold computed ram addresses - like for data_array[var]. The PRODH/L registers can be used for bit shifts, some data array index calculations and some other general stuff. This covers 80%-90% of the interrupt dispatcher and drivers. For any driver that needs more than these, I just declare that driver as an interrupt service routine and tell it what additional data or sections or h/w registers to save. But the downside was it would always re-save WREG, BSR, and STATUS on me. With V3.0 I can now avoid that resave. And yeah, I had to play some more games to get this to work (the first RETIE turns interrupts back on). But it can be a big win in that the majority of light weight drivers don't pay the price of saving a large context that only a few certain drivers needed. The new .tmpdata actually makes this strategy no longer as easy to do (there are always ways - just not as easy anymore). Anyway, context save/restores is a longish post but will try. I'll start off with what the C18 manual says about latency. Latency more than just speed is what matters in real-time systems. The latency of an interrupt service routine is broken down into the following chunks: 1. Processor redirecting execution to the interrupt vector (.3-.4usec) 2. Interrupt vector execution (.4 usec) 3. Interrupt service routine preamble (context save, variable) 4. Interrupt dispatch (locate the interrupt to be serviced) (~.2-.3 usec per interrupt to be checked) 5. "Driver" for that interrupt (varies, but usually <10usec) 6. Interrupt service routine epilogue (context save) 7. RETIE, return and turn interrupts back on More later. I'll need to find some specific examples of C lines using the different compiler managed resources to show why they need to be saved. Sometimes its not obvious what gets used. |
Re: Interrupts, Interrupts, and more Interrupts!
2 Attachment(s)
How do functions return values and do I need to be aware of them in interrupt routines?
----------------------------------------------------------------------------------------- From the C18 compiler manual: Code:
3.2.3 Return Valuescall any routine, for example, that returns a long then you now need to save/restore the MATH_DATA section. If the routine is one of your own, then creating one just for use by the interrupt routine that passes back the long via a global would eliminate the need to save the MATH_DATA section. What are compiler managed resources and do I save them in interrupt routines? ----------------------------------------------------------------------------- There is only one register within the PIC's CPU - the working register: WREG. Almost all instructions need to move data into the WREG before operating on it. Since there are intermediate results that may need to be calculated within a C source line before arriving at the final results for the user, the compiler uses a number of different resources within the chip. The compiler "owns" these resources and uses them as needed - both within user code and interrupt routines. The result is the user could be in the middle of computing the array index for: data = sample[ndx]; when an interrupt occurs. This line could be using the FSR0 register, PROD register, and .tmpdata section. The interrupt service routine if it too uses any of the compiler managed resources MUST save them. Otherwise very bad things happen. Bad things would not necessarily happen immediately or all the time mind you... but they would eventually happen. If your interrupt routine "forgot" to save PCLAT for example and used a function callback based in ram (*callback_function)(). Then most of the time the robot would run fine as PCLAT isn't used too often by the compiler and you might have only one other place in your user code that has C code that results in the compiler using this register. Your robot will run most of the time just fine but occasionally flakes out and wierd stuff happens. Thats because eventually the code associated with that one line of C in user code was in the middle of processing, had loaded part of the PCLAT register set and then got interrupted. When the processor returned back to continue running the user code, the PCLAT no longer had YOUR data, it had some left over junk from the interrupt service routine. Weird stuff follows. So, what is the full list of compiler manager resources? From the C18 manual; Code:
3.4 COMPILER-MANAGED RESOURCESHow do I know which of these registers I've used in my interrupt routine or device driver? ------------------------------------------------------------------------------------------ Good question. Some are pretty obvious, others not so obvious. I think that is why the V3.0+ compiler now saves them ALL unless you exclude them with the nosave= option. For example, if you call a routine that returns a long anywhere in the interrupt service routine or from any of the drivers it calls, you just used the MATH_DATA section and need to save it! Called an routine that returns an unsigned int? Add PRODH/L to the context save list. Did any array calculations using a variable array index like data[var] = 0? You just used FSR0 and possibly PROD which is often used to calculate the array offset. Did any array manipulation like in the adc driver? Add the .tmpdata section as it is used for intermediate results. Add these to the list. And so on. The easiest way to figure out whats been used is to open up the View->Program Memory window in MPLAB. Search for your interrupt routine that you are interested in and look at the assembler -- it lists most of the symbols in the code listing. See any __tmp_0? You just used .tmpdata section. See the attached figures for an example. This is the only sure fire method I know of to figure out definitively what got used. This may seem extreme, but the driver code of MOST drivers is fairly simple and straight forward. You only end up looking at a small chunk of code. Don't want to do that? Enlist some help from a mentor - or just break down and save everything as a last resort. This last resort works particularly well if the 'bot is flaking out and you suspect the interrupt service routine. If saving all the compiler managed resources still doesn't fix the problem, then there is likely a logic bump like non-atomic access to a multibyte variable within user code. How much time does it take to save context anyway? -------------------------------------------------- Saving a register takes .4usec. There is a 2-cycle MOVFF <SFR>, PREINC1 instruction that pushes the specified special funcion register onto the stack (FSR1 is ths stack). A similar instruction is required in the context restore part of the routine. Not much to worry about here. Saving a data section takes a lot longer. You can find out how big a data section is and which variables are in it by looking at the .map file from your project. The .tmpdata is 12 bytes long and MATH_DATA is 20 bytes long. The compiler generates a loop to save/restore data sections. Saving .tmpdata takes 15.6usec and 25.2usec for MATH_DATA. Since I've an interrupt service routine that takes a maximum of ~12-15usec to run with a majority of device drivers (thats start to finish), adding an additional 40.8usec to save/restore sections really hurts! With the V3.0+ compiler, a separate .tmpdata section is created for each routine declared as an interrupt. The section name is <routine_name>_tmp. That just saved us 15.6usec - our interrupt service routine just had a dramatic increase in speed! If I'm running a couple encoders, adc, serial port and have say 5000 interrupts per second, I just returned 78ms/second of processor time to be used by the user. Almost 10% more available time! Any routine that the interrupt service routine calls needs to use the same temp data section as the interrupt service routine. This requires a bit of coordination between the interrupt dispatcher and driver writers. If you didn't do this, then the driver would resort to using the .tmpdata section ... which is being used by user code ... and we'd have to be saving .tmpdata again. I'm using a complicated driver that makes lots of calls and I need to save all compiler managed resources. Is there a way around this? ------------------------------------------------------------------------------ If you have this question or even thought it, then you want the answer to be "yes". The answer is yes & no. You definitely need to save the full context when running this driver, but the main interrupt dispatch routine and other drivers don't necessarily need to save all context. There are several ways of doing this.... and it sounds like a great exercise for programmers to do some brainstorming! I've done this at least three different ways - none of them I really like, but the last one I came up with is the least offensive. It also has the advantage of being faster than the code the compiler generates. I just wish I could convince the compiler to generate a RETURN instead of a RETIE in some cases, it would so simplify things! |
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
All rx/tx data fields are byte sized and therefore access to each field is atomic by default. |
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
|
Re: Interrupts, Interrupts, and more Interrupts!
New question related to interrupts. I use EasyCv2 for Vex and would like to know how to read the values of the 6 interrupt ports (similar to the way that you use GetDigitalInput(I) to read one of the digital input ports). Basically, I want to create something similar to the FVC/FTC competition template but not be limited by the 20 second/2 minute demarcations of the template the way it currently exists.
|
Re: Interrupts, Interrupts, and more Interrupts!
Quote:
|
| All times are GMT -5. The time now is 23:46. |
Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi