So schreiben Sie Code für AVR und programmieren Atmel AVR-Mikrocontroller in C. AVR-Programmierung

Das schematische Diagramm des LPT-Port-Programmierers ist in der Abbildung dargestellt. Als Bustreiber verwenden Sie die Mikroschaltung 74AC 244 oder 74HC244 (K1564AP5), 74LS244 (K555AP5) oder 74ALS244 (K1533AP5).

LED VD1 zeigt den Aufzeichnungsmodus des Mikrocontrollers an,

LED VD2 - Lesen,

LED VD3 – Stromversorgung des Stromkreises vorhanden.

Die zur Stromversorgung erforderliche Spannung bezieht die Schaltung aus dem ISP-Anschluss, d. h. vom programmierbaren Gerät. Bei diesem Schaltkreis handelt es sich um einen neu gestalteten STK200/300-Programmierschaltkreis (zur einfacheren Bedienung wurden LEDs hinzugefügt), sodass er mit allen PC-Programmierprogrammen kompatibel ist, die mit dem STK200/300-Schaltkreis arbeiten. Um mit diesem Programmierer zu arbeiten, verwenden Sie das Programm CVAVR

Der Programmierer kann auf einer Leiterplatte hergestellt und im LPT-Steckergehäuse platziert werden, wie in den Abbildungen gezeigt:




Für die Arbeit mit dem Programmierer ist es praktisch, eine LPT-Port-Erweiterung zu verwenden, die man einfach selbst herstellen kann (z. B. aus einem Centronix-Kabel für einen Drucker), Hauptsache, die Leiter für die Erde nicht verschonen (18- 25 Verbindungsbeine) oder kaufen. Das Kabel zwischen dem Programmierer und dem programmierbaren Chip sollte 20–30 cm nicht überschreiten.

Ich habe mehr als ein- oder zweimal gesagt, dass das Erlernen von MK mit Assembler beginnen sollte. Ein ganzer Kurs auf der Website war diesem Thema gewidmet (obwohl es nicht sehr konsistent ist, aber ich kombiniere es nach und nach zu einem angemessenen Erscheinungsbild). Ja, es ist schwierig, das Ergebnis wird nicht am ersten Tag sein, aber Sie werden lernen zu verstehen, was in Ihrem Controller passiert. Sie werden wissen, wie es funktioniert, und nicht wie ein Affe die Quellen anderer Leute kopieren und versuchen zu verstehen, warum es plötzlich nicht mehr funktioniert. Darüber hinaus ist es für C viel einfacher, Redneck-Code zu erstellen, der im ungünstigsten Moment mit einer Mistgabel herauskommt.

Leider möchte jeder sofort Ergebnisse erzielen. Also beschloss ich, den anderen Weg zu gehen – ein Tutorial über C zu machen, aber seine Unterwäsche zu zeigen. Ein guter Embedder-Programmierer hält seine Hardware immer fest an der Schraube und lässt nicht zu, dass sie ohne Erlaubnis einen einzigen Schritt ausführt. Zuerst kommt also der C-Code, dann was der Compiler produziert hat und wie das Ganze eigentlich funktioniert :)

Andererseits liegt die Stärke von C in der Codeportabilität. Wenn Sie natürlich alles richtig schreiben. Aufteilung der Arbeitsalgorithmen und ihrer Hardware-Implementierungen in verschiedene Teile des Projekts. Um den Algorithmus dann auf einen anderen Mikrocontroller zu übertragen, reicht es aus, nur die Schnittstellenschicht neu zu schreiben, in der alle Aufrufe an die Hardware geschrieben werden, und den gesamten Arbeitscode unverändert zu lassen. Und natürlich die Lesbarkeit. Der C-Quellcode ist auf den ersten Blick leichter zu verstehen (obwohl es mir zum Beispiel egal ist, worauf ich hinweisen soll – sei es C oder ASM :)), aber wiederum, wenn alles richtig geschrieben ist. Auf diese Punkte werde ich auch achten.

Als Experimentierhardware dient mein Debugboard, auf dem der Löwenanteil aller Beispiele installiert wird.

Das erste C-Programm für AVR

Auswahl eines Compilers und Einrichten der Umgebung
Es gibt viele verschiedene C-Compiler für AVR:
Das hier zunächst einmal IAR AVR C- gilt mit ziemlicher Sicherheit als der beste Compiler für AVR, weil Der Controller selbst wurde in enger Zusammenarbeit zwischen Atmel und Spezialisten von IAR entwickelt. Aber man muss für alles bezahlen. Und dieser Compiler ist nicht nur eine teure kommerzielle Software, sondern verfügt auch über so viele Einstellungen, dass es sehr aufwändig ist, ihn einfach darin zu kompilieren. Ich habe wirklich keine Freundschaft mit ihm aufgebaut; das Projekt verrottete wegen seltsamer Fehler in der Verknüpfungsphase (später fand ich heraus, dass es ein schiefer Riss war).

Der Zweite kommt WinAVR GCC- ein leistungsstarker optimierender Compiler. Vollständig Open Source, plattformübergreifend, im Allgemeinen alle Freuden des Lebens. Es lässt sich auch perfekt in AVR Studio integrieren, sodass Sie direkt dort debuggen können, was verdammt praktisch ist. Im Allgemeinen habe ich es gewählt.

Es gibt auch CodeVision AVR C ist ein sehr beliebter Compiler. Es wurde aufgrund seiner Einfachheit populär. Innerhalb weniger Minuten erhalten Sie darin ein funktionsfähiges Programm – der Startcode-Assistent erleichtert dies erheblich, indem er Standards für die Initialisierung aller möglichen Dinge festlegt. Ehrlich gesagt bin ich etwas misstrauisch – als ich ein von diesem Compiler geschriebenes Programm zerlegen musste, stellte sich heraus, dass es sich um eine Art Chaos und nicht um Code handelte. Eine schreckliche Menge an unnötigen Bewegungen und Vorgängen, die zu einer beträchtlichen Menge an Code und einer langsamen Leistung führten. Möglicherweise liegt jedoch ein Fehler in der DNA der Person vor, die die Original-Firmware geschrieben hat. Außerdem will er Geld. Nicht so stark wie IAR, aber spürbar. Und im Demomodus können Sie nicht mehr als 2 KB Code schreiben.
Natürlich gibt es einen Riss, aber wenn man stehlen will, ist es eine Million im IAR-Sinne :)

Es gibt noch Image Craft AVR C Und MikroC aus der Mikroelektronik. Ich musste keines davon verwenden, aber S.W.G. sehr lobend MikroPascal, sagen sie, eine schrecklich praktische Programmierumgebung und Bibliotheken. Ich denke, MicroC wird nicht schlechter sein, aber es wird auch bezahlt.

Wie gesagt, ich habe mich entschieden WinAVR Aus drei Gründen: Es ist kostenlos, es lässt sich in AVR Studio integrieren und es gibt einfach eine Menge vorgefertigten Codes für alle Gelegenheiten.

Laden Sie also die WinAVR-Installation mit AVR Studio herunter. Als nächstes wird zunächst das Studio installiert, dann wird WinAVR darüber gerollt und in Form eines Plugins an das Studio angehängt. Ich empfehle dringend, WinAVR in einem kurzen Pfad zu installieren, etwa C:\WinAVR. Auf diese Weise vermeiden Sie viele Probleme mit Pfaden.

Ein Projekt erstellen
Also, das Studio ist installiert, C ist eingeschraubt, es ist Zeit, zu versuchen, etwas zu programmieren. Beginnen wir mit dem Einfachsten, dem Einfachsten. Starten Sie das Studio, wählen Sie dort ein neues Projekt als AVR-GCC-Compiler aus und geben Sie den Namen des Projekts ein.

Es öffnet sich ein Arbeitsfeld mit einer leeren *.c-Datei.

Jetzt kann es nicht schaden, die Anzeige der Pfade in den Studio-Lesezeichen zu konfigurieren. Gehen Sie dazu zu:
Menü Extras – Optionen – Allgemein – Dateiregisterkarten und wählen Sie „Nur Dateiname“ aus der Dropdown-Liste. Andernfalls ist die Arbeit nicht möglich – die Registerkarte enthält den vollständigen Pfad der Datei und es werden nicht mehr als zwei oder drei Registerkarten auf dem Bildschirm angezeigt.

