AVR Programming 03: Reading And Compiling Code

In the last installment of our tutorial series we built a simple circuit on a breadboard and programmed an ATmega168 to make it run. That proves that you know how to follow directions, but the eureka moments of doing everything yourself are on the way. This time around you will get down and dirty with the datasheet, learning where each line of the sample code came from, and give your recently installed compiler a test drive. We will:

  • Talk about bitwise operators and how they work when coding for microcontrollers
  • Discuss C code shorthand
  • Review the sample code from Part 2 and talk about what each line of code does
  • Learn to compile code

If this is the first you’ve heard about our AVR Programming series, head back to Part 1 and start from the beginning. Otherwise, take a deep breath and we’ll being after the break.

Series roadmap:

Prerequisites

  • You must know something about C code. The ability to read it is probably good enough, Google can help you with the rest as you learn.
  • It helps if you have a text editor that includes syntax highlighting. I’m purely a Linux user and I like to use both Kate and Gedit depending on my mood. But I also use nano from the shell quite frequently. This is a tool and your choice is purely personal preference.
  • Grab the sample code from part 2 of the series. I’ve embedded it below but you may want it in a separate windows for reference.
  • Datasheets; the instruction manual for hardware. Grab the datasheet for the ATmega168 as I’ll be referencing specific pages as examples. Knowing how to look up information in the datasheet and turn it into code will make it easy for you to use any chip in the AVR family.

Bitwise Operators

Even though we’ll be writing code in the C language, we’re quite close to the hardware when programming microcontrollers. Because of this you must understand bitwise operators. Not just kind of, not intuitively, you should know them well enough to teach them to someone else without looking it up.

Hands down the best explanation I’ve ever come across is by [Eric Weddington], who also co-authored the makefile that came with my example code. It is also known as Programming 101. Read it, know it, love it. But I’ll try to give a quick crash course for those to lazy to read his whole lesson.

Code Symbol Logic Function
| OR
& AND
~ NOT
^ XOR
<< Shift Left
>> Shift Right

The list above shows all of the code symbols and their logic operation.

  • OR – true if either or both bits being compared are 1
  • AND – true only if both bits being compared are 1
  • NOT – results in the opposite of a value (~1 = 0, ~0 = 1)
  • XOR – exclusive OR… true if one bit being compared is 1 but false if neither or both of them are
  • Shift Left – moves bits left within a binary number. (1<<0 = 0b0001, 1<<4 = 0b1000)
  • Shift Right – moves bits to the right a desired amount (0b1000>>2 = 0b0010)

We’re going to use Shift Left all the time in our code because it’s a quick way to build a binary number. We’re always working in binary numbers made up of eight bits. Those bits are numbered 0-7 because counting always starts with 0 when it comes to microcontrollers. So if you want to set the fifth bit to a logic high (‘1’) you would shift ‘1’ left by 5:

1<<5

This will result in the binary number 0b00100000. If this is child’s play, move to the next section. If not, read [Eric’s] tutorial.

C Code Shorthand

I tend to use shorthand in my code as my hands often hurt from too much typing (as they do now). This saves a bit on the old ibuprofen expenditure for the month by allowing me to type less characters to accomplish the same simple assignments. Here’s a quick table of examples:

Traditional Code Shorthand Equivalent
value = value + 1; value += 1;
value = value >> 1; value >>= 1;
value = value & bitMask; value &= bitMask;
value = value | bitMask; value |= bitMask;
PORTD = PORTD ^ (1<<0); PORTD ^= (1<<0);

So basically, if I am setting a variable by using that same variable as the first operand I can just place the operator before the equals sign and put the second operand after the equals sign to accomplish the same task without typing the variable name twice. If you understood that sentence you’re doing quite well!

Jump into the sample code

Psuedocode

A good practice when developing code is to write psuedocode. Something that clearly states what you want to do in plain language. This is an outline of the structure that your program will take and it shouldn’t include any specific code, but will be replaced by that code later:

//Setup the clock
  //prepare an interrupt every 1 second

//Setup the I/O for the LED

//toggle the LED during each interrupt

