Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC interrupt tutorial

Status
Not open for further replies.
Hi All,

I need some help to interrupts in PIC programming. I use mikroC and need some tutorial on how to use the internal counter in the PIC to interrupt the running program after a certaint mount of time and also a button should be able to interrupt, and after it has interrupted and made it's job, the program should go on. Does anyone know how that's made?
 
Yes, I can tell you in general how to do this, but it would be much easier if we could both look at the same data sheet while we have this discussion.

In general:
All interrupt sources have a bit in a file register associated with the presence or absense of the interrupt condition. These have short names which end with the letter "F" which is short for flag. So T0IF would be the Timer 0 Interrupt Flag. There is another bit in a file register with a short name that ends with the letter "E" which is short for enable. This bit enables or disables each particular interrupt. So T0IE would be Timer 0 Interrupt Enable. Finally there is a global interrupt enable bit which enables or disable all interrupts. I think it is called GIE for Global Interrupt Enable.

When an enabled interrupt flag is set to a one(1) and GIE is also a one(1) the processor stores the address of the next instruction on the hardware stack and fetches the next instruction from code address 0x0004. At this point we usually want to save the W register, and the STATUS register in the register file. Then the code will check each of the enabled interrupts to see if it was the one that caused the interrupt. If T0IF==1 for example then we know that Timer 0 expired or overflowed. We clear T0IF, and load a new value in Timer 0, restore the STATUS and the W register and exit with a RETFIE instruction. The next instruction to be executed is at the address that was stored on the hardware stack when the execution address was changed to 0x0004. In other words it picks up where it left off.

How to get your compiler to compile an interrupt routine is somethig you will have to consult the compiler manual for.
 
That was a very describing explanation and I was left out in the cold by the flags. As I understood there are different interrupts either a timer or on certain ports, but the central is to enable them by setting GIE to the value 1. If an interrupts is provided a flag is changed to 1, and the PIC will search for the interrupt among the RBIE, TOIF... and the set the appropriate flag to 1. When this is detected, what would then happen? Do I need a if-routine running in the program (not and interrupt) to check this change or will the program go to another place in the source code, where I can add my own actions e.g. a motor to run?

I found this interrupt RBIF, which should control changes/interrupts from RB4-7, but isn't it possible to detect interrupts from all ports on the PIC?

Should I set either TOIE or RBIE to zero before running the program (variable declaration) and clear all appropriate flag bit in the INTF, RBIF or TOIF interrupts services?

And finally I'm blank about the use of the RETFIE and the W register.

Thanks
 
I have no idea what a "describing explanation" is, but thank you very much.

As I said, a flag is just a bit in a register in the PIC's register file. These bits are set by the hardware, in the the peripheral devices, in the PIC. That is why these registers in the register file do not behave like RAM. Their values can change without the processor executing instructions or doing anything else.

In order for an interrupt to happen you muse enable the individual interrupt by setting the corresponding interrupt enable bit to a one(1) AND you must set GIE to a one(1). If you had told me which PIC you were using, I could point to a page in the datasheet, with a picture, that would make things clearer.

The PIC processor does not "search" for interrupts. There is a multiple input OR-gate whose output says "there is" an interrupt or "there is not". If there is an interrupt the current Program Counter is pushed onto the hardware stack and the value 0x0004 is "jammed" into the program counter in such a way that the next instruction executed is at address 0x0004.

In the main program you do not normally need to be concerned with checking for flags, although you can poll them with all interrupts disabled (GIE = 0). You do need to be concerned with which interrupts are enabled and disabled. When you get to address 0x0004 and have saved W and STATUS, now you have an if-the-else chain to identify which of the many flags associated with enabled interrupts caused the processor to suspend what it was doing and go to address 0x0004.

Different PIC's have different arrangements of things which generate interrupts. I know this is yelling, but WHICH PIC ARE WE TALKING AOBUT?

Yes it is a good idea to start with all the interrupt enables, and flags at zero, and you don't have to do it one bit at a time you can write entire registers. As you intialize each peripheral you can turn on it's individual enable. When initialization is complete and the application is ready to run set GIE to a one(1).

When an interrupt is executing you need for GIE to be zero so you don't end up in an infinite loop. When the PIC goes to address 0x0004 it clears GIE so no further interrupts can happen until you want them to. RETFIE is just a subroutine return which turns GIE back to a one(1). It is a return with interrupts enabled.

If you don't know about the W register it is pretty hard to explain what is going on inside the processor. W is one of the implied operands in the arithmetic and logival instructions. Since the W register and the STATUS register might be modified in an interrupt routine you need to save their values, so that when you return to the main program, the values are the same as they were before you went to the interrupt routine.
 
Honestly,

If I were you I would get a C compiler. It will make getting started so much easier.

For me anyway, once I learned the C end of things it was MUCH easier for me to figure out the assembly end of things. Like flags, registers, etc

The problem with the way I learned is that while in the C phase of learning you need to just trust that a command like enableInterupt(Timer0) will do what it says it will do. Its later that you learn the reason that works.

Also most C compilers I've seen show you the asm right next to your compiled C code. That makes it very very simple to understand whats going on without having to check the datasheet every 10seconds while learning.

Maybe you learn different then me, who knows.
 
