Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
microcontrollertechnik:1_hello_blinking_world [2020/10/05 18:14]
tfischer [Es werde Licht! Oder auch nicht...]
microcontrollertechnik:1_hello_blinking_world [2024/03/21 02:58] (aktuell)
mexleadmin
Zeile 1: Zeile 1:
-====== 1Hello Blinking World ====== +wie fi====== 1 Hello Blinking World ====== 
-===== AVR Programmierung für Dummies ===== +===== 1.1 AVR Programmierung für Dummies =====
- +
-<WRAP group> <WRAP column 30%> +
- +
-Im Video werden im ersten Teil (bis 15min30sec) die Grundkonzepte für die AVR Programmierung erklärt.  +
- +
-<panel type="warning" title="Achtung!"> +
-Ab der 16. Minute geht es in die Assembler Programmierung. Dies ist nicht Teil des Kurses Mikroprozessortechnik. +
-</panel>+
  
 ==== Ziele ==== ==== Ziele ====
Zeile 18: Zeile 10:
   - wissen was Register sind.   - wissen was Register sind.
   - wissen für was das Spezialregister DDRx (x={A, B, C, D}) nützlich ist.   - wissen für was das Spezialregister DDRx (x={A, B, C, D}) nützlich ist.
- +<WRAP>
-</WRAP> <WRAP column 65%>+
  
 ==== Video ==== ==== Video ====
  
-{{youtube>Q1rR1IgBgcU?size=700x550}}+<WRAP
  
-</WRAP> </WRAP>+Im Video werden im ersten Teil (bis 15min30sec) die Grundkonzepte für die AVR Programmierung erklärt.  
 + 
 +<panel type="warning" title="Achtung!"> 
 +Ab der 16. Minute geht es in die Assembler Programmierung. Dies ist nicht Teil des Kurses Mikroprozessortechnik. 
 +</panel> 
 + 
 +{{youtube>Q1rR1IgBgcU}} 
 + 
 +</WRAP>  
 + 
 +===== 1.2 Es werde Licht! Oder auch nicht... =====
  
-===== Es werde Licht! Oder auch nicht... ===== +<WRAP>
-<WRAP group>  +
-<WRAP column 30%>+
  
 ==== Ziele ==== ==== Ziele ====
Zeile 35: Zeile 34:
 Nach dieser Lektion sollten Sie: Nach dieser Lektion sollten Sie:
  
-  - wissen, wie man im Atmel Studio ein Projekt anlegt.+  - wissen, wie man im Microchip Studio ein Projekt anlegt.
   - wissen, wie der Programmierumgebung die Taktfrequenz des Microcontrollers festgelegt wird.   - wissen, wie der Programmierumgebung die Taktfrequenz des Microcontrollers festgelegt wird.
   - die wichtigsten Bitmanipulationen (Bitmaske zum setzten und löschen eines einzelnen Bits, togglen) kennen und anwenden können.   - die wichtigsten Bitmanipulationen (Bitmaske zum setzten und löschen eines einzelnen Bits, togglen) kennen und anwenden können.