This program is so simple that the psuedocode seems unnecessary, but it will keep you focused and help stave off errors on larger projects.

The Actual Code

The main.c from the Part 2 sample code is embedded below. Take a minute to match up the parts of the psuedocode above with actual code blocks below.

/*
* Hackaday.com AVR Tutorial firmware
* written by: Mike Szczys (@szczys)
* 10/24/2010
*
* ATmega168
* Blinks one LED conneced to PD0
*
* http://hackaday.com/2010/10/25/avr-programming-02-the-hardware/
*/

#include <avr/io.h>
#include <avr/interrupt.h>

int main(void)
{

  //Setup the clock
  cli();			//Disable global interrupts
  TCCR1B |= 1<<CS11 | 1<<CS10;	//Divide by 64
  OCR1A = 15624;		//Count 15624 cycles for 1 second interrupt
  TCCR1B |= 1<<WGM12;		//Put Timer/Counter1 in CTC mode
  TIMSK1 |= 1<<OCIE1A;		//enable timer compare interrupt
  sei();			//Enable global interrupts

  //Setup the I/O for the LED

  DDRD |= (1<<0);		//Set PortD Pin0 as an output
  PORTD |= (1<<0);		//Set PortD Pin0 high to turn on LED

  while(1) { }			//Loop forever, interrupts do the rest
}

ISR(TIMER1_COMPA_vect)		//Interrupt Service Routine
{
  PORTD ^= (1<<0);		//Use xor to toggle the LED
}

