• Pic® Basic

  • Interrupts in Proton and PIC Issues

    Some thoughts on interrupts and PICs ® by Blake N0VLN (breed on forum)

    This issue keeps coming up on the forum and for good reason. Interrupts are very complicated and little things can cause very bad things to happen. Many of which are very difficult if not almost impossible to figure out. I have over 20 years experience in assembler for PIC and am just now working with Proton. My experience lets me "see" things that most wouldn't and how Proton may or may not deal with the issues. Since I need to have a post or two to be able to stay on the forum I figured I'd pass on my knowledge of the issue. Sorry if this is long.

    Okay, for starters the first thing to do when dealing with hardware interrupt routines is to always have a set of variables that only the interrupt routines use. I normally have general use variables for passing data between routines, counters in routines, etc (dta, tmp, cnt). I always define a new set of these variables and add an I in front of them (idta, itmp, icnt) to indicate that they are for the interrupt routines only. This, obviously, works well. The real problem turns up when passing data between the interrupt routines and the main program. Obviously we have to be able to do this otherwise the interrupts typically wouldn't do us much good.

    First lets talk about passing byte sized variables. Typically there are no problems with this. For example, I have a hardware serial input buffer that grabs characters from the port and stuffs them into a "ring" buffer (I use an array in Proton). I have the buffer and 2 pointers. The input pointer (ipnt) is read and written to by the interrupt routine and the output pointer (opnt) is read and written to by the main program loop. I'll post my Proton modified code for it if anyone is interested. The thing is, when a character comes in the input pointer (ipnt) is incremented (written to by the interrupt routine) and the character saved in the buffer. The main program loop then looks at the ipnt and if its larger than the opnt then it knows there is a character(s) available in the buffer. It reads it (them) out and increments the opnt until it equals the ipnt (all characters handled). Obviously there are rollover issues that are dealt with but that’s not important here. With this arrangement there are no collision or corruption issues.

    The really big problem comes in when you have to pass a word sized variable (as I often do) between the main program and interrupt routine. This comes from the fact that it actually takes at least 4 PIC instructions to deal with a word sized variable. For example- dtaw=idtaw would be coded as
    movf idtaw,w low byte
    movwf dtaw low byte
    movf idtawh,w high byte
    movwf dtawh high byte
    The problem is that an interrupt can come in anywhere between these steps. So, say we get the first byte transferred and get an interrupt. Now we have different data in idtaw. Now when we return from the interrupt the high byte gets written with the wrong data. This can have very bad effects (say the data was 1ff and new data is 200- this could result in dtaw having a value of 2ff). This goes both ways (to and from interrupt routines).

    The only way to deal with this is to turn off interrupts when doing a word (or larger variable- a far worse situation) data transfer while in the main program loop. I wondered if Proton dealt with this issue and looked at the generated code (nope). You would think that just putting in a command of gie=0 before a transfer would work. Not so!!! I ran across this issue years ago in a document but can't find it now. This is where it gets really complicated. The command gie=0 is a single assembler command but it is possible for an interrupt to get flagged and set during the clearing of gie. This may not be an issue for all PICs but it was for some I've worked with. So what happens- you clear the interrupts but an interrupt is logged part way through the instruction (they take 4 clock cycles complete). Now the instruction is completed but the interrupt is set so the interrupt is handled and the return from the interrupt sets gie back to on. In the Microchip documentation they said the only way to deal with this issue is to clear gie, check and make sure it is clear, and re-clear it if it isn't before continuing on. In proton this would be
    While gie=1

    Yes, although the probability of this happening is remote it can happen and cause serious intermittent problems.

    Another place where disabling interrupts is important is when writing to the PIC internal eeprom. If you don't disable the interrupts a write to the eeprom can fail. This comes from the fact that you have to set up the data and address to be written, enable the write, then write a pattern (55,AA) to the control register for the write to take place. If an interrupt happens during the process of writing the pattern the data will not be written to the eeprom. There are two ways to deal with this- read the data written back and if it is not correct re-write it (probably a good idea in general- I do it) or make sure interrupts are off before writing to the eeprom. I was hoping that maybe Proton dealt with this for eeprom writes but looking at the generated code it doesn't. I understand it though, trying to cover all possibilities for interrupts would be an almost impossible task (not to mention those who are getting away with their code that seems to work and putting in turning off interrupts messing things up ticking them off).

    The point here is you have to turn off interrupts when transferring word+ sized variables and eeprom writes and a simple gie=0 won't do. You must call (gosub) an interrupt disable routine before doing any of these things.
    Gieoff: while gie=1

    Making the jump to Proton kind of got me thinking about some of the issues with interrupts and how I may need to deal with them with Proton. That got me to going “hmmm” and digging around the forum on the issue of interrupts. I saw a lot of posts on the subject and realized that these little issues I had to deal with in the assembler world might be things that weren’t so obvious. So I made the post hoping that some may find the information useful.

    I am posting my snippet of code for the serial buffer here. It’s the Protonized version of how I’ve always done it in assembler. I apologize if this is not the proper way to do this or I mess up the posting of the code. This is the first and only forum I am on so I still have lots to learn about the correct way to do things. Hope folks find it useful. Many of you will probably notice my lack of efficient Proton coding which comes from my thinking in assembler all these years and this was (is) my first Proton project. That will improve with time as I get more comfortable with how the compiler handles things (learning what I can get away with).

    I hope this information is of some use to those trying to use interrupts.

    Blake (breed on forum)