CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Best way to code a UI menu system?

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
soundscu



Joined: 29 Jun 2007
Posts: 62
Location: Raleigh, NC

View user's profile Send private message Visit poster's website

Best way to code a UI menu system?
PostPosted: Thu Apr 30, 2009 4:33 pm     Reply with quote

Hi,

It's time to code yet another User Interface. This time I have a 2-line LCD and 4 buttons:
- But1: cycle through categories (there are currently 9 of them)
- But2: cycle through parameters for the current category (different numbers of parameters for each category)
- But3: increment the current value
- But4: decrement the current value
- A special action occurs if both But3 and But4 are pressed together, the specifics vary with the particular parameter

The ideal system would make it easy to add, remove, or change categories and parameters. I'd like the structure to be a table at the top of the code that includes things like the string to be displayed on the LCD, and the position where it should be displayed. Similarly, the type of variable (i.e. signed or not, int1/int8/int32/float) and min/max range limits should be part of the table. The category/parameter name will always appear on lcd line1, the value being adjusted will appear on line 2.

I'm not asking anyone to write this for me, but I'm interested in comments about how you would do it. Particularly how to setup the table of strings for easy access by printf() statements in the code below.

Thank you for your time!
Jim
bungee-



Joined: 27 Jun 2007
Posts: 206

View user's profile Send private message

PostPosted: Thu Apr 30, 2009 5:13 pm     Reply with quote

In a project, where I had menus with several layers I used code like this:


Code:

struct mnu {
   char c_string[9];
};

const struct mnu menu1[6] =
{
   {"Cas    "},
   {"Datum  "},
   {"Alarm  "},
   {"Svet.  "},
   {"Izhod  "}
};


And for each sublayer another constant like that.
soundscu



Joined: 29 Jun 2007
Posts: 62
Location: Raleigh, NC

View user's profile Send private message Visit poster's website

PostPosted: Fri May 01, 2009 9:27 am     Reply with quote

Most of my hair is now pulled out. :)

I have created a system of nested structures that would seem to provide the menu system I need:

Code:

#define MAX_CATEGORIES 8
#define MAX_MENU_ITEMS 8

//flag data type for future printf() functions:
#define VINT 0
#define VLINT 1
#define VSINT 2
#define VSLINT 3
#define VFLOAT 4

//a menu list, to be used within a category:
struct menu_structure
  {
  char param[9];              //8 characters + 0x00
  int8 vtype;                 //VINT (int8), VLINT (int16, int32), VFLOAT (float)
  float *mem_addr;  //pointer to memory location being modified by UI
  float incr;       //amount to inc/dec value with UI buttons
  float min;        //range limit - min value, 0 = no limiting
  float max;        //range limit - max value, 0 = no limiting
  };
 
//a category consists of a name and a menu list:
struct cat_structure
  {
  char name[8];               //7 characters + 0x00, leave 1 blank character before param
  struct menu_structure menu[MAX_MENU_ITEMS];
  }; 
 
const struct cat_structure category[MAX_CATEGORIES] =
  {
  //no data values declared yet
  };


This compiles successfully, but with no data. Since it's a structure of constants, I can't do anything useful with it.

If I declare a name for the first category, this also compiles succesfully:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
    "Global"
  };


And I can proceed one more level, declaring a name for the first menu item in this category:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
  //name      param     vtype   *mem_addr incr  min   max
    {"Global",
        {"Enc1"}
    }
  };


And multiple categories like this will also compile:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
  //name      param     vtype   *mem_addr incr  min   max
    {"Global",
        {"Enc1"}
    }
    {"Mot1P",
        {"Pos1"}       
    }
    {"Mot1S",
        {"Spd1"}       
    }
    {"Mot1A",
        {"Accel1"}       
    }
    {"M1Tune",
        {"PID Kp"}       
    }   
  };


