Tuesday, 3 June 2014

PORT Manipulation & 7 Segment LED Interfacing - Working with the Simple Labs Seven Segment cum DTMF Shield

What is Port Manipulation?

So far we have been working on controlling individual pins on the Induino R3. Now we will explore as to how we can use groups of pins together. Pins of a microcontroller are normally grouped together as PORTS. Each PORT can have 6 - 8 Pins (Though 8 Pins is pretty much the standard!). We can control each of these Ports using associated registers and this process of controlling a whole port instead of individual pins is called Port Manipulation.  

Lets take a look at how Pins are mapped on the Atmega 328 Microcontroller on the Induino R3 / Arduino. Take a look at the image here


From the image, we can see that digital pins 0 to 7 are numbered as PD0 to PD7, This denotes they are Part of PORT D. So PORT D consists of pins 0 - 7. Here is how the pins are grouped to ports
  • PORT B => digital pins 8 to 13
  • PORT C => analog input pins A0 - A5
  • PORT D => digital pins 0 to 7

Every PORT has 3 Registers Associated with it
  1. DDR => Data Direction Register - This Register Stores Information as to if a pin is Input or Output
  2. PORT => This Register is a Output Register and all Output states of a pin are written to this register.
  3. PIN => This is a Input Register and Read Only. This register contains the current state of pin configured for Input.

The following are the registers associated with the 3 PORTS of the ATmega328.
  • PORT B => DDRB, PORTB, PINB
  • PORT C => DDRC, PORTC, PINC
  • PORT D => DDRD, PORTD, PIND
Note: Incase of a Board like the Arduino Mega 2560 there are more Ports however the naming convention for the registers will be the same as the above. 

Now Lets look at how these Registers work. Lets take PORT D. All the 3 registers associated with 

Digital Pin 7Digital Pin 6Digital Pin 5Digital Pin 4Digital Pin 3Digital Pin 2Digital Pin 1Digital Pin 0
DDRD1 (OUTPUT) 0  (INPUT)  0  (INPUT)  0  (INPUT)  0  (INPUT)  0  (INPUT)  0  (INPUT)  0  (INPUT) 
PORTD1 (HIGH)1 (Internal Pull up Enabled)0 (Default)0 (Default)0 (Default)0 (Default)0 (Default)0 (Default)
PIND1 (HIGH)1 (HIGH)0 (LOW)0 (LOW)0 (LOW)0 (LOW)0 (LOW)0 (LOW)

In the table above, in the Register DDRD is a 8 bit register with the least significant bit representing pin 0 and the most significant bit representing pin 7. Now in this register the 7th pin is configured as OUTPUT and the remaining pins are configured as INPUTs. When you use the pinMode function, this is the register that gets modified based on the pin number you specify.

The PORTD register has the 7th pin set as a HIGH. This is the register that gets modified when you use the digitalWrite function. Note, The 6th Pin is a Input pin and setting this as HIGH enables the Internal Pullup resistor. The remaining pins are in their default state - LOW.

The PIND register has the value pertaining to the current state of the PIN. In this case it would have HIGH for the 6th & 7th pins,  as they've both been set to HIGH in the PORTD register. However if there is a button connected to PIN 6 (which is configured as INPUT), the value of the 6th pin would vary everytime the button is pressed.

Port Manipulation Example

Lets try a blink program example that uses Port Manipulation. Lets try to Blink all the 3 LEDs on pins 11, 12 & 13 at the same time.


/*  Induino R3 User Guide - Program 21.0 - Port Manipulation Example using the Induino R3 */

/* This program Controls the LEDs 11, 12 & 13 on the Induino R3 using PORT Manipulation*/

void setup()
{
  // Set the 3 Most significant bits corresponding to Pins 11, 12 & 13 as Output in the Data Direction Register
  // You can use binary data directly by preceeding with B
  DDRB = B111000; // This is the same as saying DDRB = 56;
  // Try commenting the above line and uncommenting the below line
  //DDRB = 56;
  // Initialise Serial Communication to see Register Values
  Serial.begin(9600);
}

void loop()
{
  // Set the 3 Most significant bits corresponding to Pins 11, 12 & 13 as HIGH in the PORT Register
  PORTB = B111000;
  // Store the current value of the PIN Register in the variable VAL just to see how the PIN register works
  int val = PINB ;
  Serial.println(val); // Print the value of the variable VAL
  delay(1000); // a delay as in a blink program ;)
  
  // Set the 3 Most significant bits corresponding to Pins 11, 12 & 13 as LOW in the PORT Register
  PORTB = B000000;
  // Store the current value of the PIN Register in the variable VAL
  val = PINB ;
  Serial.println(val); // Print the value of the variable VAL
  delay(1000); // a delay as in a blink program ;) 
  
}