I would say,
f you don't know about the W register it is pretty hard to explain what is going on inside the processor.
The reason he doesn't know about the W reg, is because (I think) he went to C first, with out learning any assembly. OP states:
I use mikroC and need some tutorial on how to use the internal counter in the PIC
I guess we all do learn in different ways though. I keep thinking and wanting to go back to assembly, because I learned the most about the hardware using assembly. The higher level the language, the more abstraction there is.
Regards,
Robert
 
He already is using a C-compiler. His problem is that C-compilers don't deal with machine specifics like interrupts very well. Most of the handling is non-standard and compiler specific.

If the compiler supports it, everything I've described in my post can be written in C. It is essential to understand these details in order to write and debug interrupt routines in any language. The alternative is endless folly.
 
I unserstand your post, but if someone had a source code (prefering a C code) with a lot of comment in it, that'll properly help me understand where it goes, when the adress is 0x0004. I still can't figure that out.

By the way, I learned it somehow the hard way with the PICDEM2 board and the MPLAB, but I didn't really understood it into the details with arithmetic and logical instructions, so I went on with the C. I know that the W-register (working register) stores a specific value, but were it's used I still can't figure out.

And as a final comment - especially to Papabravo, I'm using is the PIC16F877A and the PIC18F452.

::Maybe I'll subscribe the "Nuts & Volt" later::
 
I found this bit of code and made my own comments (please correct me if I'm wrong).
 

Attachments

  • pic_source_code.jpg
    pic_source_code.jpg
    35.9 KB · Views: 2,811
I also find this C code, maybe that's easier for you to explain me where the program goes to with interrupt

/*
* Project name:
Timer_Wdt (Demonstration of the Watchdog Timer usage)
* Copyright:
(c) Mikroelektronika, 2005.
* Description:
This code demonstrates using interrupts. Program toggles LEDs on PORTB each
second. It also demonstrates how to embed Assembly language blocks and how
to clear the Watchdog timer.
* Test configuration:
MCU: P16F877A
Dev.Board: EasyPIC2
Oscillator: HS, 08.0000 MHz
Ext. Modules: -
SW: mikroC v2.00
* NOTES:
Turn on the configuration bit WDT_ON.
*/

unsigned short a, b;
unsigned int cnt;

void interrupt() { // Interrupt is triggered by the watchdog
cnt++ ; // timer, since it doesn't get refreshed
TMR0 = 96; // on time
INTCON = 0x20;
}//~

// main routine
void main() {
a = 0;
b = 1;
OPTION_REG = 0x84; // Enable watchdog timer
TRISB = 0;
PORTB = 0xFF;
cnt = 0;
TMR0 = 96;
INTCON = 0xA0;

do {
asm CLRWDT ;
if (cnt == 400) {
PORTB = ~PORTB;
cnt = 0;
}
} while(1);
}//~!
 
Electronics4you,

The assembly language code is a good illustration, but I have one question. What value is being written to TMR0 by the 'movwf TMR0' instruction. Should there be and instruction that loads the W register with a meaningful value before you write it to TMR0?

It is not possible to verify by inspection of the C code what the compiler is doing. That is part of the problem. Just because you create a function called "interrupt()" does not mean that the compiler will do the right thing. I suppose it is barely possible that this is the way your compiler works, but you must dig deeply into the compiler documentation and find the non-standard syntax which tells the compiler that it is dealing with an interrupt function. Then you must look at the compiler output with the assembly language statements and the c code interleaved to see that it did the right thing.

In assembly language it is easy. Any code that executes after the Program Counter is set to 0x0004 is by definition an interrupt routine.

In the 16F877A datasheet(DS39582B,(c)2003) please refer to page 153, figure 14-10 for a diagram of the OR gate that continuously monitors all the flags(file register bits) which can cause an interrupt. I don't know if this will help you but, an interrupt is like forcing a special "call 0x0004" in between two instructions. The call is not really there in the instruction stream but the processor acts as if it was. What is special about this call instruction is that further interrupts are disabled. When the RETFIE returns from the special call everything should be the same as it was before the call. W has the same value and the STATUS bits are the same as they were. Some RAM locations and other file register may have changed, but that should have no effect on the code that was executing befor the interrupt happened. It should be an OBVIOUS programming principle that you cannot change file registers in both an interrupt routine and the main code. You can READ and TEST things in both places but you cannot WRITE them in both places. If you need to work with multibyte quantities in both places you need to disable interrupts while you manipulate things in the main code, then reenable them when you are done. If you think about this for a minute the reason will be obvious.
 
It's all getting much clearer so I wrote this code in C and then compiled it to assembler. If you find something suspicious please post it

... and yes there should be loaded a value into the TMR0IF bit.

I figured out, that mikroC know the function interrupt within all its declarations like INTCON.RBIF and OPTION_REG.PSA. Hope you can figure out the messy code format.
 

Attachments

  • asm-code.jpg
    asm-code.jpg
    106.9 KB · Views: 748
  • c-code.jpg
    c-code.jpg
    32.6 KB · Views: 1,156
Bravo Sir!

You did it all with just a little bitty push. It would seem that your compiler considers that any function named "interrupt" is going to be linked at location 0x0004. Not only that but it produces a very nice interlist with the assembly code and the c code interleaved.

Good Luck on the rest of your project.
 
No, it is the phonetic rendering of how I hear my wife and her father. There is also a wooden sign in the kitchen that says "Velkommen" so I put 2 and 5 together and got eight.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top