PIC UART and External Interrupt Problems

Discussion in 'Embedded Systems and Microcontrollers' started by crazyengineer, Oct 4, 2012.

  1. crazyengineer

    Thread Starter Member

    Dec 29, 2010
    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

    Code ( (Unknown Language)):
    2. //=================================================================================================
    3. //  Includes, Defines, and Global Variables
    4. //=================================================================================================
    5. #include <p18F2420.h>
    6. #include <delays.h>
    7. #include <portb.h>
    8. #include <usart.h>
    9. #include <stdio.h>
    10. #include <stdlib.h>
    11. #pragma config OSC = HS
    12. #pragma config WDT = OFF
    13. #pragma config PBADEN = OFF
    14. #pragma config LVP = OFF
    15. #pragma config PWRT = ON
    16. int current_angle=0;
    17. char message[30];
    18. //=================================================================================================
    19. //
    20. //=================================================================================================
    25. //=================================================================================================
    26. //  Function Prototypes
    27. //=================================================================================================
    28. void CONFIG(void);
    29. //=================================================================================================
    30. // 
    31. //=================================================================================================
    36. //=================================================================================================
    37. //  External Interrupt Handling
    38. //=================================================================================================
    40. #pragma interrupt ISR
    41. void ISR ()
    42. {
    43.     if(INTCONbits.INT0IF==1)                        //  Checks to see if the external interrupt has been triggered.
    44.     {
    45.         current_angle+=1;
    46.         INTCONbits.INT0IF=0;                        //  Clear the external interrupt flag
    47.     }
    48. }
    51. #pragma code InterruptVectorHigh = 0x08             //  This function simply jumps to the ISR code shown above.
    52. void InterruptVectorHigh (void)
    53. {
    54.     _asm
    55.     goto ISR //jump to interrupt routine
    56.     _endasm
    57. }
    58. #pragma code
    59. //=================================================================================================
    60. //
    61. //=================================================================================================
    66. //=================================================================================================
    67. //  Main Loop
    68. //=================================================================================================
    69. void main(void)
    70. {
    71.     CONFIG();
    73.     while(1)                            //  Run the program forever and let the interrupts take over.
    74.     {
    75.         sprintf(message,"Current Angle: %d \r\n",current_angle);
    76.         putsUSART(message);
    77.         Delay10KTCYx(125);
    78.     }
    80. }
    81. //=================================================================================================
    82. //
    83. //=================================================================================================
    88. //=================================================================================================
    89. //  Configuration Function
    90. //=================================================================================================
    91. void CONFIG(void)
    92. {
    93.     /* Code for handling all of the interrupt */
    94.     RCONbits.IPEN=1;                    //  Enable interrupt priority
    95.     INTCONbits.GIE=1;                   //  Enable global interrupts
    96.     INTCONbits.INT0IE=1;                //  Enable INT0 interrupts
    97.     INTCONbits.INT0IF=0;                //  Set external interrupt flag to zero
    98.     INTCON2bits.RBPU=1;                 // Disable Pull up
    99.     INTCON2bits.INTEDG0=1;              //  Interrupt enable on rising edge
    100.     INTCON3bits.INT1IE=1;               //  Enable External interrupt 1
    101.     INTCON2bits.INTEDG1=1;              //  Enable External interrupt on rising edge
    102.     INTCON3bits.INT1IF=0;               //  Set flag to zero
    103.     TRISBbits.TRISB0=1;                 //  Set PORTB0 to input
    104.     TRISBbits.TRISB1=1;                 //  Set PORTB1 to input
    105.     TRISBbits.TRISB2=0;                 //  Set PORTB2 to output
    106.     PORTB=0x03;                         // Enable high on inputs
    108.     /* USART Configuration */
    109.     TRISC=0x00;
    111. }
    112. //=================================================================================================
    113. // 
    114. //=================================================================================================
  2. t06afre

    AAC Fanatic!

    May 11, 2009
    What kind of debugging measures have taken? Have you done any single stepping and used the stimulus editor
  3. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    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:

    Code ( (Unknown Language)):
    1. #define GetVariable(Dest, Source) while(Dest != Source) Dest = Source
  4. t06afre

    AAC Fanatic!

    May 11, 2009
    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.
  5. crazyengineer

    Thread Starter Member

    Dec 29, 2010
    I tried to debug it, but I keep getting PK3Err0040 error message.
  6. t06afre

    AAC Fanatic!

    May 11, 2009
  7. t06afre

    AAC Fanatic!

    May 11, 2009
    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.
  8. raychar


    Nov 8, 2011
    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.

  9. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    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:
  10. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    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:

    Code ( (Unknown Language)):
    2. #pragma interrupt ISR
    3. void ISR ()
    4. {
    5.     if(INTCON3bits.INT1IF==1)            // Checks to see if the external
    6.                                          // interrupt [B]#1 [/B]has been triggered.
    7.     {
    8.         current_angle+=1;
    9.         INTCON3bits.INT1IF=0;            // Clear the external interrupt[B] #1 [/B]flag
    10.     }
    11. }
    Last edited: Oct 15, 2012
  11. takao21203

    Distinguished Member

    Apr 28, 2012
    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).
  12. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    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?
  13. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    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.
  14. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    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.

    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.
  15. nsaspook

    AAC Fanatic!

    Aug 27, 2009

    Oh I just knew this would start up again. :(
  16. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    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.
  17. nsaspook

    AAC Fanatic!

    Aug 27, 2009

    Every standard machine language code has a goto as it can reduce computation when every nanosecond counts, every language that doesn't have one has a feature that emulates (with the use of more resources) the same behavior with a structured "PC" title.
    Last edited: Oct 19, 2012
  18. takao21203

    Distinguished Member

    Apr 28, 2012
    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.
  19. THE_RB

    AAC Fanatic!

    Feb 11, 2008
    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.

    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!
  20. MMcLaren

    Well-Known Member

    Feb 14, 2010
    Oh my goodness, did someone actually try to offer this as credible authoritative (C18 compiler) advice (LOL)?