Jump to content
Sign in to follow this  
drumanart

Interrupt On Portb Problem

Recommended Posts

Hello.

I spent a entire afternoon in trying to get en interrupt on a PIC18F4420 on portb,2 working.

 

At portb,2 I have the IRQ pin of an nRF24L01 attached. So, this pin drives low when data is received and I would like that the program jumps to the void interrupt(void) routine.

For test purpose the program stays for ever in the interrupt function.

With my code the int2if flag gets high before a change of portb,2 occurs, but the idea is that the interrupt happens if the pin is pulled down.

 

To configure the PIC I made an init function:

.

void init(){
 osccon = 01110000b; // internal oscillator at 8 MHz.
 while (!iofs); // iofs OSCCON stable bit.

 adcon1 |= 0x0F; // All digital IO's, no analog ports required.


 trisb = 00000100b; // PortB, RB2 In, used to provoke an interrupt.
 intcon = INTCON_CONF; // 11101000b, GIE/PEIE enabled, PortB interrupt on change enabled, TMR0IE enabled,
 rcon  = RCON_CONF; // 00011100b, interrupt priority disabled.
 intcon3 = INTCON3_CONF; // 10010000b  INT2IP enabled, RB2, INT2IE, enabled.
 intcon2 = INTCON2_CONF; // 00000001b  Pullup enabled, Interrupt RB2 falling edge, High priority.
}

void interrupt(void){
 char aux = portb;
 if (int2if){   // to see if something happens.
 while(1);
 }
}

 

Thanks Martin

Interrupt_on Portb.c

Share this post


Link to post
Share on other sites

Hi

 

Your interrupt configuration is a bit messy.

You have enabled both INT2 and port B interrupt on change, you also have TMR0 interrupt enabled.

Also changed a few priority flags.

 

If you don't want to use priority you should leave al IP flags =1 (default) otherwise your interrupts will be vectored to a difeerent signal path and then blocked by IPEN or some other high level flags (look at the circuit diagram at page 92 of the datasheet).

 

By your "interrupt" function I would say you want to use INT2 not IOC.

 

The GIE flags are only enabled after all the other interrupt configuration are done and IF flags cleared, otherwise your program might be sent to the ISR before you even finish configuring all teh interrupt features.

 

 

I suggest the following sequence for enabling INT2.

void init(void){
 osccon = 01110000b; // internal oscillator at 8 MHz.
 while (!iofs); // iofs OSCCON stable bit.
 adcon1 |= 0x0F; // All digital IO's, no analog ports required.
 trisb = 00000100b; // PortB, RB2 in, to provoke an interrupt.

 intcon2.RBPU = 0;			// enable port B pull ups
 intcon2.INTEDG2 = 0;		// INT2 active on falling edge
 intcon3.INT2IE = 1;		// enable INT2 interrupt
 intcon.PEIE = 1;			// enable interrupts from peripherals

 // everything set now we can enable interrupts
 intcon3.INT2IF = 0;		// clear a possible pending interrupt
 intcon.GIE = 1;			// enable interrupts

} // void init(void)


void interrupt(void){
 char aux = portb;

 //  *** ERROR *** if (int2if){			// to see if something happens.
 if(intcon3.INT2IF){
intcon3.INT2IF = 0;  // clear flag to signal alredy processed
while(1);		// ONLY HALT IN HERE FOR INT2
 }			// if(intcon3.INT2IF)
}		// void interrupt(void)

 

 

Best regards

Jorge

Edited by JorgeF

Share this post


Link to post
Share on other sites

The thing is slightly more complex, as the program creates two PW, with timer0 and timer1. At start up I have to set the GIE & PEIE to configure the ISP module (wirless radio nRF24L01).

I changed the messy code following your advice and I can receive wireless data from the nRF24L01, but as soon as I start the timers (the disabled code underneath) the INT2IF want get high anymore and I can't receive wireless data any more.

So, either the PW works or the nRF24L01 works.

Still there is a missmatch.

Thanks Martin

 

 