Projektaufbau
Im Allgemeinen gilt es als klassisch, eine Make-Datei zu erstellen, in der alle Abhängigkeiten beschrieben sind. Und das ist wahrscheinlich richtig. Aber für mich, der mit vollständig integrierten IDEs aufgewachsen ist, gefällt es mir uVision oder AVR Studio Dieser Ansatz ist zutiefst fremd. Deshalb werde ich es auf meine Weise machen, alles mit Studiomitteln.

Drücken Sie den Knopf mit dem Zahnrad.


Dies sind die Einstellungen für Ihr Projekt bzw. die Einstellungen für die automatische Generierung einer Make-Datei. Auf der ersten Seite müssen Sie lediglich die Frequenz eingeben, mit der Ihr MK arbeiten soll. Dies hängt von den Sicherungsbits ab, daher gehen wir davon aus, dass unsere Frequenz 8000000 Hz beträgt.
Achten Sie auch auf die Optimierungslinie. Jetzt gibt es -Os – das ist Größenoptimierung. Lassen Sie es vorerst so, wie es ist, dann können Sie versuchen, mit diesem Parameter zu spielen. -O0 ist überhaupt keine Optimierung.

Der nächste Schritt besteht darin, die Pfade zu konfigurieren. Fügen Sie dort zunächst Ihr Projektverzeichnis hinzu – Sie fügen dort Bibliotheken von Drittanbietern hinzu. Der Pfad „.\“ erscheint in der Liste.

Die Make-Datei wurde generiert. Sie können sie im Standardordner Ihres Projekts ansehen. Werfen Sie einfach einen Blick darauf und sehen Sie, was sich dort befindet.


Das ist alles für den Moment. Klicken Sie überall auf OK und gehen Sie zur Quelle.

Formulierung des Problems
Ein leeres Blatt Papier ist verlockend, eine raffinierte Idee umzusetzen, da das banale Blinken einer Diode nicht mehr funktioniert. Lasst uns sofort den Stier bei den Hörnern packen und die Verbindung mit dem Computer herstellen – das ist das Erste, was ich mache.

Es wird so funktionieren:
Wenn eine Eins (Code 0x31) über den COM-Port ankommt, schalten wir die Diode ein, und wenn eine Null ankommt (Code 0x30), schaltet sie sich aus. Darüber hinaus wird alles über Interrupts erledigt und die Hintergrundaufgabe wird das Blinken einer anderen Diode sein. Einfach und sinnvoll.

Zusammenbau der Schaltung
Wir müssen das USB-USART-Konvertermodul mit den USART-Pins des Mikrocontrollers verbinden. Nehmen Sie dazu eine Brücke aus zwei Drähten und stecken Sie diese über Kreuz auf die Stifte. Das heißt, wir verbinden den Rx des Controllers mit dem Tx des Konverters und den Tx des Konverters mit dem Rx des Controllers.

Am Ende ist dies das Diagramm:


Ich denke nicht darüber nach, die restlichen Pins anzuschließen, mit Strom zu versorgen oder zurückzusetzen, das ist Standard.

Code schreiben

Lassen Sie mich gleich einen Vorbehalt machen, dass ich nicht speziell auf die Beschreibung der C-Sprache selbst eingehen werde. Dafür gibt es einfach riesig viel Material, angefangen von der klassischen „C Programming Language“ von K&R bis hin zu diversen Handbüchern.

Ich habe eine solche Methode in meinem Vorrat gefunden; ich habe sie einmal verwendet, um diese Sprache zu lernen. Alles dort ist kurz, klar und auf den Punkt gebracht. Ich stelle es nach und nach zusammen und ziehe es auf meine Website.

Es stimmt, dass noch nicht alle Kapitel übertragen wurden, aber ich denke, das wird nicht mehr lange dauern.

Es ist unwahrscheinlich, dass ich es besser beschreiben kann. Deshalb werde ich vom Schulungskurs aus statt einer detaillierten Erklärung der Feinheiten einfach direkte Links zu einzelnen Seiten dieses Handbuchs bereitstellen.

Bibliotheken hinzufügen.
Zunächst fügen wir die notwendigen Bibliotheken und Header mit Definitionen hinzu. Schließlich ist C eine universelle Sprache und wir müssen ihm erklären, dass wir speziell mit AVR arbeiten, also schreiben Sie die Zeile in den Quellcode:

1 #enthalten

#enthalten

Diese Datei befindet sich im Ordner WinAVR und es enthält eine Beschreibung aller Register und Ports des Controllers. Darüber hinaus ist alles dort knifflig, mit Bindung an einen bestimmten Controller, der vom Compiler über übermittelt wird machen Datei im Parameter MCU und basierend auf dieser Variablen wird eine Header-Datei mit einer Beschreibung der Adressen aller Ports und Register für diesen bestimmten Controller mit Ihrem Projekt verbunden. Wow! Ohne sie ist es auch möglich, aber dann können Sie keine symbolischen Registernamen wie SREG oder UDR verwenden und müssen sich die Adresse jedes einzelnen wie „0xC1“ merken, was Kopfschmerzen bereiten wird.

Das Team selbst #enthalten<имя файла> ermöglicht es Ihnen, den Inhalt einer beliebigen Textdatei zu Ihrem Projekt hinzuzufügen, beispielsweise eine Datei mit einer Funktionsbeschreibung oder einen anderen Code. Und damit die Direktive diese Datei finden konnte, haben wir den Pfad zu unserem Projekt angegeben (das WinAVR-Verzeichnis ist dort standardmäßig bereits registriert).

Hauptfunktion.
Ein C-Programm besteht ausschließlich aus Funktionen. Sie können in beliebiger Reihenfolge und auf unterschiedliche Weise verschachtelt und voneinander aufgerufen werden. Jede Funktion verfügt über drei erforderliche Parameter:

  • Der Rückgabewert ist z.B. Sünde(x) gibt den Wert des Sinus von x zurück. Kurz gesagt, wie in der Mathematik.
  • Die übertragenen Parameter sind die gleichen X.
  • Funktionskörper.

Alle übertragenen und zurückgegebenen Werte müssen abhängig von den Daten von einem bestimmten Typ sein.

Jedes C-Programm muss eine Funktion enthalten hauptsächlich als Einstiegspunkt in das Hauptprogramm, sonst ist es überhaupt nicht C :). Durch das Vorhandensein von main im Quellcode einer anderen Person aus einer Million Dateien können Sie verstehen, dass dies der Hauptteil des Programms ist, in dem alles beginnt. Fragen wir also:

1 2 3 4 5 int main(void) ( return 0 ; )

int main(void) ( return 0; )

Das war's, das erste einfachste Programm wurde geschrieben, es spielt keine Rolle, dass es nichts tut, wir haben gerade erst angefangen.

Lassen Sie uns herausfinden, was wir getan haben.
int Dies ist der Datentyp, den die Hauptfunktion zurückgibt.

Natürlich in einem Mikrocontroller hauptsächlich Grundsätzlich kann nichts zurückgegeben werden, und theoretisch sollte dies auch der Fall sein void main(void), aber GCC ist ursprünglich für den PC konzipiert und dort kann das Programm den Wert nach Abschluss an das Betriebssystem zurückgeben. Daher GCC auf void main(void) schwört auf Warnung.

Das ist kein Fehler, es wird funktionieren, aber ich mag keine Warnungen.

Leere Dies ist in diesem Fall die Art von Daten, die wir an die Funktion übergeben hauptsächlich kann daher auch nichts von außen annehmen Leere- Ein Dummy. Ein Stub wird verwendet, wenn keine Notwendigkeit besteht, etwas zu übertragen oder zurückzusenden.

Hier sind sie { } Geschweifte Klammern sind ein Programmblock, in diesem Fall der Rumpf einer Funktion hauptsächlich, der Code befindet sich dort.

zurückkehren- Dies ist der Rückgabewert, den die Hauptfunktion nach Abschluss zurückgibt. Da wir ein int, also eine Zahl, haben, müssen wir eine Zahl zurückgeben. Obwohl dies immer noch keinen Sinn ergibt, weil... Auf dem Mikrocontroller können wir von main aus nur nirgendwo hingehen. Ich gebe null zurück. Weil es egal ist. Der Compiler ist jedoch normalerweise intelligent und generiert für diesen Fall keinen Code.
Obwohl, wenn pervers, dann von hauptsächlich Sie können zum MK gehen – zum Beispiel in den Bootloader-Bereich gehen und ihn ausführen, aber dies erfordert ein einfaches Basteln mit der Firmware, um die Übergangsadressen zu korrigieren. Im Folgenden werden Sie es selbst sehen und verstehen, wie es geht. Wofür? Das ist eine andere Frage, in 99,999 % der Fälle ist dies nicht notwendig :)