The 7-Segment Display

7-Segment displays are another common component in the world of electronics. These displays have 8 LEDs split into different segments designed to be able to display numerals from 0-9 and a dot.  All The LEDs have a common ground / supply line. There are 5 pins at the top and 5 pins at the bottom. The middle pins in the top and bottom are connected to each other internally and have to be connected to Ground / Supply depending upon the type of the 7-segment Display. You can control each segment like an individual LED. We shall use Port Manipulation to Control these as a set.


The Simple Labs' DTMF / 7-Segment Shield

This shield lets you experiment with DTMF or 7-Segment Displays (You can use only one of these at any given time).  Check here for how to work with the DTMF part.


The shield lets you control up to two Common Cathode type 7-Segment Displays. The shield provides you with options to control the COM pin of the 7-Segment Displays using either an IO or by connecting the COM pin Directly to Ground through a set of jumpers. COM pin of the Digit 1 can be set to be controlled by Digital Pin 10 and COM pin of the Digit 1 can be set to be controlled by Digital Pin 11. See the image below.

Pin Mappings for 7-Segment Displays on the Shield are as per the following image


First Lets try to Control one of 7-Segment Displays. Set the jumper to connect the COM pin of display 1 to ground. (Refer to Jumper Settings image above). You can either remove the other display or set the jumper for it to connect its COM pin to the IO.

Now that we are all set, lets understand what we are about to do.
  • If you noticed, we have used all pins on PORT D to connect to the segments of the 7-Segment Display. This will help us control all the segments at once.
  • To display the number '1' on the 7-segment display, we need to make the segment 'b' (digital Pin 0) & 'c' (digital Pin 4) high and keep all other segments low.

Here's a table with values to display the different numbers

2^7 = 1282^6 = 642^5 = 322^4 = 162^3 = 82^2 = 42^1 = 22^0 = 1
Digital Pin 7Digital Pin 6Digital Pin 5Digital Pin 4Digital Pin 3Digital Pin 2Digital Pin 1Digital Pin 0
DigitDecimal ValueBinary ValueSegment D Segment DP Segment E Segment C  Segment F  Segment G  Segment A  Segment B 
0187101110111 (HIGH)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)0 (LOW)1 (HIGH)1 (HIGH)
117000100010 (LOW)0 (LOW)0 (LOW)1 (HIGH)0 (LOW)0 (LOW)0 (LOW)1 (HIGH)
2167101001111 (HIGH)0 (LOW)1 (HIGH)0 (LOW)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)
3151100101111 (HIGH)0 (LOW)0 (LOW)1 (HIGH)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)
429000111010 (LOW)0 (LOW)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)0 (LOW)1 (HIGH)
5158100111101 (HIGH)0 (LOW)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)0 (LOW)
6190101111101 (HIGH)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)0 (LOW)
719000100110 (LOW)0 (LOW)0 (LOW)1 (HIGH)0 (LOW)0 (LOW)1 (HIGH)1 (HIGH)
8191101111111 (HIGH)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)
9159100111111 (HIGH)0 (LOW)0 (LOW)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)1 (HIGH)


We are going to store the above decimal values in an array such that the 0th element of the array will hold the value '187' which is the value to be written to the output register to produce a '0' on the 7 segment display. So anytime we want to display a numeric value, we simply call the array variable with the value of the digit we want to display.

Try the following program. You should see numbers from 0 to 9 being displayed. Ensure you set the jumpers of the Displays to have the common pin connected to GND.


/*  Induino R3 User Guide - Program 21.1 - Controlling a single 7-Segment LED on the Simple Labs 7-Segment / DTMF Shield using the Induino R3 */

/* This program displays the numbers 0 to 9 on the 7 Segment display*/

// An array variable to store the decimal values to be written to the PORT register to produce various digits on the 7 segment.
// The 0th array index holds the value to display '0'. the 1st array index holds the value to display '1' and so on

int seven_seg_val[]={187,17,167,151,29,158,190,19,191,159};