void interrupt(void){
   if (spi_interrupt){
  spi_handle_interrupt();
   }

 else if (int2if){
  int2if = 0;				  //  IRQ of nRF24L01, if low reads data buffer.
  RX_TX_nRF();
  }

  //reload 20ms periode time.
   else if (tmr0if){
 tmr0l = 100;								   // Value for TIMER0: 100d.= 20ms - 50Hz
 tmr0if = false;							    // Clear INTCON overflow bit.(TMR0)
 count = 1;
 PW_0 = true;                                  // Strats SERVO 1 pulse.
 tmr1on = true ;                               // Enable TIMER1.
 tmr1if = false;                                 // PIR1 (clear timer1 overflow flag)
 tmr1h = TIME1_FIX;                      // Load fixed value into TIMER1 register;
 tmr1l = TIME_ON1;                       // Variable value for Servor 1.
 }

 else if ((tmr1if) && (count == 1)){
  PW_0 = false;                                  // Stops SERVO 1 pulse.
  PW_1 = true;                                   // Starts SERVO 2 pulse.
  tmr1if = false;                                  // PIR1 (clear timer1 overflow flag).
  tmr1h = TIME1_FIX;                       // Fixed value;
  tmr1l = TIME_ON2;                         // Value to be loaded for Servor 2.
  count++;
  }
 else if ((tmr1if) && (count == 2)){
  PW_1 = false;								 // Stops SERVO 2 pulse.
  tmr1on = false;							   // disable TIMER1.
  count = 0;
  }
 gie = true;				   // re-enable GIE.
}
void main()
{
init();                             // device and peripheral initialisation.

unsigned char get_byte;
char count = 0x02;
nrf_rx_mode();              // nRF receiver mode & power up.
peie = false;
gie = false;                    // disable all interrupts.

intcon2 = INTCON2_CONF;		 // 00000000b  Pullup enabled, Interrupt RB2 falling edge, Low priority.
intcon3 = INTCON3_CONF;		 // 00010000b RB2, INT2IE, enabled, INT2IP disabled.
peie = true;                                  // peripheral interrupts inabled.
int2if = false;                                // clear a possible pending interrupt.

// sets the timers for the PW modulation.

// if I enable this code the wireless module wont work, but the PW works.
/*t0con = TIME0_CONF;          // Timer0 enabled, precaler set to 1/128, 8 bit and load
 tmr0l = TIMEFR_VAL;		   // Value for TIMER0: 100d.= 20ms - 50Hz.

 t1con = TIME1_CONF;          // 00111000b, T1CON, 1/8 presc. Timer1 disabled. 
 tmr1h = TIME1_FIX;              // 11111110b    // to get the above times in ms. (8 bit) 
 tmr1if = false;                        // PIR1 (clear timer1 overflow flag).
 pie1 =  PIE1_CONF;             // TMR1 overflow bit enabled & EUSART interrupt enabled.
 tmr0ie = true;                        // timer 0 ext. interrupt enabled.*/

gie = true;                               // start all interrupts.			 

while (1){

 PW_LED_OnOff();              // toggle LED  Off.
 }
}
//
//

// function sends the received data to USART
void RX_TX_nRF(){
 unsigned char get_byte;
 char count = 0x02;
  PW_LED_OnOff();					  // DEBUG ONLY.
 nrf_read_fifo();					    // reads data from the payload.
 while (count < 5){                    // control function, prints to serial port.
   get_byte = nrf_get_byte(count);    // gets the data from data[] array.
   serial_printf(get_byte);		   // print the return value to the terminal.
   count++;
   }
   count = 0x02;                        // set the pointer in spi_exchange function.
   nrf_reset_status();			    // clear nrf24L01 status reg.
}
//
//
void init(){
 osccon = 01110000b;           // internal oscillator at 8 MHz.
 while (!iofs);						  // iofs OSCCON stable bit.

 adcon1 |= 0x0F;                   // All digital IO's, no analog ports required.
 trisa = 10111111b;               // RA6, ID_LED.
 trisb = 00000100b;               // RB0, RB1 PW out. RB2 IRQ for nRF24L01.
 trisc = 11000000b;               // RC6, RC7 RX; RC0 LED white, RC4 LED yellow.

 portb = 0x00;
 gie = true;                               // set GIE & PEIE to configure the nRF24L01.
 peie = true;

  // Serial interface
  serial_init(34);					  // (51) 38400 baud; (34) 57600 baud; DEBUG, before: BRG = 25 (19200 baud at 8MHz).
  serial_printf("initializing serial port");
  serial_print_lf();

  // Wireless interface
  nrf_init();                               // init the nRF24L01.
  serial_printf("nRF24L01 config done...");
  serial_print_lf();
 return;
}

Share this post


Link to post
Share on other sites

Hi

 

 

I only had time for a quick browse of your code, but found a couple of things that seem odd.

 

Assuming that the comments for INTCON2_CONF and INTCON3_CONF are correct.