The first few lines are comments for the benefit of human eyes and will not be used by the microcontroller. Comments in C are prefaced by two slashes (//) for single line comments or encased in slash-star (/*) and star-slash (*/) pairs for multiline comments. It’s a good idea to write comments that detail the program, what it does, what hardware it runs on, and any other helpful information. I find that I often reuse code from past projects and a bit of information at the top of the file helps locate what I’m looking for quickly.

The Includes

The next thing you see are the includes:

#include <avr/io.h>
#include <avr/interrupt.h>

Includes tell the compiler that we’re going to be using things from other files. In this case, two files from AVR Libc that came with the cross-compiling toolchain we installed in Part 1. These are C files that allow us to use human-readable (and rememberable!) code when working with the hardware on the chip. The io.h file rolls header files for all of the supported AVR chips into one. We define what processor we’re using in our makefile, and the appropriate header file is automatically chosen from io.h when we compile our code later in this tutorial.

In our example code I’ve used names like DDRD, PORTD, TCCR1B, OCR1A, TIMSK1, etc. All of these have addresses that are pointed to using the io.h file. This allows us to call pins on the chip by the names like PORTD which are the same across all AVR variants instead of register addresses like 0x0B which has different functions on different chips. Most likely you’ll need to include io.h in every AVR program you use, and doing so makes your code more portable. The interrupt.h file is only needed if you are using interrupts, something we’ll talk about as we look at the next code block

Setting up the clock for use with interrupts

Processors need a clock signal in order to work. AVR chips can use external clocks like a crystal oscillator or a ceramic resonator, but they come from the factory configured to use the internal RC oscillator as the system clock (read more on page 28 of the datasheet). The internal RC oscillator of the ATmega168 runs at approximately 8.0 MHz depending on voltage stability and temperature. It also ships with the DIV8 fuse enabled which divides the clock signal down to 1.0 MHz. For the sample program I wanted an LED to blink between on and off, changing about once a second. Here’s the code block that sets that functionality up:

  //Setup the clock
  cli();			//Disable global interrupts
  TCCR1B |= 1<<CS11 | 1<<CS10;	//Divide by 64
  OCR1A = 15624;		//Count 15624 cycles for 1 second interrupt
  TCCR1B |= 1<<WGM12;		//Put Timer/Counter1 in CTC mode
  TIMSK1 |= 1<<OCIE1A;		//enable timer compare interrupt
  sei();			//Enable global interrupts

The very first line has something to do with interrupts. An interrupt is a great feature of microprocessors. Basically you tell the chips to watch for a certain condition. When it matches that condition it will stop what it is doing no matter where it is, and run a different set of code called an Interrupt Service Routine (ISR). Because we are about to change some settings having to do with interrupts, we don’t want anything (like an interrupt) to stop us in the middle of this process. I’ve used a command that is available to us because we included interrupt.h at the beginning of our file. The command is cli(); which disables all interrupts. Once we are done with our settings we must remember to enable them again, using the sei(); command. You can see I’ve done that at the bottom of this code block

Now we want to watch for the passage of 1 second worth of time. The four lines in between these two commands are used to setup a counter to do just that. Because the internal oscillator is running at 1 MHz, or 1 Million cycles per second, we must trigger an interrupt every 1 million cycles. The biggest timer this chip has is 16-bits which can only count from 0 to 65,535. In other words, we don’t have a timer that can count high enough to measure such a large number of cycles.

Fortunately, we have the option to use a divider with our timer, called a prescaler. To do so we look in the datasheet on page 134 to see a chart outlining the clock select. It shows prescaler options which divide the system clock by 1, 8, 64, 256, and 1024. Knowing that we want to count 1,000,000 cycles we can use a bit of math to choose the best prescaler:

1,000,000 / 1 = 1,000,000
1,000,000 / 8 = 125,000
1,000,000 / 64 = 15,625
1,000,000 / 256 = 3,906.25
1,000,000 / 1024 = 976.5625

The math only leaves us with one choice. That’s because using a prescaler of 1 or 8 results in a number of cycles that is larger than 65,536 so our 16-bit timer can’t count high enough. Prescalers of 256 and 1024 give results that are not a whole number. If we don’t use a whole number we introduce an inaccuracy in our timing because we can’t measure a fraction of a cycle. A prescaler of 64 meets both our needs, being a whole number that is smaller than the limits of our 16-bit counter.

How can we set up this prescaler? The datasheet tells all. Looking at “Timer/Counter1 Control Register B” (TCCR1B) which spans pages 133 and 134 we can find the answer. Diagram 15-5 shows a clock settings table. In our case we need to set CS10 and CS11 to ‘1’ on the TCCR1B register. To do this we use an OR operator and Left Shift a ‘1’ to the location of the CS10 and CS11 bits:

  TCCR1B |= 1<<CS11 | 1<<CS10;

Because this is our first real bitwise math let’s look at it in depth. First off, we’re only setting two bits on the register so we do not want to use just an equals sign. If I had done that, this command would force all other bits to zero. Instead, I use shorthand code to use the OR operator to compare TCCR1B with a bitmask containing a ‘1’ at the correct location for the CS10 and CS11 bits. Any other bits on the  TCCR1B register that are set to ‘1’ will remain so.

I’ve created a bitmask to the right of the |= operator. As I talked about in the includes section, CS10 and CS11 are defined in io.h. But looking at the TCCR1B register we can see that CS10 is on bit 0 and CS11 is on bit 1. If you solved your math problem longhand it would look like this:

1<<CS11 | 1<<CS10;
1<<1 | 1<<0;
0b00000010 | 0b00000001;
0b00000011;

This is the method that you use for setting any bit for any purpose. It really is that simple. Build a bitmask and apply it to a register or variable. Just remember to be careful about preserving data that might already be stored on a register or in a value but using the OR operator during assignment.

Now that we have a divided clock source for the counter, and a target number of 15,625 cycles to watch for thanks to the math above. We can use one of the modes of Timer1, the Clear Timer on Compare Match (CTC), to trigger an interrupt at that exact cycle count. Take a look at page 121 of the datasheet and you will see we need to set OCR1A to our target value. We’ll set it to 15,624, one less than our cycle count because microcontroller timers start counting with the number zero, not one. This time we will use an equal sign because there are no other values stored in this register:

  OCR1A = 15624;

I also need to set the timer mode I want to use. Table 15-4 on page 133 has a lot of information on this. As discussed before, I want to use CTC mode so that narrows my choices on this table down to just two. I can choose between those because I know I’m using the value of OCR1A as the largest number the timer should count to, or TOP. The chart tells me to set the WGM12 bit on the TCCR1B register to 1.:

  TCCR1B |= 1<<WGM12;

This could have been done at the same time as the timer prescaler because they’re set on the same register. But it’s fine to do it in two steps because I’ve used the OR operator, making sure I’m not changing any of the other bits on this register.

The next step can be a “gotcha” for new developers. Everything is now setup correctly for our timer to trigger an interrupt at the appropriate interval. But if we don’t set the “interrupt enable” flag for that particular event, the interrupt will never happen. Page 136 of the datasheet cryptically discusses the use of the Timer/Counter Output Compare A Match Interrupt Enable. Setting this bit to 1 will enable the CTC interrupt we are planning to use:

   TIMSK1 |= 1<<OCIE1A;

Simple right? Do it a few times and it will be. There’s a lot of functionality with the timers on these chips and wading through the register settings is the price you pay for that power. But now we’re ready to go with 1-second interrupts.

Initializing the Input/Output pins

When an AVR chip resets, the pins are all placed in tri-state mode. At the beginning of the program any input and output pins need to be setup for their desired function. Starting on page 73 of the datasheet you can read about using pins as general input and output. There are three registers for each pin that we will generally be concerned with: Data Direction Register (DDR), Port register (PORT) and Pin register (PIN). Each of these will be suffixed with a letter corresponding to which set of pins we are working with. I’ve connected the LED to Port D so I need to work with DDRD, PORTD, and if I was using inputs, PIND.

  //Setup the I/O for the LED

  DDRD |= (1<<0);		//Set PortD Pin0 as an output
  PORTD |= (1<<0);		//Set PortD Pin0 high to turn on LED

The code above is used to set up an LED. Setting a bit on DDRD to 1 will make the corresponding pin an output. Setting it to zero would make it an input. Here I’ve set up an output because we are driving an LED. Outputs can be turned on or off by setting a 1 or a 0 to the PORT register respectively. So above I’ve used PORTD to turn on bit 0 which corresponds to pin connected to the LED.

If we were using a pin as an input the PORT register would be used to enable or disable an internal pull-up resistor and the PIN register would be used to measure the logic value currently present on that pin. Table 13-1 on page 74 shows the various states of I/O pins, but I’ll cover it more in part 4 of this series.

The Loop

Embedded programs must have an infinite loop that prevents the program from getting to the end and exiting. That’s because if our program exits the chip will just sit there and do nothing (after all, there’d be no program running). In this case I don’t need the loop to do anything since I’ve already set up the hardware and I’m using an interrupt to blink the LED:

  while(1) { }			//Loop forever, interrupts do the rest

I’ll add functionality to the loop in Part 4 or the series, but for now the ‘while(1)’ loop just traps the program and does nothing else.

Handling the interrupt

Everything is now setup and ready to go, but nothing will happen unless we write code that does something after the interrupt happens. This is called an Interrupt Service Routine (ISR). The rest of the code is halted and this routine is run. It is best to keep this as short as possible, which is easy here because we just need to toggle the LED:

ISR(TIMER1_COMPA_vect)		//Interrupt Service Routine
{
  PORTD ^= (1<<0);		//Use xor to toggle the LED
}

If you look at page 62 of the datasheet you can see that the interrupt source for Timer/Counter1 Compare A match is called “TIMER1 COMPA”. We take this and use it as the input variable for the ISR, replacing spaces with underscores and adding a lower case “vect” at the end. This is how the compiler knows which ISR belongs to different interrupt sources. As for the LED itself, I’ve used the XOR operator and a bitmask. The bitmask ensures that only bit 0 will be changed.

Compiling Code

Before we leave this segment of the tutorial series you should give your compiler a test-drive.

The compiler takes our C code and turns it into a file that can be written to the microcontroller. The ins and outs of a compiler get a bit hairy and this isn’t the time to explain those details. But as you learn to write embedded code you should make an effort to also learn how this code will be interpreted by the compiler. Doing so will prevent a lot of headaches caused by optimization (the compiler trying to streamline your bloated C code) and it will allow you to make the most of your hardware both in terms of programming space, and functionality.

But for now there’s a make file included in the example source from Part 2. If you haven’t already, unzip that package and navigate to the ‘src’ directory. There are two files in that directory, main.c and makefile. A makefile is a way to automate the compiling process. This one compiles, links, and programs a C code source file. If you look at the makefile you’ll notice that there are several user settings near the top. You need to setup the microprocessor for which you’ve written code, the name of the source file you’ve written (TARGET = main), the programmer you’re using (from the AVRdude list discussed in Part 2), and the port path for the programmer.

If you type ‘make’ you should be able to compile the example program. Unless you have an AVR Dragon programmer and you’re running Linux you’ll get an error when it tries to program the chip, but it should compile the code and output several extra files:

$  ls -la
total 84
drwxr-xr-x 2 mike mike  4096 2010-11-04 14:20 .
drwxr-xr-x 3 mike mike  4096 2010-11-01 14:55 ..
-rw-r--r-- 1 mike mike   894 2010-10-24 12:34 main.c
-rw-r--r-- 1 mike mike    23 2010-11-04 14:20 main.d
-rw-r--r-- 1 mike mike    13 2010-11-04 14:20 main.eep
-rwxr-xr-x 1 mike mike  7121 2010-11-04 14:20 main.elf
-rw-r--r-- 1 mike mike   750 2010-11-04 14:20 main.hex
-rw-r--r-- 1 mike mike  5224 2010-11-04 14:20 main.lss
-rw-r--r-- 1 mike mike  5171 2010-11-04 14:20 main.lst
-rw-r--r-- 1 mike mike 14464 2010-11-04 14:20 main.map
-rw-r--r-- 1 mike mike  3972 2010-11-04 14:20 main.o
-rw-r--r-- 1 mike mike  1454 2010-11-04 14:20 main.sym
-rw-r--r-- 1 mike mike 10235 2010-10-24 10:44 makefile

‘main.hex’ is the file that you can program onto the microcontroller. This makefile is extremely versatile. You can also see that it output ‘main.eep’ which can be used to program the EEPROM on the chip if your code includes default data stored in the EEPROM. It can also be altered to output an assembler file, or binaries in different formats.

If you’re compiler didn’t spit out this information, there’s something wrong with your toolchain. Use your friend Google to search for any error messages and see if you can’t get things fixed up. Another great exercise would be to modify this file to work with your programmer. If you managed to get AVRdude working in Part 2 of this series, this alteration is as simple as changing the makefile to use those same settings.

Conclusion

That’s it for now. In the next installment of this series I’ll be talking about fuse bits, writing our own code, and I’ll try to touch on many of the different peripheral features of this chip. I’m plan to augment the original circuit with a few more LEDs (so make sure you have at least 8 of them and their matching resistors) along with adding a button for input. Thanks for reading!

Follow Me

@szczys

Resources

Atmel AVR ATmega168 Datasheet (PDF)

AVR Libc manual

40 thoughts on “AVR Programming 03: Reading And Compiling Code

  1. There are some excellent tutorials on AVRFreaks , a nod goes out to abcminiuser who has a few very well written ones that explain everything in a clear methodical manner.

    If you see this miniuser write a book mate , it would blow any other avr programming book I’ve read out teh water !

  2. Bit operations are my favorite part about low-level programming – working with individual 1’s and 0’s.

    “Play me off Keyboard Cat!”
    <<= 5
    "me off Keyboard Cat!"
    <<= 5
    "f Keyboard Cat!"
    <<= 5
    "board Cat!"
    <<= 5
    " Cat!"
    <<= 5

  3. @smokedasphalt: Yes, you can use an ATmega8, but not with the HEX file. Change the makefile so that it uses this chip, and make sure all of the Timer/Counter1 settings are the same by looking at the datasheet for that chip. In fact, you should be able to do with for any chip as long as you do the work to make sure time is running as you expected. This is because not all chips ship with a 1.0Mhz system clock (the tiny13 uses a 1.2Mhz clock), not all chips have a 16-bit counter, and the register settings for those timers are different depending on the chip you’re using. Now compile the package and if everything went okay you will have a usable HEX file for the new hardware.

  4. I liked the tutorial. It gives a good basis. One comment about the main loop section. Depending on your compiler, a lack of main loop may do different things. It is possible that if you forget it the micro will just power itself off after returning from main, but it is also very possible that the compiler will not do any protection and just keep executing arbitrary code that happens to be in flash. If you look at what the microcontroller is doing, it reads some memory, does an action and repeats. There is nothing that guarantees it will stop at the end of main.

  5. For a slow blinking LED on an Atmega8 use this code:

    #include
    #define F_CPU 16000000UL
    #include
    int main(void)
    {
    DDRD |= (1 << PD0); // PD0 Output Pin
    while(1) // loop for ever
    {
    PORTD |= (1 << PD0); // to activate PD0
    _delay_ms(63);
    PORTD &= ~(1 << PD0); // to deactivate PD0
    _delay_ms(63);
    }
    }

    works for me

  6. This is the first time I’ve done anything like this, so I decided to wait for the next tutorial to see how you do buttons and the other stuff.

    But I grew bored, so in the mean while, I decided to tinker with the setup, set all the 8 portds to output, set the interrupt counter to count to 1, and simply incremented PORTD every time by 1.

    Then I cut the ground wire going to the LEDs and added a small speaker in between. Now I have a nice synthesizer with light effects, because touching the LED leads to the ground bus generates different sounds.

    Another funny thing is, that I had to leave the 1k resistors out from the DAPA cable everywhere except on RESET to make it work, which in turn made the AVR run on current from the SCK wire at about 2.7 volts and 2 milliamps every time GND is connected but the battery isn’t, at least according to my cheap multimeter. The LED still glows just a tiny bit, but it keeps on blinking and the whole thing is drawing just about 5 milliWatts.

  7. hello,
    i’ve tried the code in part 2 on an ATMEGA8-16PU
    but the led connected to PORTD0 blinks at 2Hz if i use the count value of 15624,the blinking changes to 1Hz if i use the count value 65535 ..please tell me the reason for this.

  8. Fuse settings?

    Not an expert, but I think you need to make sure the fuse settings are correct and the code thinks the clock speed is the same as the actual clock speed.

  9. I don’t know if anyone who can answer this question has subscribed to the comments for this post, but I have a question.

    I took Mike’s code and tried to alter it to make my setup blink at 2Hz, just to get a feel for how to alter the frequency using the timers. No matter what I change, it seems that I cannot make the LED blink faster than the 1Hz Mike has set it to. After doing a -e in avrdude, I reloaded my altered .hex and still the frequency doesn’t change. My altered code:

    //Setup the clock to 2Hz
    cli(); //Disable global interrupts
    TCCR1B |= 1<<CS11 | 1<<CS10; //Divide by 64
    OCR1A = 7812; //Count 7812 cycles for .5 second interrupt (2Hz)
    TCCR1B |= 1<<WGM12; //Put Timer/Counter1 in CTC mode
    TIMSK1 |= 1<<OCIE1A; //enable timer compare interrupt
    sei(); //Enable global interrupts

    The 7812 is basically an arbitrary number since I don't quite understand how to work the math to count for half a second. From what I understand, Mike says we use 15k cycles to count 1 second, so I just half-ed that to go for .5 seconds. I imagine all the other stuff should stay the same.

    Advice?

  10. Hey did anyone notice that he talks about “Looking at “Timer/Counter1 Control Register B” (TCCR1B) which spans pages 133 and 134 we can find the answer.” but in the code he uses “ISR(TIMER1_COMPA_vect)” A, not B. Sooo I’m confused. Does this still work?

  11. Hey did anyone notice that he talks about “Looking at “Timer/Counter1 Control Register B” (TCCR1B) which spans pages 133 and 134 we can find the answer.” but in the code he uses “ISR(TIMER1_COMPA_vect)” A, not B. Sooo I’m confused. Does this still work?

  12. I read it all and now I am very anxious to have it working in my board. The whole tutorial is excellent. It touch the very simple beginning as well as a deep programming issue like timer/counter interrupt with the same clarity and easy. Wow!

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.