PIC16F628A - Help with clarification on datasheet

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.

C:
// Defines 
#define _XTAL_FREQ 4000000         // running the chip at 4Mhz
#define ID_MAX 63                  // highest ID number possible. 6 bits base^n
#define ID 1                       // #DEFINE for hardcoded ID's. Safe route for presentation
#define BOOT_STALL 750             // Number of mS to delay upon receiving ACK from raspi  
#define TMR_TWO_PERIOD 62500       // Remaing counts, 245 rolls roughly one second. 50% duty cycle
//#define TMR_TWO_HALF_PERIOD        // see above...
#define DISTRESS 0x11              // distress state
#define ALERT 0x02                 // alert state 
#define BOOT_MASK 0x40             // boot packet mask
#define ALARM_MASK 0xC0            // alarm packet mask

volatile char rx_buffer;           // dumping UART data in here for processing 
int timer_1_overflow_count = 0;    // Timer 1 overflow counter, keeps track of how often timer 1 overflows 
int timer_2_overflow_count = 0;    // Timer 2 overflow counter, keeps track of how often timer 2(16bit) overflows
int id_ok = 0;                     // Set high when id is echoed(accepted) back by raspi. 
volatile int tmr1_rc = 0;          // counts rolls of timer 1
volatile int tmr2_rc = 0;          // counts rolls of timer 2
char strobe_ok = 0x0;              // determines weather or not its ok for the strobe light to come on 
char vibe_ok = 0x0;                // determines weather or not its ok for the vibration to come on 
char packet = 0x0;                 // transmission packet
volatile char state = 0x00;        // 0x01 distress 0x02 alert        

// ISRS
void interrupt ISR (void)
{
     /* External RB0 rising edge interrupt */
    if(INTF)                             // RBO/INT interrupt, must clear flag in software. Disable Rx interrupt.
    {
        state = DISTRESS;
        INTF = 0;
    }
       
    if(RCIF && state != DISTRESS)        // If i receive a message from the network
    {                            
        rx_buffer = RCREG;
        CREN = 0;                                 // handles possible OREN situation
        CREN = 1;
        if(state != DISTRESS)
            state = ALERT;
    }
    // End Receiving ISR 
   
    // Timer 1(16 bit) ISR transmits distress signal 
    if(TMR1IF)
    {
        tmr1_rc++; 
        TMR1 = 0x7AE0;                   // dumping 31456 inside the register. Leaving roughly 1 second worth of counts remaining
        TMR1IF = 0;
    }
    // end timer 1 ISR
    // Timer 2 ISR drives piezo buzzer 
    if(TMR2IF)                           // timer 2 overflowed, used to control peizo
    {
        tmr2_rc++; 
        TMR2IF = 0;                      // reset flag  

    }
    // End Timer 2 ISR 
   
   
}

int id_start = 0;                        // starting point for ID's 0-63. 64 possible ID's. Only used for dynamic IDing system


