Engineering Programming Final Project

For my final group project in Engineering programming at Portland State University, we were tasked to create a counter on a 7 segment display. Even though the course was on C programming, we were allowed to use Arduino. I did need to keep the code beginner friendly due to the other group members being new to programming. But since I just needed to know the difference between C and C++, I took the “diagnostic” requirement the extra mile, and created a GUI interface using Processing to test the display.

First things first! How does this display work? I connected my power supply’s positive power lead to the L1 pin, and through a 220 ohm resistor, connected my negative lead to one of the A through Dp pins. In the image below, it happened to be the B pin. Then slowly turning up the voltage on the power supply to 3.3v and I saw the segment LED turn on. If it didn’t light up the first time, I would just switch the leads.

Verifying display has common anode and checking LED brightness with a 220 ohm resistor

Alright, now to add more resistors to the rest of the cathodes, and hook up the Arduino Nano to all the pins and attempt to turn all the LED segments for a single digit.

Lighting up all the segments for a single digit

Check! How about all of the digits?

Testing is the Nano could handle the current for all four digits

NICE! One last test is to select a specific digit to light up by itself.

Testing turning on a single digit with the Nano using programming

All set. Now to do some coding. The simplest way I could think of to create “digits” on the display was to create an char array with the first element in the array to hold the 1’s and 0’s for zero, and the second element would hold the 1’s and 0’s for one, and so on. The following is slightly modified for the sake of a simpler explanation, the full code linked the bottom will have what I demo’d for the final grade. Here is some code to create digits, as well as a few other lines to use later in the explanation:

/*
 * === Making numbers that don't change human readable ===
 */
// Making pin numbers human readable
// We ended up using a Arduino Mega 2560,
// so the select line pins got moved to 50, 51, 52, and 53
#define selectA       50
#define selectB       51  
#define selectC       52  
#define selectD       53


/*
 * === Creating our characters and select line arrays ===
 */
/*            A
 *          F   B
 *            G
 *          E   C
 *            D   Dp
 *          
     
 *          
 *         8  7654321 
 *      0b(Dp)GFEDCBA
 *      
 */
                          //   D
                          //   pGFEDCBA
const char characters[16]= { 0b11000000, //0
                             0b11111001, //1
                             0b10100100, //2
                             0b10110000, //3
                             0b10011001, //4
                             0b10010010, //5
                             0b10000010, //6
                             0b11111000, //7
                             0b10000000, //8
                             0b10010000, //9
                             0b10001000, //A
                             0b10000011, //B
                             0b11000110, //C
                             0b10100001, //D
                             0b10000110, //E
                             0b10001110  //F
                           };
                       
const char displays[] = { selectA,
                          selectB,
                          selectC,
                          selectD };

How is this useful? “AVR® 8-bit microcontrollers control applications through their digital Input and Output (I/O) pins. These pins can monitor any voltage present as a high impedance input and supply or sink current as a high or low voltage digital output. These pins are usually organized in groups of eight and referred to as a port. The AVR uses the alphabet to name these ports, for example: PortA, PortB, etc. The pins of PortA are referred to as PA0 – PA7.” (https://microchipdeveloper.com/8avr:ioports)

I have the Nano’s microcontroller’s (ATMEGA328P) PortC output pins connected to the display so that the PC0 pin controls the ‘A’ LED segment, and PC1 pin controls the ‘B’ LED segment, and so forth. See above code snippet for arrangement of A, B, C, D, E, F, G, and D(ecimal) P(oint) LED segments.

This is done for the purpose of speed. If the LED segments were connected to different ports, we would need to set each output pin separately:

char lower_nibble = 0b1101;
char upper_nibble = 0b0000;

// If bits 4 through 7 were connected to the lower nibble of Port C:
PORTC |= lower_nibble;

// and bits 0 through 4 were connected to upper nibble of Port A:
PORTA |= (upper_nibble << 4);

// It would still be two calls

The problem gets even worse if we use Arduino digitalWrite() function. Here is an example of how the digit ‘3’ would be set:

digitalWrite(2, HIGH);
digitalWrite(3, LOW);
digitalWrite(4, HIGH);
digitalWrite(5, HIGH);
digitalWrite(6, LOW);
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);

It doesn’t look too bad, but calling the digitalWrite function takes quite a bit of time. Let me show you how to do that in one statement if we are only using pins attached to PortC:

PORTC = 0b10110000;

Not only is it less code to write, but that one assignment takes less time than a single digitalWrite call.

Now, we want all our digits on the display to start off as ‘0’, so will need to create variables to hold the 1’s and 0’s for each digit, as well as variables to hold the numerical value of that digit.

Then we can create an array to hold the char arrays for our digits.

char ones, tens, hundreds, thousands;
int i_ones, i_tens, i_hundreds, i_thousands;

