Samstag, 8. November 2014

Input Output

This second post is about digital in and output (short: I/O), i.e. on/off-switching of ATMEGA8 pins, or checking if pins are at high or low potential.
"high", "on", "VCC", "1" are the same in this contex, as well as "low", "off", "GND", "0". They refer to the two different electrical potentials or logic levels in the digital ciruit. (VCC=Voltage Common Collector, GND=ground).

More details about I/O are all described in the ATMEGA8 datasheet, but I try to provide the minimum information to get things done. The notion of "minimum" is of course purely subjective.

Output:

Driving a pin high or low means that the controller internally connects it either to VCC or GND, respectively. The controller has a set of pins that can be driven high or low: The general purpose I/O lines. Groups of 8 pins are controlled by 3 registers (DDRx, PORTx, PINx) each. A register is a special 8bit value in the processor. There are 23 I/O pins controlled by 9 registers: DDR[B,C,D], PORT[B,C,D] and PIN[B,C,D].

In the last post, I've made an LED blink by making the pin PD0 of the controller.  For each I/O pin, the corresponding bit in the DDRD register (DDRD:0 = least significant bit, or 0-th bit, of register DDRD) decides if it is used as input (DDRD:0 = 0) or output (DDRD:0 = 1).
To power a LED, a driven output is needed. The DDRD:0 bit can be set by writing the value 1 in the register
DDRD = 1; // the 8 bits of DDRD are: 00000001
Names such as DDRD are defined in the <avr/io.h> header file.
The previous statement also sets all the other bits of DDRD to 0. If the other bits should be preserved in whatever state they are, one can use the following syntax (I assume familiarity with bitwise operators here):
DDRD |= 1;
Likewise one could make the pin PD3 and output by writing
DDRD |= 8; // the bits of DDRD are: 00001000 (binary representation of 8)
To make the code more readable, it is good practice to use the following notation:
DDRD |= (1 << DDD3); // (1<<DDD3) is same as (1<<3)
Setting two bits of a register can be done like this (set bits 0 and 3 of DDRD to 1):
DDRD |= (1<<DDD0)|(1<<DDD3);
To drive the B-pins or C-pins, one has to write to DDRB and DDRC, respectively.
If DDRD:0 is set to 1, the pin PD0 is high if PORTD:0 is 1 and pin PD0 is low if PORTD:0 is 0.
Changing a pin can be done by XOR operation (as shown in the previous post):
PORTD ^= 1;
Or to make it more readable:
PORTD ^= (1 << PD0);

Input:

To check if a pin is high or low, one has to check the bit-content of the registers PIN[B,C,D]. Reading from PORT[B,C,D] will not work.
To check if pin PD0 is high or low one has to check bit PIND:0 like this
if (PIND & 1)
{
    // PD0 is high.
}
else
{
    // PD0 is low.
}
Or like this to make it more readable:
if (PIND & (1<<PD0)) { //...

If a pin (PD0) is configured as input (DDRD:0 = 0), the value of PORTD:0 specifies if a pull-up resistor is connected (PORTD:0 = 1) or not connected (PORTD:0 = 0) to the pin.
The pull-up connector ensures that in case of no external signal on the pin, reading the pin-bit will give 1. The only way to read a 0 from that pin is, if it is externally driven low (for example by a connection to GND via a push button).


Getting started

I recently started to play more seriously with the ATMEGA8 microcontroller from atmel. The same device runs on the Arduino boards. Since I like doing things from scratch, I prefer to play with the bare microcontroller. In this blog I'll share my experiences. Another intention is the following: The ATMEGA8 device is very well documented in the datasheet. But reading tens of pages to know how to enable a timer or the ADC is tedious, especially for beginners. I intend to go through different features of the ATMEGA8 and provide minimal programs to enable and use them.

In order to get started, a programmer is necessary. That is not the case if you have an Arduino board.
I own a programmer from Tuxgraphics.org. Their webpage contains lots of ATMEGA related information and electronics in general. The programmer I bought was a kit that I needed to solder. Also the firmware wasn't installed on the device. But one can load the firmware to it without having a programmer. I it is very interesting to see how it works.

However, building a kit is tedious if you just want to get started with playing. Tuxgraphics.org offers assembled programmers as well. But if you are short on money, you can find lots of (incredibly) cheap offers for programmers on ebay. I recently bought an USBasp programmer from a Chinese manufacturer for about 4 EURO. I donated a bit of money to the inventor of hard and firmware. I think it is fair and it supports the open hardware and open source movement.

All you need in addition is a breadboard, one ATMEGA8 controller in a DIP (dual in-line) package that you can place on the board and some wires plus all components you want to control (such as an LED and a ~500 ohms resistor). No soldering required at all.

The "hello world" in microcontroller programming is a blinking LED. I will show how to do this in this first post.
My programmer has a 10pin adapter. The basic wiring is like this:
The LED is connected from VCC through a 220 ohm resistor to the PD0 pin of the controller.

If the hardware is set up, the controller can be programmed with a firmware. To create the firmware, I write programs in C. To get them compiled and copied to the controller I need C-compiler and a programming software. I use Linux and the packages "avr-gcc" (compiler) "avrdude" (programming software).

The program that makes the LED blink (blink.c) looks like this:

#include <avr/io.h>
#define F_CPU 1000000UL  // 1 MHz
#include <util/delay.h>

int main(void)
{
 DDRD = 1;
 while(1)
 {
  _delay_ms(200);
  PORTD ^= 1;
 }
}

To get this program running on the microcontroller, three steps are neede:

1. Compiling
avr-gcc -mmcu=atmega8 -Os blink.c
The flag -mmcu=atmega8 informs the compiler about the target arcitecture. -Os optimizes for size of the resulting program.
The result of this call is a file "a.out" which has to be converted to a .hex file that can be copied to the program memory of the controller. 

2. Converting
avr-objcopy -O ihex a.out blink.hex
The flag -O ihex specifies the desired output format and the resulting file is "blink.hex"

3. Loading
avrdude -p m8 -c usbasp -U flash:w:blink.hex
copies the program to the controller, where -p m8 specifies the target device (or part), -c usbasp is my programmer and -U flash:w:blink.hex specifies the program data.
If the last command doesn't work you may not have access to the USB device. The simplest way to solve this is to become root before loading the firmware.

If a LED is connected, it should start blinking now. If everything is working, the door is open to the fascinating world of embedded programming!