Skip to main content
Engineering LibreTexts

9.3: LED Output

  • Page ID
    25734
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Now that the sensor and analog input function have been verified, let’s combine this with an LED display. Instead of printing values to the Serial Monitor, we will light up a string of LEDs. This will require digital output (write) code as explored in a prior exercise. To keep the effort simple and just show “proof of concept”, we’ll light up to four LEDs. Each LED will require an appropriate transistor driver circuit (or consider a Darlington driver array such as the ULN2003 that contains seven drivers in a single IC). We shall use the bottom four bits of port B for the output signals, Arduino pins 8 through 11. Consider the code below:

    /* Read analog V2. Uses FSR (lower) and 10k (to +5V) on pin A0. Lights 4 LEDs
     off of port B.0:3 to indicate force. Active high drivers. */
    
    #define ANALOG_IN_PIN 0
    
    // These are the bits to be used on port B for the LEDs
    #define LEDMASK0      0x01
    #define LEDMASK1      0x02
    #define LEDMASK2      0x04
    #define LEDMASK3      0x08
    #define LEDMASK       (LEDMASK0 | LEDMASK1 | LEDMASK2 | LEDMASK3)
    
    void  lightLEDsBar( int a );
    
    int prior=0;
    
    void setup()
    {
      Serial.begin(9600);
      analogReference( DEFAULT );
      // set LED driver bits to output mode
      DDRB |= LEDMASK;
    }
    
    void loop()
    {
      int a;
    
      a = analogRead( ANALOG_IN_PIN );
      if( a != prior )
      {
        Serial.println(a);
        lightLEDsBar( a );
        prior = a;
      }
    }
    
    void  lightLEDsBar( int a )
    {
    
      if( a > 820 )
        PORTB |= LEDMASK; // light everything
      else
      {
        if( a > 615 )
        {
          PORTB &= (~LEDMASK3); // extinguish top
          PORTB |= (LEDMASK2 | LEDMASK1 | LEDMASK0); // light everything below
        }
        else
        {
          if( a > 410 )
          {
            PORTB &= (~(LEDMASK3 | LEDMASK2));
            PORTB |= (LEDMASK1 | LEDMASK0);
          }
          else
          {
            if( a > 205 )
            {
              PORTB &= (~(LEDMASK3 | LEDMASK2 | LEDMASK1));
              PORTB |= LEDMASK0;
            }
            else
              PORTB &= (~LEDMASK);
          }
        }
      }
    }
    

    Of primary interest here is the requirement to initialize DDRB for output on bits 0:3 which will be used to drive the LEDs. Separate bit masks are defined for each output pin for ease of code maintenance. A new function has been created to handle the lighting chores, lightLEDsBar(). This function will create a bar graph based on the value of the argument passed to it. As there are four LEDs, we can slice the 1024 range into five segments. If the value is below 20% (about 205) no LEDs are lit. If it’s between 20% and 40% only the lowest LED is lit. As the value increases more LEDs are lit until, at 80% or higher, all LEDs are lit. The function is little more than a big cascading if/else. In any given region first the top LEDs are unlit and then the remaining LEDs below are lit. Note that the technique above is more efficient than using the digitalWrite() function because we can set the state of several bits at once by directly accessing PORTB. In contrast, digitalWrite() would require one call for each bit/LED. The Serial Monitor code has been left in so that you can monitor the results to make sure everything is working properly.

    While leaving the FSR in place, wire four LED drivers to Arduino pins 8 through 11. An LED current of 10 milliamps should be sufficient. Enter the code above, compile and transfer it. To test, simply open the Serial Monitor and press on the FSR. As the force increases, the values on the Serial Monitor should decrease and fewer LEDs should light. You can think of this as an “absence of force” meter. While this might sound backwards, it can be quite useful. There are times when we want something to be under pressure and a lack of force serves as a warning (i.e. more LEDs lit means a bigger problem).

    Sometimes a string of LEDs are arranged to mimic an analog meter. In such an instance we might wish to only light a single LED rather than all of the LEDs below it. We might call this “dot mode” versus “bar mode”. Version three of the program shows dot mode with an interesting twist. Also, the Serial Monitor code has been removed. Note the decrease in program size.

    /* Read analog V3. Uses FSR (lower) and 10k (to +5V) on pin A0. Lights 4 LEDs 
    off of port B.0:3 to indicate force. Dot display mode. Active high drivers. */
    
    #define ANALOG_IN_PIN 0
    
    // These are the bits to be used on port B for the LEDs
    #define LEDMASK0      0x01
    #define LEDMASK1      0x02
    #define LEDMASK2      0x04
    #define LEDMASK3      0x08
    #define LEDMASK       (LEDMASK0 | LEDMASK1 | LEDMASK2 | LEDMASK3)
    
    void  lightLEDsDot( int a );
    
    int prior=0;
    
    int thresh[] = {820, 615, 410, 205};
    void setup()
    {
      analogReference( DEFAULT );
      DDRB |= LEDMASK;  // set LED driver bits to output mode
    }
    
    void loop()
    {
      int a;
    
      a = analogRead( ANALOG_IN_PIN );
      if( a != prior )
      {
        lightLEDsDot( a );
        prior = a;
      }
    }
    
    void  lightLEDsDot( int a )
    {
      int *p;
      p = thresh;
    
      if( a > *p++ )
      {
        PORTB &= (~(LEDMASK2 | LEDMASK1 | LEDMASK0)); // turn off bottom 3
        PORTB |= (LEDMASK3);                          // light only topmost
      }
      else
      {
        if( a > *p++ )
        {
          PORTB &= (~(LEDMASK3 | LEDMASK1 | LEDMASK0));
          PORTB |= (LEDMASK2);
        }
        else
        {
          if( a > *p++ )
          {
            PORTB &= (~(LEDMASK3 | LEDMASK2 | LEDMASK0));
            PORTB |= LEDMASK1;
          }
          else
          {
            if( a > *p )
            {
              PORTB &= (~(LEDMASK3 | LEDMASK2 | LEDMASK1));
              PORTB |= LEDMASK0;
            }
            else
              PORTB &= (~LEDMASK);
          }
        }
      }
    }
    

    The Dot variant is similar to the Bar version except that all LEDs are extinguished with the exception of the single “top most” LED. You might wonder why we go to trouble of extinguishing the individual effected bits instead of just setting them all to off (ANDing with 0xf0) and then setting the desired bit on. The reason is because this might create a quick toggle on/off of the desired “top most” bit. In this application you’d probably never see the difference but there might be applications where this toggling can be a problem. In any case, the total amount of machine code generated is roughly the same either way.

    The more interesting alteration of the function is that an array of values is used for the thresholds rather than fixed, hard-coded values. This makes the program much more flexible should other levels be required in the future. To use a different set of thresholds, all that needs to be done is a pointer assignment to the base of the appropriate threshold array at the start of the function. Also, note in particular the use of the post-increment operator on the pointer p. This is generally more efficient than the typical array indexing method.


    This page titled 9.3: LED Output is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by James M. Fiore via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.