I need to implement a Timer Terminal Count Interrupt. Since it is easy to write the code in C, I am creating the ISR in C. I need suggestions on which is a better method to call the ISR.
The mapping is already done in boot.asm, should I overwrite it with the C function or should I call the C function from the Timer8_ISR? Also, should I use ljmp or LCALL to call the C function.
You can write the ISR in C or use the placeholder ISR f() call created
by designer. Some references -
Re: How do I implement an interrupt service routine in C on the PSoC1?
C interrupt service routines can be implemented in the PSoC1 by making an interrupt declaration in C and adding an ljmp to an assembly language file. This article explains both steps needed. Note there is a difference in the C ISR declaration between the Imagecraft compiler and the Hi-Tech compiler. This difference is detailed below.
C ISR Declaration in Imagecraft
The Imagecraft compiler uses the #pragma interrupt_handler directive. This is documented in section 6.6 of the C Language Users Guide. The #pragma interrupt_handler will tell the C-compiler to save/restore registers and use an interrupt return at the end of execution. The Imagecraft usage is as follows for an interrupt named foo:
#pragma interrupt_handler foo
//Place interrupt service code here
Make sure you are using the correct name in your #pragma statement, since the Imagecraft compiler will not give you a warning if you make a mistake with the name used in the #pragma (and your ISR will not work properly).
C ISR Declaration in Hi-Tech
For Hi-Tech, the interrupt declaration is made in the function declaration instead of a #pragma. This is documented in section 3.8.1 of the Hi-Tech User Manual:
void interrupt foo(void)@0x00
//Place interrupt service code here
Do not forget (if C ISR) to place a ljmp in the boot.tpl file in root project
directory. If you C ISR is named TimeISR then typing will be "ljmp _TimeISR"
Also, as a general practice try to minimize the f() calls within the ISR, this creates
a lot of overhead/stack push that can really slow down machine response. Generally
set a flag, leave ISR, and process in main().
Lastly if GPIO ISR, all GPIO pin ISRs are aggregated into one, so you have to use a
mask to test for which pin created the ISR.
Difference between LJMP and LCALL
LJMP is a pure branching instruction. CPU jumps to location specified and doesn’t return back to location from where it branched. If ljmp instruction is called from an assembly ISR, for example as shown in the SleepTimer_Interrupt_Project, then the “reti” instruction after the ljmp instruction is not executed.
org 64h ;Sleep Timer Interrupt Vector
In order to take care of restoring the registers and PC, we need to declare the C function as an interrupt handler using #pragma as follows
#pragma interrupt_handler SleepTimer_ISR_C
Here, “SleepTimer_ISR_C” is the interrupt handler C function. The compiler automatically adds “reti” instruction at the end of this function and hence PC and Flag registers are automatically restored.
LCALL is just like an interruption in normal sequential execution and after executing the called routine, the CPU gets back to place where it left. So, the “reti” already present in the assembly ISR will execute to restore the registers and PC. No need of #pragma directive declaration for the C functions.
Here, the C function is just like any other C function and you need to manually preserve and restore the virtual registers and page pointers before calling the C function from assembly routine. This can be done by using preserve and restore macros as shown below.
The preservation and restoration of CPU_CONTEXT costs around 150 to 200 instructions cycles, 16 to 20 bytes of stack usage and 46 to 59 bytes of flash. Due to this overhead, LCALL method is not recommended for time critical ISR’s.
So, actually speaking you can either call the c function from the Timer ISR or map it directly in boot.asm but it is better to map it in boot.asm so that the execution is faster.