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.

Project Help and Ideas » Scrolling menu selection

January 29, 2011
by SpaceGhost
SpaceGhost's Avatar

Here's something I have been playing around with. I put together a fairly simple vertically "scrolling" menu. You scroll the "arrow" (>) to the output that you want to activate. Pushing one button turns on the selected output, pushing another button turns it off. Two buttons are used to either navigate up the menu or back down, and the menu "wraps."

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:

// PC2 -- MENU DE-SELECT PUSHBUTTON (pin 25)
// PC3 -- MENU DOWN PUSHBUTTON (pin 26)
// PC4 -- MENU UP PUSHBUTTON (pin 27)
// PC5 -- MENU SELECT PUSHBUTTON (pin 28)

// PB1 --  (+) output (pin 15)
// PB2 --  (+) output (pin 16)
// PB3 --  (+) output (pin 17)
// PB4 --  (+) output (pin 18)
// PB5 --  (+) output (pin 19)
// PC0 --  (+) output (pin 23)

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Set the 6 output pins (sourcing outputs)
  DDRB |= (1<<PB1);
  DDRB |= (1<<PB2);
  DDRB |= (1<<PB3);
  DDRB |= (1<<PB4);
  DDRB |= (1<<PB5);
  DDRC |= (1<<PC0);

   // Set the 4 input pins (sinking inputs)
  DDRC &= ~(1<<PC2); // set PC3 as input (pin 25)
  DDRC &= ~(1<<PC3); // set PC3 as input (pin 26)
  DDRC &= ~(1<<PC4); // set PC4 as input (pin 27)
  DDRC &= ~(1<<PC5); // set PC2 as input (pin 28)

  // turn on the internal resistors for the input pins 
  PORTC |= (1<<PC2); // turn on internal pull up resistor (pin 25)
  PORTC |= (1<<PC3); // turn on internal pull up resistor (pin 26)
  PORTC |= (1<<PC4); // turn on internal pull up resistor (pin 27)
  PORTC |= (1<<PC5); // turn on internal pull up resistor (pin 28)

  // declare the variables to represent each pushbutton input
  uint8_t enter_selection;
  uint8_t deselect;
  uint8_t menu_up;
  uint8_t menu_down;
  uint8_t j; // for selection indicator

    for(j = 0; j <= 6; j++){

        // Turn off all six outputs

            PORTB &= ~(1<<PB1);
            PORTB &= ~(1<<PB2);
            PORTB &= ~(1<<PB3);
            PORTB &= ~(1<<PB4);
            PORTB &= ~(1<<PB5);
            PORTC &= ~(1<<PC0);

        lcd_line_one(); 
        fprintf_P(&lcd_stream, PSTR("> output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));

        }

    j=0; // start position selection indicator @ "output one"

while(1) {

    menu_up = (PINC & (1<<PC4)) >> PC4;
    if (menu_up == 0)

    {   if(j==0)
        j=6;
        if(j>0)         //set 0 as lowest allowed count
        j = j - 1;      // if count down pressed, lower count by one
        delay_ms(300);   // switch "debounce"
    }

    menu_down = (PINC & (1<<PC3)) >> PC3;
    if (menu_down == 0)

    {   j = j + 1;      //if button pressed, increase count by one
        if (j >= 6 )    //set 0 as lowest allowed count, and if i is < 5,
        j = 0;           //count loops to five
        delay_ms(300);   // switch "debounce"
    }

    deselect = (PINC & (1<<PC2)) >> PC2;
    enter_selection = (PINC & (1<<PC5)) >> PC5;

    // Pushing pin 26 pushbutton advances to selection option(s).
    // Pushing pin 27 pushbutton backs up to selection option(s).

    if ( j == 0)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("> output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 0 ) && (enter_selection == 0)) // pushbutton on pin 28 (PC5)

    {   PORTB |= (1<<PB1); // turns pin 15 output ON

    }

    if (( j == 0 ) && (deselect == 0)) // pushbutton on pin 25 (PC2)

    {   PORTB &= ~(1<<PB1); // turns pin 15 output OFF

    }

    if ( j == 1)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("> output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 1 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB2); // turns pin 16 output ON

    }

    if (( j == 1 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB2); // turns pin 16 output OFF

    }

    if ( j == 2)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   ")); 
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("> output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 2 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB3); // pin 17 output ON

    }

    if (( j == 2 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB3); // pin 17 output OFF

    }

    if ( j == 3)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output four  ")); // output four selected
    }

    if (( j == 3 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB4); // pin 18 output ON

    }

    if (( j == 3 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB4); // pin 18 output OFF

    }

    if ( j == 4)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output four  "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output five  ")); //output five selected
    }

    if (( j == 4 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB5); // pin 19 output ON

    }

    if (( j == 4 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB5); // pin 19 output OFF

    }

    if ( j == 5)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output four  "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output five  "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output six   "));    
    }

    if (( j == 5 ) && (enter_selection == 0))

    {   PORTC |= (1<<PC0); // pin 23 output ON

    }

    if (( j == 5 ) && (deselect == 0))

    {   PORTC &= ~(1<<PC0); // pin 23 output OFF

    }

}

   return 0;

}

