Writing a C ISR

E-mail Print PDF
( 1 Vote )
Question: How do I write an ISR in C for PSoC 1?

Answer:  Let us write a C ISR called MyCounterISR to be called to process the interrupt from a counter user module named Counter16_1.

1. Define the C function as an ISR using the #pragma interrupt_handler directive.  This should be placed in the beginning of main.c or any other source file along with other function prototypes.
#pragma interrupt_handler MyCounterISR

2. Write the C function for the ISR
void MyCounterISR(void)
{
   // Write code to process the interrupt here
}
Now, the compiler knows that MyCounterISR is an ISR function and will terminate the function with reti instead of ret.  The compiler also will add code before and after the function to preserve and restore all the virtual registers and page mode registers.

3. Now there are two methods to call this ISR.

Method-1: From inside the User module's ISR file 
PSoC Designer generates an ISR file for each user module.  This file is named as <User Module Name>INT.asm and can be found under the "lib >> Library Source Files" folder in the project view.  The Counter16_1 for which we are writing this ISR will have a file Counter16_1INT.asm.  Inside this file, there is a custom area for writing code to process the interrupt.  Place an "ljmp _MyCounterISR" instruction inside this area.

_Counter16_1_ISR:

   ;@PSoC_UserCode_BODY@ (Do not change this line.)
   ;---------------------------------------------------
   ; Insert your custom code below this banner
   ;---------------------------------------------------
   ;   NOTE: interrupt service routines must preserve
   ;   the values of the A and X CPU registers.

   ljmp _MyCounterISR
   ;---------------------------------------------------
   ; Insert your custom code above this banner
   ;---------------------------------------------------
   ;@PSoC_UserCode_END@ (Do not change this line.)

   reti

When an interrupt is generated from Counter16_1, the processor first goes to the interrupt vector in boot.asm.  The code in the interrupt vector passes control to the ISR inside the Counter16_1INT.asm file.  The ljmp instruction we placed inside this ISR will pass control to the C ISR that we have written.

Method-2: From inside boot.asm 
All interrupt vectors are located inside boot.asm file.  This file is automatically generated by the PSoC Designer.  PSoC Designer will automatically place ljmp instructions to the ISR files of user modules in their respective interrupt vectors.  For example, if we placed the Counter16_1 in DBB00 and DBB01, PSoC Designer will place the ljmp instruction in the interrupt vector for DBB01, as below.

    org   24h                      ;PSoC Block DBB01 Interrupt Vector
    ljmp    _Counter16_1_ISR
    reti

Now, we have to replace this
"ljmp    _Counter16_1_ISR" with the jump to our own C ISR.  Replacing the code in boot.asm will not help.  boot.asm is a dynamically built file which is generated every time a "Generate Application" command is executed.  Any changes made to the boot.asm will be lost during this regeneration.  So, we need to make the modifications to the template file, boot.tpl, which is found in the main project directory.  Open boot.tpl and comment out the code in the interrupt vector for DBB01 and add the code to jump to the C ISR.

Change this:

    org   24h                      ;PSoC Block DBB01 Interrupt Vector
    `@INTERRUPT_9`
    reti

to this:

    org   24h                      ;PSoC Block DBB01 Interrupt Vector
    ;`@INTERRUPT_9`
    ljmp _MyCounterISR
    reti

Important Notes:
1. This method can only be used when you are writing the complete ISR in C.  The ljmp instruction should be placed in the very beginning of the ISR file or boot.tpl.  This method cannot be used for partial ISRs where some code from the ISR has already executed before branching to the C ISR, for example, like branching to a C function from inside an ADC ISR to process the results.  For this type of application, the C function should written as normal C function (not an interrupt_handler) and should be called from inside the custom user code area, not jumped to.
2. Method-1 above has the advantage of portability and is easy to maintain.  If you happen to relocate the user module to a different digital block, the PSoC Designer will automatically relocate the ISR branching code to the new interrupt vector in boot.asm.  The code that we placed inside the user module's ISR will remain the same.  This method has a slight disadvantage that it requires an extra jump.  The first jump is made from boot.asm to the ISR file.  The second jump is made from inside the ISR file to our C ISR.  For time critical applications, this extra jump may matter, where Method-2 is useful
3. Method-2 is better in terms of execution time, but needs maintenance.  For example, if you relocate the Counter16 from DBB00/DBB01 to DBB10/DBB11, then you have to open boot.tpl and manually relocate the ljmp _MyCoutnerISR instruction to the DBB11 interrupt vector.
Comments (0)
Only registered users can write comments!