AVR BASIC

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Motivation

Ausgaben dedr AVR-Testumgebung via serieller Schnittstelle

Welcher Mikrocontroller-Programmierer kennt das Problem nicht: man hat eine schicke Firmware auf den MC gebrannt, braucht schnell eine neue (einfache) Funktionalität und will/kann nicht gleich an den C-Code der Firmware ran. Was liegt also näher, Funktionen beliebig "nachladen" und ausführen zu lassen? Dieses Ansinnen mit Binär-Code-Fragmenten zu machen, dürfte ein schwieriges, wenn nicht sogar unmögliches Unterfangen sein. Und man ist wieder von einem Kompiler abhängig. Script-Sprachen sind da viel besser geeignet, da sie verständlicher und leichter zu programmieren sind. Voraussetzung ist dabei natürlich, dass ein entsprechender Script-Interpreter auf der Zielplattform verfügbar ist.

Jetzt könnte man natürlich einen eigenen und u.U. speziell für Mikrocontroller designden Sprachsyntax entwickeln und implementieren (Bsp. ECMDSript bei ethersex). Viel sinnvoller erscheint es aber, wenn man auf alt bewährte und bekannte Dinge zurückgreift: jeder (alte) Programmierer hat mal mit Basic angefangen oder zumindestens davon gehört! Der Zufall wollte es, dass Adam Dunkle ein C-Gerüst für einen kleinen und ressourcenschonenden TinyBasic-Interpreter (uBasic) veröffentlicht hat. Mit minimalen Modifikationen ist das Ding auch auf einem AVR sofort lauffähig und beeindruckt durch den geringen Ressourcen-Verbrauch.

Ziel ist es, einen universellen Basic-Interpreter zu haben, der in andere, in C geschriebene AVR-Programme einfach eingebunden werden kann. Die Basic-Programme sollen dann über vorhandene Schnittstellen (seriell, Ethernet o.ä.) geladen werden oder sind auf einem externen Speichermedium (SD-Card, Dataflash o.ä.) verfügbar und einlesbar.

Es gibt noch einige andere Projekte, die Basic-Interpreter für AVR-Hardware zur Verfügung stellen, aber sich von dem hier vorgestellten Projekt dahingehend abgrenzen, da sie quasi einen eigenständigen Basic-Computer (vergleichbar mit dem Home-Computern der späten 80er Jahre) darstellen:

Auf Adam Dunkles Urversion aufsetzend, entstand AVR-uBasic, welches mittlerweile in mehreren Versionen von unterschiedlichen Entwicklern gibt. Diese differierenden Versionen sind dabei den jeweils etwas anderen Zielvorstellungen der Entwickler geschuldet. Im Forum findet man dazu auch einige Diskussionsbeiträge:

In der Folge sollen hier diese unterschiedlichen Versionen mit ihren Zielen, Eigenheiten und Entwicklungsstand vorgestellt werden.

Version von Uwe Berger

Ziele, Merkmale, Einschränkungen, Ausblick

Ziel:

Für ein anderes Mikrocontroller-Projekt wurde eine Möglichkeit gesucht, externe Programme innerhalb einer bestehenden Firmware auf dem MCU ausführen zu können. Da der Basic-Interpreter also nur ein Bestandteil innerhalb (teilweise sehr umfangreicher) Firmware sein sollte und man doch recht beschränkt bei den zur Verfügung stehenden Resourcen ist (SRAM, Programmspeicher etc.) ist, wurde in dieser Version von AVR-Basic besonders darauf geachtet, das wenig Maschinencode und dynamischer Speicher verbraucht wird.