Wir haben es geschafft und sind weitergezogen. Fügen wir eine Variable hinzu, wir brauchen sie nicht wirklich und es macht keinen Sinn, Variablen ohne sie einzuführen, aber wir lernen. Wenn Variablen innerhalb des Funktionskörpers hinzugefügt werden, sind sie lokal und existieren nur in dieser Funktion. Wenn Sie die Funktion verlassen, werden diese Variablen gelöscht und der RAM-Speicher für wichtigere Zwecke reserviert. .

1 2 3 4 5 6 int main(void) (unsigned char i; return 0;)

int main(void) ( unsigned char i; return 0; )

ohne Vorzeichen bedeutet unsigniert. Tatsache ist, dass in der binären Darstellung das höchstwertige Bit dem Vorzeichen zugewiesen wird, was bedeutet, dass die Zahl +127/-128 in ein Byte (char) passt, aber wenn das Vorzeichen verworfen wird, passt es von 0 bis 255. Normalerweise ist das Zeichen nicht erforderlich. Also ohne Vorzeichen.
ich ist nur ein Variablenname. Nicht mehr.

Jetzt müssen wir die Ports initialisieren und UART. Natürlich können Sie die Bibliothek nehmen und verbinden und eine Art UartInit(9600) aufrufen; aber dann weiß man nicht, was wirklich passiert ist.

Wir machen das:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void ) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1)#define HI(x) ((x)>>8) #define LO(x) ((x)& 0xFF) UBRRL = LO(bauddivider) ; UBRRH = HI(bauddivider) ; UCSRA = 0 ; UCSRB = 1<< RXEN| 1 << TXEN| 1 << RXCIE| 0 << TXCIE; UCSRC = 1 << URSEL| 1 << UCSZ0| 1 << UCSZ1; }

