# Recursive function call?

Status
Not open for further replies.

#### johnl69

##### Member
I keep getting "error: (1089) recursive function call to "_Screen" " and I dont know why?

The progrm worked when I had the "show_menu()" function as the main routine but I want the "Screen()" first but I need it callable from the menu array.

C:
#include  <stdio.h>
#include  <stdlib.h>
#include  <xc.h>
#include  "lcd.h"
#include  "lcd_extra.h"
#include  "i2c.h"

// **************** DEFINES *************************************************

#define ENTER   PORTAbits.RA1 ;
#define UP      PORTAbits.RA2 ;
#define DOWN    PORTAbits.RA3 ;
// I2C
#define MSB(x) ((x>>4)+ '0')            //Display Most Significant Bit of BCD number
#define LSB(x) ((x & 0x0F) + '0')       //Display Least Significant Bit of BCD number

#define I2C_SCL    TRISCbits.TRISC3
#define I2C_SDA    TRISCbits.TRISC4

// ******************* Global Variables *************************************
// I2C
void write_ds1307(unsigned short addressReg, unsigned short w_dataReg); //Function to write to the DS1307
char Binary2BCD(char aa); //Convert Binary to BCD
char BCD2Binary(char aa); //Convert BCD to Binary
char time[] = "00:00:00";

char ON_Blue[] = "00:00";
char OFF_Blue[] = "00:00";
char ON_Main[] = "00:00";
char OFF_Main[] = "00:00";

char second;
char minute;
char hour;
char hr;

char ON_Blue_Hour = 0b00010000;
char ON_Blue_Minute = 0b01000101;
char OFF_Blue_Hour = 0b00100000;
char OFF_Blue_Minute = 0b0011000;

char ON_Main_Hour;
char ON_Main_Minute;
char OFF_Main_Hour;
char OFF_Main_Minute;

char temp_hour;
char temp_minute;

unsigned short set_count = 0;
short set;
char absolute(char a);

unsigned char line_cnt = 0;
unsigned char from = 0;
unsigned char till = 0;
unsigned char temp = 0;
unsigned char button;
unsigned char selected = 1;

const char *text;
unsigned char up;
unsigned char down;
unsigned char enter;
void ( *fp) (void);

/*
*
*/

const char menu_000[] = " MAIN Zero "; //0
const char menu_001[] = " Set Time"; // 1
const char menu_002[] = " Lights"; // 2
const char menu_003[] = " Dosage"; // 3
const char menu_004[] = " Done"; // 4

const char menu_100[] = " SUB Zero "; //5
const char menu_102[] = " Colour"; // 7   Time On/Off

/*     array discription

|
|      |---------------------- If Up pressed goto
|      |   |------------------ If Down pressed goto
|      |   |   |-------------- If Enter Pressed goto
|      |   |   |   |---------- Function on enter
*/

{menu_000, 0, 0, 0, 0}, //0
{menu_001, 1, 2, 1, Set_Time}, //1
{menu_002, 1, 3, 5, 0}, //2 // Goto lights 6
{menu_003, 2, 4, 3, 0}, //3
{menu_004, 3, 4, 4, Screen}, //4

{menu_101, 5, 6, 5, Lights_ON_OFF}, //5
{menu_102, 5, 7, 6, Lights_Colour}, //6
{menu_104, 7, 8, 1, 0}, //8
};

//*************  MAIN *******************************************************

void main(void) {
ADCON1 = 0x0F; // No analog, all digital i/o
TRISA = 0b00001110;
LATA = 0b00001110;

TRISB = 0x00;
TRISC = 0x00;
TRISG = 0x00;
LATB = 0x00;
LATG = 0x00;

OpenXLCD(FOUR_BIT & LINES_5X7); // Start LCD
WriteCmdXLCD(SHIFT_DISP_LEFT);

OpenI2C(MASTER, SLEW_OFF); // Initialize I2C module
SSPADD = 9; // Baud rate clock
IdleI2C();
StartI2C();
while (SSPCON2bits.SEN);

Screen();

}

// *************************** functions **********************************

for (;;) {

if (selected > 4) {
line_cnt = 4;
} else {
line_cnt = 1;
}

for (unsigned char a = 1; a <= 4; a++) {
lcd_gotoxy(line_cnt, 3);
line_cnt++;
}
lcd_gotoxy(selected, 1);
putrsXLCD(">");
// ENTER
if (PORTAbits.RA1 == 0) {
__delay_ms(20);
while (PORTAbits.RA1 == 0);

ClearXLCD();

} else {
}
}

// UP
if (PORTAbits.RA2 == 0) {
__delay_ms(20);
while (PORTAbits.RA2 == 0);
ClearXLCD();

}

// DOWN
if (PORTAbits.RA3 == 0) {
__delay_ms(20);
while (PORTAbits.RA3 == 0);
ClearXLCD();

}
}
}

