PIC16F628A - Help with clarification on datasheet

Discussion in 'Embedded Systems and Microcontrollers' started by odm4286, Apr 13, 2016.

  1. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    I'm still having trouble with the interrupts in my project so I've taken the time to dig a little deeper into the datasheet. In the interrupt section there is the following. I need help understanding the part in bold.

    "The Interrupt Control register (INTCON) records individual interrupt requests in flag bits. It also has individual and global interrupt enable bits. A Global Interrupt Enable bit, GIE (INTCON<7>) enables (if set) all un-masked interrupts or disables (if cleared) all interrupts. Individual interrupts can be disabled through their corresponding enable bits in INTCON register. GIE is cleared on Reset. The “return-from-interrupt” instruction, RETFIE, exits interrupt routine as well as sets the GIE bit, which reenables RB0/INT interrupts. The INT pin interrupt, the RB port change interrupt and the TMR0 overflow interrupt flags are contained in the INTCON register. The peripheral interrupt flag is contained in the special register PIR1. The corresponding interrupt enable bit is contained in special registers PIE1. When an interrupt is responded to, the GIE is cleared to disable any further interrupt, the return address is pushed into the stack and the PC is loaded with 0004h. Once in the interrupt service routine, the source(s) of the interrupt can be determined by polling the interrupt flag bits. The interrupt flag bit(s) must be cleared in software before re-enabling interrupts to avoid RB0/ INT recursive interrupts. For external interrupt events, such as the INT pin or PORTB change interrupt, the interrupt latency will be three or four instruction cycles. The exact latency depends when the interrupt event occurs (Figure 14-15). The latency is the same for one or two-cycle instructions. Once in the interrupt service routine, the source(s) of the interrupt can be determined by polling the interrupt flag bits. The interrupt flag bit(s) must be cleared in software before re-enabling interrupts to avoid multiple interrupt requests."

    For some reason, my INT interrupt never fires...I'm wondering if my Rx interrupt is holding it up some how? Does this mean every time an interrupt fires I need to set GIE after I clear that specific interrupt flag? Thanks in advance for any help!.

    Here is a dumb question, in my circuit VDD is 3.3V. Is RB0 still expecting 5V to register a high or is 3.3 ok?
    Really pulling my hair out over this one...here is the code.

    Code (C):
    1.  
    2. // Defines
    3. #define _XTAL_FREQ 4000000         // running the chip at 4Mhz
    4. #define ID_MAX 63                  // highest ID number possible. 6 bits base^n
    5. #define ID 1                       // #DEFINE for hardcoded ID's. Safe route for presentation
    6. #define BOOT_STALL 750             // Number of mS to delay upon receiving ACK from raspi  
    7. #define TMR_TWO_PERIOD 62500       // Remaing counts, 245 rolls roughly one second. 50% duty cycle
    8. //#define TMR_TWO_HALF_PERIOD        // see above...
    9. #define DISTRESS 0x11              // distress state
    10. #define ALERT 0x02                 // alert state
    11. #define BOOT_MASK 0x40             // boot packet mask
    12. #define ALARM_MASK 0xC0            // alarm packet mask
    13.  
    14. volatile char rx_buffer;           // dumping UART data in here for processing
    15. int timer_1_overflow_count = 0;    // Timer 1 overflow counter, keeps track of how often timer 1 overflows
    16. int timer_2_overflow_count = 0;    // Timer 2 overflow counter, keeps track of how often timer 2(16bit) overflows
    17. int id_ok = 0;                     // Set high when id is echoed(accepted) back by raspi.
    18. volatile int tmr1_rc = 0;          // counts rolls of timer 1
    19. volatile int tmr2_rc = 0;          // counts rolls of timer 2
    20. char strobe_ok = 0x0;              // determines weather or not its ok for the strobe light to come on
    21. char vibe_ok = 0x0;                // determines weather or not its ok for the vibration to come on
    22. char packet = 0x0;                 // transmission packet
    23. volatile char state = 0x00;        // 0x01 distress 0x02 alert        
    24.  
    25. // ISRS
    26. void interrupt ISR (void)
    27. {
    28.      /* External RB0 rising edge interrupt */
    29.     if(INTF)                             // RBO/INT interrupt, must clear flag in software. Disable Rx interrupt.
    30.     {
    31.         state = DISTRESS;
    32.         INTF = 0;
    33.     }
    34.        
    35.     if(RCIF && state != DISTRESS)        // If i receive a message from the network
    36.     {                            
    37.         rx_buffer = RCREG;
    38.         CREN = 0;                                 // handles possible OREN situation
    39.         CREN = 1;
    40.         if(state != DISTRESS)
    41.             state = ALERT;
    42.     }
    43.     // End Receiving ISR
    44.    
    45.     // Timer 1(16 bit) ISR transmits distress signal
    46.     if(TMR1IF)
    47.     {
    48.         tmr1_rc++;
    49.         TMR1 = 0x7AE0;                   // dumping 31456 inside the register. Leaving roughly 1 second worth of counts remaining
    50.         TMR1IF = 0;
    51.     }
    52.     // end timer 1 ISR
    53.     // Timer 2 ISR drives piezo buzzer
    54.     if(TMR2IF)                           // timer 2 overflowed, used to control peizo
    55.     {
    56.         tmr2_rc++;
    57.         TMR2IF = 0;                      // reset flag  
    58.  
    59.     }
    60.     // End Timer 2 ISR
    61.    
    62.    
    63. }
    64.  
    65. int id_start = 0;                        // starting point for ID's 0-63. 64 possible ID's. Only used for dynamic IDing system
    66.  
    67.  
    68. void main()
    69. {
    70.     // ENABLING INTERRUPTS
    71.     GIE = 1;                       // General interrupt enable
    72.     PEIE = 1;                      // peripheral interrupt enable
    73.    
    74.    
    75.  
    76.     // IO SETUP
    77.     TRISA = 0xBD;                  // RA1 & RA6 outputs, rest inputs
    78.     TRISB = 0x2F;                  // RB4, RB6, RB7 outputs, rest inputs
    79.     CMCONbits.CM = 0b111;          // turn off comparator
    80.    
    81.     // Timer setup
    82.     /* TMR 0 Setup used to control strobe light */                
    83.     // No Prescaler in timer mode...
    84.     OPTION_REGbits.T0CS = 0;       // timer mode
    85.    
    86.     /* TMR 1 Setup used to control Tx distress packet */
    87.     T1CONbits.T1CKPS = 0b11;       // 1:8 prescaler 125000 counts = second. Roughly 2 rolls = one second. 16 bit timer
    88.     TMR1 = 0x7AE0;                 // dumping 31456 inside the register. Leaving roughly 1 second worth of counts remaining
    89.    
    90.     /* TMR 2 Setup */
    91.     T2CONbits.T2CKPS = 0b10;       // 1:16 prescaler 62500 counts = second. Roughly 245 rolls = one second.
    92.      
    93.    
    94.     /*************************************************       Boot Sequence Starts Here        ***********************************************************/
    95.     /* details coming soon */
    96.    
    97.     // Check xbee health. Loops until xbee is powered on
    98.     while(!RB5)                    // if xbee isn't on(RB5 low)....(using a while as an if statement just incase xbee comes on after MCU)
    99.     {
    100.         RA1 = 0;                   // turn GIL off...
    101.         RA6 = 1;                   // turn RIL on...
    102.         __delay_ms(1000);          // wait one second
    103.         RA6 = 0;                   // turn RIL off...    
    104.         __delay_ms(1000);          // wait one second
    105.     }
    106.     RA1 = 1;                       // Turn GIL on
    107.     // if we make it this far xbee is ok...time to init the UART
    108.     /************************ UART COMMS SETUP ********************************************/
    109.     BRGH = 1;                      // highspeed selected, designed 9600 actual 9615                      
    110.     SPBRG = 25;                    // baud rate = Fosc/16(SPBRG+1)
    111.     CSRC = 0;                      // Asynchronous mode, internal BRG
    112.     TXEN = 1;                      // Transmit enable
    113.     SYNC = 0;                      // Asynchronous mode
    114.     SPEN = 1;                      // Enable serial comm
    115.     CREN = 1;                      // Continuous receive
    116.     RCIE = 1;                      // RS232 receive interrupt enable
    117.     /************************ END UART COMMS SETUP ****************************************/
    118.    
    119.     /* lets build our boot packet Dynamic IDs
    120.     packet = packet | 0x40;        // or'ing the packet bitfield with 01000000 in order to turn on bit 6. Now its a boot packet
    121.     while(1)                       // manually breaking this loop once we get an accepted ID or max out
    122.     {
    123.         packet = packet | id_start; // copying any high bits from id_start into packet. So now we have a "boot packet" with an id stored in bits 0-5
    124.         TXREG = packet;             // send it out to the network, only the raspi responds to boot packets
    125.     }*/
    126.    
    127.     /* Lets build our boot packet Static IDs */
    128.     packet = packet | BOOT_MASK;   // or'ing the packet bitfield with 01000000 in order to turn on bit 6. Now its a boot packet
    129.     packet = packet | ID;          // copying any high bits from ID into packet. So now we have a "boot packet" with an id stored in bits 0-5
    130.     while(rx_buffer != packet)     // keep broadcasting every 1.5 seconds until raspi echos it back. "Handshaking" in a very very simple/unreliable sense
    131.     {
    132.         TXREG = packet;
    133.         RA1 = 0; RA6 = 1;          // toggling lights to signal handshaking....
    134.         __delay_ms(BOOT_STALL);    // delay for x seconds, doesn't really need to be that long
    135.         RA1 = 1; RA6 = 0;          // toggling lights to signal handshaking....
    136.         __delay_ms(BOOT_STALL);    // delay for x seconds, doesn't really need to be that long*/
    137.         if(id_ok)                  // id is ok, no need to loop anymore
    138.             break;
    139.     }
    140.    
    141.     /************************************************* Connected to Wireless e Data Network ***********************************************************/
    142.     /*************************************************       Boot Sequence Ends Here        ***********************************************************/
    143.    
    144.     /*************************************************           Device Active              ***********************************************************/
    145.     //T0IE = 1;                    // timer 0 overflow interrupt enable
    146.     INTE = 1;                      // RBO/INT interrupt enable
    147.     INTEDG = 1;                    // RBO/INT RISING edge, for now
    148.     RA1 = 1;                       // turn on GIL
    149.     while(1)                       // Loop forever...state machine
    150.     {
    151.         // state machine
    152.         switch(state)
    153.         {
    154.             case DISTRESS :
    155.                 RA1 = 0;                         // kill GIL
    156.                 RA6 = 1;
    157.                 INTE = 0;                        // disable INT interrupt
    158.                 RCIE = 0;                        // RS232 receive interrupt disabled
    159.                 CREN = 0;
    160.                
    161.                 vibe_ok = 0x00;
    162.                 if(!TMR1ON)
    163.                 {
    164.                     T1CONbits.TMR1ON = 1;        // turn timer1 on Tx clock
    165.                     T2CONbits.TMR2ON = 1;        // turn timer2 on Strobe clock
    166.                     TMR1IE = 1;                    // timer 1 overflow interrupt enable
    167.                     TMR2IE = 1;                    // timer 2 overflow interrupt enable
    168.                 }
    169.                 if(tmr1_rc >= 5)                 // 5 rolls..5 seconds.
    170.                 {
    171.                     packet = packet | ALARM_MASK;// turn it into a fall packet
    172.                     packet = packet | ID;        // attaching ID to last 6 bits
    173.                     TXREG = packet;              // transmit the distress signal                                  
    174.                     tmr1_rc = 0;                 // reset roll counter
    175.                 }
    176.                 if(tmr2_rc >= 122)               // 122 rolls = half second
    177.                 {
    178.                     RB4 = !RB4;                  // drive/silence peizo
    179.                     RB7 = !RB7;                  // drive/silence strobe
    180.                     tmr2_rc = 0;
    181.                 }
    182.                 break;
    183.             case ALERT :
    184.                 if((rx_buffer & ALARM_MASK) == ALARM_MASK)   // someone transmitted, &'ing packet against fall mask 0xC0. Latching state, unlatched by INTF ISR
    185.                 {
    186.                     RCIE = 0;                    // disable Rx Interrupt, someone fell we're in alarm until the device is power cycled. Ignore all other incoming packets
    187.                     CREN = 0;
    188.                     vibe_ok = 1;                 // putting us in "help" or "alert" mode
    189.                     while(RCREG)
    190.                         if(!RCREG)
    191.                             GIE = 1;
    192.                 } else if ((rx_buffer & BOOT_MASK) == BOOT_MASK && !id_ok) {  // we've received a boot packet
    193.                     if(rx_buffer == packet)      // boot packet matches what was sent out    
    194.                     {
    195.                         id_ok = 1;               // id accepted by network
    196.                         while(RCREG)             // clear the fifo buffer
    197.                             if(!RCREG)
    198.                                 GIE = 1;
    199.                     }
    200.                 }
    201.                 if(vibe_ok & tmr2_rc >= 122)     // we're ok...but someone else fell wake up and help! Vibe on
    202.                 {
    203.                     RB6 = !RB6;                // Vibe toggle
    204.                     tmr2_rc = 0;
    205.                 }
    206.                 break;
    207.             default :
    208.             ;
    209.         }
    210.     }
    211. }
    212.  
     
    Last edited: Apr 13, 2016
  2. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,648
    764
    Read about RETFIE

    RETFIE excerpt.png
     
  3. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    Depending on what you meant by "fires".

    As the data sheet suggests, you should never ever set gue within the iar. Many hobbyists do that and that's plain wrong.

    Once you exit the isr, gie is automatically set for you so there is nothing else for you to do.
     
  4. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    Thank you both for the replies. I'll have to play around more, I still cannot figure out why my INT interrupt never fires after I enter my while(1) loop. Everything up until that point in the code works fine, I just never enter the DISTRESS case of my state machine.
     
  5. Papabravo

    Expert

    Feb 24, 2006
    10,144
    1,790
    There are two separate conditions for being able to service an interrupt. GIE being equal to 1 is only the first condition. Each interrupt also has a separate flag just for that particular interrupt. Normally this flag for the particular interrupt is a set it and forget it proposition during initialization.
     
  6. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    Thanks Pb, strange thing is if I comment my Rx isr out completely and set packet = to A (faking the "Ack") the RB0 interrupt fires no problem. I may resort to polling just to get this ready for presentation but I'm trying my best to avoid that and do it the right way.
     
    Last edited: Apr 13, 2016
  7. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,388
    1,605
    Do not try to reset GIE in your interrupt routine. Assuming you coded the ISR correctly then C is doing this for you, and if you do it yourself bad things may happen like the ISR is called while it is still running. Very bad.

    Do you set TXIE to get these interrupts to fire? I see no mention of that or PIE1 in your code.
     
  8. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    No, do I need to? I've only used tmr1, tmr2, Rx, and INT interrupts. I've also set PEIE to 1 to enable peripheral interrupts. Am I missing something?
     
  9. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,388
    1,605
    Yes, you need to enable the interrupts from the USART. That is what you are missing. ;)
     
  10. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,388
    1,605
    Every interrupt source needs to be individually enabled. The perf interrupts also need the second enable for historical reasons as they came way later in the design of the core.
     
  11. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    hmm I hope so! I'm still confused, here is a list of the interrupts I've enabled. Remember my Rx interrupt works perfectly, its the INT interrupt that is giving me a hard time :confused:

    Code (C):
    1.  
    2. GIE = 1;                       // General interrupt enable
    3. PEIE = 1;                      // peripheral interrupt enable
    4. INTE = 1;                      // RBO/INT interrupt enable
    5. INTEDG = 1;                    // RBO/INT RISING edge, for now
    6. RCIE = 1;                      // RS232 receive interrupt enable
    7.  
    In short, I want to interrupt when I receive data but ONLY if RB0 isn't high. I'm starting to wonder if polling is a better option for this?
     
    Last edited: Apr 13, 2016
  12. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    You don't need to enable txie if only receiving data.

    9600 bps gets to about 1000 ticks at 1Mips. So if you run it close, you may end up looping in the isr.
     
  13. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    Ahh so a slower transfer rate may help? On both sides PIC and xbee or just the xbee side
     
  14. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    Just the receiver. Basically at 9600bps, the pic received each byte every 1ms. You need to make sure that your isr finishes within 1ms.

    Or rcif is always set and you miss data.
     
  15. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    Hmm that sounds like what I'm struggling with. It seems like I get stuck in the rx isr, hopefully slowing down the rate will give the program some "breathing room". Sorry for the dumb question, but if my xbee and pic communicate bidirectionally, wouldn't changing the bit rate on one affect communications?
     
  16. dannyf

    Well-Known Member

    Sep 13, 2015
    1,811
    362
    Well, I don't know what your code is trying to do but on its surface, it has some oddities:

    Code (Text):
    1.  
    2.    if(RCIF && state != DISTRESS)        // If i receive a message from the network
    3.     {                            
    4.         rx_buffer = RCREG;
    5.         CREN = 0;                                 // handles possible OREN situation
    6.         CREN = 1;
    7.         if(state != DISTRESS)
    8.             state = ALERT;
    9.     }
    10.  
    11.  
    What's the point of testing "state != DISTRESS" again in the isr? And what happens if RCIF is set but state == DISTRESS?

    And here is this one:

    Code (Text):
    1.  
    2.                    while(RCREG)
    3.                         if(!RCREG)
    4.                             GIE = 1;
    5.  
    What exactly is it trying to do?

    9600bps isn't too fast, even for a slow PIC. If your PIC has trouble keeping up at that speed, something else is happening.
     
  17. odm4286

    Thread Starter Active Member

    Sep 20, 2009
    155
    5
    That first bit was a little "debug" code I through in there out of fustration. Basicly I wanted to skip that ISR if state = DISTRESS. Once in the distress state I don't care about incomming messages.

    The second part is just my way of clearing the fifo buffer. Is there a better way?
     
Loading...