void setup()
{
  // Set all the pins from 0 to 7 as output.
  // You can use binary data directly by preceeding with B
  DDRD = B11111111; // This is the same as saying DDRD = 255;

}

void loop()
{
  // A for loop to iterate through values 0 to 9 that have to be displayed on the 7 segment
  for(int i=0; i< 10; i++)
  {
    // Write the set of values for the current digit to the PORT register
    PORTD = seven_seg_val[i];
    delay(1000);
  }
}

Displaying Multiple Digits

Next lets see how we can produce multiple digits using multiple 7 segments (we will be working with two 7 segment displays however the process will be the same for n number of 7 segment displays!)

To produce multiple digits, we are going to control the common pin and change the values displayed on each of the digits at very high speed so that it appears to the viewer as if a multi digit number is being displayed constantly.

Lets see how to display the number 23.

  1. First control the common pin of the display 1 to turn it ON and control the common pin of the display 2 to turn it OFF
  2. Then Display the number 2 on the display 1 for a short amount of time (very short!). You do this by writing the corresponding value for 2 to the PORT register and adding a delay
  3. At the end of the delay write '0' to the PORT register to clear it
  4. Now control the common pin of the display 1 to turn it OFF and control the common pin of the display 2 to turn it ON
  5. Now Display the number 3 on the display 2 for a short amount of time (very short!). You do this by writing the corresponding value for 3 to the PORT register and adding a delay
  6. At the end of the delay write '0' to the PORT register to clear it
  7. go back and keep repeating from step 1 to 6 
Here's the program to display the number '23'
/*  Induino R3 User Guide - Program 21.2 - Displaying the number 23 using the two 7-Segment LEDs on the Simple Labs 7-Segment / DTMF Shield using the Induino R3 */

/* This program displays the number 23 on the 7 Segment LEDs */

// An array variable to store the decimal values to be written to the PORT register to produce various digits on the 7 segment.
// The 0th array index holds the value to display '0'. the 1st array index holds the value to display '1' and so on

int seven_seg_val[]={187,17,167,151,29,158,190,19,191,159};

void setup()
{
  // Set all the pins from 0 to 7 as output.
  // You can use binary data directly by preceeding with B
  DDRD = B11111111; // This is the same as saying DDRD = 255;
  // set the control pins for the common lines of both the 7 segment displays as OUTPUT
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  // write a LOW signal to the control pins for the common lines to keep them both disabled
  digitalWrite(10,LOW);
  digitalWrite(11,LOW);
}

void loop()
{
  // A for loop to iterate through values 0 to 9 that have to be displayed on the 7 segment
  for(int i=0; i< 100; i++)
  {
    // Turn ON Display 1 using the control Pin
    digitalWrite(10,HIGH);
    // Turn OFF Display 2 using the control Pin
    digitalWrite(11,LOW);
    // Write the set of values for the digit '2' to the PORT register
    PORTD = seven_seg_val[2];
    // A Short Delay
    delay(1);
    // Reset the PORT Register to 0
    PORTD = 0;
    
    // Turn OFF Display 1 using the control Pin
    digitalWrite(10,LOW);
    // Turn ON Display 2 using the control Pin
    digitalWrite(11,HIGH);
    // Write the set of values for the digit '3' to the PORT register
    PORTD = seven_seg_val[3];
    // A Short Delay
    delay(1);
    // Reset the PORT Register to 0
    PORTD = 0;
  }
}

Displaying Multiple Digits - 0 to 99

Here's an extension of the above program to display the numbers from 0 to 99
/*  Induino R3 User Guide - Program 21.3 - Displaying the numbers 0 to 99 using the two 7-Segment LEDs on the Simple Labs 7-Segment / DTMF Shield using the Induino R3 */

/* This program displays the numbers 0 to 99 on the 7 Segment LEDs */

// An array variable to store the decimal values to be written to the PORT register to produce various digits on the 7 segment.
// The 0th array index holds the value to display '0'. the 1st array index holds the value to display '1' and so on

int seven_seg_val[]={ 187,17,167,151,29,158,190,19,191,159};

void setup()
{
  // Set all the pins from 0 to 7 as output.
  // You can use binary data directly by preceeding with B
  DDRD = B11111111; // This is the same as saying DDRD = 255;
  // set the control pins for the common lines of both the 7 segment displays as OUTPUT
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  // write a LOW signal to the control pins for the common lines to keep them both disabled
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);
}

