Monthly Archives: January 2014

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