I’ve had an arduino for a while now, and I’ve done a few projects that I would like to deploy. Rather than using an arduino at about £16 per board, I decided I’d try to learn how to program AVR chips, the price of which varies, are in the range of £2-£5/chip. This tutorial will discuss what I’ve done so far — namely, the chip equivalent of “hello world” (flashing an LED) and using the timers for PWM (pulse-width modulation), which will be coming up in another tutorial.
The AVR chip I’m using in this tutorial is the ATMega48, which I’m programming using the USBTinyISP, which I bought in kit form from Adafruit. Below is a photograph of my setup.
The black component you can see to the left of the breadboard is a ZIF socket I soldered to some header sockets, which allows me to easily program the chip and plug other wires into, of course, if you want to, you can simply plug the chip straight into some breadboard (but this puts a fair amount of force on the legs). Below is a picture of how to wire the chip up to your programmer.
I’ll assume you have your programmer installed working. In order to compile the code we write for AVR, you’ll need the avr flavour of gcc, and also avrdude (to upload the code). For all the windows users out there, there’s WinAVR, which will install all of the tools you need to compile and upload your code to the AVR chip. I’m also using the AVR Eclipse Plugin, which makes compiling and up loading much simpler — in their getting started guide they also walk you through compiling and uploading the code, so its a handy resource for people who would like to learn more about this.
Bitwise Operations
This code is a lot more low-level than what you’d use for the Arduino. It makes fairly heavy use of bitwise operations so here’s a quick overview of the main ones we use.
Bitwise OR
The | character represents the bitwise OR. It compares each bit of its parameters, and if one of those bits is 1, the output bit is 1, as is shown in the truth table below.
| p | q | p | q |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
So the operation 010 | 101 would output 111, and the output 001 | 000 would output 001. This is useful for switching bits on when you don’t want to affect any other bits in the register.
Bitwise AND
The & character represents the bitwise AND. It compares each bit of its parameters, and will output a 1 only if both bits are 1, as can be seen in the truth table.
| p | q | p & q |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
The operation 010 & 101 would output 000, and 111 & 010 would output 010. A common use of the AND operator is to apply masks to values — to ensure some bits are always off. For example if we wanted the first and third bits of a register always to be 0 (maybe setting them to 1 could signal the end of the world), when setting that register with a certain value we can simply & it with the mask 010.
Bitwise XOR (eXclusive OR)
I’m not actually using this operator in this tutorial, but I thought I’d include it anyway, it has interesting uses — one of which is a simple encryption algorithm, which if used correctly is impossible (I believe) to break. The ^ character represents a bitwise XOR in C, and it is similar to the OR operator above except that its output is only 1 if only one of the two input bits is 1. See the table below.
| p | q | p ^ q |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Left Shift Operator <<
This operator simply shifts the bit in a variable by some number of places. For example the operation 0001 << 1 would result in the number 0010, and the number 0001 << 2 would result in 0100. If you shift a bit too far, the value will ‘overflow’ 0001 << 4 results in 0000 (assuming we’re using some 4-bit data type). This is generally not something you want to do… Incidentally there is also a >> operator, which shifts the bits of the left parameter right — i.e 0100 >> 1 outputs 0010.
Flashing LEDs!
Right, now we’ve got that over and done with, lets get our flash on. Firstly, we’ll do this without the use of a timer (i.e. we’ll hack it out with some loops), then, in a later blog post (which should be coming soon — I just split it off from this one in order to reduce the length).
If you look at the Atmel spec sheets for the ATMega48, you can see that the pins on the chip are divided into 3 ports — B, C and D. Each port, except for some reason port C, has 8 pins (C has only 7) numbered 0 to 7. The pins are arranged as shown in the picture below (note that pins are labelled with their port letter then their pin number).
Wire your LEDs to a breadboard (don’t forget your resistors), and connect their grounds to one of the spare ground pins on your programmer (see the diagram above). We’re going to make these two LEDs flash alternately. Connect the positive leg of one LED to C5, and the positive leg of the other LED to B1.
Create a C file and include the header file avr/io.h Next create a main function that returns an int and no parameters. In this function create a local variable int “next” and set that equal to 1.
The first thing we’ll need to do is to tell the chip that we want pins B1 and C5 to be outputs (similar to the pinMode function in arduino). To do this we need to set their ‘Data Direction’ in the Data Direction Register on the chip (DDR). We do this by shifting a 1 bit into the “DDB1″ bit of the DDRB register, and a 1 bit to the DDC5 bit of the DDRC register. These register names take the form DDR<Port Letter> and the individual bits of those registers DD<Port Letter><Leg Number>.
Next, create an infinite while loop in which we’ll place the code to make the LEDs flash. To turn a pin on, you set the corresponding bit in its port’s register to 1 (and to turn it off you set it to 0). See the next lump of code to see how you do this.
#include <avr/io.h>
int main(void)
{
int next = 1;
/*Make Pin 1 of port B an output*/
DDRB = 1 << DDB1;
/*Make Pin 5 of port C an output*/
DDRC = 1 <<DDC5;
__asm__ volatile ("nop");
while(1)
{
PORTB = next << PB1;
PORTC = (1-next) << PC5;
/* Put in a delay (we could use the AVR
* function for this). The ASM instruction "nop"
* tells the CPU to do nothing for one clock cycle.
* The volatile keyword tells the compiler not
* to optimise this instruction out.
*/
for(int a = 0; a < 100; a++)
for(int b = 0; b < 1000; b++)
__asm__ volatile ("nop");
next = 1-next;
}
return 1;
}
The above code is all you need for some flashing LEDs. The first two instructions in the while loop are the ones that do most of the work — they shift bits into the correct registers in order to turn the LED on or off. Again the registers are named PORT<Port Letter> and the individual pin data bits P<Port Letter><Pin Number>. When coding these chips, remember that ints are only 8 bits long, don’t do as I did and have this count to 1000 000 in the delay as the number will overflow before it ever gets there (resulting in the program sitting there, spinning forever).
Anyway, that’s it for now, if you have any questions be sure to leave a comment below — I’m no expert, but I’ll do my best to answer your questions. If you have suggestions as to how I can improve this code, or tools I can use to speed up development, then please comment. Keep checking back because another tutorial on how to use timers is imminent.



0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.