Achtung! Ich bin (angehender) Informatiker und kein Elektrotechniker/ISTler o.Ae. .
Was ich hier schreibe basiert auf “bei mir hats so geklappt”, aber das kann oft auch reines Glueck gewesen sein. Dieser Artikel ist noch nicht vollstaendig fertig, aber ich bin mir unsicher ob er das jemals sein wird.
Wenn man nur eine einfache Aufgabe umsetzen moechte, braucht man nicht immer einen teuren und grossen Arduino, sondern kann auch einen einfachen und guenstigen AVR-Microcontroller wie z.B. den ATtiny2313 fuer 2-3Euro benutzen. Es gibt jedoch ein paar Nachteile dabei:
- Keine Arduino-IDE: Es muss in echtem C geschrieben werden und ueber das Terminal mit laengeren Befehlen compiliert und uebertragen werden.
- Keine Arduino-Library: Es wird noch ein Stueck hardwarenaeher programmiert (Register und Bit-Operatoren)
- Kein USB und Onboardprogrammer: Es muss ein ISP-Programmer benutzt werden. Wir werden den guenstigen und Linux-kompatiblen mySmartUSB light fuer 16Euro benutzen.
- Die AVRs besitzen keinerlei Schutz gegen DAUs und so ist es unvermeintlich, dass man mal ausversehn einen AVR zerstoert. Bestellt also immer ein paar zuviel.
- Eine externe Stromversorgung wird benoetigt. Es laesst sich ein AVR auch relativ einfach ueber USB speisen, aber die Gefahr bei einem Kurzschluss den Computer zu zerstoeren ist doch sehr hoch. Nehmt also ein Labornetzteil oder Batterien.
Auf der anderen Hand sind AVRs sehr guenstig und haben ungefaehr den gleichen Funktionsumfang wie Arduinos. Sie sind nur benutzerunfreundlicher.
Anschluss des AVR an den Programmer
Erstaunlicherweise hat mir das die groessten Probleme gemacht. Durch meine Unerfahrenheit mit den entsprechenden Dokumentationen war mir nie ganz klar, ob das jetzt alles richtig-rum ist oder spiegelverkehrt etc.. Die folgende Skizze soll den richtigen Anschluss hoffentlich ein wenig erleichtern. Um nicht jedesmal wieder alles miteinander verbinden zu muessen, empfiehl es sich einen Sockel oder ein freies Breadboard zu verwenden, auf das man den AVR dann nur noch druecken muss.