Assuming that "spi_interrupt", "tmr0if", int2if, .... are macros that resolve to the correct INTCON and PIRx registers/bits.

 

I noticed that

1 - The "Init()" function is globaly enabling interrupts before they are configured. (GIE and PEIE), they are latter disabled in main.

2 - INTCON2_CONF is doing: **** RB pullup enable + INT0, INT1 & INT2 falling edge + INT0 & PortB IOC low priority ****

3 - INTCON3_CONF is doing: **** INT1 disabeld + INT2 enable + INT1 and INT2 low priority + clear INT1 & INT2 IF ****

4 - You are enabling global interrupts (GIE) in the "interrupt" function. DON't, this is done in the correct time by the PIC himself.

5 - Several interrupt sources where configured as low priority but priorities are not enabled, the interrupts can be lost.

6 - Also I see no "interrupt_low" function to handle low priority interrupts.

7 - In the last "else if" of the ISR the IF for TMR1 is not beeing cleared. The PIC will be trapped reentering the ISR. Worst even because you are enabling GIE what means it will reenter the ISR in a recursive way (before even leaving it) overflowing the stack.

8 - In the ISR you call "RX_TX_nRF()" that in turn enters a loop transfering all characters from the nRF fifo to a serial port. This will leave the PIC trapped inside the ISR for too long, some interrupts might be lost.

 

Just in this small details there are more than enought issues that will prevent the program working as expected.

 

 

Best regards

Jorge

 

PS: It looks like you still didn't diggest all the info from http://forum.sourceboost.com/index.php?showtopic=5062

Edited by JorgeF

Share this post


Link to post
Share on other sites

Hi Jorge.

So, I assume only if the IPEN of the RCON register is set the High/Low priority bits are valid and an interrupt_low function is activated.

 

 

