AMTEL ATMega328Der RasPi kann allein über die GPIO-Ports schon ziemlich flexibel externe Bauelemente ansteuern oder Daten einlesen. Die Anbindung eines relativen smarten Mikrocontrollers, der sich zudem in der Laufzeit neu programmieren lässt, eröffnet noch viel mehr Möglichkeiten. Der ATmega328 von AVR zum Beispiel hat eine USART-Schnittstelle, so dass man mit einem Terminal mit dem IC kommunizieren kann. Der IC kann auf diese Weise Textausgaben an den RasPi schicken oder komplett neu programmiert werden. Er kann über den RasPi quasi „lernen“, was er aus seinen Umgebungsdaten „erfährt“.

Der Atmega328

Der ATmega328 ist ein Mikrokontroller von AVR und kann mit integrierter CPU, mit RAM und EEPROM eine ganze Menge anstellen. Er steckt daher auch im Arduino und hat damit eine riesige Community ins Leben gerufen. Das Arduino Board ist gut zum Kennenlernen und Experimentieren mit  AVR-Mikrocontrollern. Das Programmieren der AVR-ICs ist verhältnismäßig einfach. Die Arduino-Community stellt zum Beispiel eine einfache IDE zur Verfügung und der Hersteller gibt zu diesen ICs ebenfalls eine mächtige IDE heraus. Beide Entwicklungsumgebungen bringen hunderte von Bibliotheken und Beispielprogrammen mit. Wenn man sich also für Mikrocontroller interessiert, ist der Einstieg über die AVR-ICs besonders leicht verdaulich.

Die Werkzeuge

Der ATmega328 im Arduino Uno wird über die Elektronik auf dem Board per USB-Schnittstelle programmiert. Die Software dazu ist in die Arduino IDE integriert. Theoretisch könnte man den ATmega328 im Arduino Uno programmieren und dann aus der Fassung ziehen, um ihn in eigenenen Schaltungen zu nutzen. Es gibt jedoch auch gute Anleitungen für das Programmieren eines AVR-ICs mit dem Arduino Uno. Wenn man den IC jedoch direkt programmieren will, braucht man einen so genannten ISP Programmer. Den gibt es günstig (5 bis 10 EUR) im Internet. Mit der geeigneten Software (avrdude) kann dann losprogrammiert werden. DSC_0049

Hier noch einmal die vollständige Liste:

  1. Steckbrett
  2. Klemmbrücken
  3. ATmega328P
  4. Eine LED und ein 220 Ohm-Widerstand
  5. USBasp ISP Programmer
  6. Raspberry Pi

Normalerweise müsste der ISP-Programmer per Steckkabeln mit dem ATmega328 verbunden werden. Um das Anstecken des Programmers zu erleichtern, habe ich mir einen Adapter dafür gebaut. Der hat einen Anschluss für den ISP Programmer und verteilt die Kontakte dann ein einer wilden Kabelage an die Pins des ATMega328. Der Vorteil ist, dass man den Adapter nach dem Programmieren wieder abziehen kann und der ATmega im Steckbrett bleiben kann.

Programmer

Den ATmega328 programmieren

Der ISP Programmer spricht über die SPI-Schnittstelle mit dem ATmega328. Das heißt, die Anschlüsse des USBasp für SPI (MOSI, MISO, SCLK, RESET) und die Spannungsversorgung (VCC, GND)  wird mit den entsprechenden Pins des ATmega328 verbunden (die Verdrahtung übernimmt mein oben erwähnter Adapter):

USBasp-to-AVR

Ich nutze die Software avrdude zum Programmieren unter Linux. Avrdude kann mit dem USBasp von Haus aus umgehen und braucht lediglich eine Rattenschwanz an Kommandozeilen-Parameter. Ich habe mir die Konfiguration aus den Beispielprogrammen des Buchs Make:AVR-Programming kopiert. Aber der Reihe nach. Da der ATmega mit C programmiert wird, braucht man die avr-Entwicklungstools. Ich nutze pidora auf dem RasPi, daher installiere ich die avr-tools mit dem folgenden Aufruf:

sudo yum install avrdude avrdude-doc binutils-avr avr-libs gcc-avr gdb-avr

Und hier das Makefile für das Programm blinkLED, indem alle relevaten Optionen für avrdude zur Programmierung eines ATmega328 stecken:

 MCU = atmega328p
 F_CPU = 8000000
 BAUD = 9600
 MAIN = blinkLED.c
 EXTRA_SOURCE_DIR = ../AVR-Programming-Library/
 EXTRA_SOURCE_FILES = USART.c
 PROGRAMMER_TYPE = usbasp
 CC = avr-gcc
 OBJCOPY = avr-objcopy
 OBJDUMP = avr-objdump
 AVRSIZE = avr-size
 AVRDUDE = avrdude
 CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU)UL -DBAUD=$(BAUD) -Os -I. -I$(EXTRA_SOURCE_DIR)
 CFLAGS += -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums
 CFLAGS += -Wall -Wstrict-prototypes
 CFLAGS += -g -ggdb
 CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--relax
 CFLAGS += -std=gnu99
 TARGET = $(strip $(basename $(MAIN)))
 SRC = $(TARGET).c
 EXTRA_SOURCE = $(addprefix $(EXTRA_SOURCE_DIR), $(EXTRA_SOURCE_FILES))
 SRC += $(EXTRA_SOURCE)
 SRC += $(LOCAL_SOURCE)
 HEADERS = $(SRC:.c=.h)
 OBJ = $(SRC:.c=.o)
 all: $(TARGET).hex
 %.hex: %.elf
 $(OBJCOPY) -R .eeprom -O ihex $< $@
 %.elf: $(SRC)
 $(CC) $(CFLAGS) $(SRC) --output $@
 %.eeprom: %.elf
 $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@
 eeprom: $(TARGET).eeprom
 clean:
 rm -f *.elf *.hex *.obj *.o *.d *.eep *.lst *.lss *.sym *.map *~
 flash: $(TARGET).hex
 $(AVRDUDE) -c $(PROGRAMMER_TYPE) -p $(MCU) $(PROGRAMMER_ARGS) -U flash:w:$<

 

Das eigentliche Programm ist eher übersichtlich:

#include <avr/io.h>
#include <util/delay.h>
#include "USART.h" 

#define LED PB0
#define LED_DDR DDRB
#define LED_PORT PORTB
#define DELAYTIME 1000
#define setBit(sfr, bit) (_SFR_BYTE(sfr) |= (1 << bit))
#define clearBit(sfr, bit) (_SFR_BYTE(sfr) &= ~(1 << bit))
#define toggleBit(sfr, bit) (_SFR_BYTE(sfr) ^= (1 << bit)) 

int main(void)
{
  setBit(LED_DDR, LED);
  initUSART();
  printString("Hallo!\r\n");
  while (1)
  {
    setBit(LED_PORT, LED);
    printString("AN\r\n");
    _delay_ms(DELAYTIME);
    clearBit(LED_PORT, LED);
    printString("AUS\r\n");
    _delay_ms(DELAYTIME);
  }
  return (0);
}

Die eingebundene Header-Datei USART.h entstammt ebenfalls dem Beispiel-Code des Buches Make:AVR-Programming. Die Dateien USART.h und USART.c müssen dem obigen Code nach in einem Verzeichnis „AVR-Programming-Library“ oberhalb der Code-Datei liegen. Sie werden im Makefile mit EXTRA_SOURCE_DIR = ../AVR-Programming-Library/ referenziert.

Und damit kann das Programm in den ATmega328 geladen werden:

make flash

Falls die Schaltung schon steht, fängt die LED sofort an zu blinken. Das zeigt, dass das Programmieren grundsätzlich geklappt hat.

Die Schaltung

Der Aufbau für ein erstes einfaches Experiment ist dann im nächsten Bild dargestellt. Eine LED soll Feedback geben, ob überhaupt etwas passiert. Die Ansteuerung erfolgt über Pin 14 des ATmega328. Den Kondensator (100nF) zwischen Pin 7 und Pin 8 braucht man eigentlich nicht. Er hält lediglich die Programmierspannung sauber.

DSC_0064Nach der Programmierung wird später die serielle Schnittstelle mit dem RasPi verbunden. Dann kann auf einem seriellen Terminal gehört werden, was der ATmega328 über die Leitung funkt. Die Verdrahtung kann man mit etwas Mühe dem folgenden Bild entnehmen:

DSC_0068Demnach versorgt der RasPi den programmierten ATmega328 mit Spannung (+5V von Pin 26, GND von Pin 22). Das serielle Terminal kommt über Pin 20 (TX – senden) und Pin 18 (RX – empfangen). Der RX-Port des RasPi wird an den TX-Port (Pin 3) des ATmega328, und der TX-Port des RasPi wird an den RX-Port (Pin 2) des ATmega328 angeschlossen.

Gucken, was läuft

Wenn der RasPi mit dem ATmega328 verbunden ist, sorgt die Spannung des RasPi für das Anlaufen des IC. Die blinkende LED zeigt, dass der ATmega328 sein Programm nicht vergessen hat. Nun kann die Ausgabe über ein Terminalprogramm betrachtet werden. Auf dem RasPi muss dafür die standardmäßig aktivierte Terminalausgabe von getty deaktiviert werden:

sudo systemctl disable serial-getty@ttyAMA0.service

Ein Terminalprogramm für die Kommandozeile ist zum Beispiel screen. Unter pidora ist die Installation des Pakets „screen“ notwendig (sudo yum install screen). Ein Aufruf von screen für die UART-Schnittstelle des RasPi lautet:

sudo screen /dev/ttyAMA0 9600 8N1

Das liefert die folgende Ausgabe:

pi_uartMan sieht: Es spricht. Ich hoffe, ich konnte zeigen, dass die Programmierung eines ATmega328 relativ einfach ist. Der ATmega328 hat etliche Beinchen für digitale Aus- und Eingaben, konvertiert analoge Signale in digitale und er versteht Protokolle wie SPI, I2C und USART um mit anderen ICs zu reden. Das eigentlich interessante ist, dass er mit C-Programmen zu beliebiger Logik gebracht werden kann. Mit einem kleinem Bootloader kann er sogar über die USART-Schnittstelle programmiert werden. Das heißt, er kann zur Laufzeit mit neuer Logik versehen und damit flexibel auf Umgebungsparameter eingestellt werden. Wie ein Bootloader in den ATmega328 gebracht wird, zeige ich in einem nächsten Blog.