Die Skizze entspricht der Breadboard-Draufsicht. Die Kerbung des AVR sollte offensichtlich sein. Der Anschluss fuer den Programmer entspricht einem entsprechenden Steckplatz auf dem Breadboard. Eine zusaetzliche Stromversorgung sollte nicht noetig sein.
Compilieren und Uebertragen
Zum testen werden wir folgenden Code benutzen, der sich grade so compilieren laesst aber absolut keinen Nutzen hat. Wir verwenden keine IDE sondern einfach unseren Lieblingseditor (in meinem Falle ViM).
./helloworld.c
// 1MHz #define F_CPU 1000000UL #include <avr/io.h> int main(void){ return 0; }
Nun bauen wir uns unser Makefile. Make ist ein in der Linux-Welt sehr beliebtes und gewoehnlich vorinstalliertes Build-Tool. Einmal erstellt werden wir mit ‘make compile’ compilieren und mit ‘make write’ den AVR programmieren koennen ohne jedes mal ein laengeres Kommando eingeben zu muessen. Ein Makefile wird fuer jedes Projekt angelegt und definiert ein paar Funktionen (in unserem Falle werden das ‘compile’, ‘write’ und ‘clean’ sein) fuer dieses Projekt. Das folgende Makefile ist auf eine einzige .c-Datei ohne Header ausgelegt.
./makefile
#basiert auf: http://www.lima-city.de/thread/makefile-fuer-avr-gcc #CFILE.c enthaelt den Code. CFILE=helloworld #Type des AVR AVRTYPE=attiny2313 compile: avr-gcc -mmcu=$(AVRTYPE) -O2 $(CFILE).c -o $(CFILE).o write: avr-objcopy -j .text -j .data -O ihex $(CFILE).o $(CFILE).hex avrdude -V -p $(AVRTYPE) -c stk500v2 -P /dev/ttyUSB0 -U flash:w:$(CFILE).hex clean: rm $(CFILE).hex $(CFILE).map $(CFILE).o $(CFILE).out
Falls du einen anderen Dateinamen oder AVR benutzen willst, kannst du das in den Parametern einfach einstellen.
Wenn wir den AVR mit dem mySmartUSB light und gleichen mit dem Computer verbunden haben, gehen wir in der Ordner mit helloworld.c und makefile und fuehren folgendes aus:
- ‘make compile’ zum compilieren
- ‘make write’ zum Uebertragen des compilierten Programms auf den AVR
- ‘make clean’ falls du wieder einen sauberen Ordner haben willst.
Die Ausgabe sollte folgendermassen aussehen:
Wenn beim ersten Befehl was fehl schlaegt, ist entweder etwas in deinem Code falsch oder dir fehlt eine Library. Wenn beim zweiten etwas fehlt schlaegt hast du vermutlich bei der Verkabelung was falsch gemacht.
Ein Program mit Input und Output
Jetzt wo wir den AVR Programmieren koennen, koennen wir anfangen nuetzliche Programme zu schreiben, insbesondere die Pins zu benutzen. Der Arduino-Nutzer wird sich jetzt vermutlich ein wenig in die Steinzeit zurueckversetzt fuehlen, den ganz so einfach wie auf dem Arduino ist dies nicht. Vielleicht ist dir schon in der Verkabelungsskizze aufgefallen, dass die Pins nicht von 1-N durchnummeriert sind, sondern auch einen Buchstaben haben. Der Grund hierfuer ist, dass es fuer jede Pin-Gruppe ein 8Bit-Register(wird in C wie ein Variable benutzt) gibt, in dem fuer B und D jeweils ein Bit fuer einen Pin stehen. Bei der A-Gruppe handelt es sich um analoge Pins, die mehr als nur ein Bit brauchen, weswegen es auch nur 3 Stueck von diesen gibt.