Zeile 42: Zeile 41:
   * kurzes Youtube Video: [[https://www.youtube.com/watch?v=j1neQHSU5n0|ein einzelnes Bit setzen oder löschen mit bitweisen Operationen]]   * kurzes Youtube Video: [[https://www.youtube.com/watch?v=j1neQHSU5n0|ein einzelnes Bit setzen oder löschen mit bitweisen Operationen]]
  
-</WRAP> <WRAP column 65%>+</WRAP> <WRAP>
 ==== Video ==== ==== Video ====
    
-{{youtube>2VPM2Q0JtxI?size=700x400}}+{{youtube>kHnxozE4X2I}}
  
-</WRAP> </WRAP>+</WRAP> 
  
 ==== Übung ==== ==== Übung ====
Zeile 53: Zeile 52:
  
 --> I. Vorarbeiten # --> I. Vorarbeiten #
-  - installieren Sie [[0._hilfsmittel#atmel_studio|SimulIDE und Atmel Studio]]+  - installieren Sie [[0_hilfsmittel#Microchip_studio|SimulIDE und Microchip Studio]]
   - falls es Probleme bei der Programmierung gibt: nutzen Sie die [[elektronik_labor:Tipps für die Fehlersuche]]   - falls es Probleme bei der Programmierung gibt: nutzen Sie die [[elektronik_labor:Tipps für die Fehlersuche]]
  
 <-- <--
---> II. Eingabe in Atmel Studio # +--> II. Eingabe in Microchip Studio # 
-  - öffnen Sie Atmel Studio+  - öffnen Sie Microchip Studio
   - Anlegen eines neuen Projekts   - Anlegen eines neuen Projekts
     - ''File'' >> ''New'' >> ''Project...''     - ''File'' >> ''New'' >> ''Project...''
     - Wählen Sie "GCC C Executable Project", da ein ausführbares C Code Beispiel erstellt werden soll     - Wählen Sie "GCC C Executable Project", da ein ausführbares C Code Beispiel erstellt werden soll
     - Geben Sie bei Name ''Einfuehrung_v01'' und drücken Sie auf ''Ok''     - Geben Sie bei Name ''Einfuehrung_v01'' und drücken Sie auf ''Ok''
-    - Der Cursor sollte nun im Eingabefeld des Suchfenster für die Microcontroller stehen. Geben Sie dort ''328'' ein +    - Der Cursor sollte nun im Eingabefeld des Suchfenster für die Microcontroller stehen. Geben Sie dort ''88'' ein 
-    - Wählen Sie den ATmega328 aus den möglichen Chips aus und drücken Sie auf ''Ok''+    - Wählen Sie den mega88 aus den möglichen Chips aus und drücken Sie auf ''Ok''
   - Eingabe und Kompilieren des Code   - Eingabe und Kompilieren des Code
     - Ersetzen Sie den vorhandenen Code, durch den rechts stehenden Code      - Ersetzen Sie den vorhandenen Code, durch den rechts stehenden Code 
Zeile 70: Zeile 69:
     - Im unteren Teil des Fensters sollte nun die Ausgabe des Kompilers sichtbar werden. Diese sollte ''========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped =========='' lauten     - Im unteren Teil des Fensters sollte nun die Ausgabe des Kompilers sichtbar werden. Diese sollte ''========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped =========='' lauten
   - Auswählen der hex-Datei   - Auswählen der hex-Datei
-    - im Atmel Studio finden Sie rechts im Fenster den "Solution Explorer"+    - im Microchip Studio finden Sie rechts im Fenster den "Solution Explorer"
     - gehen Sie dort im Solution Explorer zu ''Solution'' >> ''Einfuehrung_v01'' >> ''Output Files''      - gehen Sie dort im Solution Explorer zu ''Solution'' >> ''Einfuehrung_v01'' >> ''Output Files'' 
     - klicken Sie mit rechter Maustaste auf ''Einfuehrung_v01.hex'' und wählen Sie Pfad und Name aus     - klicken Sie mit rechter Maustaste auf ''Einfuehrung_v01.hex'' und wählen Sie Pfad und Name aus
Zeile 77: Zeile 76:
 --> III. Ausführung in Simulide # --> III. Ausführung in Simulide #
   - Öffnen Sie SimulIDE (unter ...\bin\simulide.exe)   - Öffnen Sie SimulIDE (unter ...\bin\simulide.exe)
-    - links in SimulIDE sollten Sie den Komponenten Browser finden. Wählen Sie dort ''Micro''>>''AVR''>>''atmega''>>''atmega328'' +    - links in SimulIDE sollten Sie den Komponenten Browser finden. Wählen Sie dort ''Micro''>>''AVR''>>''atmega''>>''mega88'' 
-    - Ziehen Sie den Eintrag ''atmega328'' per Drag and Drop in den Arbeitsbereich (rechter, beiger Teil des Fensters) +    - Ziehen Sie den Eintrag ''mega88'' per Drag and Drop in den Arbeitsbereich (rechter, beiger Teil des Fensters) 
-    - Es sollte nun ein Chip names ''atmega328-1'' dargestellt sein+    - Es sollte nun ein Chip names ''mega88-1'' dargestellt sein
   - Erstellen der Ausgangsschaltung   - Erstellen der Ausgangsschaltung
-    - Im Programm wurde im auf PortD das 6bit angesprochen. Entsprechend soll auch hier am Port der Ausgang genutzt werden. Am Chip ist dieser mit D6 gekennzeichnet+    - Im Programm wurde im auf PortB das 2bit angesprochen. Entsprechend soll auch hier am Port der Ausgang genutzt werden. Am Chip ist dieser mit B2 gekennzeichnet
     - Fügen Sie eine LED (im Komponenten Browser über Output LED) und ein Massepotential ein (Sources Ground)     - Fügen Sie eine LED (im Komponenten Browser über Output LED) und ein Massepotential ein (Sources Ground)
     - Die Komponenten können mit dem Kontextmenu (Rechtsklick) gedreht und gespiegelt werden. Außerdem ist mit der Auswahl von ''Properties'' im Kontextmenu die Änderung von      - Die Komponenten können mit dem Kontextmenu (Rechtsklick) gedreht und gespiegelt werden. Außerdem ist mit der Auswahl von ''Properties'' im Kontextmenu die Änderung von 
-    - Verbinden Sie die LED mit Masse und mit Port D6. Achten Sie auf die richtige Richtung der LED. Die Verbindungen lassen sich dadurch erstellen, dass auf ein Komponenten-Pin geklickt wird und die Linie zu einem nächsten Komponenten-Pin gezogen wird.+    - Verbinden Sie die LED mit Masse und mit Port B2. Achten Sie auf die richtige Richtung der LED. Die Verbindungen lassen sich dadurch erstellen, dass auf ein Komponenten-Pin geklickt wird und die Linie zu einem nächsten Komponenten-Pin gezogen wird.
   - Flashen der Software    - Flashen der Software 
     - Klicken Sie rechts auf den Microcontroller und wählen Sie ''Load firmware''     - Klicken Sie rechts auf den Microcontroller und wählen Sie ''Load firmware''
Zeile 102: Zeile 101:
   - Welche [[Vorgaben für die Softwareentwicklung]] wurden verletzt, trotzdem das Programm lauffähig ist? (Interrupts werden erst in späteren Übungen erklärt)   - Welche [[Vorgaben für die Softwareentwicklung]] wurden verletzt, trotzdem das Programm lauffähig ist? (Interrupts werden erst in späteren Übungen erklärt)
   - Wie könnte ein Ampel-Licht-Abfolge oder Lauflicht aus 4 Dioden erstellt und programmiert werden? Welche Optimierungen könnten im Code vorgenommen werden? Welche Komponente in SimulIDE kann genutzt werden? Wie kann die Farbe der LEDs geändert werden?   - Wie könnte ein Ampel-Licht-Abfolge oder Lauflicht aus 4 Dioden erstellt und programmiert werden? Welche Optimierungen könnten im Code vorgenommen werden? Welche Komponente in SimulIDE kann genutzt werden? Wie kann die Farbe der LEDs geändert werden?
-  - Lesen Sie auf Mikrocontroller.net im Kapitel [[https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29|Warteschleifen]] die "erste Seite", also bis: \\ <WRAP right 80%>> Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.</WRAP> +  - Lesen Sie auf Mikrocontroller.net im Kapitel [[https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29|Warteschleifen]] die "erste Seite", also bis: \\ ** Abhängig von der Version der Bibliothek verhalten sich die Bibliotheksfunktionen etwas unterschiedlich.** 
-  - Öffnen Sie in Simulide die RAM Table (je nach Version: über Reiter ''RAM Table'' links bei der Komponentenauswahl oder Rechtsklick auf Microcontroller >> ''RAM Table''und analysieren Sie das Verhalten der Register ''PORTD'' und der Mehrzweckregister R0...R31. Welches Mehrzweckregister scheint ein Abwärtszähler zu sein?+  - Registeranalyse 
 +    - Öffnen Sie in Simulide die RAM Table (Rechtsklick auf Microcontroller ''Open Mcu Monitor.'' >> ''RAM Table'' 
 +    - Analysieren Sie das Verhalten der Register ''PORTB'' und der Mehrzweckregister ''R0...R31'' (eintragen von z.B. ''PORTB'' unter Name in der ersten Zeile und drücken von <Return>, oder anklicken bzw. markieren des Registers mit dem Cursor und <Return>)\\ Welches Mehrzweckregister scheint ein Auf- oder Abwärtszähler zu sein? (siehe dazu auch "Was ist eigentlich ein Register?" unter [[1_hello_blinking_world#Weiterführende Fragen und Infos]]) 
 +    - Zählt der Zähler aufwärts oder abwärts? Ändern Sie dazu die Simulationsgeschwindigkeit bei den Einstellungen. Die Einstellungen sind über das Zahnrad in der Menüleiste oben links zu erreichen. Im Reiter ''Simulation'' kann die Geschwindigkeit geändert werden.
  
 <-- <--
Zeile 110: Zeile 112:
  
 </WRAP><WRAP column 35%> </WRAP><WRAP column 35%>
-<code C [enable_line_numbers=1, start_line_numbers_at=1] main.c>+<sxh c; first-line: 1>
 #define F_CPU 8000000UL #define F_CPU 8000000UL
  
Zeile 118: Zeile 120:
 int main(void) int main(void)
 { {
- DDRD=0b01000000+ DDRB=0b00000100
-    while (1)  + while (1) 
-    +
- PORTD |= (1<<6);+ PORTB |= (1<<2);
  _delay_ms(1000);  _delay_ms(1000);
- PORTD &= ~(1<<6); + PORTB &= ~(1<<2); 
- _delay_ms(1000);  + _delay_ms(1000); 
-    +
-} +}</sxh>
-</code>+
  
 <WRAP right><panel type="default">  <WRAP right><panel type="default"> 
Zeile 138: Zeile 139:
 ~~PAGEBREAK~~ ~~CLEARFIX~~ ~~PAGEBREAK~~ ~~CLEARFIX~~
  
-===== Weiterführende Fragen und Infos ====+===== 1.3 Weiterführende Fragen und Infos ====
  
 --> Was hat es mit "Release" und "Debug" in der Standard-Toolbar auf sich?# --> Was hat es mit "Release" und "Debug" in der Standard-Toolbar auf sich?#
Zeile 149: Zeile 150:
 <-- <--
  
---> Was ist DDRDPORTD?#+--> Was ist DDRBPORTB?#
  
 Die Anschlüsse (Pins) des Chips sind in 8er Gruppen sortiert, den sogenannten Ports. Die Anschlüsse (Pins) des Chips sind in 8er Gruppen sortiert, den sogenannten Ports.
-Für jeden Port sind jeweils drei Register-Bytes vorhanden: DDRx, PORTx und PINx. Diese Speicherstellen ermöglichen die Konfiguration des Ports. Die Bits in den Registern stehen für die einzelnen Anschlüsse: So ist zum Beispiel das 6. Bit in DDRDPORTD und PIND für den Anschluss D6 zuständig.+Für jeden Port sind jeweils drei Register-Bytes vorhanden: DDRx, PORTx und PINx. Diese Speicherstellen ermöglichen die Konfiguration des Ports. Die Bits in den Registern stehen für die einzelnen Anschlüsse: So ist zum Beispiel das 2. Bit in DDRBPORTB und PINB für den Anschluss B2 zuständig.
  
 Das Bit im **DDRx** (__D__ata __D__irection __R__egister) wählt die Richtung des Pins aus. Wenn dort logisch Eins geschrieben wird, wird der entsprechende Pin als Ausgangspin konfiguriert. Wenn dort logisch Null geschrieben wird, wird der entsprechende Pin als Eingangspin konfiguriert.  Das Bit im **DDRx** (__D__ata __D__irection __R__egister) wählt die Richtung des Pins aus. Wenn dort logisch Eins geschrieben wird, wird der entsprechende Pin als Ausgangspin konfiguriert. Wenn dort logisch Null geschrieben wird, wird der entsprechende Pin als Eingangspin konfiguriert. 
Zeile 159: Zeile 160:
 Wenn das gewünschte Bit in PORTx logisch Eins geschrieben wird und der Pin als Ausgangspin konfiguriert ist, wird der Portpin auf high (eins) gesetzt. Wenn das gewünschte Bit in PORTx logisch Null geschrieben wird und der Pin als Ausgangspin konfiguriert ist, wird der Portpin auf Low (Null) getrieben.  Wenn das gewünschte Bit in PORTx logisch Eins geschrieben wird und der Pin als Ausgangspin konfiguriert ist, wird der Portpin auf high (eins) gesetzt. Wenn das gewünschte Bit in PORTx logisch Null geschrieben wird und der Pin als Ausgangspin konfiguriert ist, wird der Portpin auf Low (Null) getrieben. 
  
-Auch wenn ein Pink als Eingangspin konfiguriert wurde, hat PORTx eine Funktion. Wenn in diesem Fall das gewünschte Bit in PORTx logisch eins geschrieben wird, wird der Pull-up-Widerstand aktiviert. Ein Pull-up-Widerstand ist ein höherohmiger Widerstand (im Bereich $20 k\Omega$ ... $100 k\Omega$), der bei nicht weiter verbundenem Pin den ausgegebenen Wert auf logisch Eins zieht. Um den Pull-up-Widerstand auszuschalten, muss das gewünschte Bit in PORTx logisch Null geschrieben werden oder der Pin muss als Ausgangspin konfiguriert werden. +Auch wenn ein Pink als Eingangspin konfiguriert wurde, hat PORTx eine Funktion. Wenn in diesem Fall das gewünschte Bit in PORTx logisch eins geschrieben wird, wird der Pull-up-Widerstand aktiviert. Ein Pull-up-Widerstand ist ein höherohmiger Widerstand (im Bereich $20~\rm k\Omega$ ... $100~\rm k\Omega$), der bei nicht weiter verbundenem Pin den ausgegebenen Wert auf logisch Eins zieht. Um den Pull-up-Widerstand auszuschalten, muss das gewünschte Bit in PORTx logisch Null geschrieben werden oder der Pin muss als Ausgangspin konfiguriert werden. 
  
 Das Einlesen der Signale wird in einem späteren Kapitel erklärt. Das Einlesen der Signale wird in einem späteren Kapitel erklärt.
Zeile 167: Zeile 168:
 Vielleicht kennen Sie noch die Begriffe Register oder Schieberegister aus den Grundlagen der Digitaltechnik (siehe [[grundlagen_der_digitaltechnik:speicherelemente#anwendungen_mit_flipflops|Anwendungen mit Flipflops]]). {{wpde>Register_(Prozessor)|Register}} Vielleicht kennen Sie noch die Begriffe Register oder Schieberegister aus den Grundlagen der Digitaltechnik (siehe [[grundlagen_der_digitaltechnik:speicherelemente#anwendungen_mit_flipflops|Anwendungen mit Flipflops]]). {{wpde>Register_(Prozessor)|Register}}
 sind im allgemeinen Speicherzellen mit besonderen Funktionen, auf denen der Rechenkern besonders schnell zugreifen kann. Im Folgenden sind eine Auswahl an Registern kurz beschrieben: sind im allgemeinen Speicherzellen mit besonderen Funktionen, auf denen der Rechenkern besonders schnell zugreifen kann. Im Folgenden sind eine Auswahl an Registern kurz beschrieben:
-  * AVR-Microcontroller haben 32 **General Purpose Register (R0...R31)**. Diese sind sozusagen der Schreibtisch des Prozessors, d.h. in diesen befinden sich lokale Variablen, Zwischenergebnisse oder Zieladressen zum Ein- und Auslesen. Diese Register werden auch im Ram in den ersten 32 Speicherzellen abgebildet und können in Simulide über die "RAM Table" ausgegeben werden (z.B. Eingabe von R24).+  * AVR-Microcontroller haben 32 **General Purpose Register (R0...R31)**, also Mehrzweckregister. Diese sind sozusagen der Schreibtisch des Prozessors, d.h. in diesen befinden sich lokale Variablen, Zwischenergebnisse oder Zieladressen zum Ein- und Auslesen. Diese Register werden auch im Ram in den ersten 32 Speicherzellen abgebildet und können in Simulide über die "RAM Table" ausgegeben werden (z.B. Eingabe von 24 für R24).
   * Der **Programm Counter (PC)** ist ein internes Register, welches auf den nächsten Befehl für den Rechenkern zeigt. Intern bedeutet, es kann nicht direkt über den Code ausgelesen oder geändert werden. Indirekt geht dies schon, da der PC nach jedem abgearbeiteten Befehl auf den darauffolgenden zeigt. Bei jedem Sprung in eine Funktion oder Wiederholung einer Schleife weist der PC i.d.R. auf eine Stelle, die nicht direkt aufsteigend erreichbar wäre.   * Der **Programm Counter (PC)** ist ein internes Register, welches auf den nächsten Befehl für den Rechenkern zeigt. Intern bedeutet, es kann nicht direkt über den Code ausgelesen oder geändert werden. Indirekt geht dies schon, da der PC nach jedem abgearbeiteten Befehl auf den darauffolgenden zeigt. Bei jedem Sprung in eine Funktion oder Wiederholung einer Schleife weist der PC i.d.R. auf eine Stelle, die nicht direkt aufsteigend erreichbar wäre.
   * Das **Status Register (SREG)** zeigt verschiedene Stati des letzten ausgeführten Befehls an. War z.B. das Ergebnis der letzten Berechnung Null, so wird das Z-Bit im SREG gesetzt. Die angezeigten Werte im SREG helfen dem Prozessor schnell auf Ergebnisse zu reagieren. Weitere Details zum SREG finden sich im Kapitel [[https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/ATmegaX8.chm/7.htm|7. Der Kern der AVR-CPU]] des deutschen AVR Datenblatts.   * Das **Status Register (SREG)** zeigt verschiedene Stati des letzten ausgeführten Befehls an. War z.B. das Ergebnis der letzten Berechnung Null, so wird das Z-Bit im SREG gesetzt. Die angezeigten Werte im SREG helfen dem Prozessor schnell auf Ergebnisse zu reagieren. Weitere Details zum SREG finden sich im Kapitel [[https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/ATmegaX8.chm/7.htm|7. Der Kern der AVR-CPU]] des deutschen AVR Datenblatts.
Zeile 176: Zeile 177:
 -->  Wie findet man die Namen der Anschlüsse? # -->  Wie findet man die Namen der Anschlüsse? #
  
-Die Namen sind im Datenblatt des verwendeten Microcontrollers zu finden. Das lässt sich in diesem Fall in einer Suchmaschine über ''atmega 328 "datasheet" site:microchip.com filetype:pdf'' finden, da es sich beim Datasheet um ein PDF des Herstellers Microchip handelt. Zum Lesen der Datenblätter empfiehlt sich ein Download und die Betrachtung über einen PDF-Viewer, welcher ein Inhaltsverzeichnis als Seitenleiste ermöglicht (z.B. Acrobat Reader)Ansonsten ist das Inhaltsverzeichnis häufig auch auf den hinteren Seiten des Datenblatts zu finden.+Die Namen sind im Datenblatt des verwendeten Microcontrollers zu finden. Datenblätter lassen sich allgemein mittels auf zwei Wegen finden: 
 +    - Mittels einer Suchmaschine über ''[IC Name] "datasheet" site:[Herstellername].com filetype:pdf'', da es sich beim Datasheet um ein PDF handelt.  
 +    - Direkt über die Hersteller-Seite 
 +Leider gibt es gerade bei dem ATMEGA88 auch ein **veraltetes** Datenblatt, welches just das ist, wass sich z.B. über Google leichter finden lässt\\ 
 +in diesem Fall muss also über die Herstellerseite gesucht werden, bzw. bei der Suchmaschine ''ATMEGA88'' eingeben und anschließend auf die [[https://www.microchip.com/en-us/product/atmega88|Herstellerseite]] klicken
  
-Die gesuchte Pinbelegung ist für den ATmega328P konkret auf Seite 3 unter "1Pin Configurations":+Zum Lesen der Datenblätter empfiehlt sich ein Download und die Betrachtung über einen PDF-Viewer, welcher ein Inhaltsverzeichnis als Seitenleiste ermöglicht (z.B. Acrobat Reader). Ansonsten ist das Inhaltsverzeichnis häufig auch auf den hinteren Seiten des Datenblatts zu finden.
  
-<WRAP right>{{url>https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf#page=3+Die gesuchte Pinbelegung ist für den ATmega88 konkret auf Seite 3 unter "1. Pin Configurations": 
 + 
 +<WRAP right>{{url>https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega48_88_168_megaAVR-Data-Sheet-40002074.pdf#page=9
  600,400 noborder}}  600,400 noborder}}
 </WRAP> </WRAP>
  
 Falls Sie mit dem englischen Datenblatt Probleme haben, kann ich die [[https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/ATmegaX8.chm/|deutsche Übersetzung des ATmega88]] empfehlen, welcher sich im Wesentlichen ähnlich verhält. Beachten Sie aber, dass viele weitere Datenblätter nur in englisch vorhanden sind. Ein Umgang mit der englischen Dokumentation sollte also erlernt werden. Falls Sie mit dem englischen Datenblatt Probleme haben, kann ich die [[https://www-user.tu-chemnitz.de/~heha/viewchm.php/hs/ATmegaX8.chm/|deutsche Übersetzung des ATmega88]] empfehlen, welcher sich im Wesentlichen ähnlich verhält. Beachten Sie aber, dass viele weitere Datenblätter nur in englisch vorhanden sind. Ein Umgang mit der englischen Dokumentation sollte also erlernt werden.
 +
 +~~PAGEBREAK~~ ~~CLEARFIX~~
  
 <--  <-- 
Zeile 201: Zeile 210:
 Richtig bemerkt. Das liegt daran, dass der verwendete Chip in Simulide auf 16 MHz läuft. Dies ist in der Simulation oben unter dem Start-Button zu sehen.  Richtig bemerkt. Das liegt daran, dass der verwendete Chip in Simulide auf 16 MHz läuft. Dies ist in der Simulation oben unter dem Start-Button zu sehen. 
  
-In Realität wird die Taktfrequenz durch die Randbedingungen wie Performance, Stromverbrauch, Spannungsversorgung  oder Platinengröße vorgegeben. Die reale Hardware des ATmega 328 beinhaltet einen internen Taktgeber mit 8 MHz, welche durch einen Teiler auf 1 MHz reduziert sind. Über einen externen Quarz an den Pins PB6 und PB7 kann bei der realen Hardware (mit weiteren Einstellung der Fuses) ein externen Takt eingespeist werden. +In Realität wird die Taktfrequenz durch die Randbedingungen wie Performance, Stromverbrauch, Spannungsversorgung  oder Platinengröße vorgegeben. Die reale Hardware des ATmega 88 beinhaltet einen internen Taktgeber mit 8 MHz, welche durch einen Teiler auf 1 MHz reduziert sind. Über einen externen Quarz an den Pins PB6 und PB7 kann bei der realen Hardware (mit weiteren Einstellung der Fuses) ein externen Takt eingespeist werden. 
 Häufig werden hierbei - neben ganzzahligen MHz - auch Vielfache von 256 genutzt, wie z.B. 12,288 MHz. Dies vereinfacht das exakte Abzählen von (Milli)Sekunden, da intern hierzu 8-Bit-Zähler genutzt werden können. Häufig werden hierbei - neben ganzzahligen MHz - auch Vielfache von 256 genutzt, wie z.B. 12,288 MHz. Dies vereinfacht das exakte Abzählen von (Milli)Sekunden, da intern hierzu 8-Bit-Zähler genutzt werden können.
 Der Takt kann bei der realen Hardware in der Regel nicht zur Laufzeit beliebig geändert werden, sondern liegt fest vor. Der Takt kann bei der realen Hardware in der Regel nicht zur Laufzeit beliebig geändert werden, sondern liegt fest vor.
Zeile 219: Zeile 228:
 Im Prinzip würde folgender Code das auch ermöglichen: Im Prinzip würde folgender Code das auch ermöglichen:
  
-<code C [enable_line_numbers=1, start_line_numbers_at=1] main.c>+<sxh c; first-line: 1>
 #define F_CPU 8000000UL #define F_CPU 8000000UL
  
Zeile 227: Zeile 236:
 int main(void) int main(void)
 { {
- // Die Zahl in folgender Zeile gibt die Bitposition in der nächstne Zeile an:+ // Die Zahl in folgender Zeile gibt die Bitposition in der nächsten Zeile an:
  //     76543210  //     76543210
- DDRD=0b01000000;  // D6 soll als Ausgang genutzt werden+ DDRB=0b00000100;  // B2 soll als Ausgang genutzt werden
     while (1)      while (1) 
     {     {
- PORTD 0b01000000; // Das Bit für D6 wird gesetzt; am Ausgang liegt VCC an+ PORTB 0b00000100; // Das Bit für B2 wird gesetzt; am Ausgang liegt VCC an
  _delay_ms(1000);  _delay_ms(1000);
- PORTD = 0b00000000; // Das Bit für D6 wird gelöscht; am Ausgang liegt 0V an+ PORTB = 0b00000000; // Das Bit für B2 wird gelöscht; am Ausgang liegt 0V an
  _delay_ms(1000);   _delay_ms(1000);
     }     }
 } }
-</code>+</sxh>
  
 Hierbei gibt es aber mehrere Probleme: Hierbei gibt es aber mehrere Probleme:
Zeile 244: Zeile 253:
   - Es wird hier nicht nur das gewünschte Bit gesetzt, sondern auch die anderen Bits manipuliert. Das ist in diesem konkreten Fall zunächst kein Problem. __ABER__: Jede Software sollte so entwickelt werden, dass Sie den höchsten Grad an Wartbarkeit und Erweiterbarkeit aufzeigt. Es ist also wichtig auf eine gewisse Codehygiene ("{{wpde>Clean Code}}") zu achten. Es ergeben sich sonst Probleme, wenn eine komplexere Umsetzung nach Jahren weiterentwickelt werden soll.   - Es wird hier nicht nur das gewünschte Bit gesetzt, sondern auch die anderen Bits manipuliert. Das ist in diesem konkreten Fall zunächst kein Problem. __ABER__: Jede Software sollte so entwickelt werden, dass Sie den höchsten Grad an Wartbarkeit und Erweiterbarkeit aufzeigt. Es ist also wichtig auf eine gewisse Codehygiene ("{{wpde>Clean Code}}") zu achten. Es ergeben sich sonst Probleme, wenn eine komplexere Umsetzung nach Jahren weiterentwickelt werden soll.
  
-Achten Sie also darauf, dass die Umsetzung im Code genau das tut was gewünscht ist. In diesem Fall soll nur das Bit D6 manipuliert und alle anderen unverändert belassen werden.+Achten Sie also darauf, dass die Umsetzung im Code genau das tut was gewünscht ist. In diesem Fall soll nur das Bit B2 manipuliert und alle anderen unverändert belassen werden.
  
 Hiermit ergibt sich die Frage, wie genau nur ein Bit in einem Register geändert werden kann. Dies ist über die Funktionen der logischen Bitmanipulation möglich (UND, ODER, etc.). Hierzu wird das gewünschte Register mit einer Maskierung verknüpft.  Hiermit ergibt sich die Frage, wie genau nur ein Bit in einem Register geändert werden kann. Dies ist über die Funktionen der logischen Bitmanipulation möglich (UND, ODER, etc.). Hierzu wird das gewünschte Register mit einer Maskierung verknüpft. 
Zeile 255: Zeile 264:
 '' ''
 Die fett markierten Bits mit logisch Eins in der Maske MSK gewährleisten also, dass die Bits in der Disjunktion ''R01|MSK'' gesetzt sind. Die fett markierten Bits mit logisch Eins in der Maske MSK gewährleisten also, dass die Bits in der Disjunktion ''R01|MSK'' gesetzt sind.
-In C wäre hierfür ''R01 = R01|MSK;'' zu schreiben - R01 ist im LED-Code PORTD; in MSK sollte nur das Bit gesetzt sein. Die Programmiersprache C bietet die Möglichkeit mathematische Operatoren auch noch etwas umzuschreiben: ''R01 |= MSK;'' +In C wäre hierfür ''R01 = R01|MSK;'' zu schreiben - R01 ist im LED-Code PORTB; in MSK sollte nur das Bit gesetzt sein. Die Programmiersprache C bietet die Möglichkeit mathematische Operatoren auch noch etwas umzuschreiben: ''R01 |= MSK;'' 
-Auch für das Setzen eines einzelnen Bits in MSK kann eine logische Bitmanipulation genutzt werden: das Verschieben aller Bits nach links. Der Code ''1<<6'' erzeugt ein Byte mit dem Inhalt ''0b01000000''. Für den Neuling mag ''1<<6'' etwas schwerer zu lesen sein - für den fortgeschrittenen Entwickler ist dies leichter zu lesen. Daneben bietet die inkludierte Bibliothek ''%%<avr/io.h>%%'' die Möglichkeit auf weitere defines zuzugreifen, z.B. PORTD6 welches durch ersetzt wird. Damit ließe sich der Code für des Setzen eines Bits schreiben zu:+Auch für das Setzen eines einzelnen Bits in MSK kann eine logische Bitmanipulation genutzt werden: das Verschieben aller Bits nach links. Der Code ''1<<2'' erzeugt ein Byte mit dem Inhalt ''0b00000100''. Für den Neuling mag ''1<<2'' etwas schwerer zu lesen sein - für den fortgeschrittenen Entwickler ist dies leichter zu lesen. Daneben bietet die inkludierte Bibliothek ''%%<avr/io.h>%%'' die Möglichkeit auf weitere defines zuzugreifen, z.B. PB2 welches durch ersetzt wird. Damit ließe sich der Code für des Setzen eines Bits schreiben zu:
  
-<code C [enable_line_numbers=1, start_line_numbers_at=13] main.c+<sxh c; first-line: 13> 
- PORTD |= 1<<PORTD6; // Das Bit für D6 wird gesetzt; am Ausgang liegt VCC an + PORTB |= 1<<PB2; // Das Bit für B2 wird gesetzt; am Ausgang liegt VCC an 
-</code>+</sxh>
  
 Für das Löschen kann ein ähnliches Konzept genutzt werden. Statt eine "VerODERung mit 1" (im Code ''%%R01|0b01000000%%'') zum Setzen, muss hier eine "VerUNDung mit 0" (im Code ''%%R01|0b10111111%%'') genutzt werden: Für das Löschen kann ein ähnliches Konzept genutzt werden. Statt eine "VerODERung mit 1" (im Code ''%%R01|0b01000000%%'') zum Setzen, muss hier eine "VerUNDung mit 0" (im Code ''%%R01|0b10111111%%'') genutzt werden:
Zeile 275: Zeile 284:
 Der Code ergibt sich dann zu: Der Code ergibt sich dann zu:
  
-<code C [start_line_numbers_at=15] main.c> +<sxh c; first-line: 15
- PORTD &= ~(1<<PORTD6); // Das Bit für D6 wird gelöscht; am Ausgang liegt 0V an + PORTB &= ~(1<<PB2); // Das Bit für B2 wird gelöscht; am Ausgang liegt 0V an 
-</code>+</sxh>
  
 <-- <--
Zeile 285: Zeile 294:
 Alle Befehle mit ''%%#%%'' sind nur Compileranweisungen. Der Compiler setzt den C-Code in maschinenlesbaren Code um. Die Compileranweisungen werden aber nicht in maschinenlesbaren Code umgesetzt, sondern weisen den Compiler an verschiedene Dinge zu tun. ''%%#define%%'' speziell weist den Compiler an eine dargestellte Zeichenfolge durch eine andere zu ersetzen. Beispiel: Alle Befehle mit ''%%#%%'' sind nur Compileranweisungen. Der Compiler setzt den C-Code in maschinenlesbaren Code um. Die Compileranweisungen werden aber nicht in maschinenlesbaren Code umgesetzt, sondern weisen den Compiler an verschiedene Dinge zu tun. ''%%#define%%'' speziell weist den Compiler an eine dargestellte Zeichenfolge durch eine andere zu ersetzen. Beispiel:
  
-<code C [start_line_numbers_at=1] main.c> +<sxh c; first-line: 1>
 #define DUMMY 5 #define DUMMY 5
  
Zeile 297: Zeile 305:
  ...  ...
 } }
-</code>+</sxh>
  
 In diesem Beispiel wird vor der Übersetzung des Codes die Zeichenfolge ''DUMMY'' durch ''5'' ersetzt und dann erst kompiliert. Beim Ersetzen wird keine Typisierung (Datentypen) beachtet. Weiterhin wird der definierte Code nicht vorher berechnet, was zu Problemen bei der Priorisierung führen kann:  In diesem Beispiel wird vor der Übersetzung des Codes die Zeichenfolge ''DUMMY'' durch ''5'' ersetzt und dann erst kompiliert. Beim Ersetzen wird keine Typisierung (Datentypen) beachtet. Weiterhin wird der definierte Code nicht vorher berechnet, was zu Problemen bei der Priorisierung führen kann: 
  
-<code C [start_line_numbers_at=1] main.c> +<sxh c; first-line: 1>
 #define DUMMY1 500 #define DUMMY1 500
 #define DUMMY2 5 + 3 #define DUMMY2 5 + 3
Zeile 312: Zeile 319:
  ...  ...
  uint8_t a = DUMMY1; // DUMMY1 ist größer als 500  uint8_t a = DUMMY1; // DUMMY1 ist größer als 500
- uint8_t b = DUMMY2 * 2; // Ergebnis ist 5 + 3 *2 = 16 , nicht 10+ uint8_t b = DUMMY2 * 2; // Es wird 5 + 3 *2 = 11 ausgegebenund nicht (5 + 3)*2 = 16
  ...  ...
 } }
-</code>+</sxh>
  
-Prinzipiell kann also ''%%#define%%'' für Zahlenwerte genutzt werden, sofern die obengenannten Grenzen beachtet werden. Diese Probleme lassen sich für Zahlenwerte über die Verwendung von static const umgehen (z.B. ''static const int var = 5;''). In Microcontrollern werden von der Programmierumgebung häufig auch defines vorgegeben (z.B. ''PORTD'', ''PORTD6''). Der große Vorteil von Compiler-Definitionen besteht darin, dass so die verschiedene eher schwer lesbare, aber häufig verwendete Codeschnipsel anschaulicher umschrieben werden können. Folgende defines werden beispielsweise häufig genutzt:+Prinzipiell kann also ''%%#define%%'' für Zahlenwerte genutzt werden, sofern die obengenannten Grenzen beachtet werden. Diese Probleme lassen sich für Zahlenwerte über die Verwendung von static const umgehen (z.B. ''static const int var = 5;''). In Microcontrollern werden von der Programmierumgebung häufig auch defines vorgegeben (z.B. ''PORTB'', ''PB2''). Der große Vorteil von Compiler-Definitionen besteht darin, dass so die verschiedene eher schwer lesbare, aber häufig verwendete Codeschnipsel anschaulicher umschrieben werden können. Folgende defines werden beispielsweise häufig genutzt:
  
-<code C [start_line_numbers_at=1] main.c> +<sxh c; first-line: 1
-#define SET_BIT(PORT, BIT) ((PORT) |=  (1 <<(BIT))) // Port-Bit Setzen +#define SET_BIT(BYTE, BIT) ((BYTE) |=  (1 << (BIT))) // Bit Zustand in Byte setzen 
-#define CLR_BIT(PORT, BIT) ((PORT) &= ~(1 <<(BIT))) // Port-Bit Loeschen +#define CLR_BIT(BYTE, BIT) ((BYTE) &= ~(1 << (BIT))) // Bit Zustand in Byte loeschen 
-#define TGL_BIT(PORT, BIT) ((PORT) ^=  (1 <<(BIT))) // Port-Bit Toggeln +#define TGL_BIT(BYTE, BIT)  ((BYTE) ^=  (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle) 
-</code>+</sxh>
  
 <-- <--
Zeile 329: Zeile 336:
 --> Wie sähe der Code aus, wenn man sich an die Vorgaben für sauberen Code hielte?# --> Wie sähe der Code aus, wenn man sich an die Vorgaben für sauberen Code hielte?#
  
-Hierbei ist zu beachten, dass auch die Verwendung von delays vermieden werden soll. Diese sind ein "aktives Nichtstun" des Prozessors. Das heißt er kann in der Zeit nicht andere Aufgaben erledigen. Ein Multitasking ist damit nicht möglich. Da dies einer nachträglichen Erweiterung des Codes im Weg steht, sollten **generell keine delays** verwendet werden. Eine Ausnahme davon bildet Treiber-Code in welchem Microsekunden genaue Asgaben an einem Pin gemacht werden müssen.+Hierbei ist zu beachten, dass auch die Verwendung von delays vermieden werden soll. Diese sind ein "aktives Nichtstun" des Prozessors. Das heißt er kann in der Zeit nicht andere Aufgaben erledigen. Ein Multitasking ist damit nicht möglich. Da dies einer nachträglichen Erweiterung des Codes im Weg steht, sollten **generell keine delays** verwendet werden. Eine Ausnahme davon bildet Treiber-Code in welchem Pegeländerungen an einem Pin Microsekunden-genau umgesetzt werden müssen.
  
-<code C [enable_line_numbers=1, start_line_numbers_at=1] main.c>+<sxh c; first-line: 1>
 #define F_CPU 8000000UL #define F_CPU 8000000UL
-#define SET_BIT(PORT, BIT) ((PORT) |=  (1 <<(BIT))) // Port-Bit Setzen +#define SET_BIT(BYTE, BIT) ((BYTE) |=  (1 << (BIT))) // Bit Zustand in Byte setzen 
-#define TGL_BIT(PORT, BIT) ((PORT) ^=  (1 <<(BIT))) // Port-Bit Toggeln+#define TGL_BIT(BYTE, BIT)  ((BYTE) ^=  (1 << (BIT))) // Bit Zustand in Byte wechseln (toggle)
  
 #define LED_WAIT_TIME 1000 // Dauer bis zum Taktwechsel am Pin #define LED_WAIT_TIME 1000 // Dauer bis zum Taktwechsel am Pin
-#define LED_PIN PORTD6 // Pin an dem die LED anschlossen ist +#define LED_PIN PB2 // Pin an dem die LED anschlossen ist 
  
 #include <avr/io.h> #include <avr/io.h>
Zeile 344: Zeile 351:
 int main(void) int main(void)
 { {
- SET_BIT(DDRD, LED_PIN);+ SET_BIT(DDRB, LED_PIN);
     while (1)      while (1) 
     {     {
- TGL_BIT(PORTD, LED_PIN);+ TGL_BIT(PORTB, LED_PIN);
  _delay_ms(LED_WAIT_TIME);  _delay_ms(LED_WAIT_TIME);
     }     }
 } }
-</code>+</sxh>
  
 <-- <--
  
---> Beim "sauberen Code" wird PORTD6 genutzt - wo kommt das her? Wo liegt DDRD eigentlich ab? #+--> Beim "sauberen Code" wird PB2 genutzt - wo kommt das her? Wo liegt DDRB eigentlich ab? #
  
-Klicken Sie auf den fraglichen Code-Snipsel (z.B. Variablen- oder Funktionsname) mit einem Rechts-Klick und wählen Sie ''Goto Implementation'' (Alternativ <Alt>+<G>) . Wird beispielsweise PORTD6 eingegeben und die Implementierung angezeigt, ist zu sehen, dass PORTD6 auch nur ein define ist: ''%%#define PORTD6 6%%''. Selbiges ist bei DDRD zu finden; dort wird aber über den define der Text durch ''%%_SFR_IO8(0x0B)%%'' ersetzt. ''%%_SFR_IO8(0x0B)%%'' ist ein Befehl für den Microcontroller, um auf ein __S__pecial __F__unction __R__egister des __I__nput-__O__utput-Registerbereichs zugreifen, welches im Speicher unter ''0x0B'' abliegt.+Klicken Sie auf den fraglichen Code-Snipsel (z.B. Variablen- oder Funktionsname) mit einem Rechts-Klick und wählen Sie ''Goto Implementation'' (Alternativ <Alt>+<G>) . Wird beispielsweise PB2 eingegeben und die Implementierung angezeigt, ist zu sehen, dass PB2 auch nur ein define ist: ''%%#define PB2 2%%''. Selbiges ist bei DDRD zu finden; dort wird aber über den define der Text durch ''%%_SFR_IO8(0x05)%%'' ersetzt. ''%%_SFR_IO8(0x05)%%'' ist ein Befehl für den Microcontroller, um auf ein __S__pecial __F__unction __R__egister des __I__nput-__O__utput-Registerbereichs zugreifen, welches im Speicher unter ''0x05'' abliegt.
  
 <-- <--