void Screen(void){

ClearXLCD();

for (;;) {
//Read Time and Date, when simulating with proteus, it will pick up the system Clock
//from your PC and might display the AM if your PC time is in 24Hrs mode
hr = hour & 0b00011111;

time[0] = MSB(hr);
time[1] = LSB(hr);
time[3] = MSB(minute);
time[4] = LSB(minute);
time[6] = MSB(second);
time[7] = LSB(second);

//Display Time and Date in 12Hrs Mode (with PM or AM)
lcd_gotoxy(1, 1);
putrsXLCD(time);

// Poll Enter switch
if (PORTAbits.RA1 == 0) {
__delay_ms(20);
while (PORTAbits.RA2 == 0);
}

// Check Alarms
if (hr >= ON_Blue_Hour) {
if (minute >= ON_Blue_Minute) {
PORTBbits.RB1 = 1;
}
}
}
}

void Set_Time(void) {

ClearXLCD();
lcd_gotoxy(1, 1);
putrsXLCD("Set time");
while (BusyXLCD()); // Wait if LCD busy
putrsXLCD("Works!");
Delay_100ms(10);
ClearXLCD();
}

void Lights_ON_OFF(void) {

ClearXLCD();

do {

lcd_gotoxy(1, 3); // Line 1
putrsXLCD("Blue ON");

ON_Blue[0] = MSB(ON_Blue_Hour);
ON_Blue[1] = LSB(ON_Blue_Hour);
ON_Blue[3] = MSB(ON_Blue_Minute);
ON_Blue[4] = LSB(ON_Blue_Minute);

lcd_gotoxy(1, 11);
putrsXLCD(ON_Blue);

OFF_Blue[0] = MSB(OFF_Blue_Hour);
OFF_Blue[1] = LSB(OFF_Blue_Hour);
OFF_Blue[3] = MSB(OFF_Blue_Minute);
OFF_Blue[4] = LSB(OFF_Blue_Minute);

lcd_gotoxy(2, 3); // Line 2
putrsXLCD("Blue OFF");
lcd_gotoxy(2, 12);
putrsXLCD(OFF_Blue);

ON_Main[0] = MSB(ON_Main_Hour);
ON_Main[1] = LSB(ON_Main_Hour);
ON_Main[3] = MSB(ON_Main_Minute);
ON_Main[4] = LSB(ON_Main_Minute);

lcd_gotoxy(3, 3); // Line3
putrsXLCD("Main ON");
lcd_gotoxy(3, 11);
putrsXLCD(ON_Main);

OFF_Main[0] = MSB(OFF_Main_Hour);
OFF_Main[1] = LSB(OFF_Main_Hour);
OFF_Main[3] = MSB(OFF_Main_Minute);
OFF_Main[4] = LSB(OFF_Main_Minute);

lcd_gotoxy(4, 3); // Line 4
putrsXLCD("Blue OFF");
lcd_gotoxy(4, 12);
putrsXLCD(OFF_Main);

} while (1);
}

void Lights_Colour(void) {
ClearXLCD();
putrsXLCD("Colour");
while (BusyXLCD()); // Wait if LCD busy
putrsXLCD("Works!");
Delay_100ms(10);
ClearXLCD();
}

ClearXLCD();
while (BusyXLCD()); // Wait if LCD busy
putrsXLCD("Works!");
Delay_100ms(10);
ClearXLCD();
}

char
absolute(char a) {
if (a < 0)
return -a;
return a;
}

//from the date and time registers.
{
char r_data;
StartI2C(); // Start condition I2C on bus
IdleI2C();
IdleI2C();
IdleI2C();
StopI2C(); // Stop condition I2C on bus

RestartI2C(); // Start condition I2C on bus
IdleI2C();
IdleI2C();
r_data = ReadI2C(); // read the value from the RTC and store in result
IdleI2C();
NotAckI2C(); // Not Acknowledge condition.
IdleI2C();
StopI2C(); // Stop condition I2C on bus
return (r_data);
}

void write_ds1307(unsigned short address, unsigned short w_data) //call this function to write date and time
//to the date and time registers.
{
StartI2C(); // Start condition I2C on bus
IdleI2C();
IdleI2C();
IdleI2C();
IdleI2C();
StopI2C(); // Stop condition I2C on bus

}

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Where are your function definitions.... As they are not defined the compiler only seeing the calls to the functions set in the structure initialization...

If you add the definitions at the top ... see what happens..

#### johnl69

##### Member

C:
#include <xc.h> // include processor files - each processor file is guarded.

void Screen(void);
void Set_Time(void);
void Lights_ON_OFF(void);
void Lights_Colour(void);
void Lights_Fade(void);
moved them to the top of Controller.C but its still the same

the only way I can get it to work is to have the menu first and then call the screen() function but I really need it the other way around.

#### johnl69

##### Member
So I have the program running and use an external interrupt to call the menus?

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
It looks like you want a screen waiting for a button press... Here is my non interrupt driven routines.

