PIC16 UART Interrupt Receive - System hangs

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
Hi all,

Firstly, I have fixed my problem (so the system now works) but I dont know why!

Basically, I have a PIC16 controller with a configuration such that when a character is received on the RX line it will dump that into a buffer in SRAM. I know that the buffer mechanism works because that same code is being used for other things. The UART is configured correctly and I have read all the documentation and errata for the device.

Now here is what I dont understand.

Upon receiving the first character the processor hangs. I cannot debug the system because I am using all IO as IO (none free for the debugger) so I dont know why its latching up. I also noticed that if I disconnect the RX line and then probe that line with an oscilloscope probe it causes the latching of the device. I then added a pull up resistor which fixed the latchup from the probe (but still latches up when a character is sent).

The hang is not being caused by overflow and frame errors as my code (which you will see shortly) addresses those problems.

However, there was a temporary fix (that makes no sense).....

When the PIC hangs, I can reset the chip manually by pulling MCLR down and when I do, the chip spontaneously works again as if nothing ever happened AND characters reception works great! By trial and error I discovered that the error was somewhere around the interrupt code because if I disable the interrupt for UART reception, the chip would not hang when a byte came down the RX line. I then put my ISR code into the main loop to see if the code was at fault but the code worked fine (just meant that the chip would not handle the incoming byte as soon as it had arrived).

So, I have a device that hangs when RCIF is triggered, works after a RESET and works if I disable the interrupt. Eventually, I changed the interrupt code so that the first interrupt to be handled was the RCIF. Magically, it now works (yes, I have other interrupts being checked including timers and CCP).

Here is the old ISR "decider" code - this hangs on RCIF
Code:
banksel PIR1
btfsc PIR1, CCP1IF                    ; Was the PS2 Clock line triggered?
goto ps2Handle                        ; If it was, handel it!
btfsc INTCON, INTF                    ; Was the CS pin triggered?
goto csHandel                        ; Chip select handel
btfsc INTCON, TMR0IE                ; Was the interrupt the timer?
goto timerInterrupt                    ; Sort out the timer
btfsc PIR1, RCIF                    ; Has a byte been received over UART?
goto uartInterrupt                    ; Store the byte         
goto interrupt.end                    ;
Here is the new ISR "decider" code - This works great
Code:
banksel PIR1
btfsc PIR1, RCIF                    ; Has a byte been received over UART?
goto uartInterrupt                    ; Store the byte         
btfsc PIR1, CCP1IF                    ; Was the PS2 Clock line triggered?
goto ps2Handle                        ; If it was, handel it!
btfsc INTCON, INTF                    ; Was the CS pin triggered?
goto csHandel                        ; Chip select handel
btfsc INTCON, TMR0IE                ; Was the interrupt the timer?
goto timerInterrupt                    ; Sort out the timer
goto interrupt.end                    ;
 
Last edited:

LesJones

Joined Jan 8, 2017
4,188
Have you cleared the interrupt at the end of the interrupt service routine ? Posting your interrupt service routines would be a great help.

Les.
 

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
@ericgibbs
The interrupts are enabled (both peripheral and global) otherwise the timer and PS2 would not work.
This code is located at 0x0004 (ISR location) and the gotos are used for two reasons:
A) Calls use the stack (not desired)
B) The code needed to handel the ISRs are a tad bulky
 

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
Code:
uartInterrupt:
banksel RCSTA
btfsc RCSTA, OERR
goto uartOhandel
btfsc RCSTA, FERR
goto uartFhandel
goto uartGetReg

uartOhandel:       
bcf RCSTA, SPEN
bcf RCSTA, CREN
nop
nop
nop
nop
bsf RCSTA, CREN
bsf RCSTA, SPEN
retfie

uartFhandel;
banksel RCREG
movfw RCREG
movfw RCREG
movfw RCREG
retfie
                   
uartGetReg:       
movfw RCREG
retfie
 

LesJones

Joined Jan 8, 2017
4,188
You don't have any code in your interrupt handler to save and restore registers that can be changed by the interrupt routine. for example status register and bankselect register. This should also be done in subroutines.

