A calculator is a simple and essential tool in everyday life, and it can
also be a fun and educational project to build one yourself. One way to
do this is by using an ATmega32 microcontroller, which is a powerful
and versatile chip that can be programmed to perform a wide range of
functions. In this blog post, we will show you how to build a simple
calculator using the ATmega32 microcontroller.
Here a simple calculator build with ATmega32 microcontroller is illustrated. The calculator uses the Atmega32 as a calculator brain and controller. It takes user input from 4x4 keypad and outputs the result on 16x2 LCD(Liquid Crystal Display). This is a very simple calculator useful to learn how to interface input and output devices with microcontroller, learn how to write C program code for taking input from keypad, perform mathematical calculation and send the result of the calculation to the LCD display output device.
The first step in building a calculator is to gather the necessary
components. The main component of the calculator is the ATmega32
microcontroller, which can be purchased from a variety of electronics
retailers. Additionally, you will need a 16x2 LCD display, a keypad, and
a few other components such as resistors, capacitors, and a power
supply.
The following shows the circuit schematic of the ATmega32 calculator.
In the above circuit drawing, the 4x4 keypad is connected to port C of the microcontroller and the 16x2 LCD are connected to the port A.
Once you have all the components, you can begin assembling the calculator. The ATmega32 microcontroller is the brain of the calculator, and it is responsible for performing the calculations and displaying the results on the LCD display. The keypad is used to input the numbers and operations, and it is connected to the ATmega32 microcontroller as shown in the circuit diagram above.
Once you have assembled the hardware, you can begin programming the
ATmega32 microcontroller. This is done using a programming language such
as C, and you will need a programmer such as AVR MKII to upload the code to the
microcontroller. To learn how to upload code see the tutorial How to Program ATmega328p using ATMEL Studio & AVR ISP MKII.
The program code for this calculator consist C program files for performing the actual calculation, read keypad, write to LCD, the main C program and an sbit C file. So the files are:
1. main C program
2. keypad header and C program
3. LCD header and C program
4. sbit header program
5. calculation header and C program
These calculator programing files are provided below.
1. main C program
The main C program code is below.
#include <avr/io.h>
#include <stdio.h>
#include <util/delay.h>
#include "lcd.h"
#include "sbit.h"
#include "keypad.h"
#include "calc.h"
int main(void){
lcdinit();
unsigned char key; // Key char for keeping record of pressed key
int num1, num2; // First number, second number
char func; // Function to be performed among two numbers
lcdgoto(5,1);
lcdstr("ATmega32 ");
lcdgoto(4,2);
lcdstr("Calculator");
_delay_ms(2000);
lcdclear();
while(1)
{
key = keypress(); //get numb1
lcdcmd(0x01); // Clear LCD screen
lcdchar(key); // Echo the key pressed to LCD
num1 = get_num(key); // Get int number from char value, it checks for wrong input as well
if(num1 != Error) // If correct input then proceed, num1==Error means wrong input
{
key = keypress(); //get function
lcdchar(key); //Echo the key pressed to LCD
func = get_func(key); //it checks for wrong func
if(func!='e') //if correct input then proceed, func=='e' means wrong input
{
key = keypress(); //get numb2
lcdchar(key); //Echo the key pressed to LCD
num2 = get_num(key); //Get int number from char value, it checks for wrong input as well
if(num2!=Error) //if correct input then proceed, num2==Error means wrong input
{
key = keypress(); //get equal sign
lcdchar(key); //Echo the key pressed to LCD
if(key == '=') //if = is pressed then proceed
{
switch(func) //switch on function
{
case '+':
disp_num(num1+num2);
break;
case '-':
disp_num(num1-num2);
break;
case 'x':
disp_num(num1*num2);
break;
case '/':
disp_num(num1/num2);
break;
}
}
else //key other then = here means error wrong input
{
if(key == 'C') //if clear screen is pressed then clear screen and reset
lcdcmd(0x01); // Clear LCD screen
else
DispError(0); //Display wrong input error
}
}
}
}
}
return 0;
}
This main program code uses keypad, lcd ,sbit and calculator program code as follows.
2. keypad header and C program
The keypad program codes consist of a header file keypad.h and c program file keypad.c. These controls the keypad aspect in the main.c program above. They have functions which are called in the main program code. These program code are below.
a. keypad .h
#include <avr/io.h>
#include <util/delay.h>
unsigned char keypress();
b. keypad.c
#include <avr/io.h>
#include "util/delay.h"
#include "sbit.h"
#define R1 SBIT(PORTC, 0)
#define R2 SBIT(PORTC, 1)
#define R3 SBIT(PORTC, 2)
#define R4 SBIT(PORTC, 3)
#define c1 SBIT(PINC, 4)
#define c2 SBIT(PINC, 5)
#define c3 SBIT(PINC, 6)
#define c4 SBIT(PINC, 7)
unsigned char keypress(void)
{
DDRC=0b00001111;
PORTC=0b11110000;
while (1)
{
R1=0; R2=1; R3=1; R4=1;
_delay_us(50);
if (c1==0){while (c1==0);return ('7');}
if(c2==0){while (c2==0); return ('8');}
if (c3==0){while (c3==0); return ('9');}
if(c4==0){while (c4==0);return ('/');}
R1=1; R2=0; R3=1; R4=1;
_delay_us(50);
if (c1==0){while (c1==0); return ('4');}
if(c2==0){while (c2==0); return ('5');}
if(c3==0){while (c3==0); return ('6');}
if(c4==0){while (c4==0); return ('x');}
R1=1; R2=1; R3=0; R4=1;
_delay_us(50);
if (c1==0){while (c1==0); return ('1');}
if (c2==0){while (c2==0); return ('2');}
if (c3==0){while (c3==0); return ('3');}
if (c4==0){while (c4==0); return ('-');}
R1=1; R2=1; R3=1; R4=0;
_delay_us(50);
if (c1==0){while (c1==0); return ('C');}
if (c2==0){while (c2==0); return ('0');}
if (c3==0){while (c3==0); return ('=');}
if (c4==0){while (c4==0); return ('+');}
}
}
3. LCD header and C program
The LCD program code consist also of a header file lcd.h and c program file lcd.c. These program code controls the LCD aspect from the main.c program above. They have functions which are called in the main C program. The programming code for these files are provided below.
a. lcd.h
#include <avr/io.h>
void lcdinit();
void lcdcmd(unsigned char);
void lcdchar(unsigned char);
void lcdstr(char *);
void latch(void);
void lcdgoto(uint8_t , uint8_t );
void lcdclear();
b.lcd.c
#include <avr/io.h>
#include <util/delay.h>
#include "lcd.h"
#define E (1<<PA3)
#define RW (1<<PA2)
#define RS (1<<PA1)
void lcdinit(){
//initialize PORTs for LCD
DDRA |= (1<<PA1) | (1<<PA2) | (1<<PA3) | (1<<PA4) | (1<<PA5) | (1<<PA6) | (1<<PA7); //make output for E,RW,RS and 4 bit Data
//Send Pulse to Latch the data
latch();
_delay_ms(2); //delay for stable power
// Command to set up the LCD
lcdcmd(0x33);
_delay_us(100);
lcdcmd(0x32);
_delay_us(100);
lcdcmd(0x28); // 2 lines 5x7 matrix dot
_delay_us(100);
//lcdcmd(0x0E); // display ON, Cursor ON
lcdcmd(0x0C); // display ON, Cursor ON
_delay_us(100);
lcdcmd(0x01); //clear LCD
_delay_ms(20); //wait
lcdcmd(0x06); //shift cursor to right
_delay_ms(100);
}
void latch(void){
PORTA |= E; //send high
_delay_us(500); //wait
PORTA &= ~E; //send low
_delay_us(500); //wait
}
void lcdcmd(unsigned char cmd){
PORTA = (PORTA & 0x0F) | (cmd & 0xF0); // send high nibble
PORTA &= ~RW; //send 0 for write operation
PORTA &= ~RS; //send 0 to select command register
latch(); //latch the data
PORTA = (PORTA & 0x0F) | (cmd<<4); //send low nibble
latch(); //latch the data
}
void lcdchar(unsigned char data){
PORTA = (PORTA & 0x0F) | (data & 0xF0); // send high nibble
PORTA &= ~RW; //send 0 for write operation
PORTA |= RS; //send 1 to select data register
latch();
PORTA = (PORTA & 0x0F) | (data<<4); // send high nibble
latch();
}
void lcdstr(char *str){
unsigned char k=0;
while(str[k] != 0){
lcdchar(str[k]);
k++;
}
}
void lcdgoto(unsigned char x, unsigned char y){
unsigned char firstcharadr[] = {0x80, 0xC0, 0x94, 0xD4};
lcdcmd(firstcharadr[y-1] + x-1);
_delay_us(1000);
}
void lcdclear(){
lcdcmd(0x01);
_delay_ms(10);
}
4. sbit header program
Another file used in this calculator program is the sbit.h program file. This file just creates a macro which allows to refer to a single bit from a selected port.
#ifndef _sbit_h_
#define _sbit_h_
struct bits {
uint8_t b0:1;
uint8_t b1:1;
uint8_t b2:1;
uint8_t b3:1;
uint8_t b4:1;
uint8_t b5:1;
uint8_t b6:1;
uint8_t b7:1;
} __attribute__((__packed__));
#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
#endif
5. calculation header and C program
Lastly another programming code file used is for the calculator. These are the calc.h and calc.c files. The program codes in these files controls the calculation aspect of the calculator. The functions used in these files are used in the main C program.
a.calc.h
#include <avr/io.h>
#include <util/delay.h>
#define Error 13
int get_num(unsigned char);
unsigned char get_func(unsigned char);
void DispError(int);
void disp_num(int);
b.calc.c
#include <avr/io.h>
#include <util/delay.h>
#include "calc.h"
#include "lcd.h"
int get_num(unsigned char ch) //convert unsigned char into int
{
int num = 0;
switch(ch)
{
case '0':
num = 0;
break;
case '1':
num = 1;
break;
case '2':
num = 2;
break;
case '3':
num = 3;
break;
case '4':
num = 4;
break;
case '5':
num = 5;
break;
case '6':
num = 6;
break;
case '7':
num = 7;
break;
case '8':
num = 8;
break;
case '9':
num = 9;
break;
case 'C':
lcdcmd(0x01);
num = Error;
break; //this is used as a clear screen and then reset by setting error
default:
DispError(0);
num = Error;
break; //it means wrong input
}
return num;
}
unsigned char get_func(unsigned char chf) //detects the errors in inputted function
{
if(chf=='C') //if clear screen then clear the LCD and reset
{
lcdcmd(0x01); //clear display
return 'e';
}
if( chf != '+' && chf != '-' && chf != 'x' && chf != '/' ) //if input is not from allowed funtions then show error
{
DispError(1);
return 'e';
}
return chf; //function is correct so return the correct function
}
void DispError(int numb) //displays differet error messages
{
unsigned char msg1[] = {"Wrong Input"};
unsigned char msg2[] = {"Wrong Function"};
lcdcmd(0x01); //clear display
switch(numb)
{
case 0:
lcdstr(msg1);
break;
case 1:
lcdstr(msg2);
break;
default:
lcdstr(msg1);
break;
}
}
void disp_num(int numb) //displays number on LCD
{
unsigned char UnitDigit = 0; //It will contain unit digit of numb
unsigned char TenthDigit = 0; //It will contain 10th position digit of numb
if(numb<0) {
numb = -1*numb; // Make number positive
lcdchar('-'); // Display a negative sign on LCD
}
TenthDigit = (numb/10); // Find out Tenth Digit
if( TenthDigit != 0) // If it is zero, then don't display
lcdchar(TenthDigit+0x30); // Make Char of TenthDigit and then display it on LCD
UnitDigit = numb - TenthDigit*10;
lcdchar(UnitDigit+0x30); // Make Char of UnitDigit and then display it on LCD
}
Once the code is uploaded, you can test the calculator by using it to
perform simple calculations such as addition, subtraction,
multiplication, and division. If everything is working correctly, the
calculator should display the results of the calculations on the LCD
display.
Watch the following video demonstration of how this ATmega32 microcontroller based calculator works.
In conclusion, building a calculator with an ATmega32 microcontroller is
a fun and educational project that can teach you about microcontroller
programming and electronic circuit design. The ATmega32 microcontroller
is a powerful and versatile chip that can be programmed to perform a
wide range of functions, making it a great choice for building a
calculator. With a little bit of time and effort, you can have a fully
functional calculator that you can use to perform simple calculations.
If you are interested in this microcontroller based calculator and want to learn more about LCD and keypad see the following tutorials: