Logo

AVR® Mikrocontroller in C programmieren

AVR Mikrocontroller wurden schon während ihren Entwicklungsphase dafür optimiert, in C programmiert zu werden.

Der avr-gcc ist ein freier C-Compiler, der speziell auf die Programmierung von AVR Mikrocontrollern zugeschnitten ist. Dabei steht GCC für GNU Compiler Collection. Basierend auf der GCC bietet die AVR GCC eine komplette Toolchain für die Programmierung.

Die avr-gcc Toolchain wird auch in den gängigen IDEs genutzt, mit denen AVR-Mikrocontroller programmiert werden. Z.B. auch im Atmel Studio in der PlatformIO oder in der Arduino IDE.

Wenn die IDEs zu umfangreich und umständlich zu bedienen sind oder nicht zum Betriebssystem passen, gibt es die Möglichkeit, die avr-gcc auch über die Kommandozeile zu bedienen.

Toolchain

Toolchain zur Programmierung eines Mikrocontrollers

Die avr-gcc mit Linux

Installieren der avr-gcc

In vielen aktuellen Linux-Distributionen gibt es die Möglichkeit, die avr-gcc Toolchain per Paketmanager zu installieren. Hat man die Installation durchgeführt, so kann man in der Kommandozeile den Erfolg überprüfen.

> avr-gcc
avr-gcc: fatal error: no input files
compilation terminated.

Der "Fatale Fehler" sagt dem User in diesem Fall nur, dass der Compiler nichts zu kompilieren hat, weil kein File angegeben wurde.

Die Version der installierten avr-gcc erhält man mit der Option --version.

> avr-gcc --version
avr-gcc (SUSE Linux) 7.3.1 20180323 [gcc-7-branch revision 258812]
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Compilieren eines C-Quelltextes

Um ein Programm auf den Mikrocontroller schreiben zu können, muss es im Maschinenformat vorliegen. Viele Programmierer akzeptieren das Intel-HEX-Format (Endung *.hex oder *,ihex)

Die Compilierung des C-Quellcodes läuft über mehrere Zwischenstufen.

  1. Compilieren
  2. Assemblieren
  3. Linken
  4. Format-Wandlung

Beim ersten Schritt (Compilieren) wird das C-Programm zunächst in Assembler-Code übersetzt. Dabei wird der Quellcode auf Fehler überprüft und der entstandene Assembler-Code optimiert.

Beim zweiten Schritt (Assemblieren) wird das Assembler-Programm in einen AVR-eigenen Objektcode (Endung: *.o) umgewandelt, der schon Maschinen-Code enthält.

Im dritten Schritt (Linken) werden die entstandenen Objekt-Code-Dateien zusammengesetzt und ggf. Module aus Bibliotheken (Libraries) ergänzt. Es entsteht eine AVR-Ausgabedatei (Endung: *.elf).

Im letzten Schritt wird die AVR-Ausgabedatei in ein für den Hardware-Programmer passendes Format umgewandelt (meistens *.hex).

Nun kann der Programm-Code mit dem Hardware-Programmer auf den Mikrocontroller geschrieben werden.

Bei diesen Zwischenschritten entstehen mehrere Dateien (die teilweise wieder gelöscht werden). Daher ist es notwendig, den C-Quelltext in einen eigenen Ordner unterzubringen.

Der folgende Link verdeutlicht den Ablauf anhand eines Beispiels für den ATmega328p.

Hochladen des Codes auf den Mikrocontroller

Für das Hochladen des Codes auf den Mikrocontroller benötigt man ein passendes Hardware-Programmiergerät und ein Software-Tool, das mit dem Programmiergerät zurechtkommt.

Auch hier bietet die avr-gcc Toolchain das passende Programm: AVRDUDE

AVRDUDE versteht sich mit den meisten Programmiergeräten und wird auch von den gängigen IDEs benutzt.

Mit einem Trick lässt sich ein Arduino Uno Board, das mit einem ATmega328p bestückt ist, auch ohne ein Hardware-Programmiergerät mit C-Quelltext programmieren. Dazu muss der Quelltext als HEX-File vorliegen.

Hauptbestandteile eines C-Programms

Die int main() Funktion

Mindestbestandteil jedes C-Programms ist die main Funktion.

int main(void) { }

Die main Funktion muss in jedem C-Programm vorhanden sein, das nach der Kompilierung eine ausführbare Datei ergeben soll. In der main Funktion stehen die Anweisungen, mit denen das Programm beginnt.

Grundstruktur eines AVR C-Programms

Im unten verlinkten Video wird die Grundstruktur eines C-Programms für einen ATmega328p am Beispiel des Programms "Blink" in der PlatformIO IDE erklärt.

Standardbibliotheken

Häufig eingesetzte Standardbibliotheken sind:

AVR I/O Bibiliothek

Für den Zugriff auf die zahlreichen Register von AVR Mikrocontrollern ist die avr/io.h Bibliothek gedacht.
Die Register sind hier mit den im Handbuch identischen Abkürzungen definiert und können wie eine Variable angesprochen werden.

#include <avr/io.h>

Beispielsweise kann das DDRB Register wie folgt angesprochen werden:

DDRB = 0b00000100;

Delay Bibiliothek

Delay-Routinen sind eigentlich Zählschleifen, in denen der Mikrocontroller mit Nichtstun beschäftigt wird. Wie oft eine Zählschleife durchlaufen werden muss hängt vom Arbeitstakt des Mikrocontrollers ab. Um komplizierte Berechnungen der Länge der Zählschleife zu ersparen, ist es sinnvoll die Delay Bibliothek einzubinden.
Allerdings gilt es, vor allem bei Echtzeit-Anwendungen, zu beachten, dass Delay-Routinen immer eine Verschwendung von CPU-Zeit darstellen.

#include <util/delay.h>

Die delay.h Bibliothek befindet sich im Unterorder util/.

Datentypen

Prinzipiell sind Integer-Datentypen am besten geeignet für die Arbeit mit Mikrocontrollern. Es sind zwar auch Fließkomma-Typen möglich, jedoch benötigen sie eine relativ lange Rechenzeit. Daher sollte man diese möglichst vermeiden. Bei der Bildung von Arrays sollte man den relativ kleinen Datenspeicher (SRAM-Speicher) des Mikrocontrollers berücksichtigen. Dieser ist auch mit kleinen Arrays schneller voll als man denkt.

Standardisierte Integer-Typen

Die Variablen-Typen unterscheiden sich in der avr-gcc vom C-Standard. Am besten ist es, mit standardisierten Integer-Typen zu arbeiten, die in der stdint.h definiert werden. An diesen Typen lassen sich die Bitlängen eindeutig ablesen, was bei der Einteilung des knappen Datenspeichers hilfreich ist.

Dazu wird die Header Datei stdint.h in den Quelltext eingebunden.

#include <stdint.h>

Vorzeichenbehaftete Integertypen können 8, 16, 32 oder 64 Bit lang sein:

  • int8_t
  • int16_t
  • int32_t
  • int64_t

Vorzeichenlose Integertypen können 8, 16, 32 oder 64 Bit lang sein:

  • uint8_t
  • uint16_t
  • uint32_t
  • uint64_t

Beispiel für die Zuweisung eines 8 Bit unsigned Integer:

uint8_t testvariable = 122;
char

Zeichen

In C werden einzelne Zeichen mit dem Datentyp char (Character) dargestellt. Dabei entspricht ein char einem Byte, also einem uint8_t. Die Zeichen werden in einer bestimmten Codierung (üblicherweise als ASCII-Code) gespeichert.

Zeichenkonstanten (einzelne Zeichen) werden in einfache Anführungszeichen (Single Quotes) geschrieben.

char zeichen = 'a';

Zeichenketten

Interrupts mit AVR-gcc programmieren

Interrupt Bibliothek

Für die Behandlung von Interrupts, stellt avr-gcc die Interrupt Bibliothek zur Verfügung. Diese muss zuvor in den Quellcode eingebunden werden.

#include <avr/interrupt.h>

Globale Interruptsteuerung

Interrupts werden nur freigegeben, wenn das I Bit im Statusregister SREG gesetzt ist.

Dafür stellt die Interrupt-Bibliothek in Anlehnung an die entsprechenden Maschinenbefehle zwei Makros zur Verfügung.

Das Makro sei() setzt das I Bit und schaltet Interrupts global frei.

sei();  // Interrupts global freigeben

Das Makro cli() löscht das I Bit und sperrt Interrupts global.

cli();  // Interrupts global sperren

ISRs programmieren

Für die Programmierung von Interrupt Service Routinen stellt die Interrupt Bibliothek das ISR() Makro zur Verfügung. Es nimmt dem Programmierer einige Aufgaben ab, die für korrekt funktionierende ISRs essentiell sind. So wird beispielsweise das SREG zu Beginn einer ISR gesichert und am Ende wieder zurückgeschrieben.

Syntax

ISR(IRQ_vect)
{
  // code
}

IRQ_vect: Kürzel des auslösenden IRQs + Zeichenkette "_vect".

Variablenübergabe in ISRs

Sollen Variablen an eine ISR übergeben werden, so müssen diese vorher global mit dem Zusatz volatile definiert werden. Andernfalls besteht die Möglichkeit, dass der Compiler diese Variablen (die scheinbar im Hauptprogramm nicht benutzt werden) "wegoptimiert". Die Programme funktionieren dann natürlich nicht so, wie sie es sollten.