Category Archives: ATmega16

Random Number Generator with ATmega16

Hello,

In this article I am going to explain how we built a random number generator with an LCD display on ATmega16 for our microprocessors course group project.

This is how the finished project looks like:

1. Properties

The random number generator can generate integers in a user defined interval of limited by the absoulte interval of [0, 999].

The user can choose a value in the interval which won’t be generated.

Control is done by a 4×4 matrix keyboard.

Result and user interface is displayed on an LCD screen.

A DC buzzer is used to show button press.

2. Theory

To generate the random number, instead of random number algorithm we used human insensitivity for our own good. We used a 8-bit timer counter working at 16 MHz, which will scan whole interval in 16 microseconds. As the controller is powered up timer will start counting and the user cannot possibly follow the track of the timer. When all the parameters are set, the MCU scales the 8-bit timer counter value in the interval of [0, 255] to the user defined interval. If the generated value is the same as the defined unwanted number it creates another number immidiately.

3. Software

     a. Main Function:

In the main function register definitions, LCD initialization, and interrupt enabling are done first. Then welcome screen is generated on the display.


// main funciton
int main(){

// definition of digits entered by the user
int dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,dgt6,dgt7,dgt8;

// port direction definitions
DDRB = 0b11110000;
DDRC = 0b11111111;

// portc initialization
PORTC = 0;

// lcd initialization
lcd_init();
lcd_clrscr();

// timer zero configuration without prescaling
TCCR0 |= (0 << CS02) | (0 << CS01) | (1 << CS00);

// lcd welcome screen
lcd_puts("CMPT PROJECT2 ");
lcd_puts("RANDOM GENERATOR");
lcd_puts("Burak Gunay");
lcd_puts("Kerem Serkan");

// welcome screen delay of 2 seconds
_delay_ms(2000);

// user interface screen
lcd_puts("EnterNumber1: ");
lcd_puts("EnterNumber2: ");
lcd_puts("Unwanted Number?");
lcd_puts("A:Yes/B:No : ");

In the while loop at the and of the function, input processing is done via the control of global button flag. This flag is used show which number is entered and which one is expected. The first inner while loop waits until a button is pressed. Then it calls input_check function and assings the return value to the first digit, dgt0.

After that the digit obtained is displayed and a buzz sound iz generated via beep function. At last the flag is incremented by one, therefore following while loop is started to be executed.


// input processing
while(button_flag != -1){
while(button_flag == 0){
dgt0 = input_check();
screen_display(dgt0);
beep();
button_flag++;
}
while(button_flag == 1){
dgt1 = input_check();
screen_display(dgt1);
beep();
button_flag++;
}
while(button_flag == 2){
dgt2 = input_check();
screen_display(dgt2);
beep();
button_flag++;
}
while(button_flag == 3){
dgt3 = input_check();
screen_display(dgt3);
beep();
button_flag++;
}
while(button_flag == 4){
dgt4 = input_check();
screen_display(dgt4);
beep();
button_flag++;
}
while(button_flag == 5){
dgt5 = input_check();
screen_display(dgt5);
beep();
button_flag++;
}
while(button_flag == 6){
button_flag = input_check();
beep();
}
while(button_flag == 10){
screen_display(0);
beep();
button_flag = 7;
}
while(button_flag == 11){
screen_display(0);
beep();
random_generator(dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,-1,-1,-1);
button_flag = -1;
}
while(button_flag == 7){
dgt6 = input_check();
screen_display(dgt6);
beep();
button_flag++;
}
while(button_flag == 8){
dgt7 = input_check();
screen_display(dgt7);
beep();
button_flag++;
}
while(button_flag == 9){
dgt8 = input_check();
screen_display(dgt8);
beep();
random_generator(dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,dgt6,dgt7,dgt8);
button_flag = -1;
}
}

When button_flag reaches 6, it takes the last digit of the second value of the interval and user makes a choice to have an unwanted number or not. If user chooses not to have it, flag is set to 10, and the number generation is started. Otherwise, program waits for three more digits and creates the number with the exception of the given unwanted number.

     b. Input Control

Numeric input is given by a 4×4 matrix keyboard. This is a resistive matrix which is controlled by eight digital I/O pins. The four column pins are output of the MCU and the remaining four row pins are input to the MCU. To control it we give 4-bit binary numbers which contain only one high bit to the columns and check the input at the row pins. If a digital low occurs on the row pins which means the button at the intersection of the row which had low and the column which had digital high is pressed. Therefore we continuously change the ‘active’ column and wait for a zero at the row pins.

4x4 Matrix Keyboard Schematics(1)

4×4 Matrix Keyboard Schematics [1]

And this is the C code for taking the inputs. There is an unclear point which will be discussed at the section Encountered Problems.


// input control function
int input_check(){
while(1){
// portb 7~4 are used as test variables
// input ports are set to 1 to enable pull-ups
PORTB = 0b01111111;
if(bit_is_clear(PINB,3)) return 10;
if(bit_is_clear(PINB,2)) return 4;
if(bit_is_clear(PINB,1)) return 7;
PORTB = 0b10111111;
if(bit_is_clear(PINB,3)) return 1;
if(bit_is_clear(PINB,2)) return 5;
if(bit_is_clear(PINB,1)) return 8;
if(bit_is_clear(PINB,0)) return 0;
PORTB = 0b11011111;
if(bit_is_clear(PINB,3)) return 2;
if(bit_is_clear(PINB,2)) return 6;
if(bit_is_clear(PINB,1)) return 9;
PORTB = 0b11101111;
if(bit_is_clear(PINB,3)) return 3;
if(bit_is_clear(PINB,2)) return 11;
}
}

     c. Display

In this project we used a 4×16 LCD screen and controlled it with lcd_h.h library. Since this part is quite easy to, I won’t get into detail and just will share the corresponding C code.


// lcd display function
void screen_display(int dgt){

if(button_flag == 0){
lcd_gotoxy(13,0); // setting lcd cursor
lcd_putc(dgt+48); // int + 48 = ascii code of that integer
}
if(button_flag == 1){
lcd_gotoxy(14,0);
lcd_putc(dgt+48);
}
if(button_flag == 2){
lcd_gotoxy(15,0);
lcd_putc(dgt+48);
}
if(button_flag == 3){
lcd_gotoxy(13,1);
lcd_putc(dgt+48);
}
if(button_flag == 4){
lcd_gotoxy(14,1);
lcd_putc(dgt+48);
}
if(button_flag == 5){
lcd_gotoxy(15,1);
lcd_putc(dgt+48);
}
if(button_flag == 10){
lcd_gotoxy(0,3);
lcd_puts("Yes|Number :");
}
if(button_flag == 11){
lcd_gotoxy(0,3);
lcd_puts("No ");
}
if(button_flag == 7){
lcd_gotoxy(13,3);
lcd_putc(dgt+48);
}
if(button_flag == 8){
lcd_gotoxy(14,3);
lcd_putc(dgt+48);
}
if(button_flag == 9){
lcd_gotoxy(15,3);
lcd_putc(dgt+48);
}
}

// lcd displat of result
void result_display(int LO_V, int HI_V, int UW_V, int FI_V){

char buffer[16]; // lcd line buffer

// lcd clear
lcd_clrscr();
lcd_gotoxy(0,0);

// result display
lcd_puts("A Random Number");
lcd_gotoxy(0,1);
sprintf(buffer,"Between %d-%d",LO_V,HI_V);
lcd_puts(buffer);

// check for unwanted number
if(UW_V != -111){
lcd_gotoxy(0,2);
sprintf(buffer,"Excluding %d",UW_V);
lcd_puts(buffer);
}

lcd_gotoxy(0,3);
sprintf(buffer,"Result %d",FI_V);
lcd_puts(buffer);
}

     d. Random Generation

As I explained in the Theory section, we used an 8-bit timer counter, got its current value, and scaled it to the given interval. This part only consists obtaining timer value and scaling it, which is a simple proporiton operation. Also we added some control statement to check if we generated the unwanted number or not. If so, the control statement is executed and generates a new random number immediately.


// random number generation function
void random_generator(int low1,int low2,int low3,int high1,int high2,int high3,int uw1,int uw2,int uw3){

// converting seperate digits into actual numbers
int low_limit;
low_limit = low1*100 + low2*10 + low3;

int high_limit;
high_limit = high1*100 + high2*10 + high3;

int unwanted_value;
unwanted_value = uw1*100 + uw2*10 + uw3;

// reading current timer0 value to create random number
float timer_value;
timer_value = TCNT0;

int temp_result;
int random_result;

// mapping the timer value to given interval
temp_result = (float)((timer_value/255)*(high_limit - low_limit)) + low_limit;

// cropping the fraction part of float
random_result = temp_result;

// regeneration of the number in the case of resulting unwanted number
while(random_result == unwanted_value){
timer_value = TCNT0;
temp_result = (float)((timer_value/255)*(high_limit - low_limit)) + low_limit;
}

random_result = temp_result;

// calling the final screen animation
final_animation();

// sending the result to the display function
result_display(low_limit,high_limit,unwanted_value,random_result);
}

     e. Beep

Since we used a DC buzzer, this is the simplest part of the code. We just change the input voltage from 0 to 5V with a desired delay value. The most important concern in this part is the delay time, because during the beep MCU cannot do another execution, and if you make it too long your next button press during the beep will not be read.


// beep function to indicate button press
void beep(){
PORTC = 128;
_delay_ms(500);
PORTC = 0;
}

     f. Final Animation – Loading Screen

We did this small final animation just because we can. During this process no other execution is done, so it has no essence for the project.


void final_animation(){

int i;

// lcd line buffer
char buffer[16];

// percentage counter
int counter=0;

// loading screen
lcd_clrscr();
lcd_puts("LOADING PLEASE WAIT ");
_delay_ms(500);
lcd_gotoxy(0,2);
lcd_putc('[');
lcd_gotoxy(9,2);
lcd_putc(']');

// definition of a new char
lcd_command ( 0b01000000 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;

// animation
for(i = 0; i<=100; i++){
_delay_ms(30);
lcd_gotoxy(12,2);
sprintf(buffer,"%3.d%%",i);
lcd_puts(buffer);
if(i == 10){
lcd_gotoxy(0,2);
lcd_putc(0);
}
if(i==20){
lcd_gotoxy(1,2);
lcd_putc(0);
}
if(i==30){
lcd_gotoxy(2,2);
lcd_putc(0);
}
if(i==40){
lcd_gotoxy(3,2);
lcd_putc(0);
}
if(i==50){
lcd_gotoxy(4,2);
lcd_putc(0);
}
if(i==60){
lcd_gotoxy(5,2);
lcd_putc(0);
}
if(i==70){
lcd_gotoxy(6,2);
lcd_putc(0);
}
if(i==80){
lcd_gotoxy(7,2);
lcd_putc(0);
}
if(i==90){
lcd_gotoxy(8,2);
lcd_putc(0);
}
if(i==100){
lcd_gotoxy(9,2);
lcd_putc(0);
}
}
}

 g. The Complete Code


/**
* VUT Brno FEKT - CMPT Project 2
* Random Number Generator
*
* Authors: Burak Dalogullari, Gunay Turan, Kerem Sennik, Serkan Ozatik
*
* Description: Random Number Generator which has an LCD user interface
* takes an interval between 0 ~ 999 from user with possibility of an
* unwanted number in this specified interval, and returns a random number
* on that interwal, excluding the unwanted number if there is any.
*
*/

#include // include definition file for Atmel MCU
#include // standard input and output library (needed for sprintf)
#include "lcd_h.h" // include library for LCD display
#define F_CPU 16000000UL // specify clock frequency (needed for delay library)
#include // include delay library

int button_flag = 0; // flag used for specifying the input

// main funciton
int main(){

// definition of digits entered by the user
int dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,dgt6,dgt7,dgt8;

// port direction definitions
DDRB = 0b11110000;
DDRC = 0b11111111;

// portc initialization
PORTC = 0;

// lcd initialization
lcd_init();
lcd_clrscr();

// timer zero configuration without prescaling
TCCR0 |= (0 << CS02) | (0 << CS01) | (1 << CS00);

// lcd welcome screen
lcd_puts("CMPT PROJECT2 ");
lcd_puts("RANDOM GENERATOR");
lcd_puts("Burak Gunay");
lcd_puts("Kerem Serkan");

// welcome screen delay of 2 seconds
_delay_ms(2000);

// user interface screen
lcd_puts("EnterNumber1: ");
lcd_puts("EnterNumber2: ");
lcd_puts("Unwanted Number?");
lcd_puts("A:Yes/B:No : ");

// input processing
while(button_flag != -1){
while(button_flag == 0){
dgt0 = input_check();
screen_display(dgt0);
beep();
button_flag++;
}
while(button_flag == 1){
dgt1 = input_check();
screen_display(dgt1);
beep();
button_flag++;
}
while(button_flag == 2){
dgt2 = input_check();
screen_display(dgt2);
beep();
button_flag++;
}
while(button_flag == 3){
dgt3 = input_check();
screen_display(dgt3);
beep();
button_flag++;
}
while(button_flag == 4){
dgt4 = input_check();
screen_display(dgt4);
beep();
button_flag++;
}
while(button_flag == 5){
dgt5 = input_check();
screen_display(dgt5);
beep();
button_flag++;
}
while(button_flag == 6){
button_flag = input_check();
beep();
}
while(button_flag == 10){
screen_display(0);
beep();
button_flag = 7;
}
while(button_flag == 11){
screen_display(0);
beep();
random_generator(dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,-1,-1,-1);
button_flag = -1;
}
while(button_flag == 7){
dgt6 = input_check();
screen_display(dgt6);
beep();
button_flag++;
}
while(button_flag == 8){
dgt7 = input_check();
screen_display(dgt7);
beep();
button_flag++;
}
while(button_flag == 9){
dgt8 = input_check();
screen_display(dgt8);
beep();
random_generator(dgt0,dgt1,dgt2,dgt3,dgt4,dgt5,dgt6,dgt7,dgt8);
button_flag = -1;
}
}
}

// input control function
int input_check(){
while(1){
// portb 7~4 are used as test variables
// input ports are set to 1 to enable pull-ups
PORTB = 0b01111111;
if(bit_is_clear(PINB,3)) return 10;
if(bit_is_clear(PINB,2)) return 4;
if(bit_is_clear(PINB,1)) return 7;
PORTB = 0b10111111;
if(bit_is_clear(PINB,3)) return 1;
if(bit_is_clear(PINB,2)) return 5;
if(bit_is_clear(PINB,1)) return 8;
if(bit_is_clear(PINB,0)) return 0;
PORTB = 0b11011111;
if(bit_is_clear(PINB,3)) return 2;
if(bit_is_clear(PINB,2)) return 6;
if(bit_is_clear(PINB,1)) return 9;
PORTB = 0b11101111;
if(bit_is_clear(PINB,3)) return 3;
if(bit_is_clear(PINB,2)) return 11;
}
}

// lcd display function
void screen_display(int dgt){

if(button_flag == 0){
lcd_gotoxy(13,0); // setting lcd cursor
lcd_putc(dgt+48); // int + 48 = ascii code of that integer
}
if(button_flag == 1){
lcd_gotoxy(14,0);
lcd_putc(dgt+48);
}
if(button_flag == 2){
lcd_gotoxy(15,0);
lcd_putc(dgt+48);
}
if(button_flag == 3){
lcd_gotoxy(13,1);
lcd_putc(dgt+48);
}
if(button_flag == 4){
lcd_gotoxy(14,1);
lcd_putc(dgt+48);
}
if(button_flag == 5){
lcd_gotoxy(15,1);
lcd_putc(dgt+48);
}
if(button_flag == 10){
lcd_gotoxy(0,3);
lcd_puts("Yes|Number :");
}
if(button_flag == 11){
lcd_gotoxy(0,3);
lcd_puts("No ");
}
if(button_flag == 7){
lcd_gotoxy(13,3);
lcd_putc(dgt+48);
}
if(button_flag == 8){
lcd_gotoxy(14,3);
lcd_putc(dgt+48);
}
if(button_flag == 9){
lcd_gotoxy(15,3);
lcd_putc(dgt+48);
}
}

// lcd displat of result
void result_display(int LO_V, int HI_V, int UW_V, int FI_V){

char buffer[16]; // lcd line buffer

// lcd clear
lcd_clrscr();
lcd_gotoxy(0,0);

// result display
lcd_puts("A Random Number");
lcd_gotoxy(0,1);
sprintf(buffer,"Between %d-%d",LO_V,HI_V);
lcd_puts(buffer);

// check for unwanted number
if(UW_V != -111){
lcd_gotoxy(0,2);
sprintf(buffer,"Excluding %d",UW_V);
lcd_puts(buffer);
}

lcd_gotoxy(0,3);
sprintf(buffer,"Result %d",FI_V);
lcd_puts(buffer);
}

// random number generation function
void random_generator(int low1,int low2,int low3,int high1,int high2,int high3,int uw1,int uw2,int uw3){

// converting seperate digits into actual numbers
int low_limit;
low_limit = low1*100 + low2*10 + low3;

int high_limit;
high_limit = high1*100 + high2*10 + high3;

int unwanted_value;
unwanted_value = uw1*100 + uw2*10 + uw3;

// reading current timer0 value to create random number
float timer_value;
timer_value = TCNT0;

int temp_result;
int random_result;

// mapping the timer value to given interval
temp_result = (float)((timer_value/255)*(high_limit - low_limit)) + low_limit;

// cropping the fraction part of float
random_result = temp_result;

// regeneration of the number in the case of resulting unwanted number
while(random_result == unwanted_value){
timer_value = TCNT0;
temp_result = (float)((timer_value/255)*(high_limit - low_limit)) + low_limit;
}

random_result = temp_result;

// calling the final screen animation
final_animation();

// sending the result to the display function
result_display(low_limit,high_limit,unwanted_value,random_result);
}

// beep function to indicate button press
void beep(){
PORTC = 128;
_delay_ms(500);
PORTC = 0;
}

void final_animation(){

int i;

// lcd line buffer
char buffer[16];

// percentage counter
int counter=0;

// loading screen
lcd_clrscr();
lcd_puts("LOADING PLEASE WAIT ");
_delay_ms(500);
lcd_gotoxy(0,2);
lcd_putc('[');
lcd_gotoxy(9,2);
lcd_putc(']');

// definition of a new char
lcd_command ( 0b01000000 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;
lcd_data ( 0b11111 ) ;

// animation
for(i = 0; i<=100; i++){
_delay_ms(30);
lcd_gotoxy(12,2);
sprintf(buffer,"%3.d%%",i);
lcd_puts(buffer);
if(i == 10){
lcd_gotoxy(0,2);
lcd_putc(0);
}
if(i==20){
lcd_gotoxy(1,2);
lcd_putc(0);
}
if(i==30){
lcd_gotoxy(2,2);
lcd_putc(0);
}
if(i==40){
lcd_gotoxy(3,2);
lcd_putc(0);
}
if(i==50){
lcd_gotoxy(4,2);
lcd_putc(0);
}
if(i==60){
lcd_gotoxy(5,2);
lcd_putc(0);
}
if(i==70){
lcd_gotoxy(6,2);
lcd_putc(0);
}
if(i==80){
lcd_gotoxy(7,2);
lcd_putc(0);
}
if(i==90){
lcd_gotoxy(8,2);
lcd_putc(0);
}
if(i==100){
lcd_gotoxy(9,2);
lcd_putc(0);
}
}
}

4. Encountered Problems

We had problems only at the input reading. First we kept taking garbage input. This is resulted from the lack of pull-up resistors in the development board we are using. This problem is solved by activating the MCU internal pull-ups, which can be setting the input bits in the PORTx register.

The second problem with the matrix keyboard is hardware related. Due to a misconnection on the first row, whenever we pressed 1, we got 2, whenever we pressed 2 we got 3, and so on. That’s why we changed the input check function in the C code to get the desired input.

5. The Team: Burak Daloğulları, Kerem Şennik, Serkan Özatik

We accomplished this project with the same group as we did the Kitchen Alarm Project. I appreciate their hard work during the project.

6. Exercise

Try to add a button which generates a new number with the given parameters.

If you have any problems, questions, comments, and/or objections please feel free to contact me.

Günay Turan

Source(s):

[1] The schematics is taken from: http://shree-electronics.com/interfacingkeyboard.html

 

Kitchen Alarm with Assembly

Hi,
On this entry, I will present a simple group project we did during my microprocessors course with Atmel ATmega 16. This is my first assembly project, even though the idea is simple (This project can be developed in one or two hours with C) application on assembly is a bit complicated due to the fact that the language is really primitive compared to C.
So why we still use assembly? First, it gives a better understanding of the mechanisms working in the processor because you are closer to the MCU somehow, and second, it has much smaller code size compared to C. So if your flash is limited, and code is complicated it makes easier to fit the code in the flash memory in the expense of harder implementation of the idea.
Project Description:
Kitchen alarm basicly is a countdown timer with which you set a time, start the coundown and get an alarm sound when the time is up. In this application we have a limit of 9:59 minutes because we only used three 7-segment displays. And as an additional feature we have an time addition button which adds one extra minute anytime the button is pressed without interrupting the countdown.
How to Use:
There are three buttons to control the Kitchen Alarm.
Increment Button: Each time pressed, this button increases the number displayed on the active 7-segment display before the timer started to work. When the countdown is started the button gets deactivated. If the number displayed reaches to nine and the button is pressed again, display rolls back to zero again.
Set Button: This button is used to save the digit calibrated by the increment button and activates the more significant digit. When the program is run, the least significant (rightmost) digit is active initially. After choosing the value via increment button, pressing this button saves that value and activates the next one. Doing it second time sets the second digit and activates the minute segment and the third press of the button sets the value and starts the timer. After the third press countdown starts immidately, and there is no turning back after this point. User should reset the program to make a change in the top value.
Add Minute Button: As explained above, this button is always active during and the before coundown and increases the minute by one. If the value is nine at the minute segment it changes nothing.
Algorithm:
The flowchart of the program, excluding the display function, is the following:
Flowchart of the Program
The display function is excluded because it is done by using brute force using lots of comparisons of the same type, which will be more clear when the code is read.
Assembly Code:
;VUT v Brne FEKT
;CMPT Course 1st Project: Kitchen Alarm
;
;Authors:
;	Burak Dalogullari
;	Gunay Turan
;	Kerem Sennik
;	Serkan Ozatik
;	Alican Bor
;
;How to use:
;	Onboard Switches:	|o|		|o|		|o|
;				SW2		SW1		SW0
;				Time addition	Set	   Increment
;
;	7-Segment Displays:	A   	: 	 B		C
;				minute		   seconds
;
;	When the program is excecuted, the user will see 0:00 on
;the display. At that time the active display is display-"C".
;user can use increment button(SW0) to determine that value.
;pressing the set button(SW1) will set C value and activate
;the display-B. With the same procedure user can set A value
;and setting this will automatically start the countdown.
;During the countdown user can use SW2 to get an additional
;minute. When display shows 0:00 again, buzzer will give an
;alarm signal until a reset or powerdown is applied.
;

.include "m16def_mod.inc"		;include file

;register definitions
.def    delay_idx = r16
.def    temp = r17
.def    idxA = r18
.def    idxB = r19
.def    idxC = r20
.def    zero = r21
.def    one = r22
.def    two = r23
.def    three = r24
.def    four = r25
.def    five = r26
.def    six = r27
.def    seven = r28
.def    eight = r29
.def    nine = r30
.def    set_idx = r31

; interrupt vectors
.cseg
.org	    0x0000
	rjmp	reset
.org	    0x0002
    rjmp    increment_sw_debouncer
.org        0x0004
    rjmp    set_sw_debouncer
.org        0x0012
    rjmp    time
.org        0x0024
    rjmp    addminute_sw_debouncer

;main program
.org	    0x002a
reset:
	;Stack definition
	ldi		temp, LOW(RAMEND)
	out     	SPL, temp
	ldi		temp, HIGH(RAMEND)
	out		SPH, temp

	;Digital port directions
	ldi		temp, 255
	out		DDRA, temp
	ldi     	temp, 251
	out		DDRB, temp
	ldi  	   	temp, 255
	out     	DDRC, temp
	ldi     	temp, 243
	out		DDRD, temp

	;external input settings
	;interrupts 0,1, and 2 at falling edge
	ldi		temp, (1<<INT0) | (1<<INT1) | (1<<INT2)
	out		GICR, temp
	ldi		temp, (1<<ISC01) | (0<<ISC00) | (1<<ISC11) | (0<<ISC10)
	out		MCUCR, temp

	;Digital IO configurations for 7-segment display for each digit
	ldi     zero, 0x7B
	ldi     one, 0x0A
	ldi     two, 0xB3
	ldi     three, 0x9B
	ldi     four, 0xCA
	ldi     five, 0xD9
	ldi     six, 0xF9
	ldi     seven, 0x0B
	ldi     eight, 0xFB
	ldi     nine, 0xDB

	;index initializations
	ldi     set_idx, 0
	ldi     idxA, 0
	ldi     idxB, 0
	ldi     idxC, 0
	ldi     delay_idx, 3

	;output initialization
	out     PORTA, zero
	out     PORTB, zero
	out     PORTC, zero

	sei					;enable all interrupts
;infinite loop
loop:
    	sei	            		;reenable interrupts
	rjmp    displayA			;display function call
	rjmp	loop

;software debouncer function for increment button
increment_sw_debouncer:
    ldi     temp,255
delay_loop_1:
    dec     temp
    brne    delay_loop_1
    rjmp    increment

;increment button interrupt service routine
increment:
    ldi     temp, 0
    CPSE    set_idx, temp
    rjmp    check1
    rjmp    setC

check1:
    ldi     temp, 1
    CPSE    set_idx, temp
    rjmp    setA
    rjmp    setB

setC:
    inc     idxC
    ldi     temp, 10
    CPSE    idxC, temp
    rjmp    loop
    ldi     idxC, 0
    rjmp    loop

setB:
    inc     idxB
    ldi     temp, 6
    CPSE    idxB, temp
    rjmp    loop
    ldi     idxB, 0
    rjmp    loop

setA:
    inc     idxA
    ldi     temp, 10
    CPSE    idxA, temp
    rjmp    loop
    ldi     idxA, 0
    rjmp    loop

;timer enable after the time is set
timer_enable:

	;normal mode with prescaler 1024 - TCLK ~= 1kHz
	ldi 	temp, (1<<CS02) | (0<<CS01) | (1<<CS00)
	out 	TCCR0, temp
	ldi 	temp, (1<<TOIE0)
	out 	TIMSK, temp
	rjmp    loop

;Software debouncer for set button
set_sw_debouncer:
    ldi     temp,255
delay_loop_2:
    dec     temp
    brne    delay_loop_2
    rjmp    set

;Set button interrupt service routine
set:
    inc     set_idx
    ldi     temp, 3
    CPSE    set_idx, temp
    reti
    rjmp    timer_enable
    reti

;SW debouncer for time addition button
addminute_sw_debouncer:
    ldi     temp,255
delay_loop_3:
    dec     temp
    brne    delay_loop_3
    rjmp    add_minute

;Time addition button ISR
add_minute:
    inc     idxA
    ldi     temp, 10
    CPSE    idxA, temp
    reti
    ldi     idxA, 9
    reti

;Timer overflow ISR
time:
    dec     delay_idx
    ldi     temp, 0
    CPSE    delay_idx, temp
    reti
    ldi     delay_idx, 3
    rjmp    decC
reti

decC:
    dec     idxC
    ldi     temp, 255
    CPSE    idxC, temp
    rjmp    loop
    rjmp    decB

decB:
    ldi     idxC, 9
    dec     idxB
    ldi     temp, 255
    CPSE    idxB, temp
    rjmp    loop
    rjmp    decA

decA:
    ldi     idxB, 5
    dec     idxA
    ldi     temp, 255
    CPSE    idxA, temp
    rjmp    loop
    rjmp    alarm

;Display functions for each digit and each 7-segment display
displayA:
    ldi     temp, 0
    CPSE    temp, idxA
    rjmp    displayA1
    out     PORTA, zero
    rjmp    displayB

displayA1:
    ldi     temp, 1
    CPSE    temp, idxA
    rjmp    displayA2
    out     PORTA, one
    rjmp    displayB

displayA2:
    ldi     temp, 2
    CPSE    temp, idxA
    rjmp    displayA3
    out     PORTA, two
    rjmp    displayB

displayA3:
    ldi     temp, 3
    CPSE    temp, idxA
    rjmp    displayA4
    out     PORTA, three
    rjmp    displayB

displayA4:
    ldi     temp, 4
    CPSE    temp, idxA
    rjmp    displayA5
    out     PORTA, four
    rjmp    displayB

displayA5:
    ldi     temp, 5
    CPSE    temp, idxA
    rjmp    displayA6
    out     PORTA, five
    rjmp    displayB

displayA6:
    ldi     temp, 6
    CPSE    temp, idxA
    rjmp    displayA7
    out     PORTA, six
    rjmp    displayB

displayA7:
    ldi     temp, 7
    CPSE    temp, idxA
    rjmp    displayA8
    out     PORTA, seven
    rjmp    displayB

displayA8:
    ldi     temp, 8
    CPSE    temp, idxA
    rjmp    displayA9
    out     PORTA, eight
    rjmp    displayB

displayA9:
    out     PORTA, nine
    rjmp    displayB

displayB:
    ldi     temp, 0
    CPSE    temp, idxB
    rjmp    displayB1
    out     PORTB, zero
    rjmp    displayC

displayB1:
    ldi     temp, 1
    CPSE    temp, idxB
    rjmp    displayB2
    out     PORTB, one
    rjmp    displayC

displayB2:
    ldi     temp, 2
    CPSE    temp, idxB
    rjmp    displayB3
    out     PORTB, two
    rjmp    displayC

displayB3:
    ldi     temp, 3
    CPSE    temp, idxB
    rjmp    displayB4
    out     PORTB, three
    rjmp    displayC

displayB4:
    ldi     temp, 4
    CPSE    temp, idxB
    rjmp    displayB5
    out     PORTB, four
    rjmp    displayC

displayB5:
    out     PORTB, five
    rjmp    displayC

displayC:
    ldi     temp, 0
    CPSE    temp, idxC
    rjmp    displayC1
    out     PORTC, zero
    rjmp    loop

displayC1:
    ldi     temp, 1
    CPSE    temp, idxC
    rjmp    displayC2
    out     PORTC, one
    rjmp    loop

displayC2:
    ldi     temp, 2
    CPSE    temp, idxC
    rjmp    displayC3
    out     PORTC, two
    rjmp    loop

displayC3:
    ldi     temp, 3
    CPSE    temp, idxC
    rjmp    displayC4
    out     PORTC, three
    rjmp    loop

displayC4:
    ldi     temp, 4
    CPSE    temp, idxC
    rjmp    displayC5
    out     PORTC, four
    rjmp    loop

displayC5:
    ldi     temp, 5
    CPSE    temp, idxC
    rjmp    displayC6
    out     PORTC, five
    rjmp    loop

displayC6:
    ldi     temp, 6
    CPSE    temp, idxC
    rjmp    displayC7
    out     PORTC, six
    rjmp    loop

displayC7:
    ldi     temp, 7
    CPSE    temp, idxC
    rjmp    displayC8
    out     PORTC, seven
    rjmp    loop

displayC8:
    ldi     temp, 8
    CPSE    temp, idxC
    rjmp    displayC9
    out     PORTC, eight
    rjmp    loop

displayC9:
    out     PORTC, nine
    rjmp    loop

;Alarm function with infinite loop
alarm:
    ldi     temp, 127
    out     PORTA, temp
    rjmp    finish

finish:
    rjmp    alarm

Simulation:

To simulate the project before implementing it on hardware we used Proteus ISIS with which you can program MCU’s with provided hex files and observe the results. You can reach the simulation file from this link.

Schematics

Hardware:

The hardware part is simple. We used three seven-segments, each segment directly connected to a digital I/O port (no encoding/decoding). For interrupts we used the onboard switches on the development kit, and lastly for the alarm sound we used a DC buzzer of 5V, which is controlled by one digital I/O port.

Team:

Kerem Şennik, Burak Daloğulları, Me, Serkan Özatik

Please feel free to contact anything about the project. This project is open source that you can use freely but the authors have no responsibility for its misusage.

Günay Turan