setup
main
call keyhit
process keyhit
if keyhit = x then function x
if keyhit = y then function y
if keyhit = z then function z​
draw normal screen
goto main​

function x
while
call keyhit
process keyhit
if keyhit = x then function sub x
if keyhit = y then function sub y
if keyhit = z then return​
goto while

#### johnl69

##### Member
Yeah sort of, but how do I get out of the menu array to get back to the main routine or do I need to write the menu diferently?

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Using function pointers will inevitably require some serious forethought... I usually keep away when I can..

Each sub function should have a return to the last call otherwise all hell breaks loose..

I have around 4 nested menus on my RCI unit Each has a simple return to the previous menu.. I just use a menuitem variable to keep track of where I am.. I can use this to access screen data off chip and all my menu functions are virtually identical apart from the different sub text used...

Also I almost never use clear LCD... If you have a buffer ( say 17 characters ) just write the buffer to the screen and update the buffer.. That way the code can be repeatable.. ie... you don't need as much..

I'd love to show you a snippet of my code, but as it's a commercial product.... ! Nope.

Quick question?? If the function pointer == null

C:
 if (menu[selected].fp == 0) {
ClearXLCD();
What happens?

#### johnl69

##### Member
Quick question?? If the function pointer == null

C:
if (menu[selected].fp == 0) {
ClearXLCD();
What happens?
It goes to what ever array line is in the "enter" column unless there is a funtion declared i.e;

C:
{menu_002, 1, 3, 5, 0}, //2 // Goto lights 6
goes to

C:
{menu_101, 5, 6, 5, Lights_ON_OFF}, //5
which is the sub menu for the Lights. whereas this one will go to Lights_ON_OFF function.

I used a tutorial on youtube for the menu structure and tried to adapt it for my purpose

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
I do like compact menu options, but as you only have a couple of functions, just call them when needed..

I'll see if I can modify your code... Then you will see if its better or not..

To make your code to work as is... Set the last menu to -1 instead of screen() then change the code slightly to force a return..

C:
if (menu[selected].fp == 0) {
ClearXLCD();
} else if (menu[selected].fp == -1) {
return;
} else {
}

#### johnl69

##### Member
Thankyou ill give that a try.

I made a flow chart to make it a bit easier for me to work to the yellow boxes are setting changes

#### NorthGuy

##### Well-Known Member
XC8 has so called "compiled stack". It is supposed to be more efficient, but it cannot handle recursive call. It doesn't matter whether you actually do recursive calls. It is enough if the code is complex enough that the compiler cannot figure out if the recursive calls will occur or not. In such situation, the compiler cannot build its "compiled stack", so it bails out.

I think there's a compiler option which turns off the "compiled stack" and then the compiler builds a regular stack which can support recursion. If you use this option, your code should be fine.

#### johnl69

##### Member
I do like compact menu options, but as you only have a couple of functions, just call them when needed..

I'll see if I can modify your code... Then you will see if its better or not..

To make your code to work as is... Set the last menu to -1 instead of screen() then change the code slightly to force a return..

C:
if (menu[selected].fp == 0) {
ClearXLCD();
} else if (menu[selected].fp == -1) {
return;
} else {
}

Wont compile it wont except the -1

error: (208) operands of "==" not same type
warning: (357) illegal conversion of integer to pointer

#### johnl69

##### Member
XC8 has so called "compiled stack". It is supposed to be more efficient, but it cannot handle recursive call. It doesn't matter whether you actually do recursive calls. It is enough if the code is complex enough that the compiler cannot figure out if the recursive calls will occur or not. In such situation, the compiler cannot build its "compiled stack", so it bails out.

I think there's a compiler option which turns off the "compiled stack" and then the compiler builds a regular stack which can support recursion. If you use this option, your code should be fine.
Ill have a look in the settings, Thankyou.

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
But I'm sure this doesn't require this.... All that is needed is the return to screen(); If the compiler doesn't accept the -1 ( should have done ) there will be another way... If you set the text to "return" you will be able to compare the string..

#### johnl69

##### Member
anything other then "0" or a function produces "error: (195) expression syntax".

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Try this.. You may have to include the <string.h>

C:
if (menu[selected].fp == 0) {
ClearXLCD();
} else {
}
Also the ClearXLCD() may need to be called..

#### johnl69

##### Member
Try this.. You may have to include the <string.h>

C:
if (menu[selected].fp == 0) {
ClearXLCD();
} else {
}
Also the ClearXLCD() may need to be called..
Yes That worked, Thankyou.
Had to add a counter so the main program knew it had been through the menu and to only call ClearXLCD() once otherwise it flickered...
C:
if (Clock == 1) {
Clock = 0;
ClearXLCD();
} else {
Clock = 0;
}
Now if I can only get the RTC to count to 24 and not 20 i'll be happy

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Like I said... I never use clear lcd routines... I have a small buffer that I update continually then I write the new data over the old... That way there is no flicker at all... Its called double buffering..

Forum Supporter