Les.
 

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
@LesJones the PIC has automatic context saving. All core registers are automatically popped and pushed to and from the stack upon an interrupt entry and exit.
@ericgibbs Why? You should not call functions inside an ISR becuase of potential stack overflow. Goto statements are totally fine as long as they all end at retfie which mine do. My code is perfectly stable and acceptable (the PS2 decoding is fine, the INT is fine, and even the UART now) so what is wrong with it? How would you deal with a PS2 port? Would you bit bang? Would you use non-interrupt code that would miss bits from the datastream?
 

Sensacell

Joined Jun 19, 2012
3,442
Having mainline code with a GOTO to an ISR is a very bad idea, when you hit the RETFIE instruction, the PC will be loaded with garbage and your program will be lost in space. (CRASH)

There are two ways to handle this correctly:

1) Use the interrupt mechanism correctly, have the interrupts enabled so the code jumps to the ISR when a character comes in, then exit the ISR with an RETFIE instruction. (polling the UART INT flag is totally unnecessary)

2) Poll the UART INT Flag, then process the code with a call to a subroutine that handles the UART. End the subroutine with a normal RETURN instruction.
This is not technically an interrupt at all.
 

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
For the last time, Im not calling an ISR. The ISR is executed WHEN an interrupt is fired. Then the ISR determines which interrupt was fired and then GOTOs that specific handle.
 

MMcLaren

Joined Feb 14, 2010
861
Which PIC16 are you using ? Non of the PIC16s that I have used have automatic context saving.

Les.
All of the newer "enhanced mid-range" chips have this capability, as well as LAT registers, access to WREG, indirect access to all banked RAM in a linear address space, etc., etc... You should check 'em out... They're really pretty neat...

Regards, Mike
 

MMcLaren

Joined Feb 14, 2010
861
Hi Robin.

Since PCLATH could be almost any value when an interrupt occurs, you should probably setup PCLATH in your interrupt code before using "call" or "goto" instructions. If all of your interrupt code fits into the first 2048 bytes of program memory, simply do a "clrf PCLATH" at the beginning of your interrupt routine.

Cheerful regards, Mike
 
Last edited:

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
Hi Robin.

Since PCLATH could be almost any value when an interrupt occurs, you should probably setup PCLATH in your interrupt code before using "call" or "goto" instructions.

Cheerful regards, Mike
Interesting :). Why? What effect does PCLATH have on the interrupt? From what I have gathered, the PC is pushed onto the stack and PCLATH is put into a shadow register which is then restored on retfie.
 

MMcLaren

Joined Feb 14, 2010
861
That's correct, Robin. However, a 14-bit "goto" or "call" instruction only contains 11 bits of address data, a so called 2048 word "page", and the other address bits are copied from PCLATH. This could be a problem when you have code spanning more than a single 2048 word "page" in program memory. If an interrupt occurs while running code in page 1 (0800..0FFF) the PCLATH value will affect the "goto" instructions in your ISR.

If all of your interrupt code resides in the first "page" (0000..07FF) of program memory, a single "clrf PCLATH" instruction at the top of your ISR will keep you out of trouble when your program grows to span more than a single "page" of program memory.

Have fun... Cheerful regards, Mike
 
Last edited:

John P

Joined Oct 14, 2008
2,026
How many times must the poor guy explain? PCLATH is one of the registers that an enhanced midrange PIC saves automatically. The problem must be somewhere else. And a clue is that it's important what order the interrupt flags are checked in. Hmmm...

I do see that the ISR is checking TMR0IE rather than TMR0IF. So suppose the RX interrupt goes off, the ISR reaches the test for TMR0IE and of course, it's set, so it executes that instead, so the RX interrupt never gets serviced, and after leaving the ISR you immediately see another RX interrupt, forever. That looks like it to me.
 
Last edited:

Thread Starter

Robin Mitchell

Joined Oct 25, 2009
819
@John P Finally....someone who listened to me :( (@MMcLaren I do however love your response! Learn something everyday)

It is SO strange that the ORDER of the flag checking determines if the system hangs or not. At one point, I included a watchdog timer to try and reset the system which did not fix the hang. Like I said before, it would magically work (as well) if the external reset is pulsed. Maybe there is a glitch in the CPU? It is not uncommon for micros to have an erata that tell you not to use x y and z functions because they can result in glitches.
 

John P

Joined Oct 14, 2008
2,026
Robin, I think you missed my second paragraph, which I added later, when the clue related to order of testing the flags really sank in.
 
Top