Regular Contributor
Posts: 33
Registered: ‎04-14-2009

How do I implement an interrupt service routine in C on the PSoC1?

Posts: 43
Registered: ‎04-13-2009

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

void foo(void)

//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



Note that the @0x00 does not have any effect in the PSoC. In other Hi-Tech target devices, this specifies the interrupt vector address, but in PSoC the vector address is fixed for each interrupt. The @0x00 is included to eliminate a warning from the Hi-Tech compiler.


Assembly LJMP

You will also need to place an ljmp _foo either in the interrupt vector inside boot.tpl or inside the user module generated assembly source file for interrupts (usually int.asm, or psocgpioint.asm for GPIO interrupts). If there is a user module generated interrupt assembly file, you will see an ljmp already in boot.asm which jumps to the ISR function in the assembly file. Find this ISR function and insert your ljmp to the C file within the function. Note that this ljmp should be added between the custom code banners in the assembly ISR function. Below is an example using the PSoC GPIO interrupt (in psocgpioint.asm):


;@PSoC_UserCode_BODY@ (Do not change this line.)
; Insert your custom code below this banner

 ljmp _foo

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



Additional Pitfalls - No Return to Assembly

Note that when you ljmp to a properly declared C ISR, the compiler inserts a RETI at the end of the C routine. Therefore, program execution does not return to the assembly file. Some PSoC assembly ISRs have code underneath the banner that needs to run, therefore you have to be careful and inspect the assembly in the ISR after your inserted ljmp to avoid problems. Even a single POP instruction in the generated assembly code after your inserted ljmp can cause issues, since if the POP doesn't run there could eventually be a stack overflow. The code below lays out an advanced method by which you can call a C ISR and have the code execution return to the assembly ISR. You can also use this method to enable a lower priority interrupt during the execution of a C ISR - however, you must use caution when doing this.


;This routine is intended to call a C ISR and return execution to this code
; It works by manipulating the stack so that the compiler generated
; RETI returns here.
  We also supply the value for the flags register
; that is restored by the RETI. A RETI expects the stack to contain the
; flags value to restore and the progr
am counter location to return.

;NOTE: This code introduces a stack overhead of four bytes (in addition to the
; stack space used by the C ISR)

push A          ;preserve A since we are using it
call .callstub  ;puts the two byte PC on the stack
jmp .continue   ;flow returns here after C ISR RETI
and F, 0        ;C interrupt handler expects flags to be clear
mov A, 0   
push A          ;Push Flags register value that will be popped by generated reti (in this
                ; case, 0).
                ; If your asm ISR routine uses a page mode other than 0,
                ; you would push the appropriate value onto the stack instead of 0.
                ; Note that this value also has an effect on global interrupt
                ; enable/disable upon return

ljmp _foo      ;ljmp to the C ISR. RETI will return
               ; control to a jmp to the next line

pop A          ;Restore A