void loop()
{
  // A for loop to iterate through values 0 to 9 that have to be displayed on tens position of the 7 segment display
  for(int i=0; i<10;i++)
  {
    // A for loop to iterate through values 0 to 9 that have to be displayed on ones position of the 7 segment display
    for(int j=0; j<10;j++)
    {
      // A for loop to add stability to the display - the display looks more stable if the same value is displayed for more number of times at a higher speed.
      for(int k=0; k< 100; k++)
      {
        // Turn ON Display 1 using the control Pin
        digitalWrite(10,HIGH);
        // Turn OFF Display 2 using the control Pin
        digitalWrite(11,LOW);
        // Write the set of values for the digit '2' to the PORT register
        PORTD = seven_seg_val[i];
        // A Short Delay
        delay(1);
        // Reset the PORT Register to 0
        PORTD = 0;

        // Turn OFF Display 1 using the control Pin
        digitalWrite(10,LOW);
        // Turn ON Display 2 using the control Pin
        digitalWrite(11,HIGH);
        // Write the set of values for the digit '3' to the PORT register
        PORTD = seven_seg_val[j];
        // A Short Delay
        delay(1);
        // Reset the PORT Register to 0
        PORTD = 0;
      }
    }
  }
}

Displaying a sensor value

Lets see how to display a Sensor value. We shall display temperature value using a LM35 temperature sensor. Read this tutorial before proceeding (if you already haven't!)
http://www.induino.com/2013/07/the-on-board-sensor-interface.html

Heres how we are going to do it.
  1. Get the temperature value from the analog value
  2. Split the temperature value into 2 digits -> ones and tens values
  3. First control the common pin of the display 1 to turn it ON and control the common pin of the display 2 to turn it OFF
  4. Then Display the number of the ones value on the display 1 for a short amount of time (very short!). 
  5. At the end of the delay write '0' to the PORT register to clear it
  6. Now control the common pin of the display 1 to turn it OFF and control the common pin of the display 2 to turn it ON
  7. Now Display the number of the tens value on the display 2 for a short amount of time (very short!). 
  8. At the end of the delay write '0' to the PORT register to clear it
  9. go back and keep repeating from step 1 to 6 
Here's the program to do it

/*  Induino R3 User Guide - Program 21.4 - Displaying temperature value from a  lm35 sensor using the two 7-Segment LEDs on the Simple Labs 7-Segment / DTMF Shield using the Induino R3 */

/* This program displays the temperature value from a lm35 sensor on the 7 Segment LEDs */

// An array variable to store the decimal values to be written to the PORT register to produce various digits on the 7 segment.
// The 0th array index holds the value to display '0'. the 1st array index holds the value to display '1' and so on

int seven_seg_val[]={ 187,17,167,151,29,158,190,19,191,159};


void setup()
{
  // Set all the pins from 0 to 7 as output.
  // You can use binary data directly by preceeding with B
  DDRD = B11111111; // This is the same as saying DDRD = 255;
  // set the control pins for the common lines of both the 7 segment displays as OUTPUT
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  // write a LOW signal to the control pins for the common lines to keep them both disabled
  digitalWrite(10,HIGH);
  digitalWrite(11,HIGH);
}

void loop()
{
  // Read the raw sensor value and store it in the variable
  int sensor_val = analogRead(2)/2; 
  int ones = sensor_val%10;
  int tens = sensor_val/10;
      // A for loop to add stability to the display - the display looks more stable if the same value is displayed for more number of times at a higher speed.
      for(int k=0; k< 100; k++)
      {
        // Turn ON Display 1 using the control Pin
        digitalWrite(10,HIGH);
        // Turn OFF Display 2 using the control Pin
        digitalWrite(11,LOW);
        // Write the set of values for the digit '2' to the PORT register
        PORTD = seven_seg_val[ones];
        // A Short Delay
        delay(1);
        // Reset the PORT Register to 0
        PORTD = 0;

        // Turn OFF Display 1 using the control Pin
        digitalWrite(10,LOW);
        // Turn ON Display 2 using the control Pin
        digitalWrite(11,HIGH);
        // Write the set of values for the digit '3' to the PORT register
        PORTD = seven_seg_val[tens];
        // A Short Delay
        delay(1);
        // Reset the PORT Register to 0
        PORTD = 0;
      }
}

Thats It For This Part! Enjoy... and feel free to drop us an email with questions you might have -> info@simplelabs.co.in

Visit www.simplelabs.co.in for more interesting products

Back to List of Contents