Note the absense of commas after each {category} section. But it also compiles when I add commas:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
  //name      param     vtype   *mem_addr incr  min   max
    {"Global",
        {"Enc1"}
    },
    {"Mot1P",
        {"Pos1"}       
    },
    {"Mot1S",
        {"Spd1"}       
    },
    {"Mot1A",
        {"Accel1"}       
    },
    {"M1Tune",
        {"PID Kp"}       
    }   
  };


But it all falls apart from here. I cannot successfully declare values for any more variables in the menu structure.

I cannot declare multiple parameters within a category, though I have declared it as an array with multiple elements. This does not compile:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
  // category  param    vtype   *mem_addr incr  min   max
    {"Global",
              {"Enc1"},
              {"Enc2"}
    },
    {"M1Tune",
              {"KP  "},
              {"KD  "}
    }


What I actually need is this:

Code:

const struct cat_structure category[MAX_CATEGORIES] =
  {
  // category  param    vtype   *mem_addr incr  min   max
    {"Global",
              {"Enc1",  VSINT, &enc1,    1,    0,    0},
              {"Enc2",  VSINT, &enc2,    1,    0,    0}
    }
    {"M1Tune",
              {"KP  ",  VFLOAT, &KpP1,    0.001,0,    0},
              {"KD  ",  VFLOAT, &KpD1,    0.001,0,    0}   
    }   
  };


The first error (in both cases above) is "Expect}" right after "Enc1", as if the menu structure doesn't have any more parameters. I've been staring dumbly at this for a while now. Please tell me what I am missing.

Thanks in advance!
Jim
newguy



Joined: 24 Jun 2004
Posts: 1909

View user's profile Send private message

PostPosted: Fri May 01, 2009 5:18 pm     Reply with quote

I don't have time to explain this now, but it compiles & runs properly with version 3.236. Tested with an MELabs Lab-X1 board.

Code:
#include <18F452.h>
#device adc=8
#use delay(clock=4000000,RESTART_WDT)
#fuses XT, BROWNOUT, BORV20, PUT, STVREN, NOLVP

#use fast_io(B)
#byte portb = 0xf81

int1 do_debounce = FALSE, select = FALSE, down = FALSE, up = FALSE, adjust = FALSE;
int1 initial_delay = TRUE;
int8 value1 = 0, value2 = 30, value3 = 90, value4 = 125, pointer_line = 1, menu_window_start = 1;
int8 *control, temp, repeat_delay_count = 0;

// note:  #include an LCD driver here

// You should really put these functions into a separate file which you would #include " " here

void draw_menu(void) {
   switch (menu_window_start) {
      case 1:  lcd_putc("\fLine 1\nLine 2"); // "line 1" on top line, "line 2" on bottom
               break;
      case 2:  lcd_putc("\fLine 2\nLine 3"); // top: "line 2", bottom: "line 3"
               break;
      case 3:  lcd_putc("\fLine 3\nLine 4"); // top: "line 3", bottom: "line 4"
               break;
   }
}

void draw_pointer(void) {
   if (pointer_line == menu_window_start) { // if the pointer_line is equal to the menu_window_start...
      lcd_gotoxy(20,1); // print the indicator on line 1 of the display
      lcd_putc("<");
      lcd_gotoxy(20,2);
      lcd_putc(" ");
   }
   else { // ...otherwise the indicator goes on the bottom line of the display
      lcd_gotoxy(20,1);
      lcd_putc(" ");
      lcd_gotoxy(20,2);
      lcd_putc("<");
   }
}

void draw_adjust(void) {
   switch (pointer_line) {
      case 1:  control = &value1;
               break;
      case 2:  control = &value2;
               break;
      case 3:  control = &value3;
               break;
      case 4:  control = &value4;
               break;
   }
   if (pointer_line == menu_window_start) {
      printf(lcd_putc,"\fValue %u = %03u\nSelect to Exit",pointer_line,*control);
   }
   else {
      printf(lcd_putc,"\fSelect to Exit\nValue %u = %03u",pointer_line,*control);
   }
}