The program works good. LEDs on the outputs give visual indication of the individual output states (on of off).

I experimented with putting "ON" indicators on the LCD screen that corresponded with individual outputs and their on states (putting the ON indicators on the 17th - 20th position(s) of the LCD line).

Using a lot of "if" statements and more variables I had luck getting "ON" messages to move and wrap with the first two outputs ("output one" and "output two")... But getting "output three" to work that way with the other two outputs "ON" proved to be impossible! I kinda figured when I started that it probably wouldn't work or the file would be HUGE... but gave it several tries.

The code I have works as it is - but I'd love to improve on it! Does anyone have any hints or suggestions? I'm sure there's a concept to this that I need to learn & study...

Dave

January 30, 2011
by Ralphxyz
Ralphxyz's Avatar

Hey Space Ghost, this is very interesting I am going to need a multiple button press routine to set Year/Date/Time in a new project.

Thanks for doing some of the preliminary work for me.

I have limited programming knowledge, but one point I do see is all of your "if" statements.

I believe you really want to use the switch/case syntax. Google "C switch case".

All of the ifs will work but I believe using switch/case makes your code more logical (easier) to read and I believe the compiler will generate more efficient (smaller) code.

Ralph

January 31, 2011
by SpaceGhost
SpaceGhost's Avatar

Hey thanks Ralph, I will look that up. In the meantime though I figured out how to put the "ON"s on the LCD display to correspond with the output states of the MCU. Here is the code as I have it now -

#define F_CPU 14745600

#include <stdio.h>

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

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

// PIN DEFINITIONS:

// PC2 -- MENU DE-SELECT PUSHBUTTON (pin 25)
// PC3 -- MENU DOWN PUSHBUTTON (pin 26)
// PC4 -- MENU UP PUSHBUTTON (pin 27)
// PC5 -- MENU SELECT PUSHBUTTON (pin 28)

