Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Reentrancy problem

Status
Not open for further replies.

tattee

New Member
I got this error using MikroC "Reentrancy not allowed function[Spi_Write] called in both main and interrupt threads". I used PIC16F873 and having the SPI communication at the moment.

Why do I get this problem? How will I solve this? I really have to call some functions inside the interrupt to perform some task.
 
I got this error using MikroC "Reentrancy not allowed function[Spi_Write] called in both main and interrupt threads". I used PIC16F873 and having the SPI communication at the moment.

Why do I get this problem? How will I solve this? I really have to call some functions inside the interrupt to perform some task.

A Reenterant function is one that can be called when it is already active. In the case of your interrupt routine I think you are getting interrupts while you are still inside your routine. This should not happen because the interrupt should be off while in the interupt service function.

Post the code. Use [code] ... your code here [/code] to keep the code formated.
 
Think what would happen if an interrupt came along whilst the main code was half way through transmitting a byte. Change your code so that all the SPI stuff is in either the main code or the interrupt. If your unsure how to do this then post your code (put
Code:
 before it and
after it).

Mike.
 
Im trying to communicate between PIC16F873 and a digital potentiometer using SPI communication. I attached the code below.
 

Attachments

  • test.c
    1.8 KB · Views: 295
Change it so your interrupt routine sets a variable called pot to 0/1 and change move_pot to be,
Code:
void move_pot()
{
  if(Pot==1)
    Spi_Write(0xff); 
  else
    Spi_Write(0x00); 
  SPI_CS_POT = 0;
  PORTC.F0 = 1;
  Spi_Write(0b01);                                 
  Spi_Write(0x82);                                 //Set digital potentiometer to midpoint (5K)
  SPI_CS_POT = 1;
}

Mike.
 
By the way, will it interrupt the main program? Do I have to put the mov_pot function to a loop so it does the changes as soon as the interrupt is activated?
 
Without knowing the hardware I don't know if you need to call it repeatedly. An alternative way to do it would be,
Code:
void main()
{

  init_pic();
  init_timer0();
  Spi_Init_Advanced(MASTER_OSC_DIV4,
                    DATA_SAMPLE_MIDDLE,
                    CLK_IDLE_LOW,
                    LOW_2_HIGH);
 
  move_pot();
  
  INTCON = 0b10011000;
  
  do {
    if(Pot==1)
      Spi_Write(0xff); 
    else
      Spi_Write(0x00); 

     } while(1);
 }

Or, if calling the SPI function repeatedly is a problem then set Pot to 1/2 and change the code to be,
Code:
    if(Pot==1){
      Spi_Write(0xff); 
      Pot=0;
    }else if(Pot==2){
      Spi_Write(0x00);
      Pot=0;
    }

Mike.
 
BoostC too

This is an issue with BoostC as well so perhaps with many microcontroller Cs.
In any case the code in an interrups should be as short as possible. No delays.... State variables as suggested above let you do the slow stuff in the main line.
 
Hi Guys!:)

I just wanna update you on my project. So, far the suggestion made by Pommie has had great success. Thanks again Pommie!;)

Now, Ive run into another problem.. Here's the code:

Code:
void Interrupt()
{
  if(INTCON.INTF)
  {
       SWITCH = W ;
       PORTA.F1 = 1;
       INTCON.INTF = 0;
  }
  else if(INTCON.RBIF)
  {
      if(PORTB.F5 == 0)                 
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F6 == 0)             
      {
         SWITCH = Y;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F7 == 0)
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
   }
}

void main()
{ 
   init_all();

   do {
        switch(SWITCH)
        {
          case X:
                  INTCON.GIE = 0;
                 //do something here           
                 INTCON = 0b10011000;
          break;

          case Y:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;

          case Z:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;
          
          case W:
                 INTCON.GIE = 0;
                 PORTA.F0 = 1;
                 INTCON = 0b10011000;
          break;
          }
     } while(1);

For the INTCON.RBIF part, everything worked fine. The code reacts exactly as expected everytime PORTS 5,6,and 7 are interrupted.. The problem comes when I try to interrupt the INTCON.INTF. my code doesn't interrupt at all. why is this happening?

take note that when I try to interrupt INTCON.INTF, the PORTA.F1 = 1; is executed but not the case W: statement.
 
here is the edited code:
Code:
#define VOLUME_UP            1
#define VOLUME_DOWN       2
#define DIAL              3
#define DTMF_RECIEVED      4

unsigned short SWITCH = 0;

void Interrupt()
{
  if(INTCON.INTF)
  {
       SWITCH = W ;
       PORTA.F1 = 1;
       INTCON.INTF = 0;
  }
  else if(INTCON.RBIF)
  {
      if(PORTB.F5 == 0)                 
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F6 == 0)             
      {
         SWITCH = Y;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F7 == 0)
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
   }
}

