Author Topic: PIC16F: Structuring external interrupt routines in C  (Read 3238 times)

0 Members and 1 Guest are viewing this topic.

Offline GKTopic starter

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
PIC16F: Structuring external interrupt routines in C
« on: July 14, 2017, 09:28:17 am »
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: [Select]
#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_EXT
void 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?


Bzzzzt. No longer care, over this forum shit.........ZZzzzzzzzzzzzzzzz
 

Offline AndyC_772

  • Super Contributor
  • ***
  • Posts: 4227
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
Re: PIC16F: Structuring external interrupt routines in C
« Reply #1 on: July 14, 2017, 09:37:13 am »
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!
 
The following users thanked this post: JPortici

Offline NivagSwerdna

  • Super Contributor
  • ***
  • Posts: 2495
  • Country: gb
Re: PIC16F: Structuring external interrupt routines in C
« Reply #2 on: July 14, 2017, 09:40:56 am »
FWIW

You 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... READ https://www.ccsinfo.com/forum/viewtopic.php?p=180956
« Last Edit: July 14, 2017, 10:37:39 am by NivagSwerdna »
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: PIC16F: Structuring external interrupt routines in C
« Reply #3 on: July 14, 2017, 10:22:37 am »
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 stack
and 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 file
i 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 04h

unfortunately i don't remember the correct keywords to search for this, i'll look if i still have a project where i used this method


so, i found this in a project with a PIC18, but you should get the idea..
Code: [Select]
/* Interrupt Vector Relocation */
#asm
  GLOBAL _intHroutine   ;
  GLOBAL _intLroutine   ;
#endasm
 
void 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
« Last Edit: July 14, 2017, 10:30:47 am by JPortici »
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 825
Re: PIC16F: Structuring external interrupt routines in C
« Reply #4 on: July 14, 2017, 12:47:26 pm »
Quote
Minus the 4 instruction cycles (4uS) the compiler generates to toggle pin D1 high
And 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 1Mhz

You 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 up :)

Your compiler is hiding a lot of stuff behind the scenes- as indicated by all the #'s.
« Last Edit: July 14, 2017, 12:57:15 pm by cv007 »
 

Offline GKTopic starter

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: PIC16F: Structuring external interrupt routines in C
« Reply #5 on: July 14, 2017, 01:19:06 pm »
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 flawlessly ;D


Quote
#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         data        =  0;
int         byte1       =  0;
int         byte2       =  0;
short int   start_bit   =  0;
short int   parity_bit  =  0;   
short int   stop_bit    =  0;


void Serial_read()
{
   // B0 = Keyboard serial clock input
   // B1 = Keyboard serial data input
   
   data=0;
   start_bit=0;
   parity_bit=0;
   stop_bit=0;
 
 
   while(input(pin_b0));         // start bit
   
   if (input(pin_B1))
   start_bit=1;
     
   while(!input(pin_b0));
 
     
   while(input(pin_b0));         // Bit 0
     
   if (input(pin_B1))
   bit_set(data,0);
   
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Bit 1
   
   if (input(pin_B1))
   bit_set(data,1);
     
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Bit 2
 
   if (input(pin_B1))
   bit_set(data,2);
   
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Bit 3
   
   if (input(pin_B1))
   bit_set(data,3);   
   
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Bit 4
   
   if (input(pin_B1))
   bit_set(data,4); 
   
   while(!input(pin_b0));
 
     
   while(input(pin_b0));         // Bit 5
   
   if (input(pin_B1))
   bit_set(data,5); 
   
   while(!input(pin_b0));
   
     
   while(input(pin_b0));         // Bit 6
   
   if (input(pin_B1))
   bit_set(data,6);
         
   while(!input(pin_b0));
 
   
   while(input(pin_b0));         // Bit 7
   
   if (input(pin_B1))
   bit_set(data,7);
   
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Parity bit
   
   if (input(pin_B1))
   parity_bit=1;
   
   while(!input(pin_b0));
     
   
   while(input(pin_b0));         // Stop bit
   
   if (input(pin_B1))
   stop_bit=1;
   
   while(!input(pin_b0));
}


void MAIN()
{

   loop:
   
   
   Serial_read();
   
   
   byte1=data;
   byte2=0;
   
   
   if(data==0xE0)                // Detect Extended (two-byte) key Make code
   {
      Serial_read();
      byte2=data;
   }
   
   
   if(data==0xF0)                // Ignore Break code
   {
      delay_ms(10);
      goto loop;
   }
   
     
   printf("%X/%X\n\r", byte1, byte2);      // send Make code
 
   goto loop; 
     
}
« Last Edit: July 14, 2017, 01:54:19 pm by GK »
Bzzzzt. No longer care, over this forum shit.........ZZzzzzzzzzzzzzzzz
 

Offline GKTopic starter

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: PIC16F: Structuring external interrupt routines in C
« Reply #6 on: July 14, 2017, 01:21:03 pm »
Quote
Minus the 4 instruction cycles (4uS) the compiler generates to toggle pin D1 high
And 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 1Mhz


The compiler sets the direction register each time unless you specify #USE FAST_IO (port), which I didn't.
Bzzzzt. No longer care, over this forum shit.........ZZzzzzzzzzzzzzzzz
 

Offline JPortici

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Re: PIC16F: Structuring external interrupt routines in C
« Reply #7 on: July 14, 2017, 01:25:57 pm »
I'd post the list file if it weren't for the stern disapproval of CCS due to copyright/IP issues.

are 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
 

Offline GKTopic starter

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: PIC16F: Structuring external interrupt routines in C
« Reply #8 on: July 14, 2017, 01:52:00 pm »
I'd post the list file if it weren't for the stern disapproval of CCS due to copyright/IP issues.

are you serious? screw that compiler i say!



Yeah, but despite that it is a fairly nice and full-featured suite.
« Last Edit: July 14, 2017, 01:59:08 pm by GK »
Bzzzzt. No longer care, over this forum shit.........ZZzzzzzzzzzzzzzzz
 

Offline Bruce Abbott

  • Frequent Contributor
  • **
  • Posts: 627
  • Country: nz
    • Bruce Abbott's R/C Models and Electronics
Re: PIC16F: Structuring external interrupt routines in C
« Reply #9 on: July 14, 2017, 04:22:17 pm »
What am I missing?
The only thing 'missing' is expecting a 1:1 relationship between source code and machine cycles. 33 cycles interrupt latency is perfectly normal.

Quote
looking as the list file the compiler is doing a lot of housekeeping/saving/whatever both entering and exiting the ISR
Which 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 restore any registers. 
 

Offline GKTopic starter

  • Super Contributor
  • ***
  • Posts: 2607
  • Country: au
Re: PIC16F: Structuring external interrupt routines in C
« Reply #10 on: July 14, 2017, 11:23:13 pm »
What am I missing?
The only thing 'missing' is expecting a 1:1 relationship between source code and machine cycles. 33 cycles interrupt latency is perfectly normal.

Quote
looking as the list file the compiler is doing a lot of housekeeping/saving/whatever both entering and exiting the ISR
Which 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 restore any registers.


Well 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?
 
Bzzzzt. No longer care, over this forum shit.........ZZzzzzzzzzzzzzzzz
 

Offline NorthGuy

  • Super Contributor
  • ***
  • Posts: 3143
  • Country: ca
Re: PIC16F: Structuring external interrupt routines in C
« Reply #11 on: July 16, 2017, 01:15:50 am »
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.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf