PIC UART and External Interrupt Problems

Thread Starter

crazyengineer

Joined Dec 29, 2010
156
Okay, so I created a code so that whenever external interrupt is activated (output from one encoder), it increments the current angle variable and display the current angle via serial terminal. The problem is that whenever the external interrupt activates, it completely disrupts the UART transmission and the PIC stops transmitting serial messages. Here's the code

Rich (BB code):
//=================================================================================================
//	Includes, Defines, and Global Variables
//=================================================================================================
#include <p18F2420.h>
#include <delays.h>
#include <portb.h>
#include <usart.h>
#include <stdio.h>
#include <stdlib.h>
#pragma config OSC = HS
#pragma config WDT = OFF
#pragma config PBADEN = OFF
#pragma config LVP = OFF 
#pragma config PWRT = ON 
int current_angle=0;
char message[30];
//=================================================================================================
//
//=================================================================================================




//=================================================================================================
//	Function Prototypes
//=================================================================================================
void CONFIG(void);
//=================================================================================================
//	
//=================================================================================================




//=================================================================================================
//	External Interrupt Handling
//=================================================================================================

#pragma interrupt ISR
void ISR ()
{ 
	if(INTCONbits.INT0IF==1)						//	Checks to see if the external interrupt has been triggered.
	{
		current_angle+=1;
		INTCONbits.INT0IF=0;						//	Clear the external interrupt flag
	}
}


#pragma code InterruptVectorHigh = 0x08				//	This function simply jumps to the ISR code shown above.
void InterruptVectorHigh (void)
{
	_asm
	goto ISR //jump to interrupt routine
	_endasm
}
#pragma code
//=================================================================================================
//
//=================================================================================================




//=================================================================================================
//	Main Loop
//=================================================================================================
void main(void)
{
	CONFIG();

	while(1)							//	Run the program forever and let the interrupts take over.
	{
		sprintf(message,"Current Angle: %d \r\n",current_angle);
		putsUSART(message);
		Delay10KTCYx(125);
	}

}
//=================================================================================================
//
//=================================================================================================




//=================================================================================================
//	Configuration Function
//=================================================================================================
void CONFIG(void)
{
	/* Code for handling all of the interrupt */
	RCONbits.IPEN=1;					//	Enable interrupt priority
	INTCONbits.GIE=1;					//	Enable global interrupts
	INTCONbits.INT0IE=1;				//	Enable INT0 interrupts
	INTCONbits.INT0IF=0;				//	Set external interrupt flag to zero
	INTCON2bits.RBPU=1;					// Disable Pull up
	INTCON2bits.INTEDG0=1;				// 	Interrupt enable on rising edge
	INTCON3bits.INT1IE=1;				//	Enable External interrupt 1
	INTCON2bits.INTEDG1=1;				//	Enable External interrupt on rising edge
	INTCON3bits.INT1IF=0;				//	Set flag to zero
	TRISBbits.TRISB0=1;					//	Set PORTB0 to input	
	TRISBbits.TRISB1=1;					//	Set PORTB1 to input
	TRISBbits.TRISB2=0;					// 	Set PORTB2 to output
	PORTB=0x03;							// Enable high on inputs

	/* USART Configuration */
	TRISC=0x00;
	OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE &USART_EIGHT_BIT & USART_CONT_RX & USART_BRGH_HIGH, 129);
}
//=================================================================================================
//	
//=================================================================================================
 

ErnieM

Joined Apr 24, 2011
8,377
An interrupt is supposed to stop the main loop code, that's what it's interrupting. It's not supposed to make the main code stop working forever. So job #1 is to find out where your code is hanging. As t06afre suggests stick a debugger on it and trace thru to see if it hangs on any statement.

It's not uncommon for an ISR to never exit (actually it re-fires over and over) if a flag is still set.

One thing is you have is the current_angle variable is not safe for the main loop to use: there is always the chance that while it is being used by the main loop the ISR is changing it, which can lead to a very occasional very weird result.

The fix is to make a shadow variable used by the main loop separate from the variable changed in the ISR. Then you read the ISR variable once and insure it is valid. I like this macro for the read:

Rich (BB code):
#define GetVariable(Dest, Source) while(Dest != Source) Dest = Source
 

t06afre

Joined May 11, 2009
5,934
A good point from ErnieM. Confer with the datasheet. As rule of thumb. It is up to the programmer to clear the interrupt flag. Then the interrupt handling is done.
 

t06afre

Joined May 11, 2009
5,934
I tried to debug it, but I keep getting PK3Err0040 error message.
You can get a lot done in the software simulator also (MPSIM). For that you do not need any programmer or PIC. I have given you links that explain how to use this. Did you take some time to look at them.
 

raychar

Joined Nov 8, 2011
82
It looks like the interrupt flag is not clear in the interrupt subroutines.

I'm currently making a readout using LCD and an optical encoder. Encoder processing needs an mcu to determine the motion's direction and steps, feeding pulses to another mcu's two external interrupts for increase and decrease stepping and to make calculation and displaying on LCD.

Thanks,
 

THE_RB

Joined Feb 11, 2008
5,438
Just remove that second nasty HighLevelInterrupt code that does a very suspect GOTO to the first interrupt. How the heck did you get the idea that was the way to do an interrupt? :eek:
 

ErnieM

Joined Apr 24, 2011
8,377
Just remove that second nasty HighLevelInterrupt code that does a very suspect GOTO to the first interrupt. How the heck did you get the idea that was the way to do an interrupt? :eek:
Probably by Reading The Fine Manual (RTFM) that covers the C18 compiler which details precisely this one/two-step of code to fill the interrupt vector location with (of all things) an interrupt vector to the interrupt routine.

crazyengineer: I just noticed you fire external interrupt #1 (INT1) but you test for external interrupt #0. Thus the flag you test for is never set so you never reset it, all the time the tripped flag is retriggering the interrupt.

Simon sez try this:

Rich (BB code):
#pragma interrupt ISR
void ISR ()
{ 
    if(INTCON3bits.INT1IF==1)            // Checks to see if the external
                                         // interrupt #1 has been triggered.
    {
        current_angle+=1;
        INTCON3bits.INT1IF=0;            // Clear the external interrupt #1 flag
    }
}
 
Last edited:

takao21203

Joined Apr 28, 2012
3,702
As explained above you need to make sure the interrupt handling is correct (i.E. the flags are reset).

If you don't want interruption, switch off the interrupts before you start transmission!

If you can not tolerate long breaks (with interrupt off), slice the transfer into small pieces.

Last not least, consider an interrupt driven USART handler as well. Each time a char was transmitted, you get an interrupt, and load the next char (does not take long).
 

THE_RB

Joined Feb 11, 2008
5,438
Probably by Reading The Fine Manual (RTFM) that covers the C18 compiler which details precisely this one/two-step of code to fill the interrupt vector location with (of all things) an interrupt vector to the interrupt routine.
...
Or maybe get a better compiler that does need you to goto from the middle of one function to the middle of another function just to make an interrupt work?
 

ErnieM

Joined Apr 24, 2011
8,377
Or maybe get a better compiler that does need you to goto from the middle of one function to the middle of another function just to make an interrupt work?
I seem to have missed the part where you apologized to crazyengineer (the OP here) for your bad advice concerning his code.

To further educate you: the high interrupt vector location has a scant 16 words of program memory until it hits smack dab into the low interrupt vector. Thus every compiler is the world is going to use a goto to get to a larger free code space.

Some compilers hide such "complicated" details from their targeted inexperienced user base as a marketing tool.

Please feel free to open a fresh thread if you need to discuss this further. Please do not continue to hijack this one.
 

THE_RB

Joined Feb 11, 2008
5,438
It's the job of the compiler to choose where functions go. You should know that. It does not matter at all how many ROM program words there are at the high level int position any more than it matters how many there are at the low level int position. The compiler SHOULD arrange any user interrupt C code properly in any ROM locations it chooses.

Users putting goto's from inside one C function to another is very nasty code, as I said, and there is no need to apologise for suggesting stopping using a nasty code practice. It's bad code practice, far from "proper" C practice and it's not portable code if the user wants to change over to a good compiler later.

I will however apologise for my bluntness as my suggestion could have been offered in a more polite fashion. As could yours.

ErnieM said:
... Please feel free to open a fresh thread if you need to discuss this further. Please do not continue to hijack this one.
You took the thread off topic with rude RTFM comments. My original post was entirely on-topic, as was the bulk of this post. You have shown bad manners towards me on many occasions Ernie, don't start telling me where I'm allowed to post.
 

ErnieM

Joined Apr 24, 2011
8,377
It's the job of the compiler to choose where functions go. You should know that. It does not matter at all how many ROM program words there are at the high level int position any more than it matters how many there are at the low level int position. The compiler SHOULD arrange any user interrupt C code properly in any ROM locations it chooses.
I will respond to that pertinent to this thread.

The compiler is constrained to place the interrupt vectors in the proper places so the hardware can access them. That is a far call from "any ROM locations it chooses."

Indeed it does matter "how many ROM program words there are at the high level int position." If one writes a routine that is too long then it will get called when another level of interrupt is called, that is if the code compiles at all.

In the case of PIC processors there is just enough space to do something more interesting then just inserting a simple goto at that location. While the fine manual <cough> <cough> for the fine (yes indeed) C18 compiler does not hint at this ability the savvy programmer can see the possibilities.

One may instead use a call instruction at the interrupt vector location if one so wishes, but it just slows down the process and uses more precious hardware stack space. These are both resources not to be wasted in pursuit of some arbitrary standard of code practices as the simple and direct goto works just fine.
 

takao21203

Joined Apr 28, 2012
3,702
Some CPUs have a table with interrupt vectors. These could be either in ROM, or in the RAM. Microcontrollers (in most cases) don't run programs in RAM, so what? You branch, call etc. from the interrupt vector.
 

THE_RB

Joined Feb 11, 2008
5,438
...
Indeed it does matter "how many ROM program words there are at the high level int position." If one writes a routine that is too long then it will get called when another level of interrupt is called, that is if the code compiles at all.

In the case of PIC processors there is just enough space to do something more interesting then just inserting a simple goto at that location. While the fine manual <cough> <cough> for the fine (yes indeed) C18 compiler does not hint at this ability the savvy programmer can see the possibilities.
...
Thanks for discussing the specifics. I have little understanding of that particular compiler (C18) but with PIC compilers they should do the same thing with interrupt vectors as Microchip suggests, that was are all used to from the assembler days.

The interrupt vector only has a few bytes of code space, so the correct procedure in assembler is to use a goto from the int vector directly to any area in ROM, where the int procedure itself resides.

That should be no different in a C compiler designed for a PIC! The user should have single visible function; interrupt() etc, and the compiler should treat that like any C function and place it in memory where the compiler wants it to be, and simply ties it to the ROM int vector with a single (invisible) goto.

NSAspook said:
"goto"
Oh I just knew this would start up again.
Actually I'm one of the people who uses goto's in C code, when they are a good idea, and I've copped flak myself over the years for daring to suggest using goto in C code... ;)

But I'm still stunned at the suggestion of a "general practice" of using a goto in C for the purpose of jumping out of one C function and into another C function, that is considered the very height of bad C coding practice and (in my opinion) should never have been suggested by the creators of a C compiler!
 

MMcLaren

Joined Feb 14, 2010
861
Just remove that second nasty HighLevelInterrupt code that does a very suspect GOTO to the first interrupt. How the heck did you get the idea that was the way to do an interrupt? :eek:
Oh my goodness, did someone actually try to offer this as credible authoritative (C18 compiler) advice (LOL)?
 
Top