Adding Machine With Logic Gates

64

1

2

Posted in TechnologyArduino

Introduction: Adding Machine With Logic Gates

Have you ever wondered what really happens when your calculator adds two numbers together? Sure, you probably recall the long-hand arithmetic steps you were taught as a child, but your computer or calculator doesn't have a pen and paper with which to carry out these steps. How, then, do the electrical pulses flow through your calculator to produce the sum of two numbers?

In this project, we will build an adding machine from some of the most basic digital building-blocks: logic gates. We will utilize an Arduino board to help us with the tedious stuff, like reading input from a keypad and displaying the results on an LCD screen, but we will create an ALU (Arithmetic Logic Unit) from a handful of integrated circuits to do the actual meat of the operation.

Step 1: Materials

  • 1 Arduino Mega2560 board
  • 3-5 Breadboards - The keypad and Arduino don't need to be taped to a board, but I liked keeping things tidy.
  • 2 74xx AND chips - I used the 74hc08
  • 2 74xx XOR chips - I used the 74hc86
  • 1 74xx OR chip - I used the 74hc32
  • 1 4x4 membrane keypad
  • 5 1K ohm, or greater, resistors
  • Lots of solid hook-up wire

Step 2: Connect the LCD Screen

The best place to start and get your bearings is to hook up the LCD screen and write an Arduino sketch to print some text.

Find a good spot on your breadboard where you'd like your screen to live and make the following connections.

Power and Ground

  • VSS to GND
  • VDD to +5V
  • RW to GND
  • A to +5V w/ 220 ohm resistor
  • K to GND
  • V0 to a 10K potentiometer

Digital IO

  • RS to digital pin 8
  • E to digital pin 9
  • D4 to digital pin 10
  • D5 to digital pin 11
  • D6 to digital pin 12
  • D7 to digital pin 13

Now let's power up our board and write a simple sketch to test out the screen!

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

void setup(){
  lcd.begin(16, 2); // initialize the lcd
  
  lcd.setCursor(0,0);
  lcd.print("This is row 1");
  lcd.setCursor(0,1);
  lcd.print("This is row 2");
}

void loop() {
}

Step 3: Connect the Keypad

Now let's hookup our keypad and modify our sketch to read from it and display what we type onto the LCD screen.

As shown in the diagram, the pins from the keypad are numbered from 1 to 8, right to left. In my photos, I flipped the ribbon underneath the keypad to keep everything flush with the breadboard.

Pins 1-4 correspond to the columns and 5-8 correspond to the rows.

#include <LiquidCrystal.h>
#include <Keypad.h>

LiquidCrystal lcd(8, 9, 10, 11, 12, 13);

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'=','0','C','/'}
};

byte rowPins[ROWS] = {53, 52, 51, 50}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {49, 48, 47, 46}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
  lcd.begin(16, 2); // initialize the lcd
}

void loop() {
  while (1)
  {
    char key = keypad.getKey();
    if(key)
    {
      lcd.print(key);
    }
  }
}

Step 4: Implement Naive Adding

This step gets everything fully tied together in software so that you can add values using the Arduino board. This is obviously pretty boring and not our end goal, but will lay the groundwork so that we can later swap out the boring adder with our awesome ALU.

#include <LiquidCrystal.h>
#include <Keypad.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); const byte ROWS = 4; //four rows const byte COLS = 4; //four columns char keys[ROWS][COLS] = { {'1','2','3','+'}, {'4','5','6','-'}, {'7','8','9','*'}, {'=','0','C','/'} }; byte rowPins[ROWS] = {53, 52, 51, 50}; //connect to the row pinouts of the keypad byte colPins[COLS] = {49, 48, 47, 46}; //connect to the column pinouts of the keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); long num1, num2, total; char operation; char key; byte num1Print; void setup() { lcd.begin(16, 2); // initialize the lcd } void loop() { // Get first number and operator while (1) { key = keypad.getKey(); if (key == 'C') { clearCalc(); } if (key >='0' && key <='9') { num1 = num1*10 + (key -'0'); lcd.setCursor(0,0); num1Print = lcd.print(num1); } if (key == '-' || key == '*' || key == '/') { clearCalc(); lcd.print("Operation not"); lcd.setCursor(0, 1); lcd.print("supported."); } if (key == '+') { operation = key; lcd.setCursor(num1Print+1, 0); lcd.print(operation); break; } } // Get second number and perform operation while (1) { if (key == 'C') { break; } key = keypad.getKey(); if (key == 'C') { clearCalc(); break; } if (key >='0' && key <='9') { num2 = num2*10 + (key -'0'); lcd.setCursor(num1Print+3, 0); lcd.print(num2); } if (key == '=') { doOperation(); break; } } // Wait for the user to clear the results while (1) { if (key == 'C') { break; } key = keypad.getKey(); if (key == 'C') { clearCalc(); break; } } } void doOperation() { switch(operation) { case '+': total = num1 + num2; // BORING!!! break; } lcd.setCursor(0,1); lcd.print('='); lcd.setCursor(1,1); lcd.print(total); } void clearCalc() { num1 = 0; num2 = 0; total = 0; operation = 0; num1Print = 0; lcd.clear(); }

Step 5: Layout the Logic Gates

I found it best to order the 5 logic gates as follows, from left to right:

XOR, AND, OR, AND, XOR

Now would also be a good time to connect the +5V and GND lines to each gate, as shown by the red and black wires.

A nice way to verify your gates are functioning properly is to hookup their first output to an LED and use some jumper cables to switch between +5V and GND; which logically represent 1 and 0, respectively.

Step 6: Create the ALU

