Jump to content
43oh

Implementing a simple Menu


Recommended Posts

Hi !

 

I'm new and I did search a bit but with the millions of results, it was hard for me to check them all to see if one would answer my request.

 

I would like to implement a simple 2 level menu using my MSP430. Something very simple to learn how it is done.

 

I would like to display a variable value on the 16x2 LCD on the first row (easy part I can already do this)

I would like to use 1 push button to scroll down options which are displayed on the second row all the time.

I would like to use a second push button to select the option

 

The selected option will go to the next menu level.

I would like to then continue scrolling through the second level options wich will simply change the value of the variable so I can see it worked.

 

I would like to start building a menu library so I can have 2 levels of menu depth. I will then build from this on.

 

On my current setup I still have P1.4 and P1.5 free. The remaining pins are used to drive the LCD.

 

I have a G2231 and a G2211 on a 1.4 Launchpad available (G2553 on order)

 

Thank you very much !

Link to post
Share on other sites

Hi @@Synoptic,

 

That is definitely doable.  As you said, there are lots of examples for doing similar things on the internet.  You seem to have the problem fairly well defined.  In your code, catch the button pushes in an interrupt or poll during the loop.  Record the state of the buttons and act accordingly with a if... else control structure or with switch.  There are examples for reading buttons and control structures on energia.nu: http://energia.nu/guide/

 

Try putting together the code and come back if you have an issue.... 

Link to post
Share on other sites

Hi !

So I got the interrupt working for the buttons.

Now I would like to check which one was pressed in the interrupt routine.

 

I read that I can use a Mask to determine which one was pressed, but it does not work. The two IF are always executed.

#pragma vector = PORT1_VECTOR
__interrupt void InterruptVectorPort1()
{

    unsigned char flags = P1IFG & P1IE;

    if (flags & 0x10)
    {
        P1IFG &= ~0x10;
        message1++;
    }

    if (flags & 0x20)
    {
        P1IFG &= ~0x20;
        currentHours = 0;
        currentMinutes = 0;
        currentSeconds = 0;
    }
}

It works. my button wiring was not good.

Edited by Synoptic
Link to post
Share on other sites

Glad you got it working.  Using switch to control the program flow is a common way to do it and makes it easy to see what is going on or add new pins.  For example, the following watches for the interrupt and toggles the LaunchPad LEDs (with a lot of bounce).

/* Watch for interrupts on two buttons and toggle LEDs
 * Written for the G2553 on MSP-EXP430 G2 V1.5 LaunchPad
 */

#include <msp430.h> 

#define RED_LED    BIT0                 // LaunchPad LEDs
#define GREEN_LED  BIT6
#define S1         BIT3                 // LaunchPad button
#define EXT        BIT4                 // External switch

int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;

    P1DIR |= RED_LED;
    P1OUT &= ~RED_LED;
    P1DIR |= GREEN_LED;
    P1OUT &= ~GREEN_LED;

    P1OUT = S1 | EXT;                   // Switches will be pull-up inputs
    P1REN = S1 | EXT;

    P1IE = S1 | EXT;                    // Enable interrupt for switches
    P1IES = S1 | EXT;                   // Interrupt occurs on HI --> LO

    P1IFG = 0x00;                       // Ensure no interrupts are pending on Port 1

    _BIS_SR(GIE);                       // Global interrupt enable

    while(1){}                          // Loop forever
}

#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
    switch(P1IFG & P1IE){                // Which pin was it?
        case BIT0: break;
        case BIT1: break;
        case BIT2: break;
        case BIT3:
            P1OUT ^= RED_LED;
            P1IFG &= ~ S1;               // clear interrupt
            break;
        case BIT4:
            P1OUT ^= GREEN_LED;
            P1IFG &= ~EXT;               // clear interrupt
            break;
        case BIT5: break;
        case BIT6: break;
        case BIT7: break;
        default:   break;                // should never get here
    }
}
Link to post
Share on other sites

I'd probably go with something along these lines:

- pin IRQ sets an "event" variable according to key pressed (next, select, none).
- main loop (or an event handler function within the main loop) with select/case state machine with a state for each menu entry.

Variable state for the select, with each case section having roughly this structure:

select(state) {
  [..]
  case menuX:
    if (event == select) do the action; //could be state=submenu
    else if (event == next) state = nextmenu;
    break;
  [..]
}
event = none;

Also in the main loop, write/refresh the menu text according to current state.

Link to post
Share on other sites

PS: If you want to get fancy/generic, you could implement the state/menu system with a structure. Something along these lines:

struct {
  int menu_id, // continuous but not functional, helps to keep track of the menu id's
  int next_menu, // entry when pressing next
  void (*action) (void),  // action when pressing select
  const char* label  // displayed text
} menu_t;

int current_menu = 0;

void do_select0(void) {
  current_menu = 2; // set to sub menu
}

void do_ select1(void) {
  // do main action 1
}

void do_subaction1(void) {
  // do sub action 1
}

void do_subaction2(void) {
  // do sub action 2
}

void do_gotop(void) {
  current_menu = 0; // set to top menu
}

menu_t menu[] = {
  // Top level
    { 0, 1,"Sub Menu", do_select0 },
    { 1, 0, "Action", do_select1 },
  // Sub menu
    { 2, 3, "Sub action 1", do_subaction1 },
    { 3, 4, "Sub action 2", do_subaction2 },
    { 4, 2, "Up", do_gotop }
};

and in the main loop / event handler:

switch(event) {
case next:
  current_menu = menu[current_menu].next_menu;
  write_text(menu[current_menu].label);
  break;
case select:
  menu[current_menu].action();
  write_text(menu[current_menu].label);
  break;
default;
  // do nothing
  break;
}
event = none;  // reset event variable
Link to post
Share on other sites

 

PS: If you want to get fancy/generic, you could implement the state/menu system with a structure. Something along these lines:

struct {
  int menu_id, // continuous but not functional, helps to keep track of the menu id's
  int next_menu, // entry when pressing next
  void (*action) (void),  // action when pressing select
  const char* label  // displayed text
} menu_t;

int current_menu = 0;

void do_select0(void) {
  current_menu = 2; // set to sub menu
}

void do_ select1(void) {
  // do main action 1
}

void do_subaction1(void) {
  // do sub action 1
}

void do_subaction2(void) {
  // do sub action 2
}

void do_gotop(void) {
  current_menu = 0; // set to top menu
}

menu_t menu[] = {
  // Top level
    { 0, 1,"Sub Menu", do_select0 },
    { 1, 0, "Action", do_select1 },
  // Sub menu
    { 2, 3, "Sub action 1", do_subaction1 },
    { 3, 4, "Sub action 2", do_subaction2 },
    { 4, 2, "Up", do_gotop }
};

and in the main loop / event handler:

switch(event) {
case next:
  current_menu = menu[current_menu].next_menu;
  write_text(menu[current_menu].label);
  break;
case select:
  menu[current_menu].action();
  write_text(menu[current_menu].label);
  break;
default;
  // do nothing
  break;
}
event = none;  // reset event variable

That is something in the like of what I want to do, thanks !

Link to post
Share on other sites

the example and use of menu_t seem to out of order...

struct {
 int menu_id, // continuous but not functional, helps to keep track of the menu id's
 int next_menu, // entry when pressing next  
 const char* label  // displayed text
 void (*action) (void),  // action when pressing select
} menu_t;
would match the example better
Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...