void setup() {
    i_ones = 0;
    i_tens= 0;
    i_hundreds= 0;
    i_thousands= 0;
    ones = characters[i_ones];
    tens = characters[i_tens];
    hundreds = characters[i_hundreds];
    thousands = characters[i_thousands];

    const char digits[] = { ones,
                              tens,
                              hundreds,
                              thousands };
    ...

Now in the loop, we will just “pulse” each digit’s cathode pin, which I will refer to ‘select pin’ from now on. If you’ll look at the first snippet of code, you’ll see I have an array to hold the pin number for my select pins called ‘displays’. This way I can just loop over the array:

void loop() {
    for(char i = 0; i < 4; i++) {
        PORTC = digits[i];
        digitalWrite( displays[i], HIGH);
        digitalWrite( displays[i], LOW);
    }
    ...

Now the ‘for loop’ will set PORTC’s value and bring the select pin high and immediately low before moving on to the next digit on the display. At the end of the main program loop, it would just start over.

Leaving it here, the Arduino would load ‘0’s to all the digits on the display, and the main loop would just cycle through pulsing each one. And yes, it pulses each one and moves on the next and starts over again so quickly, they look to be on all at the same time.

Quick note on setting PORTC’s value. PORTC will be assigned to the value of digits[i], which can be shown to resolve to PORTC = 0b111110011 as shown here:

PORTC = digits[i]
which resolves to
PORTC = digits[0]
which resolves to
PORTC = characters[i_ones]
which resolves to
PORTC = characters[1]
which resolves to
PORTC = 0b11111001

Remember when we assigned a binary number to PORTC earlier?!? Go ahead, take a victory lap around the room.

Ok, now sit back down, we’re almost done.

So, since the assignment wasn’t just to show ‘0’s on the display. We want this to increment. Let’s add some code to the main loop that will increment the ‘ones’ digit.

i_ones++;

Putting the code that increments ‘i_ones’ in the main loop will increment that number so fast, the digits on the display would just be a blur. All the segments would turn on and off so quickly, they would all just appear dim. The segments that are used more than others and would be turned on more often, and appear brighter, but not readable all the same.

To solve this, we will use the microcontroller’s timer peripheral. This is basically a stopwatch built-in to the microcontroller that get ignored until the stopwatch (timer) counts up to a predefined number, signals to the microcontroller, resets to zero, and starts to count up again.

Here is the code I basically copy and pasted from the ATMEGA328P’s datasheet that sets up the timer to signal the microcontroller at one second intervals:

// Good thing for datasheets and people smarter than I
void initTimer()
{
  // http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
  char cSREG;
  cSREG = SREG;             // store SREG value
  cli();                    // disable all interrupts
  SREG = cSREG;             // restore SREG value (I-bit)
    
  TCCR1A = 0;               // Zero out the timer control
  TCCR1B = 0;               // registers

  TCNT1 = tick_rate;        // preload timer 65536-16MHz/256/1Hz
  TCCR1B |= (1 << CS12);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  sei();                    // enable all interrupts
}

Explaining what is going on in the ‘initTimer’ function above is beyond the scope of this post, but if you call this function from the setup function, it will set everything up to call the interrupt vector ‘TIMER1_OVF_vect’. Interrupt is exactly what the name implies. It gets triggered, the microcontroller stops what it was doing, runs the code in the interrupt vector, and then goes back to what it was doing right before the interrupt was triggered.

What that means in a nutshell is, the function ‘TIMER1_OVF_vect’ gets called at one second intervals (in this example.) Quick note about interrupts, they need to be short as possible. Maybe something like this:

ISR(TIMER1_OVF_vect)
{
    i_ones++;
}

Now, once a second, the integer varable ‘i_ones’ gets incremented.

All there is to do now is to increment ‘i_tens’ after ‘i_ones’ is ‘9’ and is incremented.

Might just be easier to show the code:

if(i_ones > 9)
{
  i_ones = 0;
  i_tens++;
}
if(i_tens > 9)
{
  i_tens = 0;
  i_hundreds++;
}
if(i_hundreds > 9)
{
  i_hundreds = 0;
  i_thousands++;
}
if(i_thousands > 9)
{
  i_thousands = 0;
}

Extra credit reading: Taking this project an extra step further, I wrote a PC application that will send data over UART to the Arduino. There is also more code written in the Arduino to receive the data and do special “diagnostic” operations such as, decrement the counter, flash all segments for all digits, and even cycle through the segments, one at a time. Here is a video I recorded while doing testing. I can’t stress enough to record your testing sessions. If something happens while randomly and wildly clicking the mouse, you can review the tape. Here was one of the last records I did for this project:

Processing app sending data to Arduino for “diagnostics”

Ok. I hope someone found this helpful. If you wanted to view the full code, follow the link below. There is a mix of another person’s code in with my code. He wasn’t able to make the team meetings and had to inject his code, so it might look out of place or redundant. But if you’ve gotten this far, you should be fine.

https://github.com/dpnebert/Clock/blob/master/MEGA2560_Clock.ino

Please check out my recent posts to the right and thanks for following along!

Author:

Leave a Reply