int main(void) ( unsigned char i; #define XTAL 8000000L #define baudrate 9600L #define bauddivider (XTAL/(16*baudrate)-1) #define HI(x) ((x)>>8) #define LO( x) ((x)& 0xFF) UBRRL = LO(bauddivider);<

Beängstigend? Tatsächlich gibt es nur fünf letzte Zeilen echten Codes. Alles das #definieren Es handelt sich um eine Präprozessor-Makrosprache. Fast das Gleiche wie in Assembly, aber die Syntax ist etwas anders.

Sie erleichtern Ihnen die routinemäßige Berechnung der erforderlichen Koeffizienten. In der ersten Zeile sagen wir stattdessen das XTAL Sie können 8000000 sicher ersetzen, und L- Angabe des Typs, wobei long die Taktfrequenz des Prozessors ist. Das selbe Baudrate— Häufigkeit der Datenübertragung über UART.

Baudteiler bereits komplizierter, stattdessen wird der nach der Formel aus den beiden vorherigen berechnete Ausdruck ersetzt.
Gut und L.O. Und HALLO Die Low- und High-Bytes werden aus diesem Ergebnis übernommen, weil Offensichtlich passt es möglicherweise nicht in ein Byte. IN HALLO X (der Makro-Eingabeparameter) wird achtmal nach rechts verschoben, sodass nur noch das höchstwertige Byte übrig bleibt. Und in L.O. Wir führen ein bitweises UND mit der Zahl 00FF durch, als Ergebnis bleibt nur das Low-Byte übrig.

Alles, was getan wird, ist also wie #definieren Sie können es getrost wegwerfen, die notwendigen Zahlen auf einem Taschenrechner berechnen und sie sofort in die Zeilen UBBRL = … eingeben. und UBBRH = …..

Dürfen. Aber! Mach das ABSOLUT UNMÖGLICH!

Es wird so oder so funktionieren, aber Sie werden so genannte haben magische Zahlen- Werte, die aus dem Nichts und aus unbekannten Gründen übernommen wurden, und wenn Sie in ein paar Jahren ein solches Projekt eröffnen, wird es verdammt schwierig sein, diese Werte zu verstehen. Selbst jetzt muss, wenn Sie die Geschwindigkeit oder die Quarzfrequenz ändern möchten, alles neu berechnet werden, aber Sie haben ein paar Zahlen im Code geändert und das war’s. Wenn Sie nicht als Programmierer abgestempelt werden möchten, sollten Sie Ihren Code im Allgemeinen so gestalten, dass er leicht lesbar, verständlich und leicht zu ändern ist.

Dann ist alles ganz einfach:
All diese „UBRRL und Co“ sind Konfigurationsregister des UART-Senders, mit dessen Hilfe wir mit der Welt kommunizieren werden. Und nun haben wir ihnen die erforderlichen Werte zugewiesen und sie auf die gewünschte Geschwindigkeit und den gewünschten Modus eingestellt.

Aufnahmetyp 1< Bedeutet Folgendes: Nimm 1 und platziere es RXEN in Byte. RXEN Dies ist das 4. Bit des Registers UCSRB, Also 1< bildet die Binärzahl 00010000, TXEN- das ist das 3. Bit, und 1< ergibt 00001000. Einzelnes „|“ es ist bitweise ODER, also 00010000 | 00001000 = 00011000. Auf die gleiche Weise werden die verbleibenden notwendigen Konfigurationsbits gesetzt und dem allgemeinen Heap hinzugefügt. Dadurch wird die erfasste Nummer im UCSRB erfasst. Weitere Details sind im Datenblatt zum MK im Abschnitt USART beschrieben. Lassen wir uns also nicht von technischen Details ablenken.

Fertig, Zeit zu sehen, was passiert ist. Klicken Sie auf „Kompilieren“ und starten Sie die Emulation (Strg+F7).

Debuggen
Allerlei Fortschrittsbalken liefen durch, das Studio veränderte sich und neben dem Eingang zur Hauptfunktion erschien ein gelber Pfeil. Hier läuft der Prozessor gerade und die Simulation ist pausiert.

Tatsache ist, dass es anfangs tatsächlich in der Zeile UBRRL = LO(bauddivider); Schließlich handelt es sich bei define nicht um Code, sondern lediglich um vorläufige Berechnungen, weshalb der Simulator etwas langweilig ist. Aber jetzt wurde ihm klar, dass die erste Anweisung abgeschlossen ist und wenn man auf den Baum klettert E/A-Ansicht Gehen Sie zum USART-Abschnitt und sehen Sie sich dort das UBBRL-Byte an. Sie werden sehen, dass der Wert bereits vorhanden ist! 0x33.

Gehen Sie noch einen Schritt weiter. Sehen Sie, wie sich der Inhalt des anderen Registers ändert. Gehen Sie sie also alle durch und achten Sie darauf, dass alle angegebenen Bits so gesetzt sind, wie ich es Ihnen gesagt habe, und dass sie gleichzeitig für das gesamte Byte gesetzt sind. Es geht nicht weiter als Return – das Programm ist beendet.

Öffnung
Setzen Sie nun die Simulation auf Null zurück. Klicken Sie dort Zurücksetzen (Umschalt+F5). Öffnen Sie die zerlegte Auflistung, nun sehen Sie, was tatsächlich im Controller passiert. Ansicht -> Disassembler. Und nicht YAAAAAA!!! Monteur!!! GRUSEL!!! UND ES IST NOTWENDIG. Damit man später, wenn mal etwas schiefgeht, sich im Code nicht dumm verhält und in den Foren keine lahmen Fragen stellt, sondern sich sofort ins Zeug legt und schaut, wo man nicht weiterkommt. Da ist nichts Beängstigendes.

Zunächst gibt es Tops aus der Serie:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 +00000000: 940C002A JMP 0x0000002A Sprung +00000002: 940C0034 JMP 0x00000034 Sprung +00000004: 940C0034 JMP 0x00000034 Sprung +00000006: 940C0034 JMP 0 x00000034 Sprung +00000008: 940C0034 JMP 0x00000034 Sprung +0000000A: 940C0034 JMP 0x00000034 Sprung +0000000C: 940C0034 JMP 0x00000034 Sprung + 0000000E: 940C0034 JMP 0x000034 Sprung +00000010: 940c0034 JMP 0x00000034 Sprung +000012: 940c0034 JMP 0x00000034 Sprung +00000014: 940c0034 JMP 0x000 00034 Sprung +00000016: 940C003 4 JMP 0x00000034 Sprung +00000018: 940c0034 JMP 0x00000034 Sprung +0000001a: 940c0034 JMP 0x00000034 Sprung + 0000001c: 940C0034 JMP 0x00000034 Sprung +0000001E: 940C0034 JMP 0x00000034 Sprung +00000020: 940C0034 JMP 0x00000034 Sprung +00000022: 940C0034 JMP. 0x00000 034 Sprung +00000024: 940C0034 JMP 0x00000034 Sprung +00000026: 940C0034 JMP 0x00000034 Sprung +00000028: 940C0034 JMP 0x00000034 Sprung

00000000: 940C002A JMP 0x00002A JUMP +00000002: 940C0034 JMP 0x00000034 Sprung +00000004: 940C0034 JMP 0x000034 JUMP +00000006: 940C0034 JMP. 0x0 00034 JUMP +000008: 940C0034 JMP 0x00000034 Sprung +0000000a: 940c0034 JMP 0x00000034 Sprung +0000000C: 940c0034 JMP 0x00000034 Sprung +0000000EE : 940C0034 JMP 0x00000034 Sprung +00000010: 940C0034 JMP 0x00000034 Sprung +00000012: 940C0034 JMP 0x00000034 Sprung +00000014: 940C0034 JMP 0x00000 34 Sprung +00000016: 940C0034 JMP 0x00000034 Sprung +00000018: 940C0034 JMP 0x00000034 Sprung +0000001A: 940C0034 JMP 0x00000034 Sprung +0000001C : 940C0034 JMP 0x00000034 Sprung +0000001E: 940C0034 JMP 0x00000034 Sprung +00000020: 940C0034 JMP 0x00000034 Sprung +00000022: 940C0034 JMP. 0x0000003 4 Sprung +00000024: 940C0034 JMP 0x00000034 Sprung +00000026: 940C0034 JMP 0x00000034 Sprung +00000028: 940C0034 JMP 0x00000034 Sprung

Dies ist die Interrupt-Vektortabelle. Wir werden später darauf zurückkommen, aber jetzt schauen Sie einfach hin und denken Sie daran, dass es existiert. Die erste Spalte ist die Adresse der Flash-Zelle, in der sich der Befehl befindet, die zweite ist der Befehlscode, die dritte ist die Befehlsmnemonik, die gleiche Assembleranweisung, die dritte sind die Operanden des Befehls. Nun ja, automatischer Kommentar.
Wenn Sie also hinschauen, gibt es kontinuierliche Übergänge. Und der JMP-Befehlscode besteht aus vier Bytes, er enthält die rückwärts geschriebene Sprungadresse – das Low-Byte an der Low-Adresse und den Sprungbefehlscode 940C

0000002B: BE1F OUT 0x3F,R1 Ausgang zum E/A-Standort

Wenn Sie diese Null an der Adresse 0x3F aufzeichnen, sehen Sie, dass die Adresse 0x3F die Adresse des SREG-Registers ist – das Flag-Register des Controllers. Diese. Wir setzen SREG zurück, um das Programm unter Nullbedingungen auszuführen.

1 2 3 4 +0000002C: E5CF LDI R28,0x5F Sofort laden +0000002D: E0D4 LDI R29,0x04 Sofort laden +0000002E: BFDE OUT 0x3E,R29 Ausgang zum E/A-Standort +0000002F: BFCD OUT 0x3D,R28 Ausgang zum E/A-Standort

0000002C: E5CF LDI R28,0x5F Sofort laden +0000002D: E0D4 LDI R29,0x04 Sofort laden +0000002E: BFDE OUT 0x3E,R29 Ausgang zum E/A-Standort +0000002F: BFCD OUT 0x3D,R28 Ausgang zum E/A-Standort

Dadurch wird der Stapelzeiger geladen. Sie können nicht direkt in E/A-Register laden, sondern nur über ein Zwischenregister. Deshalb zuerst LDI zum Intermediate und dann von dort OUT zum I/O. Ich werde Ihnen später auch mehr über den Stapel erzählen. Beachten Sie zunächst, dass es sich um einen dynamischen Speicherbereich handelt, der am Ende des RAM hängt und Adressen und Zwischenvariablen speichert. Jetzt haben wir angegeben, wo unser Stapel beginnen soll.

00000032: 940C0041 JMP 0x00000041 Sprung

Springen Sie zum Ende des Programms, und dort gibt es ein Verbot von Interrupts und engen Schleifen auf sich selbst:

1 2 +00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relativer Sprung

00000041: 94F8 CLI Global Interrupt Disable +00000042: CFFF RJMP PC-0x0000 Relativer Sprung

Dies ist der Fall, wenn unvorhergesehene Umstände eintreten, beispielsweise das Verlassen der Hauptfunktion. Der Controller kann aus einer solchen Schleife entweder durch einen Hardware-Reset oder, was wahrscheinlicher ist, durch einen Reset durch einen Watchdog gebracht werden. Nun, oder, wie ich oben sagte, korrigieren Sie dies im Hex-Editor und galoppieren Sie dorthin, wohin unser Herz begehrt. Beachten Sie außerdem, dass es zwei Arten von Übergängen gibt: JMP und RJMP; der erste ist ein direkter Übergang zu einer Adresse. Es belegt vier Bytes und kann den gesamten Speicherbereich direkt durchspringen. Die zweite Art des Übergangs ist RJMP – relativ. Sein Befehl benötigt zwei Bytes, aber er bewegt sich von der aktuellen Position (Adresse) 1024 Schritte vorwärts oder rückwärts. Und seine Parameter geben den Versatz vom aktuellen Punkt an. Es wird häufiger verwendet, weil nimmt in einer Spülung die Hälfte des Platzes ein und lange Übergänge sind selten erforderlich.

1 +00000034: 940C0000 JMP 0x00000000 Sprung

00000034: 940C0000 JMP 0x00000000 Sprung

Und dies ist ein Sprung zum Anfang des Codes. Eine Art Neustart. Hier können Sie überprüfen, ob alle Vektoren springen. Die Schlussfolgerung daraus ist, dass, wenn Sie jetzt Interrupts aktivieren (sie sind standardmäßig deaktiviert) und Ihr Interrupt auftritt, aber kein Handler vorhanden ist, ein Software-Reset erfolgt – das Programm wird an den Anfang zurückgeworfen.

Funktion main. Alles ist ähnlich, man muss es nicht einmal beschreiben. Schauen Sie sich einfach an, wie die bereits berechnete Zahl in die Register eingetragen wird. Der Compiler-Präprozessor rockt!!! Also keine „magischen“ Zahlen!

1 2 3 4 5 6 7 8 9 10 11 12 <

00000036: E383 LDI R24,0x33 Sofortiges Laden +00000037: B989 OUT 0x09,R24 Ausgang zum E/A-Platz 15: UBRRH = HI(bauddivider); +00000038: BC10 OUT 0x20,R1 Out zum E/A-Standort 16: UCSRA = 0; +00000039: B81B OUT 0x0B,R1 Ausgang an E/A-Platz 17: UCSRB = 1<

Und hier ist der Fehler:

1 2 3 +0000003E: E080 LDI R24.0x00 Sofort laden +0000003F: E090 LDI R25.0x00 Sofort laden +00000040: 9508 RET Unterprogramm-Rückkehr

0000003E: E080 LDI R24.0x00 Sofort laden +0000003F: E090 LDI R25.0x00 Sofort laden +00000040: 9508 RET Unterprogramm-Rückkehr

Die Frage ist, warum der Compiler solche Spitzen hinzufügt. Und das ist nichts weiter als Return 0, wir haben die Funktion als int main(void) definiert und damit weitere vier Bytes umsonst verschwendet :) Und wenn Sie void main(void) machen, bleibt nur RET übrig, aber es erscheint eine Warnung , dass unsere Hauptfunktion nichts zurückgibt. Im Allgemeinen tun Sie, was Sie möchten :)

Schwierig? Scheinbar nicht. Klicken Sie im Disassembler-Modus auf Schritt-für-Schritt-Ausführung und sehen Sie, wie der Prozessor einzelne Anweisungen ausführt und was mit den Registern passiert. Wie erfolgt die Bewegung durch Befehle und die Endschleife?

Fortsetzung folgt in ein paar Tagen...

