PIC16F: Structuring external interrupt routines in C - Page 1
Forum section: electronics / general

author: GK
author_rank: Super Contributor
timestamp: July 14, 2017, 09:28:17 am
content: Disclaimer: I've never programmed external interrupts for PIC16F devices in C before. I'm using the CCS compiler, a PIC16F874 is plugged into the breadboard currently clocked at 4 MHz, giving me an instruction cycle of 1uS.For an initial external ISR test I wrote the following code, to blip an IO pin in response to a H>L edge transition of my serial clock:Code:#include <16f874A.h>#use delay(clock=4000000)#use standard_io(A)#use standard_io(B)#use standard_io(C)#use standard_io(D)#FUSES HS,NOWDT,PUT,NOPROTECT,NOLVP,NOCPD,NOWRT,BROWNOUT#use RS232(Baud=2400, INVERT, Xmit=PIN_D0)#INT_EXTvoid EXT_ISR(){output_high(pin_d1);output_low(pin_d1);}void MAIN(){ext_int_edge(H_to_L);clear_interrupt(int_ext);enable_interrupts(int_ext);enable_interrupts(global);loop:goto loop;}Here is what I get:Minus the 4 instruction cycles (4uS) the compiler generates to toggle pin D1 high, it takes a whopping 33uS just to enter the ISR after the negative transition of the serial clock.What am I missing?

author: AndyC_772
author_rank: Super Contributor
timestamp: July 14, 2017, 09:37:13 am
content: When you disassemble your code, how many instructions are there from the start of the ISR to the one which actually sets the output latch?Are you just seeing the time taken to save the context?The data sheet has almost nothing to say about interrupt latency. There may be a clue there!

author: NivagSwerdna
author_rank: Super Contributor
timestamp: July 14, 2017, 09:40:56 am
content: FWIWYou could try putting your code inside the main and polling the interrupt flag rather than allowing the ISR to be called. That will allow you to quantify the ISR overhead.(whilst interrupts are disabled to avoid an empty ISR pre-empting)Also... READhttps://www.ccsinfo.com/forum/viewtopic.php?p=180956

author: JPortici
author_rank: Super Contributor
timestamp: July 14, 2017, 10:22:37 am
content: datasheet should say everything you need about interrupt latency.If memory serves me right, when an interrupt condition arises the current instruction finishes excecution,it is then followed by a NOP, while the current value of the program counter is pushed onto the stackand then the instruction at address 04h is excecuted.i bet that the reason you are seeing a lot of latency is because the compiler is performing context saving (and then restoring) in software.it should be obvious once you look at the disassembly listing filei don't know about CCS but there is a way in XC8 to "zero" the latency that doesn't involve programming all the ISR in assembly: ISR is just another function placed at a specific address, then you create a section containing only asm("GOTO xxxx"); with the function address and tell the linker to place this section at 04hunfortunately i don't remember the correct keywords to search for this, i'll look if i still have a project where i used this methodso, i found this in a project with a PIC18, but you should get the idea..Code:/* Interrupt Vector Relocation */#asmGLOBAL _intHroutine ;GLOBAL _intLroutine ;#endasmvoid intHroutine() @0x008 {asm("GOTO 0x808");}void intLroutine() @0x018 {asm("GOTO 0x818");}/* =========================== */i used this in a bootloader (which used no interrupts) to relocate the interrupt vector.. the application would start at address 800h and by doing this the PIC interrupt vector was just a goto to the application interrupt vector, otherwise it would have been surrounded that pointless context saving

author: cv007
author_rank: Frequent Contributor
timestamp: July 14, 2017, 12:47:26 pm
content: QuoteAnd it looks like it takes 4 to set the pin low again- seems odd as it should simply be a bsf followed by a bcf- isn't that 1us? (maybe not so optimized code)edit- or maybe it is a bsf/bcf, which means your clock is running at 1MhzYou simply have to look at the code produced by the compiler if you are interested in these things.A pic12f1822 with the free XC8 will produce 12 instructions for an isr that simply sets/clears a pin. Then add in the hardware context saving (which your pic does not have, I think), then add in some code to test which irq flag is set, then add in code to clear that flag- by the time you add it all up, it adds upYour compiler is hiding a lot of stuff behind the scenes- as indicated by all the #'s.

author: GK
author_rank: Super Contributor
timestamp: July 14, 2017, 01:19:06 pm
content: OK, thanks for the replies. Yes, looking as the list file the compiler is doing a lot of housekeeping/saving/whatever both entering and exiting the ISR. I'd post the list file if it weren't for the stern disapproval of CCS due to copyright/IP issues.What I am doing is interfacing a PS/2 keyboard. Having the serial clock edge toggling interrupt driven seemed the most logical approach.I'd definitely like to learn how to implement/code external interrupts efficiently in C, but for now I have a pressing need (want) to get this up and running ASAP, so for the time being I have coded perhaps the dumbest and most inelegant non-interrupt-driven serial-read routine ever, but so far it is working for me absolutely flawlesslyQuote

author: GK
author_rank: Super Contributor
timestamp: July 14, 2017, 01:21:03 pm
content: Quote from: cv007 on July 14, 2017, 12:47:26 pmThe compiler sets the direction register each time unless you specify #USE FAST_IO (port), which I didn't.

author: JPortici
author_rank: Super Contributor
timestamp: July 14, 2017, 01:25:57 pm
content: Quote from: GK on July 14, 2017, 01:19:06 pmare you serious? screw that compiler i say!if i were you i'd check if XC8 supports your pic, and if it doesn't change pic with a more modern and probably even cheaper device.none of this can't post disassembly bullshit

author: GK
author_rank: Super Contributor
timestamp: July 14, 2017, 01:52:00 pm
content: Quote from: JPortici on July 14, 2017, 01:25:57 pmYeah, but despite that it is a fairly nice and full-featured suite.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: July 14, 2017, 04:22:17 pm
content: Quote from: GK on July 14, 2017, 09:28:17 amThe only thing 'missing' is expecting a 1:1 relationship between source code and machine cycles. 33 cycles interrupt latency is perfectly normal.QuoteWhich is necessary because the ISR has to save and restore the context of your main code.You may be able to get the compiler to do less 'housekeeping' if the code in the ISR doesn't require it. Theoretically you can toggle a pin without having to save and restoreanyregisters.

author: GK
author_rank: Super Contributor
timestamp: July 14, 2017, 11:23:13 pm
content: Quote from: Bruce Abbott on July 14, 2017, 04:22:17 pmWell I was just surprised at how much context saving the compiler was doing considering that both inside and outside of the ISR the instructions/operations were so basic.JPortici posted some useful information on how structure code to reduce ISR latency. Anything further to add to what he has suggested?

author: NorthGuy
author_rank: Super Contributor
timestamp: July 16, 2017, 01:15:50 am
content: The hardware latency is 2 cycles (3 if you want to get picky). Everything else is context saving by the C compiler - cost of using C. Perhaps XC8 will do better than CCS.Also, you can get newer PIC16F1* instead of PIC16F874. It will automatically save lots of context registers in hardware, so that the C compiler doesn't need to do this. It'll improve the latency.