Function delaration problem in mikroC Pro for PIC

Status
Not open for further replies.

wolf12

Advanced Member level 4
Joined
Mar 31, 2011
Messages
109
Helped
1
Reputation
2
Reaction score
1
Trophy points
1,298
Activity points
2,102
If I create two functions and the execute the 1st function in the 2nd function or 2nd function in the 1st function I get the problem that function is not declared depeding on the order of the code. How to fix this?
example,
void func1 (void)
{
func2();
}
void func2(void)
{
func1();
}

This order will generate a compile error and say that func2 is not defined.

void func2(void)
{
func1();
}
void func1 (void)
{
func2();
}
This will say func1 is not defined :???:
 

Your code demonstrates a form of Recursion.

Both func1() and func2() are recursive function in respect to each other.

The compiler error can be eliminated by the use of function prototypes:

Code:
#include <stdio.h>

void func1(void);
void func2(void);

void main(void)
{
    .......
    .......

}
void func1(void)
{
    func2();
}
void func2(void)
{
    func1();
}

Recursion in C and C++

Be aware that each call of either function result in a PUSH on the STACK and eventually there maybe a STACK Overflow if allowed to continue indefinitely.

BigDog
 

It worked!! Thanks alot!!
By the way #include <stdio.h> was not nessasary, I think its nessasary only for PC programs.
And since you mentioned stack overflows, I always had the problem whether nested while loops or if conditions can cause stack overflows. Do they?
 

By the way #include <stdio.h> was not nessasary, I think its nessasary only for PC programs.

The #include <stdio.h> was provide just to give you a reference point of where the function prototypes should be located. The selection of "stdio.h" was just a commonly used header file which came to mind.

And since you mentioned stack overflows, I always had the problem whether nested while loops or if conditions can cause stack overflows. Do they?

It depends on what is in the nested loops.

"If" conditional statements rarely, if ever, unless there is a double recursive function such as your example above contained within.

Do you have an example program you could post which results in a stack overflow?

BigDog
 
Reactions: FvM

    FvM

    Points: 2
    Helpful Answer Positive Rating

Code:
unsigned short i, current_duty, number,d1,d2,d3,dtmf;

//bitwise declaration for dtmf inputs
sbit std at PORTD.B0;
sbit q4 at PORTD.B1;
sbit q3 at PORTD.B2;
sbit q2 at PORTD.B3;
sbit q1 at PORTD.B4;

//bitwise declaration for L293B outputs
sbit lm1 at PORTC.B4; //left motor positive
sbit lm2 at PORTC.B5;
sbit rm1 at PORTC.B6;
sbit rm2 at PORTC.B7;

void dtmfToNumber(void);
void manualMode(void);
void modeSelection(void);
void accelerate(void);
void decelerate(void);
void turnLeft(void);
void turnRight(void);

//main function
void main(void)
{

  TRISD = 0b00011111;               // Configure PORTD
  TRISC = 0;

  for (i = 0; i <= 5; i++)
  {
         PORTD.F7 = ~PORTD.F7;     // Initialize RD7
         Delay_ms(1000);          // one second delay
  }

  Pwm1_Init(5000);
  current_duty = 0;
  Pwm1_Set_Duty(0);

  PORTD.F7 = 1;

  Pwm1_Start();
//call the password func!
  modeSelection();
}
void dtmfToNumber(void)    //number recognition
{
  dtmf.f0 = q1;        // assigning bits
  dtmf.f1 = q2;
  dtmf.f2 = q3;
  dtmf.f3 = q4;
  //recognizing
  if(dtmf == 0b0010) number = 2;
  if(dtmf == 0b0100) number = 4;
  if(dtmf == 0b0110) number = 6;
  if(dtmf == 0b0101) number = 5;
  if(dtmf == 0b1011) number = 11; // *key
  return;
}
/*removed - recursive stackoverflow problem
short dtmfToNumber(void)    //number recognition
{
  dtmf.f0 = q1;        // assigning bits
  dtmf.f1 = q2;
  dtmf.f2 = q3;
  dtmf.f3 = q4;
  //recognizing
  if(dtmf == 0b0010) return 2;
  if(dtmf == 0b0100) return 4;
  if(dtmf == 0b0110) return 6;
  if(dtmf == 0b0101) return 5;
  if(dtmf == 0b1011) return 11; // *key
}
*/
void modeSelection(void)
{
    Pwm1_Set_Duty(0); //stop the robot

    while(1)
    {
        if(std == 1)    //if user pressed a key
        {
          dtmfToNumber();
          if(number == 1)         manualMode();
        }
    }
}
void manualMode(void)  // manual mode
{ 
  while(1)
  {
   if(std == 1)      dtmfToNumber();
   if(number == 2)   accelerate();
   if(number == 4)   turnLeft();
   if(number == 5)   decelerate();
   if(number == 6)   turnRight();
   if(number == 11)  modeSelection();
  }

}
void accelerate(void)
{
         lm1 = 1;
         lm2 = 0;
         rm1 = 1;
         rm2 = 0;

         if (current_duty < 255)
         {
             current_duty++;
             Pwm1_Set_Duty(current_duty);
             Delay_ms(30);    
         }
         number = 10;  // assign a non defined number to stop automatically coming back to this func even when key is released.
         return;
}
void turnLeft(void)
{
         lm1 = 0;
         lm2 = 0;
         rm1 = 1;
         rm2 = 0;
         number = 10;
         return;
}
void decelerate(void)
{
         lm1 = 1;
         lm2 = 0;
         rm1 = 1;
         rm2 = 0;
         if(current_duty > 0)
         {
             current_duty--;
             Pwm1_Set_Duty(current_duty);
             Delay_ms(30);
         }
         number = 10;
         return;
}



