• 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.

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  "Menu_Stuct.h"
#include  "lcd.h"
#include  "lcd_extra.h"
#include  "i2c.h"


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

// MENU
#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
unsigned short read_ds1307(unsigned short addressReg); //Function to read from the DS1307
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);

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

typedef const struct MenuStructure {
    const char *text;
    unsigned char up;
    unsigned char down;
    unsigned char enter;
    void ( *fp) (void);

} MenuEntry;
/*
 *
 */

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_101[] = " On/Off"; // 6   Light Sub Menu
const char menu_102[] = " Colour"; // 7   Time On/Off
const char menu_103[] = " Fade"; // 8   RGB Settings
const char menu_104[] = " Done"; // 9  Fade up


/*     array discription

        ----------------------------------- menu name
        |
        |      |---------------------- If Up pressed goto
        |      |   |------------------ If Down pressed goto
        |      |   |   |-------------- If Enter Pressed goto
        |      |   |   |   |---------- Function on enter
 */
MenuEntry menu[] = {

    {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_103, 6, 8, 7, Lights_Fade}, //7
    {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(CURSOR_OFF & BLINK_OFF);
    WriteCmdXLCD(SHIFT_DISP_LEFT);

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

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


void show_menu(void) {

    for (;;) {

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

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

            if (menu[selected].fp == 0) {
                selected = menu[selected].enter;
                ClearXLCD();

            } else {
                menu[selected].fp();
            }
        }

        // UP
        if (PORTAbits.RA2 == 0) {
            __delay_ms(20);
            while (PORTAbits.RA2 == 0);
            selected = menu[selected].up;
            ClearXLCD();

        }

        // DOWN
        if (PORTAbits.RA3 == 0) {
            __delay_ms(20);
            while (PORTAbits.RA3 == 0);
            selected = menu[selected].down;
            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
        hour = read_ds1307(0x02); //Read Hour
        hr = hour & 0b00011111;
        minute = read_ds1307(0x01); //Read minutes
        second = read_ds1307(0x00); //Read seconds


        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);
            show_menu();
        }

        // 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();
}

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

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

unsigned short read_ds1307(unsigned short address) //call this function to read date and time
//from the date and time registers.
{
    char r_data;
    StartI2C(); // Start condition I2C on bus
    IdleI2C();
    WriteI2C(0xD0); // addresses the chip
    IdleI2C();
    WriteI2C(address); // write register address
    IdleI2C();
    StopI2C(); // Stop condition I2C on bus

    RestartI2C(); // Start condition I2C on bus
    IdleI2C();
    WriteI2C(0xD1); // addresses the chip with a read bit
    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();
    WriteI2C(0xD0); // addresses the chip
    IdleI2C();
    WriteI2C(address); // write register address
    IdleI2C();
    WriteI2C(w_data); // write register address
    IdleI2C();
    StopI2C(); // Stop condition I2C on bus

}
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
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
they are defined in "Menu_Stuct.h"

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

void show_menu(void);
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.
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
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​
draw menu screen
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
Most Helpful Member
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) {
  selected = menu[selected].enter;
  ClearXLCD();
What happens?
 

johnl69

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

C:
if (menu[selected].fp == 0) {
  selected = menu[selected].enter;
  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
Most Helpful 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) {
  selected = menu[selected].enter;
  ClearXLCD();
  } else if (menu[selected].fp == -1) {
 return;
  } else {
  menu[selected].fp();
  }
Then if should to return to screen() normally..
 

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) {
  selected = menu[selected].enter;
  ClearXLCD();
  } else if (menu[selected].fp == -1) {
return;
  } else {
  menu[selected].fp();
  }
Then if should to return to screen() normally..

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
Most Helpful Member
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..
 

Ian Rogers

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

C:
if (menu[selected].fp == 0) {
  if(!strcmp(menu[selected].text, "Done") return;
  selected = menu[selected].enter; 
  ClearXLCD();
  } else {
  menu[selected].fp();
  }
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) {
  if(!strcmp(menu[selected].text, "Done") return;
  selected = menu[selected].enter;
  ClearXLCD();
  } else {
  menu[selected].fp();
  }
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 :banghead:
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
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..
 

Ian Rogers

User Extraordinaire
Forum Supporter
Most Helpful Member
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top