Monday, May 4, 2015

AVR-C Programming - Basic Outputs

My next few posts will be about Atmel AVR programming. I've recently stopped using Arduino and decided to program AVR's strictly in C. In this post it's going to be about controlling the outputs on the AVR. This is very comparable to using digitialWrite(13, HIGH);. The mini project for this post is to make a string of LEDs light up in sequential order. What's really nice about using AVRs is that the code, for the most part, is portable to different types of AVR's.

Parts needed:

  • 8 LEDs
  • 8 200-400 ohm resistors
  • Breadboard
  • Wires
  • 5V voltage source
  • ATMega1284P (or almost any other AVR chip)
  • AVR Programmer (I'm using the Atmel ICE)


Take a look at the datasheet for the ATMega1284P so we can pick which port to use to hook the LEDs up to. On page 2 it shows the pinout for the AVR (I'm using a PDIP type). I'm going to use port B because they aren't separated and are on the same side of the chip which makes wiring a little bit easier.

Attach everything as shown in the breadboard image below:


Now all we have left to connect is a power source and our programming cable. Looking at the pinout for the 1284, we need to hook up pin 10 to 5V, pin 11 to Ground, pin 6 to the MOSI of the programming cable, pin 7 to the MISO of the programming cable, pin 8 to the SCK of the programming cable, and pin 9 to the RESET of the programming cable. One thing that originally confused me when coming from Arduino to just AVR's was that you need an external power supply to power the chip. At least with the Atmel ICE, there is no built in power source, unlike on the Arduino.
Both of these images are helpful for wiring up the programming cable.
 
When looking at the programming cable, the notch is on the left with the cable facing up (towards you)

Now that everything is connected we can finally open up Atmel Studio and start programming!

Create a new project from the start page when it first launches. Name it and save it wherever you want. The default location should be fine. Make sure that you're using a GCC C Executable File in the New Project wizard. Click Okay. Now select the chip you're using on the Device Selection window via the search bar or the list. I'm using the ATMega1284P so I selected that.

You will then have a blank program ready for your input!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*
 * Basic_Output.c
 *
 * Created: 5/1/2015 3:03:57 PM
 *  Author: www.jacobantoun.com
 */ 


#include <avr/io.h>

int main(void)
{ 
    while(1)
    {
    }
}

First thing we need to do is setup PORTB as an output register. We would need to set the Data Direction for Register B (DDRB) to all 1's in main(). There's a few ways to do that:


1
2
DDRB = 0xFF; //Sets data direction register B as outputs using hex
DDRB = 0b11111111; //Sets data direction register B as outputs using binary

I prefer to use the hex method because it's less typing. (0xFF = 0b11111111)

The while loop inside of main is where our code will continuously run, it's very similar to how void loop() works on an Arduino.

That is where we will turn on our LEDs and look at some shiny goodness! There are multiple ways to turn the LEDs on. You can turn the whole port on at a time, or each bit in the port individually. I'll show you the whole port on first, then individually:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
/*
 * Basic_Output.c
 *
 * Created: 5/1/2015 3:03:57 PM
 *  Author: www.jacobantoun.com
 */ 


#include <avr/io.h>

int main(void)
{
 DDRB = 0xFF;
 
    while(1)
    {
         PORTB = 0xFF; //Turns the whole port HIGH
    }
}

Inside of the main I set up the DDRB as an output register by setting each bit to 1. Inside of the while loop is where the whole port actually gets set high. By setting PORTB = 0xFF it sends a 1 to each bit in PORTB, which turns on every LED connected.

To turn on and off LEDs individually we would approach it like this:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * Basic_Output.c
 *
 * Created: 5/1/2015 3:03:57 PM
 *  Author: www.jacobantoun.com
 */ 


#include <avr/io.h>

int main(void)
{
 DDRB = 0xFF; // Set PORTB as an output register
 PORTB = 0; // Turn everything off
    while(1)
    {
         PORTB = (1 << PB1); // Turn on PortB, bit 1
         PORTB = (1 << PB2); // Turn on PortB, bit 2
         PORTB = (1 << PB7); // Turn on PortB, bit 7
    }
}


In the example above we are only turning on 3 bits from the port, bits 1, 2, and 7.

In the last example I'm going to show one more way of turning on individual bits, which is basically the same thing. In the example the goal is to have a single LED lit up moving up and down the port, it looks really cool.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * Basic_Output.c
 *
 * Created: 5/1/2015 3:03:57 PM
 *  Author: www.jacobantoun.com
 */ 


#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
 int delayTime = 60; // Variable used to change the delay time easier
 DDRB = 0xFF; // Set PORTB as an output register
     while(1) {
          for(int x = 0; x < 7; x++) { // x is set to 7 since we have a total of 8 bits (starting from 0)
               PORTB = (1 << x); //set the x bit high (same thing as typing in PB1, PB2, PB....)
               _delay_ms(delayTime); // Wait a certain amount of time before going through the loop again
          }
          for(int x = 7; x > 0; x--) {
               PORTB = (1 << x);
               _delay_ms(delayTime);
          }
    }
}


In the above example on line 17 and 22, the (1 << x) is basically saying to only set high the x bit. Meaning that if you typed PORTB = (1 << 3), the 3rd bit (PB3) would be the only bit set high on that port.




Feel free to leave any questions in the comments!