Skip to main content
Engineering LibreTexts

24.2: A Practical Example — Round-Robin Switch

  • Page ID
    35899
  • Let’s take a look at how we might implement a round-robin switch. A round-robin switch works in a circular manner; stepping through a sequence of settings and returning back to the start once the sequence is completed. For our example, we shall consider a single momentary contact pushbutton that cycles through a series of fan speeds: off, low, medium and high. The system will start in the off state and each successive press of the button will advance the state from low through high. Once high is reached, a further press will cycle the state back to off and the process continues in like manner. Each speed setting will be indicated via its own LED and only one LED will be on at any given time. To keep the code clean, will only examine the pushbutton and LED portion of the code and not consider how we might control the speed of a fan. Further, we shall assume that an appropriate hardware debounce circuit is incorporated with the pushbutton (e.g., a 74HC14 Schmitt with RC network) and that depressing the button will produce a logical high at our Arduino input pin. Also, the LED drivers are assumed to be active high (i.e., a high on an output pin of the Arduino will light an LED).

    The first thing to remember is that a typical microcontroller is capable of checking the state of an input pin in a fraction of a microsecond. Consequently, we cannot simply check the pin to see if it is a logical high, and if so, cycle through to the next fan speed. In the fraction of a second it would take for a human to press and release the button, the microcontroller could loop through the pin checking code thousands of times, cycling the fan speed for each loop’s check. The final fan speed would be anyone’s guess. Instead, we need to look for the low to high transition as this happens only once with each button press. Indeed, this also points up the need for switch debouncing. Without it, a single press could result in over a dozen low to high edges due to switch contact chatter resulting in a random fan setting. One way to perform the positive going edge detection is to use two variables, one for the current state of the switch and a second for the prior state of the switch (i.e., the state that was measured immediately prior to the current state). If the current state is high (on or pressed) and the prior state is low (off or not pressed) then we know we have detected a positive going edge and we can perform our processing. Note that on the next iteration of the loop, the current state will be high but the prior state will now also be high (i.e., the former current state from the prior loop iteration) and thus no processing is performed. At this point it would not matter how long the user kept their finger on the button as all current and prior states will be high. When the user finally releases the button the current state will be low with a prior state of high. This is the negative going edge and, again, we have no need to process it. The next loop iteration will produce current and prior states of low with no need to process. Eventually, when the user presses the button again we’ll see a current state of high with a prior state of low and we’ll process this edge.

    We’ll need three variables for the states, namely the current and prior pushbutton states and another for the LEDs (which indicate fan speed). The pushbuttons can be thought of as Boolean variables (0/1) while the LED variable will range from 0 through 3, 0 being “off” and 3 being “high”. For simplicity, we’ll use globals for these, unsigned chars will do nicely. The four LEDs will be positioned off of Arduino Uno pins 8 through 11 (PORTB.0:3) and the pushbutton will be connected to pin 12 (PORTB.4). The setup() function will have to set the proper directions for these port bits. This would require five separate calls to pinMode() but only two simple operations if we operate directly. Here is the beginning portion:

    // declare the state variables and initialize at 0 for “off”
    unsigned char currentstate=0;
    unsigned char priorstate=0;
    unsigned char ledstate=0;
    
    // define some values for the LED/fan state. These are conveniently
    // chosen to be the FAN/LED bit positions in PORTB
    #define FAN_OFF     0
    #define FAN_LO      1
    #define FAN_MED     2
    #define FAN_HIGH    3
    
    // declare bit masks for the four LED bits and pushbutton
    #define LED_OFF  0x01
    #define LED_LOW  0x02
    #define LED_MED  0x04
    #define LED_HIGH 0x08
    #define PBUTTON  0x10
    
    // the LED_XXX masks could also be defined like so:
    // #define LED_OFF (1<<FAN_OFF)
    
    // a convenience
    #define LED_ALL  (LED_OFF|LED_LOW|LED_MED|LED_HIGH)
    
    setup()
    {
        // set for output
        DDRB |= LED_ALL;
        // set for input
        DDRB &= ~PBUTTON;
    
        // light up the “off” LED
        PORTB |= LED_OFF;
        // by default, outputs are 0 but if for some reason the other
        // LEDs could be on, be sure to set them to off before continuing
        // PORTB &= ~(LED_LOW|LED_MED|LED_HIGH);
    }
    

    The looping code needs to check for the proper current state/prior state pair. If it is not found, there is nothing to do. If it is found, we need to turn off the existing LED and then check the current LED/fan state, increment to the next state and light that LED. There are a couple ways to do this. The first is perhaps an obvious solution using a switch/case construct. The second method is a bit more compact (pun intended).

    loop()
    {
        // get current button state
        currentstate = PINB & PBUTTON;
    
        // do we have a positive going edge?
        if( currentstate && !priorstate )
        {
            switch( ledstate )
            {
                case FAN_OFF:
                    PORTB &= ~LED_OFF;    // turn off old LED
                    PORTB |= LED_LOW;     // turn on new
                    ledstate = FAN_LOW;   // increment speed
                    break;
                    
                case FAN_LOW:
                    PORTB &= ~LED_LOW;
                    PORTB |= LED_MED;
                    ledstate = FAN_MED;
                    break;
                    
                case FAN_MED:
                    PORTB &= ~LED_MED;
                    PORTB |= LED_HIGH;
                    ledstate = FAN_HIGH;
                    break;
                    
                case FAN_HIGH:
                    PORTB &= ~LED_HIGH;
                    PORTB |= LED_OFF;
                    ledstate = FAN_OFF;
                    break;
            }
        }
    
        // update state for next loop iteration
        priorstate = currentstate;
    }
    

    The alternate version can be found on the next page. Note the code savings. Instead of checking for each fan state, we turn off all LEDs and increment the fan state. If the state would increment past the maximum then we make sure to set it to the off state. This new state tells us which LED to light. This version works with little code because of the way in which we chose our bit positions. This is not always possible (or even desirable) but can be a handy technique from time to time.

    loop()
    {
        // get current button state
        currentstate = PINB & PBUTTON;
    
        // do we have a positive going edge?
        if( currentstate && !priorstate )
        {
            // increment to new state
            if( ledstate < FAN_HIGH )
                ledstate++;
            else
                ledstate = FAN_OFF;
    
            // lazy: turn off all LEDs
            PORTB &= ~LED_ALL;
            // turn on new LED
            PORTB |= (1<<ledstate);
        }
    
        // update state for next loop iteration
        priorstate = currentstate;
    }
    
    • Was this article helpful?