void inc(void) {
   int8 hi_limit = 4;
   if (!adjust) {
      if (*control < hi_limit) {
         *control = *control + 1;
         if (*control > (menu_window_start + 1)) {
            menu_window_start++;
            draw_menu();
         }
         draw_pointer();
      }
   }
   else {
      *control = *control - 1;
      if (pointer_line == menu_window_start) {
         lcd_gotoxy(11,1);
      }
      else {
         lcd_gotoxy(11,2);
      }
      printf(lcd_putc,"%03u",*control);
   }
}

void dec(void) {
   int8 lo_limit = 1; // local variable, lower limit
   if (!adjust) { // if adjust is false (we're in the menu mode)
      if (*control > lo_limit) { // check to see if whatever control points at is indeed above the lo_limit
         *control = *control - 1; // if it can be decremented, take one away from it
         if (*control < menu_window_start) { // control points to "pointer_line", and we have to check to see if
                                 // we have to move the display window now
            menu_window_start--; // so we decrement menu_window_start, and...
            draw_menu(); // ...redraw the whole menu (which has changed)
         }
         draw_pointer(); // must redraw the indicator, because we moved it
      }
   }
   else { // adjust is true, we're in the adjust mode (not the menu mode)
      *control = *control + 1; // ...so we want to increment whatever variable is pointed to by our pointer control
      if (pointer_line == menu_window_start) { // this means the updated variable goes on top line of lcd
         lcd_gotoxy(11,1);
      }
      else { // ...otherwise we go to the bottom line of the display, because that's where the data is
         lcd_gotoxy(11,2);
      }
      printf(lcd_putc,"%03u",*control); // finally, print out the updated data
   }
}

// end of stuff which should be in a separate include file

#int_RTCC
RTCC_isr() {
   if ((temp & 0xf0) != 0xf0) { // a button is down
                              // temp stores the state of port b
                              // mask off the 4 MS bits by ANDing with 0xf0
                              // if this masked value is not equal to 0xf0, then a button is down
      if (initial_delay && ++repeat_delay_count == 8) { // if initial_delay is true and repeat_delay_count is 8...
         initial_delay = FALSE; // ...turn off the initial_delay
      }
      else if (!initial_delay || do_debounce) { // if initial_delay is false OR do_debounce is true
         do_debounce = FALSE; // set do_debounce to false
         if (!input(PIN_B4) && initial_delay) { // if the "select" button is down AND initial_delay is true
            select = TRUE; // ... make "select" true
         }
         if (!input(PIN_B5)) {
            down = TRUE;
         }
         if (!input(PIN_B6)) {
            up = TRUE;
         }
      }
   }
}

#int_RB
RB_isr() {
   temp = portb; // read state of portb and store in temp
   set_timer0(0); // timer 0 will go off 65 ms from now
   do_debounce = TRUE;
   repeat_delay_count = 0; // this variable has to do with the initial time delay and the rapid fire that follows
   initial_delay = TRUE; // do the "initial delay" before going into rapid fire mode
}

void main() {

   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(FALSE);
   setup_wdt(WDT_ON);
   setup_timer_0(RTCC_INTERNAL|RTCC_8_bit|RTCC_DIV_256);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   enable_interrupts(INT_RTCC);
   enable_interrupts(INT_RB);
   enable_interrupts(global);

   set_tris_e(0x00);
   port_b_pullups(TRUE);
   set_tris_b(0xf0);
   output_b(0x00);

   lcd_init();
   draw_menu();
   draw_pointer();

   control = &pointer_line; // set our pointer, control, to point to the variable "pointer_line"

   while (TRUE) {
      restart_wdt();
      if (select) {
         select = FALSE;
         if (adjust) {
            adjust = FALSE;
            control = &pointer_line;
            draw_menu();
            draw_pointer();
         }
         else {
            adjust = TRUE;
            draw_adjust();
         }
      }
      if (down) {
         down = FALSE;
         dec();
      }
      if (up) {
         up = FALSE;
         inc();
      }
   }
}
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group