Merkmale:

  • resourcenschonender Verbrauch an Programm- und dynamischen Speicher
  • einfach in andere Applikationen integrierbar (Plattformen: AVR, Linux)
  • Implementierung der wichtigsten Basic-Befehle (ungefähr vergleichbar mit einem TinyBasic), also u.a.:
    • for/next (mit to/downto und step möglich)
    • goto, gosub
    • if/then/else
    • print (auf Standardausgabe)
    • Grundrechenoperationen
  • Basic-Sprachumfang und einige Interna via Konfig-Datei einstellbar
  • unterstützte Variablen-/Daten-Typen:
    • vorzeichenbehaftete Ganzzahlen
    • Zeichenketten
  • Basic-Befehle zum Ansprechen von mikrocontroller-spezifischer Hardware:
    • I/O-Ports
    • ADC
    • Lesen/Schreiben des EEPROM
    • Zufallsgenerator
  • als Zahlenformat auch hexadezimale und duale Darstellung möglich (nur Eingabe)
  • wenn hardware-spezifische Dinge in der Konfig-Datei abgewählt werden, auch auf anderen Plattformen einsetzbar
  • relativ einfache Erweiterung um neue Basic-Befehle, da das Programmgerüst leicht zu verstehen ist
  • Möglichkeit der Einbindung/Ansprechen von externen C-Prozeduren und -Variablen über spezielle Basic-Befehle
  • Einlesen des Basic-Programmes von (fast) beliebigen (aber statischen) Speichermedium (z.B. SD-Card, EEPROM, Dataflash)
  • mit dem Befehl gosub ist es möglich externe Unterprogramme einzubinden
  • Array-Variablen (DIM-Befehl)
  • es sind BASIC-Programme mit und ohne Zeilennummern erlaubt
  • DATA/READ/RESTORE

Einschränkungen (Basic-Sprachumfang):

  • nur ein Basic-Befehl auf einer Programmzeile; kein Zeilenumbruch in einem Konstrukt möglich
  • kein repeat/until oder while/do
  • keine gebrochenen Zahlen

Implementierung

Die vorliegende Version orientiert sich am Grundgerüst der Urversion von Adam Dunkle. Deren Aufteilung in Parser (Tokenizer) und Basic-Befehls-Ausführung wurde beibehalten. Die Erweiterungen wurden einfach in dieses Grundgerüst mit den vorgegebenen Mechanismen integriert. Es wurden einige Verbesserungen in Hinsicht auf Speicherverbrauch und Laufzeitoptimierungen implementiert.

Der Interpreter ist als Bibliothek zu verstehen, die in eigenen Applikationen eingebunden und aufgerufen werden kann. Dazu sind lediglich die entsprechenden Header-Dateien einzubinden und einige Bibliotheks-Funktionen aufzurufen (siehe Dokumentation). Ein Beispiel dazu ist im Quelltextarchiv zu finden.

Testhardware

AVR-Hardware zum Test des Basic-Interpreters

Erste Versuche mit dem originalen uBasic kann man auf jeder Plattform machen, für die es auch einen Standard-C-Kompiler gibt. Das funktioniert auf Anhieb. Bei diesen ersten Experimenten wurden in der Urversion ein paar kleinere Bugs gefunden und teilweise bereits behoben.

Interessant wird die Sache, wenn man zusätzlich Mikrocontroller-spezifische Befehle implementieren möchte. Dazu braucht man natürlich eine entsprechende Hardware-Umgebung. Dazu reicht ein einfaches Board auf Lochraster mit folgenden Komponenten in der jeweiligen Standardbeschaltung:

  • Mega168; 16MHz-Quarz; Reset-Taster
  • MAX232 für die serielle Schnittstelle
  • ISP-Anschluss, über den die Schaltung auch mit Strom versorgt wird
  • ein paar herausgeführte I/O-Pins, um mal einen Taster oder eine LED anschliessen zu können

Dokumentation

Es befindet sich eine (halbwegs) ausführliche Dokumentation, in Form einer PDF-Datei, im entsprechenden Queltextzweig. Dort wird der derzeitige Basic-Sprachumfang, die Konfiguration des Interpreters und die Einbindung in eigene Programme beschrieben.

Version von René Böllhoff

Features,Anbindung,Erweiterung,Compiler,Ausblick

Features

  • Compiler-Unterstützung
  • Zeichengerät
  • C-Erweiterungen
  • Funktionen als Operatoren
  • Tokentyp sowie "Zeigertyp" definierbar
  • Fehlertexte aktivieren/deaktivieren
  • Weitere Basic-Statements können "eingehängt" werden
  • weitestgehende Kapselung

Beschreibung der Features und die Anbindung an bestehende Hard&Software

Basierend auf der Version von Adam Dunkels gelten prinzipbedingt diesselben Einschränkungen wie Uwe Berger schon erwähnt hat :

  • max 26 Variablen
  • nur signed integer
  • nur 1 Basic-Statment pro zeile (ausg. if/then/else)


Anbindung bzw Hard&Software

Eine "Zielhardware" in dem Sinne gibt es nicht. Ich habe das Demo auf einer Experimentierplatine aufgezogen auf der ein ATMega32 mit 16MHz werkelt. Einzig angebunden sind ein MAX232 bzw. MAX202 sowie eine SD-Karte nebst Pegelwandler. Eigentlich war das Board für etwas anderes gedacht (Update mehrerer AVR's mittels eines zus. AVR der als ISP-Programmer fungiert, inkl. SD-Karte), aber es ergab sich halt so.

Die softwareseitige Anbindung dieser Version ist relativ einfach. Man übergibt zuerst den Basic-Quell-Text an den Tokenizer. Die Übergabe funktioniert im einfachsten Fall über einen Zeiger auf den Text im RAM. Um das Basic-Programm dann auszuführen, führt man in einer Schleife ubasic_run () aus, die dann beendet wird wenn das Ergebnis der Funktion ubasic_finished() ungleich 0 ist. Danach ist das Basic-Programm angearbeitet.

Zeichengerät

Da das RAM auf einem AVR bekanntlich begrenzt ist (wie bei jedem anderen Computersystem) gibt es die Möglichkeit ein Zeichengerät zu verwenden. Dieses Kapselt den Zugriff auf den Text, womit es möglich wird unterschiedlichste Medien (Flashspeicher, SD-Karte, div. Blockmedien, externes SRAM, usw.) zur Ausführung des Basic-Codes (in Textform ebenso wie in vorcompilierter Form) verwenden zu können (so die Theorie). Die Zeichengeräte bestehen aus lediglich 5 einfachen Funktionen, die irgendwo im restlichen Quellcode vorhanden sein müssen. Die externals dazu werden automatisch erzeugt.

C-Erweiterungen

Bei C-Erweiterungen sind die Funktionen und Variablen ebenfalls automatisch als externals eingebunden und müssen sich so lediglich irgendwo im restlichen Quellcode im Zugriff befinden. Die Erweiterungen umfassen den Aufruf von C-Funktionen, den Zugriff auf C-Integer-Variablen (lesend wie schreibend), sowie den Zugriff auf C-Arrays (ebenfalls lesend und schreibend). Die Definition der Erweiterungen werden in der Konfigurationsdatei "basic_cfg.h" im Abschnitt BASIC_USES_EXTENSIONS festgelegt und es können einfach weitere Einträge hinzugefügt werden, die den Sprachumfang und die Möglichkeiten erweitern.

Compiler-Unterstützung

Für die Compiler-Unterstützung benötigt man eine Funktion die ein Byte wegschreiben kann. Sinnvollerweise sollte das Ziel entweder das SRAM sein oder aber eines der verwendeten Medien wohin die erzeugten Bytes verfrachtet werden, damit sie von dort aus später präcompiliert entsprechend schnell (Stichwort : JIT) ausgeführt werden können. Es gibt für den Compiler eine einzige Funktion, die den vorliegenden Basic-Quell-Text in Tokens und dazugehörige Daten übersetzt. Dieser Vorgang ergibt insgesamt einen extrem hohen Geschwindigkeitsvorteil, bei gleichzeitiger Reduzierung der ursprünglichen Quellcode-Größe (ca. 10-20%).

Um eine Bereichsprüfung bei Operatoren zu ermöglichen, kann man die Operatoren durch eigene Funktionen ersetzen. Dadurch ist es möglich z.b. bei Division durch 0 einzuschreiten.

Erweiterung

Die C-Erweiterungen umfassen wie schon angesprochen das Aufrufen von C-Funktionen (mit Parametern), das lesen und schreiben von C-Variablen, sowie C-Arrays. Die aufzurufenden C-Funktionen haben alle (!!) dieselbe Syntax :

I16 name (T_USER_FUNC_DATAS stParams)

Der Rückgabewert ist 16 Bit Signed und kann direkt in Basic-Ausdrücken verwendet werden, sprich es gibt in dem Sinne nur Funktionen und keine Prozeduren (ohne Rückgabewert). Der Typ der übergebenen Parameter besteht aus einem Array aus Einträgen. Jeder eintrag enthält ein Byte welches den Typ widerspiegelt der den aktuellen Parameter repräsentiert. Zur Zeit gibt es nur Integer und String. Der Eintrag besteht weiterhin aus einer Union eines Integers und eines Strings. Die Prüfung auf korrekte Übergabe der Typen muß von der aufzurufenden Funktion selbst vollzogen werden. Dafür gibt es noch keinen Mechanismus. C-Variablen können wie schon erwähnt beschrieben und gelesen werden. Bei C-Arrays gilt dasselbe, allerdings lässt sich hier noch mit einem weiteren Parameter in der Definition die Array-Größe festlegen. Im uBasic wird diese Grenze respektiert und auch geprüft. Als Erweiterung ist vorgesehen das die Typ-Prüfung bei C-Aufrufen noch durchgeführt wird, sowie bei den C-Variablen/Arrays ein Modus gesetzt werden kann der Variablen/Arrays vor dem beschreiben/auslesen schützt, quasi ein Read/Write-Flag. Außerdem soll es die Möglichkeit geben, die Arrays nicht direkt zu beschreiben/zu lesen, sondern die Werte über eine Funktion zu verarbeiten. Damit lässt sich eine dynamische Größe des Arrays realisieren. Weiterhin wäre dies die Kapselung wie man sie von den Properties in der WIN32 Programmierung her kennt.

Compiler

Der Compiler ist mit das interessanteste Feature an meiner Version. Mit diesem ist es möglich die reinen Basic-Texte als binäre Tokenliste (inkl. der ggf vorhandenen weiteren Informationen) und zus. ersetzten Sprungmarken für Gosub/Goto-Aufrufe abzuspeichern, wodurch das langwierige Durchsuchen des Quellcodes nach den Zeilennummern entfällt, und aufzurufen. Die Abarbeitung des Basic-Quelltextes erfolgt im Binär wie im Text-Modus gleichermaßen, bzw unterscheidet sich nur geringfügig in einzelnen Ausführungsfunktionen. Dadurch wird der Code transparent ausgeführt. Der Geschwinigkeitsvorteil der sich ergibt ist allerdings enorm (durch Wegfall der zeitraubenden String-Suche und noch zeitraubenderen Suche nach Zeilennummern). Je nach Art des Basic-Programms ist selbst der Compilierungsvorgang inkl Ausführung mehrfach so schnell als wenn der Text uncompiliert abgearbeitet wird. Um das Compilerfeature nutzen zu können benötigt man wie schon erwähnt eine einfachen Funktion die dem Compiler mit übergeben wird, welche ein vom Compiler erzeugtes Byte wegschreibt. Sinnigerweise ist diese Schreibfunktion so geartet das es entweder in ein vorhandenes TOKENIZER_MEDIUM, oder aber die Tokens in das RAM wegschreibt.

Ausblick

  • Testen, optimieren und erweitern der aktuellen Version
  • "Unterpgramme" & Parameterübergabe
  • Erweitern um mehrere Datentypen

Testen, optimieren und erweitern der aktuellen Version

Mit Testen und Optimieren ist in erster Linie die Funktionalität und deren Test der vorliegenden Version gemeint. Dazu zählen die Erweiterungen die Uwe Berger schon in seiner Version "drin und getestet" hat. Des weiteren ist der Test gemeint, bei welchem eine SD-Karte als Medium verwendet wird um text- wie vorcompilierte-Versionen von Basic-Programmen auszuführen.

"Unterpgramme" & Parameterübergabe

Dieser Ausblick dürfte sehr interessant sein. Es geht sich dabei darum Unterprogramme von einem belibigen Medium ausführen zu können. Dabei können (so die Idee) Parameter dem auszuführenden Unterprogramm mit übergeben werden. Dafür ist es allerdings notwendig zumindest einen neuen Variablenkontext für das Unterprogramm zu schaffen. Dieser wird dann während der Abarbeitung vom Unterprogramm verwendet, und nach dessen Beendigung freigegeben. Dabei soll die Möglichkeit der "Rückübergabe" von Parametern (integer) in Variablen vorgesehen sein.


Erweitern um mehrere Datentypen

Dieser Punkt dürfte die am leisesten klingende Zukunftsmusik sein, obwohl ich dazu auch schon einige Ideen habe. Wobei falls es zu einer solchen Version kommt, diese nur schwerlich bzw mit Hindernissen implementierbar sein dürfte.

Quellcode

Die aktuellesten Versionen sind im SVN auf mikrocontroller.net zu finden.

Links

  • [1] Diskussionsthread im Mikrocontrollerforum
  • [2] ein Vortrag auf dem CLT2011 zum BASIC-Interpreter
  • [3] das c't-Bot-Projekt setzt den BASIC-Interpreter ein