This time I will go through a "Hello World" kind of programming example to show what is needed to get the chip to do something. The embedded software version of 'Hello World' is making an LED blink, so this is what I'll do here.
I will assume that you have the Arduino hardware platform at hand, so in order not to add any hardware to that, I will use the LED attached to 'pin 13' as the blinking on. As the pin numbering on the Arduino platform is specific to that PCB, we will need to translate that into the pin numbering of the actual chip. To do that we will need the schematic of the PCB. For my examples I will use the Arduino Duemilanove - a board I had lying around from previous projects.
The schematics are found here.
Another important document to get a hold of the datasheet for the microcontroller. That will tell you all the nitty gritty details about the specific chip. I found one at Atmel, that makes these chips.
The code
Getting down to business, out "Hello World" example is seen below:#include <avr/io.h> #include <util/delay.h> int main(void) { // Set Port B pins as all outputs DDRB = 0xff; while(1) { // Sets pin 5 of port B PORTB |= _BV(5); // waits 500 ms _delay_ms(500); // Resets pin 5 of port B PORTB &= ~(_BV(5)); // Waits 1000 ms _delay_ms(1000); } return 0; }According to the schematic, 'pin 13' of the Arduino Board corresponds to pin 19 of the Atmega328p chip, which in turn is pin 5 of Port B. In the code we are doing things with just this port and pin.
From the top of the code, we first include the specific headers:
- avr/io.h: defines a lot of useful constants and macros for AVR chips.
- util/delay.h: provides the delay function.
- DDRB: Data direction register for port B
- PORTB: Data register for port B
- _BV(val): Returns an 8 bit binary value with the 'val' bit set. EG. _BV(5) returns b00100000
And with the while(1)-loop, the microcontroller just keeps on blinking. Unlike a PC-apps, endless loops are good in embedded systems - if they were not there, the device would just stop working and had to be reset after each run.
Set and reset of pins
Every time you want to change the state of one output pin you have to change the state of a group of pins called a port. A port in this case is 8-bit 'wide'. This means that every time you want to change one pit, you have to write 8 bit to a register. THis is where the _BV() macro comes in handy. However you have to apply the changes in a certain way as to not change the entire register. In the code snippet, I've applied the changes using logical operations.For setting an output pin to logical 1 (one) we use the OR-operation with the output of the _BV() macro. This way we're sure that it's set to 1 regardless of the previous state, but we don't touch the other pins. The _BV() output is used as a so called bitmask to achieve the desired effect.
For setting an output pin to logical 0 (zero) we use the AND-operation, but this time we have to invert the output of the _BV() operation. If we want to reset pin 5 as in the code above we have to apply b'11011111' to the port with the AND operation. This effectively sets pin 5 to 0 and leaves the rest as is.
For further explenation of the bitwise operations check out Wikipedia.
This was only a small example of what is possible with microcontrollers. To really exploit them, it takes an in depth knowledge of the different hardware components in the available chip - and in the surrounding electronic circuitry. In later posts I will show how to use these different hardware components as different ways to turn on that LED.