void main()
{ 
   init_all();

   do {
        switch(SWITCH)
        {
          case X:
                  INTCON.GIE = 0;
                 //do something here           
                 INTCON = 0b10011000;
          break;

          case Y:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;

          case Z:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;
          
          case W:
                 INTCON.GIE = 0;
                 PORTA.F0 = 1;
                 INTCON = 0b10011000;
          break;
          }
     } while(1);
 
oh im sorry, i wasn't able to edit the line.. here it is:
Code:
#define X            1
#define Y      2
#define Z             3
#define W      4

unsigned short SWITCH = 0;

void Interrupt()
{
  if(INTCON.INTF)
  {
       SWITCH = W ;
       PORTA.F1 = 1;
       INTCON.INTF = 0;
  }
  else if(INTCON.RBIF)
  {
      if(PORTB.F5 == 0)                 
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F6 == 0)             
      {
         SWITCH = Y;
         INTCON.RBIF = 0;
      }
      else if(PORTB.F7 == 0)
      {
         SWITCH = X;
         INTCON.RBIF = 0;
      }
   }
}

void main()
{ 
   init_all();

   do {
        switch(SWITCH)
        {
          case X:
                  INTCON.GIE = 0;
                 //do something here           
                 INTCON = 0b10011000;
          break;

          case Y:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;

          case Z:
                 INTCON.GIE = 0;
                 //do something here   
                 INTCON = 0b10011000;
          break;
          
          case W:
                 INTCON.GIE = 0;
                 PORTA.F0 = 1;
                 INTCON = 0b10011000;
          break;
          }
     } while(1);
 
The only reason I can think of for that strange behaviour is that a port B change is happening at the same time as the int0 interrupt and so the code gets executed twice and SWITCH gets set to something else. Note that a pin going from 0 to 1 will also produce a change interrupt.

What happens if you change it to,
Code:
  if(INTCON.INTF)
  {
       SWITCH = W ;
       PORTA.F1 = 1;
       INTCON.INTF = 0;
       [COLOR="Red"]INTCON.RBIE = 0;    //turn off port b interrupts
       INTCON.RBIF = 0;    //and clear it[/COLOR]
  }
  else if(INTCON.RBIF)
  {
      if(PORTB.F5 == 0)
The interrupt will get turned back on when the switch W is executed and so the code should still work correctly.

Mike.
 
INTCON.INTF now works!!:D

The problem is case W: never stops executing. It seems that INTCON.INTF never resets.
 
If the changes I suggested made it work then it is the RBIF that is causing the problem. I assume that you only want the relevant code to execute when the pin goes low. The way it is currently written you could (and did) get strange results. To make it edge triggered requires a little more code but may save you some future headaches. Let me know if you want me to post the code.

To stop case W from continually executing you need to set SWITCH to zero in the case W code.

Mike.
 
Sure, I would want to know about edge triggering. Ive been seeing those terms in the datasheet but i never really had an idea what it meant. It would be really helpful if you could post the code on how to do it.

By the way, Im new to firmware programming and I've learned a lot from you! Than you very much!!:D
 
This is how I would make it edged triggered,
Code:
#define X      1
#define Y      2
#define Z      4	//now powers of 2
#define W      8	//I.E bit values

unsigned short SWITCH = 0;
unsigned char Previous;


void Interrupt()
unsigned char Edges,Pins;
{
  if(INTCON.INTF)
  {
       SWITCH |= W;
       PORTA.F1 = 1;
       INTCON.INTF = 0;
  }
  if(INTCON.RBIF)
  {
      Pins=PORTB;		//read the inputs
      Pins=~Pins;		//invert so active low		
      Edges=Pins^Previous;	//find bits that have changed
      Edges&=Pins;		//and are now high
      Previous=Pins;		//keep copy for next time
      INTCON.RBIF = 0;
      if(Edges.5 == 0)                 
      {
         SWITCH |= X;		//set X bit
      }
      if(Edges.6 == 0)             
      {
         SWITCH |= Y;
      }
      if(Edges.7 == 0)
      {
         SWITCH |= X;
      }
   }
}

void main()
{ 
   init_all();

   do {
	if(SWITCH & X){		//if X bit set then
	    //X code
            SWITCH&=255-X;	///reset X bit
        }
	if(SWITCH & Y){
	    //Y code
            SWITCH&=255-Y;
        }
	if(SWITCH & Z){
	    //Z code
            SWITCH&=255-Z;
        }
	if(SWITCH & W){
	    //W code
	    PORTA.F0 = 1;
            SWITCH&=255-W;
        }

     } while(1);
The way it works is by keeping a previous copy of the state of PORTB. By XORing the current and previous states we find any bits that have changed. To find any bits that were previously zero and are now one we AND the bits that have changed with the current state. This is of course the wrong way around as we want to detect bits that went from 1 to 0 and so we simply invert PORTB after we read it.

The other main change needed is because two events may happen at the same time. Because of this we need the ability to tell the main program that this happened and so we set bits in SWITCH instead of a value. These changes wouldn't be required if only 1 event could happen at once.

Mike.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top