Now, could I handle the PW routine with the TMR0 and TMR1 flags as low priority interrupts in the interrupt_low function and handle the INT2IF and the SSPIF for the ISP unit as high priority interrupts? ( I don't find a way to set TMR1 as low priority, nor the SSPIF)

Or is it easier to set the RCON bit as 0 and work in PIC16CXXX Compatibility mode.

 

What is the standard way if one creates PW with timers and have a ISP unit connected.

 

Thanks Martin

 

 

 

I

Share this post


Link to post
Share on other sites

Hi

 

 

AFAIK you are assuming wrong.

As for me, my only assumption is that the defaults are OK and the PIC lives well with them; rom there on I only tweak what I really need.

So far never had major issues with anything related to the inner workings of a PIC or any other kind of processor.

 

If you look at "section 9.0, Figure 9.1 at page 92 of the datasheet" you can see that the xxxIP flags are ANDed with the xxxIE flags and the xxxIF flags.

If you put a "0" at an xxxIP flag the signal path from xxxIF to the mcu high interrupt vector is blocked, whatever the value of IPEN.

 

The major difference betwen low and high interrupts is that while executing the Low_ISR, the mcu can be interrupted by a high priority interrupt, while the High_ISR can never be interrupted.

As for the main program, obviously it can be interrupted by both levels of interrupt priority.

 

 

Using or not using interrupt priorities is a project option that like so many others depends on the specifics of the project at hand.

The same goes for distributing interrupt sources betwen the two priority levels if we decide to use priorities.

We can allways say that the same aplies to deciding when to use interrupts and when to use poolong.

 

In the end its up to you to look at your project and decide what features to use and how to use them.

 

Never the less there are a few "pocket rules" that are generally take into account when deciding this things

1 - Generating timed signals ---> Use inetrrupts to avoid glitches.

2 - Receiving data from an external device ---> Use inetrrupts as you need to handle the received data before new data arrives (don't assume anything related to the transmiting device).

3 - Transmiting data to an external device ---> Use pooling, the external device can allways wait a few extra mili seconds for the next byte.

4 - Detecting events from the outside world ---> Depends on how fast they can occur.

5 - Geting data from an internal peripheral like an ADC ---> Can use pooling The result won't rotten if it stays still waiting a few extra micro seconds.

 

But don't forget, this are only "pocket rules", the kind we give to begginers (second half hour on the subject), given the apropriate context they can all be deemed wrong.

 

After deciding what is going to be handled with interrupts, then analyse their relative importance and decide of priorities are needed.

When deciding to use priorities them decide what goes in each priority.

 

Whatever you decide never forgett the most important rule of all keep ISRs small, just take care of the imediate actions to prevent data loss or glitches and let the main program take care of the heavy work. For example use the ISR to stuff received data in a buffer and let it there for later processing by the main program.

 

 

And the all universla rule K.I.S.S.

 

 

Best regards

Jorge

Share this post


Link to post
Share on other sites

Yeep Jorge.

Thanks for the advise of the AND and OR gates graphics at page 92 of the 18Fxx data-sheet this is helpful for understanding of interrupts.

 

As I had the PW for two servos with TMR0, TMR1 working really nicely and on the other hand the SPI unit (the Nordic 24L01 wireless radio) ) using another program was working fantastic (in my opinion) I didn't expect to have this troubles.

I'm sure the mess is already at the SPI interrupt PIR1,SSPIF. Probably I forgot to clear somewhere the SSPIF as I did with the TMR1IF flag (this is why I forced the GIE flag to 1) . I suppose this messes up the further interrupt routines.

 

I'll get there, for sure ..... kind regards Martin

Share this post


Link to post
Share on other sites

Hi

 

Did you already find were are the TMR1IF and SSPIP priority flags?

 

As for the SPI comms. Unlike RS232, the SPI and I2C comms speed is controled by the master, so if the PIC is the master, you don't even need to use interrupts, you can handle the SPI comunication in the main program loop by pooling SSPIF.

 

I would leave the PWM control in the high priority ISR to avoid glitches in the servos.

Also I would never do the processing of received data in the ISR (RX_TX_nRF), even worst when it is a blocking (waiting until the end of the message) process.

 

 

Thanks for the advise of the AND and OR gates graphics at page 92 of the 18Fxx data-sheet this is helpful for understanding of interrupts.

 

It seems that my posts have two problems....

They are big. But its information density is bigger, so carefully reading is in order.

You only noticed the info on the interrupt circuitry schematic the second time I wrote about ir, and even so it took some "bolding".

 

 

 

Best regards

Jorge

Edited by JorgeF

Share this post


Link to post
Share on other sites

Oh yes, the SSPIP is flag is the PIR1,3 and the TMR1F the PIR1,0.

Yesterday I tried to find the failure of my interrupt mess without having success. Still, as soon as I start the timers (TMR0, TMR1) fto run the PW the communication with the SPI unit fails.

Today I will handle the SPI pooling the SSPIF in the main function.

Would it be also possible to use the SSPSTAT, BF (buffer full) flag to handle the SPI module?

Thanks for your passion.

Martin

Share this post


Link to post
Share on other sites

Hi

 

Check the datasheet carefully.

I'm not sure but I think the BF flag is I2C only, and not used in SPI.

 

SPI works pretty much like a loop of shift registers (master and slave), so, in concept, you have a received byte each time you finish a transmission.

So you start a transmit by writing a byte to the buffer and have a received byte there when the transmission finishes, this is, when the SSPIF goes high.

 

 

Best regards

Jorge

Share this post


Link to post
Share on other sites

Jorge,

I found the bug, I had the T1CON, 3 (T1OSCEN) flag enabled. This is why the program always jumped to the void interrupt(void ) function. Both, the TMR0 and TMR1 timers, as well as the SSPIF flag are set as high priority and I handle them using the void interrupt(void ) function and the program works great.

If I put an oscilloscope at the PW output, even in receiving continuous data stream from the wireless SPI unit (nRF24L01 is great), the puls-with is not affected at all.

I did a little trial to manage the SPI in pooling the BF flag and also this works.

 

As I have now the abilities to handel18 stepper motors the last thing I need to do is to set up the CCP module for two other PW to use with two independent servo motors.

I suppose I will be in troubles shortly because the SPI module also uses the TMR2 oscillator.

Thanks for you help.

Martin

Share this post


Link to post
Share on other sites

Hi

 

Oh yes, the SSPIP is flag is the PIR1,3 and the TMR1F the PIR1,0.

 

This was about your question on defining priorities for SPI and TIMER1.

I noticed a typo in y question, I meant SSPIP and TMR1IP, both on IPR1.

 

 

Well, if things are running, thats good, you achieved the main target.

Nevertheless take care with program organization in special with ISR workload.

Poor choices on this subjects could not be an issue in a small program but they will bite later as the program grows, and them it will be harder to correct.

 

 

Best regards

Jorge

Share this post


Link to post
Share on other sites

Your content will need to be approved by a moderator

Guest
You are commenting as a guest. If you have an account, please sign in.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

×