Off Top:
Alexei78 Ich habe ein Plugin für Firefox erstellt, das die Navigation auf meiner Website und im Forum erleichtert.
Diskussion und Download,

Irgendwie verspürte ich sofort das Bedürfnis, Ratschläge zur Auswahl einer Programmierumgebung für AVR-Controller zu geben. Wirf bloß keine Hausschuhe nach mir. Ich bin nur ein bisschen :)

Es gibt viele Programmiersprachen für Mikrocontroller. Es gibt auch eine ganze Reihe von Programmierumgebungen und es ist falsch, sie miteinander zu vergleichen. Es gibt keine besten Programmiersprachen. Das bedeutet, dass Sie die für Sie am besten geeignete Sprache und Programmierumgebung auswählen müssen.

Wenn Sie derzeit vor der Entscheidung stehen, womit Sie mit der Arbeit beginnen möchten, finden Sie hier einige Empfehlungen.

Vorherige Programmiererfahrung. Vernachlässigen Sie nicht Ihre bisherigen Programmiererfahrungen. Auch wenn es BASIC war. Auch wenn es in der Schule schon lange her ist. Programmieren ist wie Fahrradfahren – wenn man einmal angefangen hat, merkt man sich schnell alles, was man vergessen hat. Beginnen Sie mit BASIC – beherrschen Sie es – später wird es einfacher sein, etwas zu wählen, das für Ihre Zwecke besser geeignet ist.

Hilfe aus der Umwelt. Schreiben deine Freunde in Pascal? Das Problem ist für Sie gelöst – schreiben Sie in Pascal! Sie stehen Ihnen jederzeit mit Rat und Tat zur Seite, stellen Ihnen Bibliotheken zur Verfügung und geben Ihnen fertige Projekte zum Lernen. Im Allgemeinen freuen sie sich, Sie in ihrer Community willkommen zu heißen. Wenn Sie das Gegenteil tun, erhalten Sie das gegenteilige Ergebnis. Freunde der GUS-Branche werden auf Sie stoßen, wenn Sie sich für ein Assembler-Studium entscheiden. Erwarte keine Hilfe.

Gutes Buch zur AVR-Programmierung wird sehr helfen. Leider gibt es nur sehr wenige davon. Wenn Sie auf ein Buch stoßen und denken, dass alles sehr verständlich erklärt wird, probieren Sie es aus. Ich empfehle nicht, mit E-Books zu lernen; als letzten Ausweg empfiehlt es sich, sie auszudrucken. Es ist sehr umständlich, zwischen der Umgebung und dem Text der Buchdatei zu wechseln. Es ist viel angenehmer, ein Buch zu lesen und es sofort auszuprobieren, ohne durch das Umschalten abgelenkt zu werden. Außerdem kann man sich am Rand Notizen machen und die aufkommenden Ideen aufschreiben.

Die Programmierumgebung ist einfacher. Wenn für Ihre Sprache mehrere Programmierumgebungen zur Auswahl stehen, zögern Sie nicht und wählen Sie die einfachere. Lassen Sie es weniger funktional sein. Lassen Sie sie schrecklich aufgeblähten Code kompilieren. Die Hauptsache ist, einfach mit der Arbeit anzufangen. Nachdem Sie sich in einer einfachen Umgebung wohlgefühlt haben, können Sie problemlos zu einer fortgeschritteneren und „richtigen“ Umgebung wechseln. Und hören Sie nicht auf diejenigen, die sagen, dass Sie noch mehr Zeit verschwenden werden – sie liegen falsch. Grundschüler werden nicht gebeten, „Krieg und Frieden“ zu lesen; sie bekommen einfachere Bücher – mit Bildern.

Bibliotheken. Die Verfügbarkeit von Bibliotheken ist für das Sprachenlernen umstritten. Natürlich werden sie später das Leben viel einfacher machen, aber „Black Box“-Bibliotheken sind zunächst unverständlich und tragen nicht wirklich zum Verständnis der Sprache bei. Andererseits machen sie Programme leichter lesbar und ermöglichen es einem Anfänger, ohne großen Aufwand komplexe Programme zu erstellen. Kümmern Sie sich also nicht zu sehr um ihre Anwesenheit. Zumindest zunächst.

Effizienter Code. Es ist keine gute Idee, eine Programmierumgebung zum Erlernen des Programmierens ausschließlich auf der Grundlage der Effizienz des kompilierten Codes auszuwählen. Die Hauptsache ist, dass man sich zu Beginn des Studiums wohlfühlt – was dabei herauskommt, ist das Zehnte. Natürlich können Sie später daran arbeiten.

Zauberer. Jedes Gerät an Bord des Chips muss mithilfe von Ports konfiguriert werden. Das Verfahren ist recht langwierig und es sind Datenblätter erforderlich. Darüber hinaus gibt es Nuancen, die für einen Anfänger nicht leicht zu verstehen sind. Daher ist es sehr wünschenswert, Assistenten in der Umgebung zu haben. Vyzards sind automatische Tuner für SPI, I2C, USART usw. Je mehr Geräte unterstützt werden, desto besser. Sie legen die erforderlichen Peripherieparameter fest und der Assistent generiert selbst Code, der die angegebenen Parameter bereitstellt. Macht das Leben viel einfacher.


Allgemeine Empfehlungen So sollte die Programmierung in der Anfangsphase so einfach wie möglich (sogar primitiv) sein. Die Programmierumgebung sollte leicht zu erlernen sein (da Sie zunächst die Programmierung beherrschen müssen und keine Zeit damit verschwenden müssen, mit den Einstellungen herumzuspielen). Vorzugsweise russifiziert. Ein russisches Handbuch und Beispielprogramme wären ebenfalls hilfreich. Die Fähigkeit, den Kristall aus der Umgebung herauszulösen, ist wünschenswert. Wenn Sie dann die Grundlagen der Programmierung beherrschen, können Sie mit komplexeren Shells fortfahren.


Eine letzte Empfehlung: Arbeiten Sie mit einem echten Kristall. Haben Sie keine Angst, es zu verbrennen. Sammeln Sie Praxiserfahrung. Die Arbeit mit Emulatoren (z. B. Proteus) wird Sie zwar von der Arbeit mit einem Lötkolben befreien, wird Ihnen jedoch nie die Zufriedenheit verschaffen, die Sie durch das funktionierende Programm und das erste Blinken der LED bekommen! Zu verstehen, dass Sie mit Ihren eigenen Händen ein echtes Arbeitsdiagramm erstellt haben, gibt Ihnen Selbstvertrauen und Ansporn, weiterzumachen!

(7.377 Mal besucht, 1 Besuch heute)

Ich habe beschlossen, einen kurzen Einführungsartikel für diejenigen zu schreiben, die zum ersten Mal mit der Programmierung von Mikrocontrollern beginnen und noch nie mit der Sprache C vertraut waren. Wir werden nicht auf Details eingehen, sondern ein wenig über alles sprechen, um einen allgemeinen Überblick über die Arbeit mit CodeVisionAVR zu bekommen.

Ausführlichere Informationen finden Sie in englischer Sprache im CodeVision-Benutzerhandbuch. Ich empfehle außerdem die Website http://somecode.ru mit Video-Lektionen zu C für Mikrocontroller und das Buch „How to Program in C“ von Deitel Das einzige gute Buch, das ich selbst verwendet habe, begann.

Beginnen wir mit der Tatsache, dass es, egal welche Aktionen wir unternehmen, letztendlich auf die Firmware des Mikrocontrollers ankommt. Der Firmware-Prozess selbst läuft wie folgt ab: Mit einem bestimmten Programm wird eine Firmware-Datei ausgewählt, Parameter werden ausgewählt, eine Taste wird gedrückt und die Firmware wird direkt geflasht, was im Wesentlichen eine Kopie ist. Genau wie beim Kopieren von Musik oder Dokumenten von einem Computer auf ein Flash-Laufwerk ist die Physik des Vorgangs dieselbe.

Die Firmware selbst hat die Erweiterung .hex und besteht aus einer Reihe von Anweisungen in Form von Einsen und Nullen, die für den Mikrocontroller verständlich sind. Woher bekomme ich die Firmware? Sie können es von Elektronik-Websites herunterladen oder selbst schreiben. Sie können es in speziellen Programmen schreiben, die als Entwicklungsumgebung bezeichnet werden. Am bekanntesten sind mir AVR Studio, IAR, CodeVision, WinAVR... Es ist unmöglich zu sagen, welche dieser Umgebungen für jede besser oder schlechter ist. Wir können sagen, dass sich diese Programme hauptsächlich im Komfort, in der Programmiersprache und im Preis unterscheiden. Auf dieser Website wird nur CodeVision berücksichtigt.

