Friday, November 20, 2015

Four Digit Seven Segment Display and Port Manipulation with Arduino

The next enhancement for my Arduino based microtonal MIDI converter is to add a 4-digit seven segment display to show the values to be changed with encoders.  I decided to use the first two digits to display the number of steps per octave and the last two digits the base MIDI note.

I acquired a four digit common anode seven segment display.  According to the datasheet pins 6, 9, 10, and 12 are the digit pins.  These pins are used for selecting each of the four digits.  The other 8 pins are for the seven segments plus a decimal point.

I put current limiting resistors on each of the digit pins because there are only 4.  (I would need 8 resistors if I put them on the segment pins.)  The data sheet specifies a max steady current of 25 mA with a typical 2.2 volts forward.  I used 220 ohm resistors.  Then using Ohm's law the resistors will limit the current to (5 - 2.2) volts / 220 ohms = 12.7 mA, which is within the acceptable range.





I connected the seven segment pins to the 12 digital only Arduino pins 2-13.  I simply connected them in an order that made it easy to wire up.  Then using the pin mapping from the data sheet I specified the Arduino pin mapping in my code with a bunch of #define statements.  I figured it is easier to adapt the code than to have a tangled mess of wires running between the display and Arduino.


// arduino pin numbers for the seven segments and decimal point
#define PIN_A  12
#define PIN_B  8
#define PIN_C  4
#define PIN_D  6
#define PIN_E  7
#define PIN_F  11
#define PIN_G  3
#define PIN_DP 5

// arduino pin numbers for the 4 digits
#define PIN_1 13
#define PIN_2 10
#define PIN_3 9
#define PIN_4 2

I originally started out using the SevSeg library from arduino.cc.  It worked well for displaying numbers, but I found that it introduced a significant amount of latency with all the digitalWrite() calls--7 segments plus decimal point times 4 = 32 total.  So I decided to write my own code to write to the pins more efficiently.

For that I needed to learn about port manipulation.  The two data registers PORTD and PORTB map to Arduino pins 0-7 and 8-15, respectively.  When the pin mode is set as OUTPUT, then writing a 0 or 1 to these registers results in setting the pin LOW or HIGH.  So I wrote the following code to quickly set pins HIGH or LOW by writing directly to the corresponding PORT without any error checking.


pinHigh(int pin) {
  if (pin >= 2 && pin <= 7) {
    PORTD |= 1 << pin;
  }
  else if (pin >= 8 && pin <= 13) {
    PORTB |= 1 << (pin - 8);
  }
}

pinLow(int pin) {
  if (pin >= 2 && pin <= 7) {
    PORTD &= ~(1 << pin);
  }
  else if (pin >= 8 && pin <= 13) {
    PORTB &= ~(1 << (pin - 8));
  }
}