Jump to content


Photo

Implementing a simple Menu


  • Please log in to reply
11 replies to this topic

#1 Synoptic

Synoptic

    Member

  • Members
  • PipPip
  • 10 posts

Posted 03 January 2017 - 06:47 PM

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 !



#2 Fmilburn

Fmilburn

    Level 3

  • Members
  • 525 posts
  • LocationSeattle

Posted 04 January 2017 - 03:43 AM

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



#3 EdoNork

EdoNork

    Member

  • Members
  • PipPip
  • 13 posts

Posted 04 January 2017 - 09:50 AM

I don't think he is interested in a Energia solution, he hasn't ask in the related forum.

 

See this as a guide

 

https://www.youtube....h?v=PFzNBtnfJ6Y

 

Hope this helps.



#4 Synoptic

Synoptic

    Member

  • Members
  • PipPip
  • 10 posts

Posted 04 January 2017 - 12:07 PM

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, 04 January 2017 - 06:12 PM.


#5 Fmilburn

Fmilburn

    Level 3

  • Members
  • 525 posts
  • LocationSeattle

Posted 05 January 2017 - 12:36 AM

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
    }
}


#6 chicken

chicken

    Level 4

  • Members
  • 854 posts

Posted 05 January 2017 - 05:05 AM

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.



#7 chicken

chicken

    Level 4

  • Members
  • 854 posts

Posted 05 January 2017 - 05:47 AM

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

  • veryalive, LiviuM and Fmilburn like this

#8 Synoptic

Synoptic

    Member

  • Members
  • PipPip
  • 10 posts

Posted 05 January 2017 - 07:46 AM

 

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 !



#9 Rickta59

Rickta59

    Level 4

  • Global Moderators
  • 998 posts
  • LocationEastern NC


Posted 06 January 2017 - 04:18 AM

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

#10 Rickta59

Rickta59

    Level 4

  • Global Moderators
  • 998 posts
  • LocationEastern NC


Posted 06 January 2017 - 04:21 AM

i'd fix the , and make them ; however i'm editting on a tablet and it keeps running all the code lines together

#11 chicken

chicken

    Level 4

  • Members
  • 854 posts

Posted 06 January 2017 - 06:23 AM

Hmm yes, I think it should be typedef struct { .. } menu_t. And semicolons!

I always need the compiler to barf at me 3 times before I get these things right :-)

#12 Synoptic

Synoptic

    Member

  • Members
  • PipPip
  • 10 posts

Posted 06 January 2017 - 01:56 PM

Hmm yes, I think it should be typedef struct { .. } menu_t. And semicolons!

I always need the compiler to barf at me 3 times before I get these things right :-)

Me too, my C class is 20 years away from me.






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users