Menu:

Arduino AVR assignment 3

Original by B.J.D. Vermulst for AVR Studio with WinAVR. April 2010.

This assignment, we begin with a simple task: with a push on a button we want the 18F4550 to light a LED for 5 seconds. Such a system can be used at home: push the button, take the stairs and hopefully the light will be on long enough to find your way. It is switched off automatically.

button1.c
The electrical scheme is simple: the only devices that are needed are a LED with a resistor (around 470 ohms), and a button with a resistor of 10k. Connect the LED to a resistor to pin D1. Connect the button to pin D0 and ground. This is the main function:
#define ON    1
#define OFF    0

void _delay_s(int t){
    int i;

    for(i=0;i         _delay_ms(1000);
}

void PORTBset(int pin, int state){
    if(state == ON)
        PORTB |= (1 << pin);

    if(state == OFF)
        PORTB &= ~(1 << pin);
}

int main(void)
{
    DDRB = (1 << PB1);
    PORTBset(PB0,1);

    while(1){
        PORTBset(PB1,0);
        
        if (!(PINB & (1 << PB0))){
            PORTBset(PB1,1);
            _delay_s(5);
        }
    }
    return(0);
}

First, the two pins that are used are set up as input and output (default is input, so only the output definition is needed). Then a ‘1’ is written to the input, which enables the internal pullup resistor. This puts a voltage on the pin which is removed when the button is pressed. The main part of the program is a while loop that is repeated forever. The loop starts with switching off the LED. Then, the button is checked. If it is not pressed (B0 is high), the lines of the IF statement are not executed. We are at the end of the loop, and the loop is started again. If the button is pressed (B1 is high), we switch on the LED (by raising output D1). Afterwards, a delay of 5 seconds is called. Then, the end of the IF statement is reached, and also the end of the loop. The loop is started again so the LED is switched off.

Connect the button, LED and resistors and program the AVR.

button2.c
Now, we are going to make a different program for the same hardware configuration. The new task is to switch the LED on with a push on the button, and to switch it off with the next push.

void main(void)
{
    char state = 0;

    DDRB = (1 << PB1);
    PORTBset(PB0,1);
    while(1){        
        if (!(PINB & (1 << PB0))){
            state = (state+1) % 2
            PORTBset(PB1,state);
        }
    }
    return(0);
}

The line state = (state+1) % 2; toggles the value of state: from high to low, or from low to high (why?). Edit the program and try if it works. The result is, as long as you press the button, the LED is lit with half power. If you release the button, the LED is randomly on or off. Try to figure out why the program works in this way.

button3.c
To get rid off the half power state, we should wait for the button to be released after we have changed the value of state. The simplest way to make this happen is to insert the following line of code:
while(!(PINB & (1 << PB0))) {...}

This is a while loop with a condition. The loop is repeated as long as the condition is fulfilled. The while loop is repeated, as long al B0 is high. The next lines are executed if B0 goes low.

Figure out where this line has to be inserted and try your edited program.

button4.c
The result is a little bit better now: the LED isn’t lit at half power anymore, if we hold the button. The LED is on or off, but a push on the button doesn’t toggle the state of the LED always. The reason for this has to do with the hardware: the button does not make a connection one time, but is switches on and off very fast a few times before stabilizing. The number of connections is different each time you press the button. This high speed switching on and off is over within 100mS. The program should be adapted: when the button is pressed, the next 100mS the program doesn’t check if the button is released. Then the high speed switching has passed and if the microcontroller sees the button is released, it is really released by the user. Edit the program by calling
_delay_ms(100);

at the right time. Try your renewed program.

button5.c
Now we know how we can read the state of a button, we can start thinking of the original assignment: a time switch. But we make the time switch programmable: an option to program the delay (before the light is switched off) is added. The time switch should be working as follows:
• After a short push on the button, the LED will be lit for a programmed number of seconds.

• If the button is not released, the LED is switched off after 5 seconds. Then the button has to be pressed as many times as the LED has to be lit in seconds. If the button is not pressed for 2 seconds, the LED flashes the programmed number of seconds to show that a new value has been programmed.

The different parts of the program are explained in the order they are called when the program is executed. In the total program, they have to be in reverse order. A variable or function always have to be declared before you can use it in other functions!
int main(void)
{
    DDRB = (1 << PD1); //Pin D1 output, rest input
    PORTBset(PB0,1); //Enable internal pullup for PD0
    while(1){
        PORTBset(PB1,0);
        if(!(PINB & (1 << PB0))){
            pressed();
        }
    }
}

In this while loop, that is repeated forever, we call the function pressed as soon as the button is pressed.

void pressed(void){
    int time = 0;

    PORTBset(PB1,1);
    while (!(PINB & (1 << PB0))){
        _delay_ms(100);
        time++;
        if (time == 50){
            PORTBset(PB1,0);
            program();
            flash();
        }
    }
    PORTBset(PB1,1);
    _delay_s(duration);
}

In this function we first switch on the LED and we declare an integer time, which is used to count the seconds that the button is pressed continuously. Next, we enter a while loop, which is repeated as long as the button is not released. In the loop, we wait 0.1 second and increase time with 1. If time is 50 (the button is pressed for 5 seconds), we call the functions program and flash. If the button is released earlier than 5 seconds, we wait the programmed time before returning to the main while loop (which is repeated forever).

void program(void){
    int pause = 0;
    duration = 0;
    while (pause < 20){
        if ((PINB & (1 << PB0))){
            _delay_ms(100);
            pause++;
        }else{
            pause = 0;
            _delay_ms(200);
            duration++;
            while(!(PINB & (1 << PB0))){;}
        }
    }    
}

In this function, first we reset duration. Afterwards, we enter a loop that not exited until pause is 20. Pause counts the time that the button is not pressed anymore. In the loop we check if the button is pressed. If not, we wait for 0.1 second, pause is increased, and we check the button again. If the button is pressed, pause is reset and we wait for 0.2 second (why?), we increase duration with 1 and wait until the button is released.

void flash(void){
    int i;
    for (i=0; i         PORTBset(PB1,0);
        _delay_ms(500);
        PORTBset(PB1,1);
        _delay_ms(500);
        PORTBset(PB1,0);
    }
}

The function flash makes the LED flash as many times as seconds that have been programmed.

Try the new program. Check the timer: program some different values and see if the LED is lit the programmed values.


button6.c
A completely different purpose of this hardware configuration is a code lock: the LED is only switched on if you press the button in the right way. We use a code with 8 positions, every position is a long or a short button press, which is denoted with a 0 and a 1, respectively.
First, we have to distinguish between a long and a short press. The following code only lights the LED if the duration is less than 200mS:
int main(void)
{
    DDRB = (1 << PD1); //Pin D1 output, rest input
    PORTBset(PB0,1); //Enable internal pullup for PD0

    while(1){
        PORTBset(PB1,0);
        if(!(PINB & (1 << PB0))){
            _delay_ms(200);
            if((PINB & (1 << PB0))){
                PORTBset(PB1,1);
                _delay_s(1);
            }else{
                while(PINB & (1 << PB0)) {}
            }
        }
    }
    return(0);
}

button7.c
A secure lock should not be opened fast by trying all possible combinations (‘brute force’). This can be accomplished by increasing the number of combinations, but then the code becomes very long and hard to remember. Another solution is to add a waiting time after entering the wrong code. If you enter the right code, there is no problem, but if you want to try all the possible codes you have a hard time.

We should also pay attention to the fact that the program has to know when you start entering the code, otherwise it is hardly possible to open the lock after pressing the button for a few times. Our solution will be the following one: if the button is not pressed for a few seconds, a reset occurs and you can try to enter the code again.

int main(void)
{
    char code;

    DDRB = (1 << PB1)|(1 << PB2); //Pin B1 and B2 output, rest input
    PORTBset(PB0,1); //Enable internal pullup for PD0

    while(1){
        PORTBset(PB1,0);
                
        if(!(PINB & (1 << PB0))){
            code = read_code();
            if(code == 0b00001111){
                PORTBset(PB1,1);
                _delay_s(5);
            }else{
                PORTBset(PB1,1);
                PORTBset(PB2,1);
                _delay_s(10);
                PORTBset(PB2,0);
                PORTBset(PB1,0);
            }
        }
    }
    return(0);
}

The main while loop of the program is pretty simple. First, we switch of the LED. If the button is pressed, the function read_code is called to read the code that will be entered. The entered code is compared with the secret code. If the entered code is the same as the secret code, the LED is switched on and a delay of 5 seconds is called before continuing. If the entered code is wrong, we wait for 10 seconds before a new code can be entered. In this case, the secret code is 00001111: 4 times short and 4 times long.

char read_code(void){
    int i,n;
    char x = 0;

    PORTBset(PB2,1); //In code read mode

    for(i=0;i<8;i++){
        n = 0;
        while((PINB & (1 << PB0))){
            _delay_ms(20);
            n++;
            if(n==255){
                x = 0;
                PORTBset(PB2,0); //Not in code read mode
                return x;
            }
        }
        _delay_ms(400);
        x = x << 1;
        if(!(PINB & (1 << PB0))){
            x++;
        }
        while(!(PINB & (1 << PB0))) {}
    }

    PORTBset(PB2,0); //Not in code read mode
    return x;
}

The function read_code consists of a FOR loop, which is repeated 8 times. In this loop, we wait until the button is pressed. If this takes too long, the function read_code returns the value 0. It is not smart to use 00000000 as secret code in this way!
The second part is reached when the button is pressed. We first wait to distinguish between a long and a short press. If the button is still pressed, we shift a 1 into x from the right. If not, we shift a 0 into x (just shifting, a 0 is automatically moved in). The third part is just a delay to make sure that the button is released, before continuing. Otherwise, the program might think the button is pressed for a next time.

Try the lock. Replace the code with your own code and try to switch on the LED.