Now for the fun part!

It's time to perform the actual arithmetic. To do so, we need to send the two numbers we'd like to add through the series of logic gates in binary format. We will then receive the result in binary and can do with that as we please. For now, we will hookup the output bits to some LEDs for quick and easy verification of our results.

The first thing to wrap your head around is what's called the "Half-Adder" (the simpler of the two block diagrams above). This circuit consists of an XOR gate and an AND gate. With just these two gates, we can add two single-bit numbers! Pretty exhilarating, right?! To understand how this happens, follow what would happen when you send different values for A and B, and create yourself a truth table. You can see that the XOR gate is the one performing the actual summing of the values, and the AND gate is what tells you whether or not the sum resulted in a carry bit.

Of course, we'd like to add more than just single-bit numbers. To do so, we must now raise the complexity a fair bit and create what's called a "Full-Adder" (clever names, huh?). If you look closely at this other diagram, you can see that it's really two half-adders tied together with an OR gate. This new circuit is what allows us to string together single-bit adders so that they each can accept the other's carry-out bit as input to their carry-in bit. Note that for the first bit you are adding, you won't have anything to carry in, so it only applies for subsequent bits down the line.

In the photos above, I've hooked up two sets of full-adders and can sum 2-bit numbers.

Below is our finalized code for sending our values to the ALU and receiving the result. It supports up to 8-bit numbers.

#include <LiquidCrystal.h>
#include <Keypad.h> LiquidCrystal lcd(8, 9, 10, 11, 12, 13); byte numBits = 8; byte num1Bits[] = {23, 25, 27, 29, 31, 33, 35, 37}; byte num2Bits[] = {22, 24, 26, 28, 30, 32, 34, 36}; // WARNING!! Using pins 0 & 1 will interfere with serial communications. // When uploading a sketch, remove any connections to these pins. byte resultBits[] = {0, 1, 2, 3, 4, 5, 6, 7}; const byte ROWS = 4; //four rows const byte COLS = 4; //four columns char keys[ROWS][COLS] = { {'1','2','3','+'}, {'4','5','6','-'}, {'7','8','9','*'}, {'=','0','C','/'} }; byte rowPins[ROWS] = {53, 52, 51, 50}; //connect to the row pinouts of the keypad byte colPins[COLS] = {49, 48, 47, 46}; //connect to the column pinouts of the keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); long num1, num2, total; char operation; char key; byte num1Print; void setup() { lcd.begin(16, 2); for (int i = 0; i < numBits; i++) { pinMode(num1Bits[i], OUTPUT); digitalWrite(num1Bits[i], LOW); pinMode(num2Bits[i], OUTPUT); digitalWrite(num2Bits[i], LOW); pinMode(resultBits[i], INPUT); } } void loop() { // Get first number and operator while (1) { key = keypad.getKey(); if (key == 'C') { clearCalc(); } if (key >='0' && key <='9') { num1 = num1*10 + (key -'0'); lcd.setCursor(0,0); num1Print = lcd.print(num1); } if (key == '-' || key == '*' || key == '/') { clearCalc(); lcd.print("Operation not"); lcd.setCursor(0, 1); lcd.print("supported."); } if (key == '+') { operation = key; lcd.setCursor(num1Print+1, 0); lcd.print(operation); break; } } // Get second number and perform operation while (1) { if (key == 'C') { break; } key = keypad.getKey(); if (key == 'C') { clearCalc(); break; } if (key >='0' && key <='9') { num2 = num2*10 + (key -'0'); lcd.setCursor(num1Print+3, 0); lcd.print(num2); } if (key == '=') { doOperation(); break; } } // Wait for the user to clear the results while (1) { if (key == 'C') { break; } key = keypad.getKey(); if (key == 'C') { clearCalc(); break; } } } void doOperation() { switch(operation) { case '+': for (int i = 0; i < numBits; i++) { byte num1BitRead = bitRead(num1, i); byte num2BitRead = bitRead(num2, i); digitalWrite(num1Bits[i], num1BitRead ? HIGH : LOW); digitalWrite(num2Bits[i], num2BitRead ? HIGH : LOW); } total = 0; for (int i = 0; i < numBits; i++) { if (i < 5) total = total | (digitalRead(i) << i); // AWESOME!!! } break; } lcd.setCursor(0,1); lcd.print('='); lcd.setCursor(1,1); lcd.print(total); } void clearCalc() { num1 = 0; num2 = 0; total = 0; operation = 0; num1Print = 0; lcd.clear(); for (int i = 0; i < numBits; i++) { digitalWrite(num1Bits[i], LOW); digitalWrite(num2Bits[i], LOW); } }

Step 7: Add to Your Heart's Content

In the above photo, I've hooked up 4 full-adders and can sum up to 15+15. You can simply continue to tie together these adders to support more and more bits.

Thanks for checking this out; I hope you enjoyed the project! I plan to add support for multiplication in the coming months, so please check back if you're interested.

Share

    Recommendations

    • Spotless Contest

      Spotless Contest
    • Microcontroller Contest

      Microcontroller Contest
    • Science of Cooking

      Science of Cooking
    user

    We have a be nice policy.
    Please be positive and constructive.

    Tips

    Questions

    2 Comments

    pretty cool i might have to give something like this a whirl, got a handful of 7400 series logic chips kicking around.. recently i made a 4 bit adder with 120v AC relays. consisting of one half adder and 3 full adders.

    if i remember right, for my full adders i used 2 XOR gates, 3 and gates feeding into a

    tri-input OR gate.

    20171205_095527.jpg

    Cool. It is nice to see someone building a project with old school logic gates.