|
|
View previous topic :: View next topic |
Author |
Message |
soundscu
Joined: 29 Jun 2007 Posts: 62 Location: Raleigh, NC
|
Best way to code a UI menu system? |
Posted: Thu Apr 30, 2009 4:33 pm |
|
|
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
|
|
Posted: Thu Apr 30, 2009 5:13 pm |
|
|
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
|
|
Posted: Fri May 01, 2009 9:27 am |
|
|
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
|
|
Posted: Fri May 01, 2009 5:18 pm |
|
|
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();
}
}
} |
|
|
|
|
|
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
|