// PB1 --  (+) output (pin 15)
// PB2 --  (+) output (pin 16)
// PB3 --  (+) output (pin 17)
// PB4 --  (+) output (pin 18)
// PB5 --  (+) output (pin 19)
// PC0 --  (+) output (pin 23)

 int main() {
    // fire up the LCD
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
    lcd_home();

    // Set the 6 output pins (sourcing outputs)
  DDRB |= (1<<PB1);
  DDRB |= (1<<PB2);
  DDRB |= (1<<PB3);
  DDRB |= (1<<PB4);
  DDRB |= (1<<PB5);
  DDRC |= (1<<PC0);

   // Set the 4 input pins (sinking inputs)
  DDRC &= ~(1<<PC2); // set PC3 as input (pin 25)
  DDRC &= ~(1<<PC3); // set PC3 as input (pin 26)
  DDRC &= ~(1<<PC4); // set PC4 as input (pin 27)
  DDRC &= ~(1<<PC5); // set PC2 as input (pin 28)

  // turn on the internal resistors for the input pins 
  PORTC |= (1<<PC2); // turn on internal pull up resistor (pin 25)
  PORTC |= (1<<PC3); // turn on internal pull up resistor (pin 26)
  PORTC |= (1<<PC4); // turn on internal pull up resistor (pin 27)
  PORTC |= (1<<PC5); // turn on internal pull up resistor (pin 28)

  // declare the variables to represent each pushbutton input
  uint8_t enter_selection;
  uint8_t deselect;
  uint8_t menu_up;
  uint8_t menu_down;
  uint8_t j; // for selection indicator

  // declare the other variables
  uint8_t a;
  uint8_t b;
  uint8_t c;
  uint8_t d;
  uint8_t e;
  uint8_t f;

    for(j = 0; j <= 6; j++){

        // Turn off all six outputs

            PORTB &= ~(1<<PB1);
            PORTB &= ~(1<<PB2);
            PORTB &= ~(1<<PB3);
            PORTB &= ~(1<<PB4);
            PORTB &= ~(1<<PB5);
            PORTC &= ~(1<<PC0);

        lcd_line_one(); 
        fprintf_P(&lcd_stream, PSTR("> output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));

        }

    j=0; // start position selection indicator @ "output one"
    a=0;
    b=0;
    c=0;
    d=0;
    e=0;
    f=0; // and all outputs off

while(1) {

    menu_up = (PINC & (1<<PC4)) >> PC4;
    if (menu_up == 0)

    {   if(j==0)
        j=6;
        if(j>0)         //set 0 as lowest allowed count
        j = j - 1;      // if "up" button pressed, move arrow up menu
        delay_ms(300);   // switch "debounce"
    }

    menu_down = (PINC & (1<<PC3)) >> PC3;
    if (menu_down == 0)

    {   j = j + 1;      //if "down" button pressed, move arrow down menu
        if (j >= 6 )    //set 5 as highest count
        j = 0;           //count loops to zero
        delay_ms(300);   // switch "debounce"
    }

    deselect = (PINC & (1<<PC2)) >> PC2;
    enter_selection = (PINC & (1<<PC5)) >> PC5;

    if ( j == 0)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("> output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 0 ) && (enter_selection == 0)) // pushbutton on pin 28 (PC5)

    {   PORTB |= (1<<PB1); // turns pin 15 output ON
        a = 1;      
    }

    if (( j == 0 ) && (deselect == 0)) // pushbutton on pin 25 (PC2)

    {   PORTB &= ~(1<<PB1); // turns pin 15 output OFF  
        a = 0;
    }

    if ( j == 1)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("> output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 1 ) && (enter_selection == 0))  // pushbutton on pin 28 (PC5)

    {   PORTB |= (1<<PB2); // turns pin 16 output ON        
        b = 1;
    }

    if (( j == 1 ) && (deselect == 0)) // pushbutton on pin 25 (PC2)

    {   PORTB &= ~(1<<PB2); // turns pin 16 output OFF      
        b = 0;
    }

    if ( j == 2)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   ")); 
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("> output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("  output four  "));
    }

    if (( j == 2 ) && (enter_selection == 0)) // ect.,

    {   PORTB |= (1<<PB3); // pin 17 output ON      
        c = 1;
    }

    if (( j == 2 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB3); // pin 17 output OFF        
        c = 0;
    }

    if ( j == 3)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output one   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output four  ")); // output four selected
    }

    if (( j == 3 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB4); // pin 18 output ON      
        d = 1;
    }

    if (( j == 3 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB4); // pin 18 output OFF        
        d = 0;
    }

    if ( j == 4)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output two   "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output four  "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output five  ")); //output five selected
    }

    if (( j == 4 ) && (enter_selection == 0))

    {   PORTB |= (1<<PB5); // pin 19 output ON      
        e = 1;
    }

    if (( j == 4 ) && (deselect == 0))

    {   PORTB &= ~(1<<PB5); // pin 19 output OFF        
        e = 0;
    }

    if ( j == 5)

    {   lcd_line_one();     
        fprintf_P(&lcd_stream, PSTR("  output three "));
        lcd_line_two();     
        fprintf_P(&lcd_stream, PSTR("  output four  "));
        lcd_line_three();       
        fprintf_P(&lcd_stream, PSTR("  output five  "));
        lcd_line_four();        
        fprintf_P(&lcd_stream, PSTR("> output six   "));    
    }

    if (( j == 5 ) && (enter_selection == 0))

    {   PORTC |= (1<<PC0); // pin 23 output ON      
        f = 1;
    }

    if (( j == 5 ) && (deselect == 0))

    {   PORTC &= ~(1<<PC0); // pin 23 output OFF        
        f = 0;
    }

    // begin synching sequences

        if ((a == 1) && (j <= 3)) //begin "screen #1"

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));
    }

        if ((a == 0) && (j <= 3))

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((b == 1) && (j <= 3))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((b == 0) && (j <= 3))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((c == 1) && (j <= 3))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((c == 0) && (j <= 3))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((d == 1) && (j <= 3))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((d == 0) && (j <= 3))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR("   ")); // end "screen #1"
    }

        if ((b == 1) && (j == 4)) // begin "screen #2"

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((b == 0) && (j == 4))

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((c == 1) && (j == 4))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((c == 0) && (j == 4))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((d == 1) && (j == 4))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((d == 0) && (j == 4))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((e == 1) && (j == 4))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));
    }

        if ((e == 0) && (j == 4))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR("   ")); // end "screen #2"     
    }

        if ((c == 1) && (j == 5)) // begin "screen #3"

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((c == 0) && (j == 5))

    {   lcd_goto_position(0, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((d == 1) && (j == 5))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));    
    }

        if ((d == 0) && (j == 5))

    {   lcd_goto_position(1, 17);
        fprintf_P(&lcd_stream, PSTR("   "));
    }

        if ((e == 1) && (j == 5))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));
    }

        if ((e == 0) && (j == 5))

    {   lcd_goto_position(2, 17);
        fprintf_P(&lcd_stream, PSTR("   "));    
    }

        if ((f == 1) && (j == 5))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR(" ON"));
    }

        if ((f == 0) && (j == 5))

    {   lcd_goto_position(3, 17);
        fprintf_P(&lcd_stream, PSTR("   "));    
    }

}

   return 0;

}

This works pretty good, I'm happy with it. It would be pretty easy to modify also, add another output, etc. I might play around and see if I can do a one button input to turn the selected output on or off (push once for on, push again for off), and thus free up another port for another extra output...

I may have some redundant code in this program. I may try disecting it some later... I'm always of course, open to any suggestions or comments... Does this code seem kinda large for what I want it to do? It's really just a fancy selector switch...

I'm getting it with the "if" statements, I'm getting it now how to "and" things... And Rick, that counter program you helped me with was of great help here too, again!

But I know there is so much more that I need to learn! But sometimes I gotta just walk away from it for a while, too. Often that's what I gotta do to figure something out.

Anyway, hope someone else might find what I posted useful or inspire them to come up with something better.

Dave

February 01, 2011
by Ralphxyz
Ralphxyz's Avatar

You can "latch" a button by setting a variable on button press and then polling the state of the variable on another press.

Your code would process depending on the state of the variable not on the button state.

This could/should be done using interrupts so that opens up a whole new realm of programing for you.

Ralph

Post a Reply

Please log in to post a reply.

Did you know that sound travels via pressure waves in the air, and you can make these with a piezoelectric buzzer? Learn more...