void main()
{
    // ENABLING INTERRUPTS 
    GIE = 1;                       // General interrupt enable
    PEIE = 1;                      // peripheral interrupt enable
   
   

    // IO SETUP
    TRISA = 0xBD;                  // RA1 & RA6 outputs, rest inputs 
    TRISB = 0x2F;                  // RB4, RB6, RB7 outputs, rest inputs
    CMCONbits.CM = 0b111;          // turn off comparator
   
    // Timer setup
    /* TMR 0 Setup used to control strobe light */                
    // No Prescaler in timer mode... 
    OPTION_REGbits.T0CS = 0;       // timer mode
   
    /* TMR 1 Setup used to control Tx distress packet */
    T1CONbits.T1CKPS = 0b11;       // 1:8 prescaler 125000 counts = second. Roughly 2 rolls = one second. 16 bit timer
    TMR1 = 0x7AE0;                 // dumping 31456 inside the register. Leaving roughly 1 second worth of counts remaining
   
    /* TMR 2 Setup */
    T2CONbits.T2CKPS = 0b10;       // 1:16 prescaler 62500 counts = second. Roughly 245 rolls = one second. 
     
   
    /*************************************************       Boot Sequence Starts Here        ***********************************************************/
    /* details coming soon */
   
    // Check xbee health. Loops until xbee is powered on 
    while(!RB5)                    // if xbee isn't on(RB5 low)....(using a while as an if statement just incase xbee comes on after MCU)
    {
        RA1 = 0;                   // turn GIL off...
        RA6 = 1;                   // turn RIL on...
        __delay_ms(1000);          // wait one second
        RA6 = 0;                   // turn RIL off...    
        __delay_ms(1000);          // wait one second
    }
    RA1 = 1;                       // Turn GIL on
    // if we make it this far xbee is ok...time to init the UART 
    /************************ UART COMMS SETUP ********************************************/ 
    BRGH = 1;                      // highspeed selected, designed 9600 actual 9615                      
    SPBRG = 25;                    // baud rate = Fosc/16(SPBRG+1)
    CSRC = 0;                      // Asynchronous mode, internal BRG
    TXEN = 1;                      // Transmit enable
    SYNC = 0;                      // Asynchronous mode
    SPEN = 1;                      // Enable serial comm
    CREN = 1;                      // Continuous receive
    RCIE = 1;                      // RS232 receive interrupt enable
    /************************ END UART COMMS SETUP ****************************************/
   
    /* lets build our boot packet Dynamic IDs 
    packet = packet | 0x40;        // or'ing the packet bitfield with 01000000 in order to turn on bit 6. Now its a boot packet 
    while(1)                       // manually breaking this loop once we get an accepted ID or max out
    {
        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
        TXREG = packet;             // send it out to the network, only the raspi responds to boot packets
    }*/
   
    /* Lets build our boot packet Static IDs */
    packet = packet | BOOT_MASK;   // or'ing the packet bitfield with 01000000 in order to turn on bit 6. Now its a boot packet 
    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 
    while(rx_buffer != packet)     // keep broadcasting every 1.5 seconds until raspi echos it back. "Handshaking" in a very very simple/unreliable sense 
    {
        TXREG = packet;
        RA1 = 0; RA6 = 1;          // toggling lights to signal handshaking....
        __delay_ms(BOOT_STALL);    // delay for x seconds, doesn't really need to be that long
        RA1 = 1; RA6 = 0;          // toggling lights to signal handshaking....
        __delay_ms(BOOT_STALL);    // delay for x seconds, doesn't really need to be that long*/
        if(id_ok)                  // id is ok, no need to loop anymore 
            break;
    }
   
    /************************************************* Connected to Wireless e Data Network ***********************************************************/
    /*************************************************       Boot Sequence Ends Here        ***********************************************************/
   
    /*************************************************           Device Active              ***********************************************************/
    //T0IE = 1;                    // timer 0 overflow interrupt enable
    INTE = 1;                      // RBO/INT interrupt enable
    INTEDG = 1;                    // RBO/INT RISING edge, for now 
    RA1 = 1;                       // turn on GIL
    while(1)                       // Loop forever...state machine
    {
        // state machine
        switch(state)
        {
            case DISTRESS : 
                RA1 = 0;                         // kill GIL
                RA6 = 1;
                INTE = 0;                        // disable INT interrupt
                RCIE = 0;                        // RS232 receive interrupt disabled
                CREN = 0;
               
                vibe_ok = 0x00;
                if(!TMR1ON)
                {
                    T1CONbits.TMR1ON = 1;        // turn timer1 on Tx clock
                    T2CONbits.TMR2ON = 1;        // turn timer2 on Strobe clock
                    TMR1IE = 1;                    // timer 1 overflow interrupt enable 
                    TMR2IE = 1;                    // timer 2 overflow interrupt enable 
                }
                if(tmr1_rc >= 5)                 // 5 rolls..5 seconds.
                {
                    packet = packet | ALARM_MASK;// turn it into a fall packet
                    packet = packet | ID;        // attaching ID to last 6 bits 
                    TXREG = packet;              // transmit the distress signal                                  
                    tmr1_rc = 0;                 // reset roll counter
                }
                if(tmr2_rc >= 122)               // 122 rolls = half second
                {
                    RB4 = !RB4;                  // drive/silence peizo
                    RB7 = !RB7;                  // drive/silence strobe
                    tmr2_rc = 0;
                }
                break;
            case ALERT :
                if((rx_buffer & ALARM_MASK) == ALARM_MASK)   // someone transmitted, &'ing packet against fall mask 0xC0. Latching state, unlatched by INTF ISR
                {
                    RCIE = 0;                    // disable Rx Interrupt, someone fell we're in alarm until the device is power cycled. Ignore all other incoming packets
                    CREN = 0;
                    vibe_ok = 1;                 // putting us in "help" or "alert" mode
                    while(RCREG)
                        if(!RCREG)
                            GIE = 1;
                } else if ((rx_buffer & BOOT_MASK) == BOOT_MASK && !id_ok) {  // we've received a boot packet
                    if(rx_buffer == packet)      // boot packet matches what was sent out    
                    {
                        id_ok = 1;               // id accepted by network
                        while(RCREG)             // clear the fifo buffer
                            if(!RCREG)
                                GIE = 1;
                    }
                }
                if(vibe_ok & tmr2_rc >= 122)     // we're ok...but someone else fell wake up and help! Vibe on
                {
                    RB6 = !RB6;                // Vibe toggle
                    tmr2_rc = 0;
                } 
                break;
            default : 
            ;
        }
    }
}
 
Last edited:

dannyf

Joined Sep 13, 2015
2,197
Does this mean every time an interrupt fires I need to set GIE after I clear that specific interrupt flag? Th
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.
 

Papabravo

Joined Feb 24, 2006
21,158
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.
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.
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:

ErnieM

Joined Apr 24, 2011
8,377
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.
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?
 

ErnieM

Joined Apr 24, 2011
8,377
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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:

C:
GIE = 1;                       // General interrupt enable
PEIE = 1;                      // peripheral interrupt enable
INTE = 1;                      // RBO/INT interrupt enable
INTEDG = 1;                    // RBO/INT RISING edge, for now
RCIE = 1;                      // RS232 receive interrupt enable
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:

dannyf

Joined Sep 13, 2015
2,197
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.
Ahh so a slower transfer rate may help? On both sides PIC and xbee or just the xbee side
 

dannyf

Joined Sep 13, 2015
2,197
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
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.
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?
 

dannyf

Joined Sep 13, 2015
2,197
that sounds like what I'm struggling with.
Well, I don't know what your code is trying to do but on its surface, it has some oddities:

Code:
   if(RCIF && state != DISTRESS)        // If i receive a message from the network
    {                            
        rx_buffer = RCREG;
        CREN = 0;                                 // handles possible OREN situation
        CREN = 1;
        if(state != DISTRESS)
            state = ALERT;
    }
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:
                   while(RCREG)
                        if(!RCREG)
                            GIE = 1;
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.
 

Thread Starter

odm4286

Joined Sep 20, 2009
265
Well, I don't know what your code is trying to do but on its surface, it has some oddities:

Code:
   if(RCIF && state != DISTRESS)        // If i receive a message from the network
    {                           
        rx_buffer = RCREG;
        CREN = 0;                                 // handles possible OREN situation
        CREN = 1;
        if(state != DISTRESS)
            state = ALERT;
    }
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:
                   while(RCREG)
                        if(!RCREG)
                            GIE = 1;
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.
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?
 
Top