void turnRight(void)
{
         lm1 = 1;
         lm2 = 0;
         rm1 = 0;
         rm2 = 0;
         number = 10;
         return;
}
This one causes an error list.
First error is Recurtion or cross-calling of 'Mul_16x16_U'. Then Recursion or cross-calling error of for all functions except main function, and also says Not enough RAM for call stack.
 

What is the exact model of PIC which you are compiling the above program, PIC16F877A, etc?

BigDog
 

What is the exact model of PIC which you are compiling the above program, PIC16F877A, etc?

BigDog

Yes , 16F877A. Sorry I forgot to mention earlier.
 

What is the exact model of PIC which you are compiling the above program, PIC16F877A, etc?

BigDog

I found these in mikroC help
Recursive function calls can't contain any function parameters and local variables due to the PIC’s stack and memory limitations.
Functions reentrancy is allowed if the function has no parameters and local variables, or if the local variables are placed in the Rx space.

Also I found what Rx is,
This memory specifier allows variable to be stored in the Rx space (Register file). Note: In most of the cases, there will be enough space left for the user variables in the Rx space. However, since compiler uses Rx space for storing temporary variables, it might happen that user variables will be stored in the internal data SRAM, when writing complex programs.
Example // puts y in Rx space
sfr char rx y;

In the dtmfToNumber function I'm changing two local variables right? One is "dtmf", and the other is "number".
So should I put those two local variables in Rx space?

Also, can I return data from recursive functions without causing stack overflows?
 

Hi,

Actually, you problems are stemming from two routines:

Code:
void modeSelection(void)
{
    Pwm1_Set_Duty(0); //stop the robot

    while(1)
    {
        if(std == 1)    //if user pressed a key
        {
          dtmfToNumber();
          if(number == 1)         manualMode();
        }
    }
}
void manualMode(void)  // manual mode
{ 
  while(1)
  {
   if(std == 1)      dtmfToNumber();
   if(number == 2)   accelerate();
   if(number == 4)   turnLeft();
   if(number == 5)   decelerate();
   if(number == 6)   turnRight();
  [COLOR="#FF0000"] if(number == 11)  modeSelection();[/COLOR]
  }

You have double recursion occurring between the above two routines.

If you comment out the last statement in manualMode(), "if(number == 11) modeSelection();", marked in red, your program will compile without error.


I've been looking over your code and trying to figure out the best way to solve the issue.

BigDog

---------- Post added at 12:16 ---------- Previous post was at 12:07 ----------

use the keyword "reentrant" to solve your problem when you declare the function prototype....

There is no keyword "reentrant".
 
Reactions: FvM

    FvM

    Points: 2
    Helpful Answer Positive Rating
Thanks.
I was thinking to put a navigational mode function as well with another condtion if (number == 2). Thats why I want to exit from manual mode and enter the navigational mode using the mode selection function. Is there a possible way to do that?
 

Yes, I believe so.

After examining your code I am convinced the flow of the program in general could be improved quite a bit.

Please explain to me the actual purpose of your program in detail, define each task and its requirements.

If you can step by step.

BigDog
 


Its a robot controlled using a mobile phone. MT8870 is used as the dtmf decorder. MT8870 gives a 4bit output representing a dtmf tone. (the tones you hear when you press a key in a phone). I connected those 4pins to PORTD pins of 16f877a. 8870's outpus are latched. But std pin of the IC goes high only when dtmf decording happens. So you need to check its std pin to confirm that user has released the button or not.

User takes a call to the phone connected to dtmf ic and controls the robot with the keypad. dtmfToNumber function converts the 4bits to the actual number.
Main function calls the modeselection function so that user can select manual mode or navigational mode. Manual mode uses the acceleration deceleration turnLeft turnRight functions to move. If navigational mode seleted I want give the user the options to give a distance and direction. But that part not programmed yet. And in any mode I want the user to be able to exit from one mode, and use the other mode(which is where you pointed that stackoverflow occurs). I was also having an idea to put a password as well. I'll put my flowchart.
**broken link removed**
If attachment link doesn't work, use the link below.
ImageShack® - Online Photo and Video Hosting
 
Last edited:

Also, can I return data from recursive functions without causing stack overflows?

Ok,

Here is my first revision of your program.

I have implemented a structure which is named "control", it contains any required data, current states, etc of the control process of your device.

You can add or remove any number of features simply by change the STRUCT definition located at the top of the listing. The actual structure is not extern/global, therefore a pointer to the structure is passed to each and every routine. Any changes made to the contents of the structure in these routines will direct effect the actual "control" structure.

In other words this is an efficient method of passing data back to main() and all other routines.

The code compiles without error, although I have left the statement with recursive issues commented out for the moment. I will deal with it during my next revision.

Take a look at the code, test it and understand the changes I have made. I do not want to make all the changes at once, without you understanding each change made. After all you will be maintaining the code, not I.

Code:
//bitwise declaration for dtmf inputs
sbit std at PORTD.B0;
sbit q4 at PORTD.B1;
sbit q3 at PORTD.B2;
sbit q2 at PORTD.B3;
sbit q1 at PORTD.B4;

//bitwise declaration for L293B outputs
sbit lm1 at PORTC.B4; //left motor positive
sbit lm2 at PORTC.B5;
sbit rm1 at PORTC.B6;
sbit rm2 at PORTC.B7;

struct CNTRL{
    unsigned short chgmode;
    unsigned short current_duty;
    unsigned short mode;
    unsigned short d1;
    unsigned short d2;
    unsigned short d3;
    unsigned short dtmf;

};

void dtmfTomode(struct CNTRL * pCntrl);
void manualMode(struct CNTRL * pCntrl);
void modeSelection(struct CNTRL * pCntrl);
void accelerate(struct CNTRL * pCntrl);
void decelerate(struct CNTRL * pCntrl);
void turnLeft(struct CNTRL * pCntrl);
void turnRight(struct CNTRL * pCntrl);




//main function
void main(void)
{    
     struct CNTRL control;
     unsigned short i;

     TRISD = 0b00011111;               // Configure PORTD
     TRISC = 0b00000000;
     

     
     for (i = 0; i <= 5; i++)
     {
         PORTD.F7 = ~PORTD.F7;     // Initialize RD7
         Delay_ms(1000);          // one second delay
     }

     PWM1_Init(5000);
     control.current_duty = 0;
     PWM1_Set_Duty(0);

     PORTD.F7 = 1;

     PWM1_Start();
     //call the password func!
     modeSelection(&control);
}
void dtmfToMode(struct CNTRL * pCntrl)    //Mode recognition
{
     pCntrl->dtmf.f0 = q1;        // assigning bits
     pCntrl->dtmf.f1 = q2;
     pCntrl->dtmf.f2 = q3;
     pCntrl->dtmf.f3 = q4;
     
     //recognizing
     if(pCntrl->dtmf == 0b0010) pCntrl->mode = 2;
     if(pCntrl->dtmf == 0b0100) pCntrl->mode = 4;
     if(pCntrl->dtmf == 0b0110) pCntrl->mode = 6;
     if(pCntrl->dtmf == 0b0101) pCntrl->mode = 5;
     if(pCntrl->dtmf == 0b1011) pCntrl->mode = 11; // *key

}

void modeSelection(struct CNTRL * pCntrl)
{
    PWM1_Set_Duty(0); //stop the robot

    while(1)
    {
        if(std == 1)    //if user pressed a key
        {
           dtmfTomode(pCntrl);
           if(pCntrl->mode == 1)         manualMode(pCntrl);
        }
    }
}
void manualMode(struct CNTRL * pCntrl)  // manual mode
{
     while(1)
     {
         if(std == 1)    dtmfTomode(pCntrl);
         if(pCntrl->mode == 2)   accelerate(pCntrl);
         if(pCntrl->mode == 4)   turnLeft(pCntrl);
         if(pCntrl->mode == 5)   decelerate(pCntrl);
         if(pCntrl->mode == 6)   turnRight(pCntrl);
         //if(pCntrl->mode == 11)  modeSelection(pCntrl);
     }

}
void accelerate(struct CNTRL * pCntrl)
{
     lm1 = 1;
     lm2 = 0;
     rm1 = 1;
     rm2 = 0;

     if (pCntrl->current_duty < 255)
     {
        pCntrl->current_duty++;
        PWM1_Set_Duty(pCntrl->current_duty);
        Delay_ms(30);
     }
     pCntrl->mode = 10;  // assign a non defined mode to stop automatically coming back to this func even when key is released.

}
void turnLeft(struct CNTRL * pCntrl)
{
     lm1 = 0;
     lm2 = 0;
     rm1 = 1;
     rm2 = 0;
     pCntrl->mode = 10;

}
void decelerate(struct CNTRL * pCntrl)
{
     lm1 = 1;
     lm2 = 0;
     rm1 = 1;
     rm2 = 0;
     if(pCntrl->current_duty > 0)
     {
         pCntrl->current_duty--;
         PWM1_Set_Duty(pCntrl->current_duty);
         Delay_ms(30);
     }
     pCntrl->mode = 10;

}



void turnRight(struct CNTRL * pCntrl)
{
     lm1 = 1;
     lm2 = 0;
     rm1 = 0;
     rm2 = 0;
     pCntrl->mode = 10;

}

Let me know if you have any questions.

BigDog
 

I haven't done structures before, now I'm trying to understand structures. By the way, in my recursive functions, manualMode and modeSelection dont depend on each other, if manualMode is called by modeSelection function, modeSelection function might get pushed by the manualMode function, but it doesnt do any change to operation of manualMode function right? So if I reset all variables in those two functions at the begining of the function, what damage it can do ?
 

I haven't done structures before, now I'm trying to understand structures.

Yes, this is why I want to present these C techniques to you in phases, rather than overwhelm you with a total rewrite of you program.

The use of a structure greatly simplifies the passing of various parameters to the routine calls. You can add or delete virtually any type to the structure definition and access like any of the existing variables within the structure.

There is a method to my madness as they say.

The MikroC Pro User Manual does a fair job of discussing structures, if you need additional reference material let me know and I will point you in the right direction.




Give it a try.

Actually, my next step will be implementing an Interrupt Service Routine (ISR), which is a much more efficient way of monitoring for mode changes. It will also eliminate the recursion issue in your program.

Actually, the STD signal of a valid decode of DTMF is perfect for an ISR.

Do you have any questions I can answer at the moment?

BigDog
 
Reactions: FvM

    FvM

    Points: 2
    Helpful Answer Positive Rating

Yes, give some tutes for structures. I got more confused after reading the manual. Hmm, I thought about using ISR, But when using ISR the microcontroller wont wait for user input right?
Thanks.
 

Yes, give some tutes for structures. I got more confused after reading the manual.

The structure is simply a collection of various types, variables, arrays, other structures, etc.

The structure declaration is similar to a normal variable declaration:

struct CNTRL control;

"struct CNTRL" is the type, similar to int or short.

To access a member of the structure you simply:

control.current_duty = 0; // sets the control_duty member to zero.

I have passed the pointer to the "control" structure to the various routines.


The pointer being pCntrl. To access a member of the structure using the structures pointer you simply replace the "." with "->".

Example:

pCntrl->mode = 2;

The pointer to a type is simply the physical address in storage of its location, somewhat like a street address number.

To obtain the address/pointer of a type, including structures you simply:

&control

The "&" means get the address or pointer of the following structure.



Does this help?

Please feel free to ask me questions, I should be able to help you understand.


I'll also dig up some good tutorials for you.



Hmm, I thought about using ISR, But when using ISR the microcontroller wont wait for user input right?

Correct.

But not waiting is a benefit, when the STD line signals a successful decode, the PIC execution immediate jumps to the ISR which can then update the current mode variable and set a flag to alert the main process that a mode change has occurred. The PIC execution then jumps back to its original position finishes the task it was undertaking before the ISR, then checking the flag changes to the appropriate mode.

Implementing an ISR would effectively eliminate the constant polling of the STD line, but would allow mode changes to occur in a timely manner. This scheme would also remove the current reliance on the recursive routines.

Does this explanation of ISRs help? Or do you need an example?

BigDog
 

Yes, I can understand structures now, and I think I know what you are up to using ISR. Using ISR I can keep running in the main function right?, and constantly check all the variables. So I must first connect std to interrupt pin right? Any intterupt will call the dtmfToNumber function if std = 1.
And if mode = 1 (manual mode), I can make a new nested "if" structure to call the moving functions.
if mode = 2 (navigational mode), another nested "if" structure to carry out its function. I'll also use some more sbits to keep track where's the user is in a nested loop.
Or the other way, put all this if loops in ISR and the ISR calls the nessassary function ?

---------- Post added at 17:00 ---------- Previous post was at 16:58 ----------


Thanks for the links.
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…