NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Multidimentional string arrays - stuck

October 15, 2011
by claesz
claesz's Avatar

Hi,

As usual I must have misunderstood some basic C stuff. I was trying to make a basic menu system where you would have a 2D string array called menuTitle, and a 3D string array of menuItems. It would allow you an unlimited number of submenus. However I got stuck and took put some of the code into a test program to see what I had missed. It made me even more confused.

The program below "works" (it will basically print "Main menu"). However, if I reinsert the commented lines (except the duplicate declaration of menuItems, all I get is a black LCD. No compilation errors, just all characters black on the LCD when run.

Even more confusing is the fact that the same is the case if I change the declaration of menuItem from fixed numbers into the previously declared values numbMenus and numbItems (comment out line 22 and insert line 23).

I am feeling like such an idiot because this is basic, basic stuff, and yet I cannot get it to work. Hoping someone can explain what I am missing.

// initialload.c
// for NerdKits with ATmega168
// mrobbins@mit.edu

#define F_CPU 14745600

#include <stdio.h>
#include <string.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"

int main() {

    int numbMenus = 5;
    int numbItems = 10;
    char menutitle[numbMenus][20];
    //char menuitem[numbMenus][numbItems][20];
    char menuitem[5][10][20];

    strcpy(menutitle[0], "Main menu");
    //strcpy(menuitem[0][0], "Green LED");
    //strcpy(menuitem[0][1], "Red LED");

    strcpy(menutitle[1], "Sub menu");
    //strcpy(menuitem[1][0], "Blah");
    //strcpy(menuitem[1][1], "Diblah");

    // LED as output
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    fprintf_P(&lcd_stream, PSTR("%s"), menutitle[0]); //
    //lcd_goto_position(1, 0);
    //fprintf_P(&lcd_stream, PSTR("%s"), menuitem[0][0]); //

    while(1){}

    return 0;
}
October 15, 2011
by claesz
claesz's Avatar

It appears everyone is busy having a life, so I will just keep talking to myself.

I just wanted to see if I was completely insane, and therefore wrote the code in C for command line output on my PC. This works fine. It basically gives you a menu system that you can adapt as you wish, with unlimited sub menus.

However, whenever I try 3D arrays on the NK it goes berserk, and I cannot figure out why. Even with the multi dimentional arrays, it should not take up more than 1k of memory so that cannot be the problem.

This works fine on my PC: (the same thing (adapted for the LCD and with push button input of course) does not work on the ATMega (see above sample))

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int main() {

    // menu data
    int curMenu = 0; // menu currently shown
    int curItem = 1; // item currently marked (line 0 for menutitle, so starts on one)
    int cursorCount = 0;
    int menuCount = 0;
    char userin[5];
    int selection;

int i;
    int numbMenus = 5;
    int numbItems = 10;
    char menutitle[numbMenus][20];
    char menuitem[numbMenus][numbItems][20];
    int menulink[numbMenus][numbItems]; // a link to a submenu OR
    int menuactn[numbMenus][numbItems]; // menu actions - turn on LED or whatever

    strcpy(menutitle[0], "Main menu");
    strcpy(menuitem[0][0], "Green LED");
    menulink[0][0] = 1; // link to menutitle[1]; 
    menuactn[0][0] = 0; // No action - just a sub menu 
    strcpy(menuitem[0][1], "Red LED");
    menulink[0][1] = 2;
    menuactn[0][1] = 0;
    strcpy(menuitem[0][2], "\0"); // Need to initialize the end str of array

    strcpy(menutitle[1], "Green LED");
    strcpy(menuitem[1][0], "Turn on");
    menulink[1][0] = 0; // no sub menu, just action
    menuactn[1][0] = 1;
    strcpy(menuitem[1][1], "Turn off");
    menulink[1][1] = 0;
    menuactn[1][1] = 2;
    strcpy(menuitem[1][2], "Return to main");
    menulink[1][2] = 999;
    menuactn[1][2] = 0;
    strcpy(menuitem[1][3], "\0"); // Need to initialize the end str of array

    strcpy(menutitle[2], "Red LED");
    strcpy(menuitem[2][0], "Turn on");
    menulink[2][0] = 0; // no sub menu, just action 
    menuactn[2][0] = 1;
    strcpy(menuitem[2][1], "Turn off");
    menulink[2][1] = 0;
    menuactn[2][1] = 2;
    strcpy(menuitem[2][2], "Return to main");
    menulink[2][2] = 999; // code to trigger return to main menu.  Can't use 0 as that is considered no action here
    menuactn[2][2] = 0; 
    strcpy(menuitem[2][3], "\0"); // Need to initialize the end str of
array   
    // END of menu data

    // print menu
    while(1) {
        printf("--%s--\n", menutitle[curMenu]); // print menu title

        //print menu content
        while ((cursorCount < 5) && (menuitem[curMenu][menuCount][0] != '\0')) {
            printf("%s", menuitem[curMenu][menuCount]);
            printf("\n");
            cursorCount++;
            menuCount++;
        }

        i=0; while (i<100000000) {i++;}
        fgets(userin, 5, stdin); // get user input
        selection = atoi(userin);  // convert char to int
        // handle user input
        if (menuactn[curMenu][selection]) {
            // has an action
            switch (menuactn[curMenu][selection]) {
                case 1: 
                    // action 1
                    break;
                case 2: 
                    // action 2
                    break;
            }
        } else {
            // no action so must have sub menu
            switch (menulink[curMenu][selection]) {
                case 999: 
                    curMenu = 0; // return to main menu
                    break;
                default: 
                    curMenu = menulink[curMenu][selection];
                    break;
            }
        }
        cursorCount = 0;
        menuCount = 0;  
    }
    return 0;
}
October 15, 2011
by claesz
claesz's Avatar

Ok, another update. After more testing, this is the conclusion I have reached:

int a = 5;
int b = 10;
char title[a][20];
char text[a][b][20];

works in my regular C compiler, but gives crazy results on the NK. On NK this will work:

int a = 5;
int b = 10;
char title[a][20];
char text[5][10][20];

but not "char text[a][b][20];". I am sure there is a perfectly logical explanation for this, but to me in the examples above, char text[a][b][20] and char text[5][10][20] would be the same.

Perhaps someone can help me understand why.

October 17, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

I cannot think of a logical explanation for what you are seeing. If I think of one I will definitely let you know.

For now I suggest you use #define instead to lay out the size of your menus.

#define a 5
#define b 10
char title[a][20];
char text[a][b][20];

will do what you are looking for without all the overhead of creating a variable for things that should be static anyway.

Humberto

October 17, 2011
by claesz
claesz's Avatar

Thanks! That worked without any problems.

Cheers, Claesz

October 17, 2011
by BobaMosfet
BobaMosfet's Avatar

claesz-

It looks like the compiler is creating the 3D array on the stack see how it's subtracting 0x3E8 (1000 decimal) from the combination of registers r27 and r26 (possibly the stack pointer), to grow the stack around it:

char    menuitem[numbMenus][numbItems][20];
  e6:   ad b7           in  r26, 0x3d   ; 61
  e8:   be b7           in  r27, 0x3e   ; 62
  ea:   a8 5e           subi    r26, 0xE8   ; 232   <--
  ec:   b3 40           sbci    r27, 0x03   ; 3     <--
  ee:   0f b6           in  r0, 0x3f    ; 63
  f0:   f8 94           cli
  f2:   be bf           out 0x3e, r27   ; 62
  f4:   0f be           out 0x3f, r0    ; 63
  f6:   ad bf           out 0x3d, r26   ; 61
  f8:   4d b6           in  r4, 0x3d    ; 61
  fa:   5e b6           in  r5, 0x3e    ; 62
  fc:   08 94           sec
  fe:   41 1c           adc r4, r1
 100:   51 1c           adc r5, r1

You would think it would work, unless something is messing up r26/r27, or the stack is getting stepped on. I noticed that if I used defines, they stuffed the values in r24/r25 and there was no issue.

I have worked with many assembly languages on many platforms, but have not done much with ATMEGA168 yet, due to time constrains, so my interpretation may not be perfectly correct. I posted the dissassembly so that others could examine and evaluate it.

BM

October 17, 2011
by claesz
claesz's Avatar

Thanks! It goes whoosh over my head, I am afraid, but hopefully someone can throw some light on the matter.

I followed Humberto's advice and used define and there are no problems. I posted the completed code on the forum if anyone is interested.

Thanks again! Claesz

Post a Reply

Please log in to post a reply.

Did you know that interrupts can be used to trigger pieces of code when events happen? Learn more...