Fuer einen einfacheren Einstieg schauen wir uns den Code erstmal fuer Arduino an. Ich nehme hierbei einfach mal an, dass du bereits Erfahrungen mit Arduino gesammelt hast, bevor du es mit AVR versuchst. Sollte dies nicht so sein, so kannst du den Arduino-Teil auch ueberspringen. Die Schaltung kommt mit einem Button und einer Led aus. Du brauchst zum testen eine externe Spannungsquelle mit 3,5-5V (am besten Labornetzteil, aber auch Batterien oder ein Arduino koennen herhalten). Die Led braucht unter Umstaenden einen Vorwiderstand abhaengig von Betriebsspannung und Led.
//Beispiel fuer Arduino mit Pin 2 fuer Led und Pin3 fuer Button //TODO: Ungetestet int led = 2; int button = 3; void setup(){ pinMode(led, OUTPUT); //Pin als Output pinMode(button, INPUT); //Pin als Input digitalWrite(button, HIGH); //Pullup } void loop(){ if(digitalRead(button)==HIGH){ //Falls Button gedrueckt digitalWrite(led, HIGH); //Led einschalten delay(1000); //1 Sekunde warten } else { digitalWrite(led, LOW); //Led ausschalten } }
Nun bauen wir den Code fuer AVR um. Statt direkt die Pins ansprechen zu koennen muessen wir nun ueber die Ports gehen, was den Code deutlich komplizierter macht. Wir benutze auch keine Funktionen mehr fuer die Pin Ein- und Ausgabe sondern lesen direkt aus dem Register.
#define F_CPU 1000000 #include <avr/io.h> #include <util/delay.h> //Pin B0 als Led #define LED_DDR DDRB #define LED_PORT PORTB #define LED_PINNR PB0 //Pin B1 als Button #define BUTTON_DDR DDRB #define BUTTON_PORT PORTB #define BUTTON_PINNR PB1 #define BUTTON_PIN PINB int main(){ //Wir haben nichtmehr setup() und loop() sondern nur noch main() //setup() LED_DDR |= (1<<LED_PINNR);//Led-Pin als Output BUTTON_DDR &= ~(1<<BUTTON_PINNR);//Button-Pin als Input BUTTON_PORT |= (1<<BUTTON_PINNR);//PullUp //loop() while(1){ if((BUTTON_PIN & (1<<BUTTON_PINNR))==0){ //Falls Button gedrueckt. Button gedrueckt -> Low(0) LED_PORT |= (1<<LED_PINNR); //Led an _delay_ms(1000); //1 Sekunde warten } else { LED_PORT &= ~(1<<LED_PINNR); //Led aus } } //Damit der Compiler nicht meckert: return 0; }
Jedes Register hat 8 Bit die mit Bit-Operatoren modifiziert werden koennen (dies mag jetzt im Vergleich zu Arduino barbarisch wirken, kann aber ein paar CPU-Takte sparen. Angesichts der CPU-Taktrate und den Optimierungsmoeglichkeiten des Compilers, darf aber auch ohne schlechtes Gewissen einfach eine selbstzuschreibende Hilfsfunktion benutzt werden). Eine Uebersicht ueber die Operatoren, die man bei ‘hoeheren’ Programmen doch eher selten braucht, gibt es auf Wikipedia.
| Pin7 | Pin6 | Pin5 |Pin4 | Pin3 | Pin2 | Pin1 | Pin0 |
DDR ist fuer Eingabe/Ausgabe zustaendig. Wenn an der entsprechenden Stelle eine 1 steht, so wird der Pin als Ausgabe verwendet, bei einer 0 als Eingabe. Bei Eingabe sollte man nicht vergessen, den PullUp-Wiederstand zu setzen, falls man dies nicht mit Hardware geloest hat.
- Beispiel: Pin 1(B) als Ausgang => DDRB = 00000010(bin)
- DDRB |= (1<<1); // Pin1 auf ‘1’ setzen, die anderen Pins unveraendert.
- DDRB &= ~(1<<1); //Pin1 auf ‘0’ setzen, die anderen Pins unveraendert.
PORT ist fuer die Ausgangsspannung zustaendig. Wenn wir also eine Led leuchten lassen oder den PullUp aktivieren wollen, muessen wir an die entsprechende Stelle eine 1 setzen.
- Beispiel: Pin 2(b) auf HIGH => PORTB = 00000100(bin)
- PORTB |= (1<<2); // Pin2 auf ‘1’ setzen, die anderen Pins unveraendert.
- PORTB &= ~(1<<2); //Pin2 auf ‘0’ setzen, die anderen Pins unveraendert.
PIN ist fuer die Eingangsspannung zustaendig. Hier koennen wir also auslesen ob am Pin eine Spannung anliegt bzw. der Button gedrueckt/losgelassen wurde.
- Beispiel: An Pin 3(b) liegt eine Spannung an => PINB = 00001000(bin)
- if( (PINB & (1<<3))==0 ) //Falls Pin3 =’0′ ist (Button gedrueckt)
[to be continued]
Es funktioniert nicht!? Ich hatte das Problem, dass die meisten Makefiles aus anderen Tutorials zwar ohne Fehlermeldungen und aehnlichem Output durchliefen, aber leider hat sich beim AVR nichts geregt. Sollte dies bei dir auch der Fall sein, so versuch einfach mal ein paar andere Makefiles. Leider habe ich noch kein Tutorial zum Anfertigen eines solchen Makefiles gefunden, sodass es ich nach Try&Error vorgegangen bin. Da ich aber nur sehr selten AVR programmiere und dann auch nicht professionel habe ich bisher keinen Grund gesehn, mich dort selbststaendig tiefer einzuarbeiten.
You must be logged in to post a comment.