Wir haben die Umgebung geklärt. Schauen wir uns nun den Prozess des Firmware-Schreibens an. In CodeVision müssen Sie zunächst ein Projekt erstellen. Es kann mit dem Code-Assistenten erstellt oder leer sein. In jedem Fall müssen Sie den Typ des verwendeten Mikrocontrollers auswählen und dessen Frequenz angeben. Wenn Sie den Assistenten verwenden, werden Sie aufgefordert, Anfangseinstellungen auszuwählen und Quellcode mit den Einstellungen zu generieren. Als nächstes erscheint ein Fenster, in dem Sie diesen Code bearbeiten können. Sie können Ihren Quellcode zwar im Notepad schreiben und ihn dann in den Einstellungen an das Projekt anhängen.

Eine Quellcodedatei ist eine Reihe von Befehlen in einer Programmiersprache. Die Aufgabe von CodeVision besteht darin, diese Befehle in Binärcode zu übersetzen. Ihre Aufgabe besteht darin, diesen Quellcode zu schreiben. CodeVision versteht die Sprache C, Quellcodedateien haben die Erweiterung „.c“. Aber CodeVision verfügt über einige Konstrukte, die in C nicht verwendet werden, weshalb es vielen Programmierern nicht gefällt und die verwendete Sprache als C-like bezeichnet wird. Dies hindert Sie jedoch nicht daran, ernsthafte Projekte zu schreiben. Viele Beispiele, ein Codegenerator und eine große Auswahl an Bibliotheken verschaffen CodeVision einen großen Vorteil. Der einzige Nachteil ist, dass es kostenpflichtig ist, obwohl es kostenlose Versionen mit einem Codelimit gibt.

Der Quellcode muss einen Header mit dem Typ des verwendeten Mikrocontrollers und der Hauptfunktion enthalten. Beispielsweise wird ATtiny13 verwendet

#enthalten void main(void) ( );

#enthalten void main(void) ( );

Vor der Hauptfunktion können Sie die erforderlichen Bibliotheken verbinden, globale Variablen, Konstanten und Einstellungen deklarieren. Eine Bibliothek ist eine separate Datei, normalerweise mit der Erweiterung „.h“, die bereits vorab geschriebenen Code enthält. In einigen Projekten benötigen wir diesen Code möglicherweise, in anderen jedoch nicht. Beispielsweise verwenden wir in einem Projekt LCD-Displays, in einem anderen jedoch nicht. Sie können die Bibliothek für die Arbeit mit dem LCD-Display „alcd.h“ wie folgt anschließen:

#enthalten #enthalten void main(void) ( );

#enthalten #enthalten void main(void) ( );

Variablen sind Speicherbereiche, in denen bestimmte Werte abgelegt werden können. Wenn Sie beispielsweise zwei Zahlen addieren, müssen Sie das Ergebnis irgendwo speichern, um es in Zukunft verwenden zu können. Zuerst müssen Sie die Variable deklarieren, d.h. Weisen Sie ihm Speicher zu, zum Beispiel:
int i=0;
diese. Wir haben die Variable i deklariert und den Wert 0 darin platziert. Int ist der Typ der Variablen, oder einfacher gesagt, es bedeutet die Größe des zugewiesenen Speichers. Jeder Variablentyp kann nur einen bestimmten Wertebereich speichern. Int kann beispielsweise als Zahlen von -32768 bis 32767 geschrieben werden. Wenn Sie Zahlen mit einem Bruchteil verwenden müssen, muss die Variable als Float für Zeichen deklariert werden. Verwenden Sie den Typ char.

bit, _Bit 0 oder 1 char von -128 bis 127 unsigned char von 0 bis 255 int von -32768 bis 32767 unsigned int von 0 bis 65535 long int von -2147483648 bis 2147483647 unsigned long int von 0 bis 4294967295 float von ±1,1 5e- 38 bis ±3,402e38

Innerhalb der Hauptfunktion läuft das Hauptprogramm bereits. Nach der Ausführung der Funktion stoppt das Programm, sodass eine Endlosschleife erstellt wird, die das gleiche Programm ständig wiederholt.

void main(void) ( while (1) ( ); );

void main(void) ( while (1) ( ); );

Sie können in jeden Teil des Quellcodes einen Kommentar schreiben. Dies hat keinen Einfluss auf die Funktionsweise des Programms, hilft aber dabei, Notizen zum geschriebenen Code zu machen. Sie können eine Zeile mit zwei Schrägstrichen auskommentieren //danach ignoriert der Compiler die gesamte Zeile oder mehrere Zeilen /**/, zum Beispiel:

/*Grundlegende mathematische Operationen:*/ int i= 0 ; //deklariere die Variable i und weise ihr den Wert 0 zu//Addition: i = 2 + 2 ; //Subtraktion: i = 2 - 2 ; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 0//Multiplikation: i = 2 * 2 ; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 4//Division: i = 2 / 2 ; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 1

/*Grundlegende mathematische Operationen:*/ int i=0; //Variable i deklarieren und ihr den Wert 0 zuweisen //Zusatz: i = 2+2; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 4 //Subtraktion: i = 2-2; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 0 //Multiplikation: i = 2*2; //nach der Ausführung dieses Ausdrucks ist die Variable i gleich 4 //Division: i = 2/2; //Nach der Ausführung dieses Ausdrucks ist die Variable i gleich 1

Oftmals muss ein Programm von einem Codeteil zum anderen wechseln, abhängig von den Bedingungen dafür gibt es bedingte if()-Operationen, zum Beispiel:

if(i>3) //wenn i größer als 3 ist, dann weise i den Wert 0 zu ( i=0; ) /*wenn i kleiner als 3 ist, dann gehe zum Code, der dem Hauptteil der Bedingung folgt, d. h. nach Klammern ()*/

Auch if kann in Verbindung mit else verwendet werden – andernfalls

wenn ich<3) //если i меньше 3, то присвоить i значение 0 { i=0; } else { i=5; //иначе, т.е. если i больше 3, присвоить значение 5 }

Es gibt auch einen Vergleichsoperator „==“, der nicht mit „=“ verwechselt werden sollte. Nehmen wir an, die umgekehrte Operation ist nicht gleich „!="

if(i==3)//wenn i 3 ist, weise i den Wert 0 zu ( i=0; ) if(i!=5) //wenn i nicht 5 ist, weise i den Wert 0 zu ( i=0; ) )

Kommen wir zu komplexeren Dingen – Funktionen. Nehmen wir an, Sie haben einen bestimmten Code, der mehrmals wiederholt wird. Darüber hinaus ist dieser Code ziemlich groß. Es ist unbequem, es jedes Mal zu schreiben. Wenn Sie beispielsweise in einem Programm, das die Variable i irgendwie ändert, die Tasten 0 und 3 von Port D drücken, wird derselbe Code ausgeführt, der abhängig vom Wert der Variablen i die Zweige von Port B einschaltet.

