Quote:
|
This one says that the function InterruptHandlerLow() should be compiled and installed as the low-priority interrupt handler (interruptlow),
|
Actually, the following different pragma does that (installs the routine into the low-priority interrupt vector)
Code:
#pragma code InterruptVectorLow = LOW_INT_VECTOR
void InterruptVectorLow (void)
{
_asm
goto InterruptHandlerLow /*jump to interrupt routine*/
_endasm
}
In this case the pragma indicates that the following information will include executable instructions ("code") and the routine/label InteruptVectorLow should be placed in the low-priority interrupt. Well, actually it doesn't say that either. It says that it would like the following code to be known as a code section named "InterruptVectorLow" and that this section of code should start at the absolute address of LOW_INT_VECTOR which is defined in the ifi_defines.h as
Code:
#define LOW_INT_VECTOR 0x818
...but what it does is insert the InterruptVectorLow "routine" into code memory starting at absolute address x0818. Each named code section must have a unique name, and by convention the section is named after the routine in it by the programmer just to make it easy to keep things understandable.
This might seem weird as the hardware vector addresses for the PIC18F are:
Code:
0000 - reset
0008 - high priority interrupt vector
0018 - low priority interrupt vector
But the first x800 addresses contain a bootloader (my guess) and is a protected code block (also my guess) so that it isn't accidently overwritten. That means we cannot directly stuff our own interrupt routines at the real PIC vector addresses. Instead, the x0008 will contain a jump to 0x0808 and x0018 will contain a jump to x0818 to revector the interrupts outside of the protected code area so we can insert our interrupt routines there.
See section 2.8 of the compiler user's guide. Pragmas tend to be hints to the compiler and linker as to the programmer's intent. A #pragma code indicates that the following contains executable code, vs a #pragma romdata which would indicate the following contains variables and data.
This section of the manual is a little misleading. The #pragma interrruptlow is used to declare a low priority interrupt handler, but what it REALLY does is change how the compiler works for the named routine. It inserts a context save at the beginning of the routine, a context restore at the end of the routine, and changes return not to a PIC18F RETURN instruction as it would for "normal" functions, but a RETFIE (return and enable interrupts).
Code:
025E4 InterruptHandlerHigh
; context save, minimum by this version of the compiler
; by default is STATUS, BSR (bank select register), and W register
02E4 CFD8 MOVFF STATUS, PREINC1
025E6 FFE4 NOP
025E8 CFE0 MOVFF BSR, PREINC1
025EA FFE4 NOP
025EC 6EE4 MOVWF PREINC1, ACCESS
:
.
; ok, this is the end of the handler, restore context of W, BSR,
; and then STATUS before returning to user code
0261A 50E5 MOVF POSTDEC1, W, ACCESS
0261C CFE5 MOVFF POSTDEC1, BSR
0261E FFE0 NOP
02620 CFE5 MOVFF POSTDEC1, STATUS
02622 FFD8 NOP
02624 0010 RETFIE 0
If the interrupt routine uses other ram or special registers that are considered compiler managed resources, then they too have to be saved. The math and tmp sections are used by the compiler for math functions and as temporary working registers. Since a user may be in the middle of using these, then if the interrupt routine uses them they also need to be saved. Extending the pragma to list these will have the compiler add more code to save these ram sections as part of the context save/restore code it generates. Looking in the map file, you can see what all is included in these sections.
Quote:
MATH_DATA udata 0x000017 data 0x000014
.tmpdata udata 0x00002b data 0x00000c
AARGB7 0x000017 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
REMB3 0x000017 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB6 0x000018 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
REMB2 0x000018 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB5 0x000019 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
REMB1 0x000019 data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB4 0x00001a data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
REMB0 0x00001a data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB3 0x00001b data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB2 0x00001c data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB1 0x00001d data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AARGB0 0x00001e data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
AEXP 0x00001f data extern C:\MCC18\SRC\TRADIT~1\MATH\aarg.asm
BARGB3 0x000020 data extern C:\MCC18\SRC\TRADIT~1\MATH\barg.asm
BARGB2 0x000021 data extern C:\MCC18\SRC\TRADIT~1\MATH\barg.asm
BARGB1 0x000022 data extern C:\MCC18\SRC\TRADIT~1\MATH\barg.asm
BARGB0 0x000023 data extern C:\MCC18\SRC\TRADIT~1\MATH\barg.asm
BEXP 0x000024 data extern C:\MCC18\SRC\TRADIT~1\MATH\barg.asm
SIGN 0x000025 data extern C:\MCC18\SRC\TRADIT~1\MATH\cmath18.asm
FPFLAGS 0x000026 data extern C:\MCC18\SRC\TRADIT~1\MATH\cmath18.asm
FPFLAGSbits 0x000026 data extern C:\MCC18\SRC\TRADIT~1\MATH\cmath18.asm
TEMPB3 0x000027 data extern C:\MCC18\SRC\TRADIT~1\MATH\temparg.asm
TEMPB2 0x000028 data extern C:\MCC18\SRC\TRADIT~1\MATH\temparg.asm
TEMPB1 0x000029 data extern C:\MCC18\SRC\TRADIT~1\MATH\temparg.asm
TEMPB0 0x00002a data extern C:\MCC18\SRC\TRADIT~1\MATH\temparg.asm
TEMP 0x00002a data extern C:\MCC18\SRC\TRADIT~1\MATH\temparg.asm
__tmp_0 0x00002b data static C:\eclipseprojects\EasyCRuntime\OI.c
__tmp_0 0x00002b data static C:\eclipseprojects\EasyCRuntime\InputOutput.c
__tmp_0 0x00002b data static C:\mcc18\src\TRADIT~1\pmc\ADC\adcread.c
__tmp_0 0x00002b data static C:\eclipseprojects\EasyCRuntime\Motors.c
__tmp_0 0x00002b data static C:\eclipseprojects\EasyCRuntime\WPILib.c
__tmp_0 0x00002b data static C:\eclipseprojects\EasyCRuntime\clock.c
__tmp_0 0x00002b data static C:\mcc18\src\TRADIT~1\stdclib\printf.c
__tmp_0 0x00002b data static C:\mcc18\src\TRADIT~1\stdclib\vfprintf.c
__tmp_0 0x00002b data static C:\mcc18\src\TRADIT~1\stdclib\putc.c
__tmp_0 0x00002b data static C:\CPrj\2007\FrcCode_2007_8722\user_routines.c
__tmp_0 0x00002b data static C:\CPrj\2007\FrcCode_2007_8722\user_SerialDrv.c
__tmp_0 0x00002b data static C:\CPrj\2007\FrcCode_2007_8722\main.c
__tmp_0 0x00002b data static C:\CPrj\2007\FrcCode_2007_8722\mylib_isrclock.c
|
The PRODH/PRODL are where the multiplier product is stuffed. An 8bit x 8bit multiplication yields a 16 bit project, hence the two 8 bit registers. The compiler also uses these registers for various other things, including return values from int fuctions() (the W register is used for char return values from functions).
This is just one facet of interrupt routines that make it so interesting to make bullet proof interrupt handlers that always works. Other registers that could/might need to be saved are the table registers, FSRs, and others that are used by the compiler for various purposes including array computions and accesses. The safest thing to do is add a long list of things to save... but that slows down the interrupt routine by increasing the size of the context that needs to be saved... but it is the safest.
To save a minimum context requires the programmer to analyze the interrupt routine code generated to see which registers and ram are used and make sure they are in the saved context.
There are lots of games that can be played to reduce context save time. One way is to save a minimum context for the main interrupt routine and then save a different/fuller context for each specific handler.
Code:
#pragma interruptlow __InterruptHandlerLow save=PROD /* You may want to save additional symbols. */
void __InterruptHandlerLow ()
{
unsigned char int_byte;
if (INTCON3bits.INT2IF && INTCON3bits.INT2IE) /* The INT2 pin is RB2/DIG I/O 1. */
{
INTCON3bits.INT2IF = 0;
HandleInt2();
}
else if (INTCON3bits.INT3IF && INTCON3bits.INT3IE) /* The INT3 pin is RB3/DIG I/O 2. */
{
INTCON3bits.INT3IF = 0;
HandleInt3();
}
else if (INTCONbits.RBIF && INTCONbits.RBIE) /* DIG I/O 3-6 (RB4, RB5, RB6, or RB7) changed. */
{
int_byte = PORTB; /* You must read or write to PORTB */
INTCONbits.RBIF = 0; /* and clear the interrupt flag */
} /* to clear the interrupt condition. */
else
{
CheckUartInts(); /* For Dynamic Debug Tool or buffered printf features. */
}
}
#pragma code
#pragma interruptlow HandlerInt2 save=PROD,section("MATH_DATA"),section(".tmpdata") /* You may want to save additional symbols. */
void HandlerInt2() {return;}
void HandlerInt3() {return;}
In the code above, the main interrupt handler just saves W,BSR, and STATUS. If HandlerInt2() is called, it also save PROD, math and tmp sections. But if HandlerInt3() is called, then no additional context is saved. [NOTE: there is a minor problem doing something like above, but I'll leave figuring out what as an exercise to the user :-)]
As I said, lots of different games can be played with the pragmas but all require programmers to understand what they are asking the compiler to do and why. Its one of the reasons that the save list tends to grow to fairly extensive lengths.