void main(void) ( if (PIND.0== 0 ) //Überprüfen Sie, ob die Taste auf PD0 gedrückt ist( wenn (i== 0 ) //wenn i==0 PB0 aktivieren( PORTB.0= 1 ; ) if (i== 5 ) // wenn i==5 PB1 aktivieren( PORTB.1= 1 ; ) ) … if (PIND.3== 0 ) // Machen Sie dasselbe, wenn Sie die PD3-Schaltfläche überprüfen( if (i== 0 ) ( PORTB.0= 1 ; ) if (i== 5 ) ( PORTB.1= 1 ; ) ) )

void main(void) ( if(PIND.0==0) //überprüfen, ob die Taste auf PD0 gedrückt ist ( if(i==0) //if i==0 PB0 einschalten ( PORTB.0=1; ) if( i==5) // if i==5 PB1 einschalten ( PORTB.1=1; ) ) ... if(PIND.3==0) // das Gleiche tun, wenn die PD3-Taste überprüft wird ( if(i==0 ) ( PORTB.0=1; ) if(i==5) ( PORTB.1=1; ) ) )

Im Allgemeinen ist der Code nicht sehr groß, könnte aber um ein Vielfaches größer sein, sodass es viel bequemer wäre, eine eigene Funktion zu erstellen.
Zum Beispiel:

void i_check() ( if (i== 0 ) ( PORTB.0= 1 ; ) if (i== 5 ) ( PORTB.1= 1 ; ) )

void i_check() ( if(i==0) ( PORTB.0=1; ) if(i==5) ( PORTB.1=1; ) )

void bedeutet, dass die Funktion nichts zurückgibt, mehr dazu weiter unten i_check() – das ist der Name unserer Funktion, Sie können sie nennen, wie Sie wollen, ich habe sie genau so genannt – check i. Jetzt können wir unseren Code umschreiben:

void i_check() ( if(i==0) ( PORTB.0=1; ) if(i==5) ( PORTB.1=1; ) ) void main(void) ( if(PIND.0==0 ) //überprüfen, ob die Taste auf PD0 gedrückt ist ( i_check(); ) ... if(PIND.3==0) ( i_check(); ) )

Wenn der Code die Zeile erreicht, i_check(); Dann springt es in die Funktion und führt den darin enthaltenen Code aus. Stimmen Sie zu, der Code ist kompakter und klarer, d.h. Funktionen helfen dabei, denselben Code, nur eine Zeile, zu ersetzen. Bitte beachten Sie, dass die Funktion außerhalb des Hauptcodes deklariert wird, d. h. vor der Hauptfunktion. Sie können sagen, warum brauche ich das, aber beim Studium der Lektionen werden Sie oft auf Funktionen stoßen, zum Beispiel das Löschen des LCD-Bildschirms lcd_clear() – die Funktion akzeptiert keine Parameter und gibt nichts zurück, aber sie löscht das Bildschirm. Manchmal wird diese Funktion fast in jeder zweiten Zeile verwendet, sodass die Codeeinsparungen offensichtlich sind.

Es sieht viel interessanter aus, eine Funktion zu verwenden, wenn sie Werte annimmt. Beispielsweise gibt es eine Variable c und eine Summenfunktion, die zwei Werte vom Typ int annimmt. Wenn das Hauptprogramm diese Funktion ausführt, stehen die Argumente bereits in Klammern, sodass „a“ gleich zwei und „b“ gleich 1 wird. Die Funktion wird ausgeführt und „c“ wird gleich 3 .

int c= 0 ; void sum(int a, int b) ( c= a+ b; ) void main(void ) ( sum(2 , 1 ) ; )

int c=0; void sum(int a, int b) ( c=a+b; ) void main(void) ( sum(2,1); )

Eine der gebräuchlichsten ähnlichen Funktionen besteht darin, den Cursor auf dem LCD-Display zu bewegen. lcd_gotoxy(0,0); was übrigens auch Argumente akzeptiert – x- und y-Koordinaten.

Eine weitere Option zur Verwendung einer Funktion: Wenn sie einen Wert zurückgibt, ist dieser jetzt nicht mehr ungültig. Lassen Sie uns das vorherige Beispiel einer Funktion zum Addieren zweier Zahlen verbessern:

int c= 0 ; int sum(int a, int b) ( return a+ b; ) void main(void) ( с= sum(2, 1) ; )

int c=0; int sum(int a, int b) ( return a+b; ) void main(void) ( с=sum(2,1); )

Das Ergebnis ist dasselbe wie beim letzten Mal c=3, aber beachten Sie, dass wir der Variablen „c“ den Wert einer Funktion zuweisen, die nicht mehr ungültig ist, sondern die Summe zweier Zahlen vom Typ int zurückgibt. Auf diese Weise sind wir nicht an eine bestimmte Variable „c“ gebunden, was die Flexibilität bei der Verwendung von Funktionen erhöht. Ein einfaches Beispiel für eine solche Funktion ist das Lesen von ADC-Daten. Die Funktion gibt den Messwert result=read_adc(); zurück. Lassen Sie uns mit den Funktionen abschließen.

Kommen wir nun zu den Arrays. Ein Array besteht aus zusammengehörigen Variablen. Wenn Sie beispielsweise eine Sinustabelle mit mehreren Punkten haben, erstellen Sie keine Variablen int sinus1=0; int sinus2=1; usw. Hierzu wird ein Array verwendet. Sie können beispielsweise ein Array aus drei Elementen wie folgt erstellen:
int sinus=(0,1,5);
Die Gesamtzahl der Array-Elemente wird in eckigen Klammern angegeben. Den Wert des dritten Elements können Sie der Variablen „c“ wie folgt zuweisen:
с=Sinus;
Bitte beachten Sie, dass die Nummerierung der Array-Elemente bei Null beginnt, d. h. „c“ wird gleich fünf. Dieses Array hat kein Sinuselement!!!
So können Sie einem einzelnen Element einen Wert zuweisen:
Sinus=10;

Möglicherweise ist Ihnen bereits aufgefallen, dass CodeVision keine String-Variablen hat. Diese. Sie können keine Variable string hello=“hello“ erstellen; Dazu müssen Sie ein Array aus einzelnen Zeichen erstellen.

lcd_putchar(hallo); lcd_putchar(hallo); lcd_putchar(hallo);

usw.
Es stellt sich als ziemlich umständlich heraus, hier kommen Fahrräder zum Einsatz.
Zum Beispiel while-Schleife

while(PINB.0!=0) ( )

Tun Sie nichts, bis die Taste gedrückt wird – führen Sie eine leere Schleife aus.

Eine weitere Option ist die for-Schleife

int i; für (i= 0 ; ich< 6 ; i++ ) { lcd_putchar(hello[ i] ) ; }

int i; for(i=0;i<6;i++) { lcd_putchar(hello[i]); }

Die Bedeutung ist genau die gleiche wie die von while, nur die Anfangsbedingung i=0 und die Bedingung, die in jedem Zyklus i++ ausgeführt wird, werden hinzugefügt. Der Code innerhalb der Schleife ist so vereinfacht wie möglich.

Nachdem Sie Ihr Programm geschrieben haben, wird der Quellcode kompiliert und wenn keine Fehler vorliegen, erhalten Sie die begehrte Firmware im Projektordner. Jetzt können Sie den Mikrocontroller flashen und die Bedienung des Geräts genießen.

Sie sollten nicht sofort versuchen, Schleifen, Arrays und Funktionen in Ihrer Firmware zu verwenden. Ihre Hauptaufgabe besteht darin, die Firmware zum Laufen zu bringen. Tun Sie es also so, wie es für Sie einfacher ist, und achten Sie nicht auf die Größe des Codes. Es wird die Zeit kommen, in der Sie nicht nur funktionierenden Code schreiben möchten, sondern ihn auch schön und kompakt schreiben möchten. Dann wird es möglich sein, in die Wildnis der C-Sprache einzutauchen. Wer alles beherrschen möchte, dem empfehle ich noch einmal das Buch „How to Program in C“, dort gibt es viele Beispiele und Aufgaben. Installieren Sie Visual Studio, erstellen Sie eine Win32-Konsolenanwendung und üben Sie dort nach Herzenslust.


In dieser AVR-Schulung habe ich versucht, alle Grundlagen für Anfänger in der Programmierung von Mikrocontrollern zu beschreiben Avr. Alle Beispiele basieren auf einem Mikrocontroller atmega8. Das bedeutet, dass Sie zum Wiederholen aller Lektionen nur einen MK benötigen. Proteus wird als Emulator für elektronische Schaltkreise verwendet – meiner Meinung nach die beste Option für Anfänger. Die Programme in allen Beispielen sind im C-Compiler für avr CodeVision AVR geschrieben. Warum nicht in einem Assembler? Da ein Anfänger bereits mit Informationen gefüllt ist und ein Programm, das zwei Zahlen multipliziert, in Assembler etwa hundert Zeilen benötigt und in komplexen, mutigen Projekten C verwendet wird. Der CodeVision AVR-Compiler ist auf Atmel-Mikrocontroller zugeschnitten und verfügt über einen praktischen Codegenerator. Eine gute Schnittstelle und direkt vom Mikrocontroller aus kann man damit flashen.

In dieser Schulung wird anhand einfacher Beispiele erklärt und gezeigt, wie Sie:

  • Beginnen Sie mit der Programmierung von Mikrocontrollern, wo beginnen Sie, was Sie dafür benötigen.
  • Welche Programme sollten zum Schreiben von Firmware für AVR, zum Simulieren und Debuggen von Code auf einem PC verwendet werden?
  • Welche Peripheriegeräte befinden sich im MK und wie können Sie sie mit Ihrem Programm steuern?
  • Wie man fertige Firmware auf einen Mikrocontroller schreibt und wie man sie debuggt
  • So erstellen Sie eine Leiterplatte für Ihr Gerät
Um die ersten Schritte zur Programmierung des MK zu unternehmen, benötigen Sie lediglich zwei Programme:
  • Proteus ist ein Emulatorprogramm (darin können Sie eine Schaltung entwickeln, ohne auf echtes Löten zurückgreifen zu müssen, und dann unser Programm auf dieser Schaltung testen). Wir werden zunächst alle Projekte in Proteus starten und dann das reale Gerät löten.
  • CodeVisionAVR ist ein C-Programmiersprachen-Compiler für AVR. Darin werden wir Programme für den Mikrocontroller entwickeln und direkt daraus wird es möglich sein, einen echten MK zu flashen.
Starten Sie Proteus nach der Installation
Er lädt uns ein, uns die mit ihm einhergehenden Projekte anzusehen, wir lehnen höflich ab. Lassen Sie uns nun die einfachste Schaltung darin erstellen. Klicken Sie dazu auf das Symbol und optisch passiert nichts. Jetzt müssen Sie auf den Kleinbuchstaben klicken P (aus Bibliothek auswählen) Im Komponentenlistenbereich öffnet sich das Komponentenauswahlfenster
Geben Sie im Maskenfeld den Namen der Komponente ein, die wir in der Bibliothek finden möchten. Beispielsweise müssen wir einen Mega8-Mikrocontroller hinzufügen
Zeigen Sie in der Ergebnisliste auf Mega8 und drücken Sie die Taste OK. Der Mega8-Mikrocontroller erscheint in unserer Komponentenliste
Daher fügen wir der Komponentenliste einen weiteren Widerstand hinzu, indem wir das Wort Maske in das Feld eingeben res und LED LED

Um Teile im Diagramm zu platzieren, klicken Sie auf das Teil, dann auf das Diagrammfeld, wählen Sie die Position der Komponente aus und klicken Sie erneut. Um dem Diagramm links eine Erdung oder ein allgemeines Negativ hinzuzufügen, klicken Sie auf „Terminal“ und wählen Sie Erdung. Indem wir alle Komponenten hinzufügen und verbinden, erhalten wir diese einfache Schaltung
Das war's, jetzt ist unser erstes Schema fertig! Aber Sie fragen sich vielleicht: Was kann sie tun? Nichts. Nichts, denn damit der Mikrocontroller funktioniert, müssen Sie ein Programm dafür schreiben. Ein Programm ist eine Liste von Befehlen, die der Mikrocontroller ausführt. Wir benötigen den Mikrocontroller, der auf einem Bein montiert wird PC0 logisch 0 (0 Volt) und logisch 1 (5 Volt).

Schreiben eines Programms für einen Mikrocontroller

Wir werden das Programm in C-Sprache mit dem CodeVisionAVR-Compiler schreiben. Nachdem wir den Lebenslauf ausgeführt haben, werden wir gefragt, was wir erstellen möchten: Quelle oder Projekt Wir wählen Letzteres aus und klicken auf OK. Als nächstes werden wir aufgefordert, den CVAVR CodeWizard-Assistenten zu starten (dies ist ein unschätzbares Werkzeug für Anfänger, da es das Hauptgerüst des Programms generieren kann). wählen Ja
Der Assistent beginnt mit der aktiven Registerkarte „Chip“, hier können wir das Modell unseres MK auswählen – das ist Mega8, und die Frequenz, mit der der MK arbeiten soll (standardmäßig ist Mega8 auf eine Frequenz von 1 Megahertz eingestellt), also legen wir fest Alles wie im Screenshot oben gezeigt. Gehen Sie zur Registerkarte „Ports“.
Der atmega8-Mikrocontroller verfügt über 3 Ports: Port C, Port D, Port B. Jeder Port hat 8 Pins. Port-Beine können zwei Zustände haben:
  • Ausfahrt
Mit dem DDRx.y-Register können wir den Pin als Eingang oder Ausgang festlegen. Wenn drin
  • DDRx.y = 0 - die Ausgabe funktioniert wie folgt EINGANG
  • DDRx.y = 1 Pin läuft weiter AUSFAHRT
Wenn der Pin als Ausgang konfiguriert ist, können wir ihn auf logisch 1 (+5 Volt) und logisch 0 (0 Volt) setzen. Dies geschieht durch Schreiben in das PORTx.y-Register. Als nächstes werden wir ausführlich über I/O-Ports sprechen. Nun stellen wir alles wie im Screenshot gezeigt ein und klicken auf Datei->Generieren, Speichern und Beenden. Als nächstes fordert uns CodeWizard auf, das Projekt zu speichern. Wir speichern es und sehen uns den Code an:

#enthalten //Bibliothek zum Erstellen von Zeitverzögerungen void main(void) ( PORTB=0x00; DDRB=0x00; PORTC=0x00; DDRC=0x01; // PC0-Pin zu einem Ausgang machen PORTD=0x00; DDRD=0x00; // Timer/Zähler 0 Initialisierung TCCR0=0x00; // Timer/Zähler TCCR1A=0x00; OCR1BL=0x00; // Initialisierung von Timer/Zähler ) Initialisierung TIMSK=0x00; // Analogkomparator-Initialisierung ACSR=0x80;


Hier mag Ihnen alles beängstigend und ungewohnt vorkommen, aber in Wirklichkeit ist nicht alles so. Der Code kann vereinfacht werden, indem die Initialisierung von MK-Peripheriegeräten, die wir nicht verwenden, entfällt. Nach der Vereinfachung sieht es so aus:

#enthalten //Bibliothek für die Arbeit mit dem Mega8-Mikrocontroller #include //Bibliothek zum Erstellen von Zeitverzögerungen void main(void) ( DDRC=0x01; /* den PC0-Pin zum Ausgang machen Der Eintrag 0x01 mag Ihnen unbekannt vorkommen, und dies ist nur die Zahl 1 in hexadezimaler Form, diese Zeile ist äquivalent zu 0b00000001 im Binärformat, dann werde ich so schreiben.*/ while (1) ( )


Alles ist gut. Damit die LED jedoch blinkt, müssen wir den Logikpegel am PC0-Pin ändern. Dazu müssen Sie der Hauptschleife mehrere Zeilen hinzufügen:

#enthalten //Bibliothek für die Arbeit mit dem Mega8-Mikrocontroller #include //Bibliothek zum Erstellen von Zeitverzögerungen void main(void) ( DDRC=0x01; /* den PC0-Pin zum Ausgang machen Der Eintrag 0x01 mag Ihnen unbekannt vorkommen, und dies ist nur die Zahl 1 in hexadezimaler Form, diese Zeile ist äquivalent zu 0b00000001 im Binärformat, dann schreibe ich es genau so.*/ while (1) // Hauptschleife des Programms (// öffnet die Operatorklammer der Hauptschleife des Programms PORTC.0=1; / / setze Pin 0 von Port C 1 auf „delay_ms(500); // setze eine Verzögerung von 500 Millisekunden PORTC.0=0; // setze Port C auf Pin 0 delay_ms(500); // setze eine Verzögerung von 500 Millisekunden“ ;//schließt die Operatorklammer der Hauptprogrammschleife)


Das war's, der Code ist jetzt fertig. Klicken Sie auf das Symbol „Alle Projektdateien erstellen“, um unser Programm zu kompilieren (in MK-Prozessoranweisungen zu übersetzen). Im Exe-Ordner, der sich in unserem Projekt befindet, sollte eine Datei mit einer Hex-Erweiterung erscheinen, dies ist unsere Firmware-Datei für den MK. Um unsere Firmware dem virtuellen Mikrocontroller in Proteus zuzuführen, müssen Sie auf das Bild des Mikrocontrollers in Proteus doppelklicken. Es erscheint ein Fenster wie dieses
Klicken Sie auf das Ordnersymbol im Feld „Programmdatei“, wählen Sie die Hex-Datei unserer Firmware aus und klicken Sie auf „OK“. Jetzt können wir eine Simulation unserer Schaltung durchführen. Klicken Sie dazu auf die Schaltfläche „Play“ in der unteren linken Ecke des Proteus-Fensters.


Aktie