Übersetzungen dieser Seite:

Dies ist eine alte Version des Dokuments!


TPS/SPS Assembler

Die Idee kam mir im Zusammenhang mit der Arduino SPS. Es ist zwar toll, mal eben an der Hardware das Programm ändern zu können. Das funktioniert im Wohnzimmer oder am Schreibtisch auch sehr gut. Ich setzt die SPS aber eben auch in meinen RC Modellen ein und da habe ich selten die Möglichkeit, nach Einbau, an die Tasten zu gelangen. Geschweige denn in aller Geduld die Codes einzuhacken. Ein smartes Handy hat ja mittlerweile jeder dabei. Also dachte ich mir eine Möglichkeit aus, wie ich mit Handy, etwas Hardware und einem Internetdienst meine Programme in die SPS bekomme.

Dazu braucht es

  • eine SPS der neuesten Firmware (>.10)
  • ein Smartphone mit Internet und USB OTG
  • ein USB OTG Kabel
  • einen FTDI
  • und ein Stück Kabel

Der tatsächliche Download des Programms auf die SPS erfolgt über ein serielles Protokoll. Das kann man sogar mit einem einfachen Terminalprogramm (oder dem eingebauten seriellen Monitor in der ArduinoIDE) machen. Gestartet wird dieser neue Programmiermodus, wenn nach einem Reset der SPS die SEL (und zwar nur Diese) Taste gedrückt bleibt. Das Protokoll kennt nur 3 Befehle R(ead), W(rite) und E(xit). Mit Read wird das aktuelle Programm ausgegeben und mit Write ein neues eingelesen. Format ist bei beiden Optionen Intel HEX. Mit Exit oder einem erneuten Reset verlässt man diesen Modus wieder.

Um mit dem Handy mit der SPS zu kommunizieren, braucht man zusätzlich einen USB/Seriell-Konverter. Die Gängigsten sind findet man unter der Bezeichnung FTDI. Das ist ein einfacher USB Seriell Konverter, der sowohl mit dem PC wie auch mit einem Handy funktioniert. Weiterhin muss das Handy USB OTG unterstützen. (Einfach mal bei seinem Handy googeln.) Damit haben wir eine einfache serielle Schnittstelle am Handy. Jetzte fehlt nur noch ein Terminalprogramm auf dem Handy, fertig.

Zu allerletzt muss man seine Programmidee in das IntelHEX Format konvertieren. Dazu dient dieser kleine Service hier auf der Webseite. Der Assembler übersetzt, wie jeder andere Assembler, handgeschriebenen Mnemonic Code in ein maschinenlesbares Programm. Hier eben in ein Programm für die TPS/SPS.
Dabei werden folgende Features unterstützt:

  • Ausgabeformate: Intel HEX Code, SPS Emulator Source und HEX Text
  • Eingabeformate: TPS Assembler
  • Unterstützung von folgenden TPS Systemen: HOLTEK, ATmega8, Arduino SPS, ATtiny84 SPS
  • automatische Berechnung von Sprüngen und Unterprogrammen
  • Unterstützung von Labels für Sprünge
  • Unterstützung von Kommentaren
  • Unterstützung von Makros
  • Unterstützung verschiedener Assemblerdirektriven

Die Online-Version findest du hier: Online TPS Assembler public BETA
Dabei werden folgende Mnemonics unterstützt:

Programmsteuerung

Mnemonic short description Beschreibung
NOP No Operation es wird nichts gemacht, aber die Ausführung kostet trotzdem etwas Zeit und Prozessorzyklen
WAIT #x wait <time> Verzögerung des Programms, 1⇐ x ⇐ 15, nach der vorher stehenden Tabelle . Wahlweise kann auch ein String verwendet werden. Mögliche Werte dazu: 1ms, 2ms … 60s.
RJMP #x Jump back Sprung zurück um x Befehle, oder zu :label
PAGE #x page #x Setzen des Page Registers 0 ⇐ x < 16
JMP #x jump to #x + 16 * page Springe nach Adresse #x + (16 * page) 0 ⇐ x < 16
LOOPC #x loop c Ist C>0: C=C-1; Springe nach #x + (16*page), sonst gehe zum nächsten Befehl
LOOPD #x loop d Ist D>0: D=D-1; Springe nach #x + (16*page), sonst gehe zum nächsten Befehl
SKIP0 Skip if A = 0 Überspringe nächsten Befehl wenn A = 0
AGTB Skip if A > B Überspringe nächsten Befehl wenn A > B
ALTB Skip if A < B Überspringe nächsten Befehl wenn A < B
AEQB Skip if A = B Überspringe nächsten Befehl wenn A = B
DEQ#y #x Skip if Din.X = Y Überspringe nächsten Befehl wenn der Eingang #y 0 oder 1 ist. x=0,1
PRG#x Skip if PRG = x Überspringe nächsten Befehl wenn der PRG TAster gedrückt(0) oder nicht gedrückt (1) ist. x=0,1
SEL#x Skip if SEL = x „ nur für SEL
CALL #x Call #x + (16 * page)
RTR Return Zurück springen nach einem Call-Befehl oder am Ende einer Subroutine
CASB #x Call sub #x Aufruf der Subroutine x, 1 ⇐ x ⇐ 6
DFSB #x Define sub #x Definierung der Subroutine x, 1 ⇐ x ⇐ 6
REST Restart program
PEND Program end

Laden und Speichern

Mnemonic short description Beschreibung
LDA ##x A = #x Register A wird mit dem festen Wert #x geladen
SWAP Swap A & B Tauschen der Register A und B
MOV X,Y X = Y kopiert ein Register in ein anderes X und Y dürfen folgende Werte annehmen: A, B, C, D, E, F
PUSH push A on stack Das A Register wird auf dem Stack gelegt
POP Pop value from stack to A Wert aus dem Stack in das A Register übertragen

Mathematik

Mnemonic short description Beschreibung
INC A = A + 1 Increment A
DEC A = A - 1 Decrement A
ADD A = A + B
SUB A = A - B
MUL A = A * B
DIV A = A / B
AND A = A and B
OR A = A or B
XOR A = A xor B
NOT A = not A
MOD A = A % B
BYTE A = A + 16 * B
BSUBA A = B - A

Ein/Ausgabe

Mnemonic short description Beschreibung
LDA DIN A = Din Register A wird mit dem Wert vom digitalen Eingang geladen. Alle 4 Bit.
LDA DINx A = Din.x Register A wird mit dem Wert vom digitalen Eingang #x geladen. x = 1..4
LDA ADCx A = ADC.x Register A wird mit dem Wert vom analogen Eingang #x geladen. x = 1..2
LDA RCx A = RC.x Register A wird mit dem Wert vom RC Empfängereingang #x geladen. x = 1..2
PORT #x Dout = #x Direkte Ausgabe von Wert #x auf den Ausgängen. 0 ⇐ x ⇐ 15.
STA DOUT Dout = A Ausgabe von A auf den Ausgängen
STA DOUTx Dout.x = A Ausgabe von A.x auf den Ausgang x = 1..4
STA PWMx PWM.x = A Der Wert aus dem A Register wird als PWM.x ausgegeben. x = 1,2
STA SRVx Servo.x = A Der Wert aus dem A Register wird als Servo.x ausgegeben. x = 1,2

Byte Befehle

Mnemonic short description Beschreibung
BLDA ADCx A = ADC.x Bytewert auslesen eines Analogeinganges x = 1,2
BLDA RCx A = RC.x Bytewert auslesen eines Fernsteuerungskanals x = 1,2
BSTA PWMx PWM.x = A Der Byte Wert aus dem A Register wird als PWM.x ausgegeben. x = 1,2
BSTA SRVx Servo.x = A Der Byte Wert aus dem A Register wird als Servo.x ausgegeben. x = 1,2
TONE Tone A Ausgabe eines Tones nach Midi 32 ⇐ A ⇐ 108

Labels

Für die Jump, Loop und Call Befehle kann auch ein Label verwendet werden. Der zugehörige Page Befehl muss aber selber implementiert werden. Die Berechnung der Sprungadresse erfolgt dabei automatisch, wenn das Argument des Page Befehles als :? angegebenen wird. Beispiel:

:Loop
PORT #4
WAIT 200ms
PORT #0
WAIT 200ms
PAGE :?
JMP :Loop

Labels sind bei den Mnemonics RJMP, JMP, LOOPC, LOOPD und CALL erlaubt. Auch die beiden erweiterten Befehle DFSB und CASB zur Unterprogrammausführung unterstützen Labels.
Somit funktioniert auch sowas:

CASB :Blink
...

DFSB :Blink
PORT #4
WAIT 200ms
PORT #0
WAIT 200ms
RTR

Kommentare

Kommentare sind erlaubt. Es gibt 2 Arten von Kommentaren. Blockkommentare gehen über mehrere Zeilen, Starten mit „\*“ und Enden mit „*/„
Zeilenkommentare werden mit ; gestartet, stehen hinter dem Argument und der Assembler ignoriert den Rest der Zeile.
Beispiel:

/*
Kommentar über mehrere Zeilen
*/
:loop
PORT #0x0F ; Zeilenkommentar
WAIT 200ms
PORT #0x00
WAIT 200ms
RJMP :loop

JMP, CALL, LOOPx und PAGE

Für die absoluten Sprünge werden immer das Argument des Mnemonics und zusätzlich der Inhalt des PAGE Registers verwendet. Die Adresse berechnet sich dann aus: adr = #x + (16 * PAGE). Nun muss man sich vorher die PAGE berechnen und den entsprechenden PAGE Befehl vor den Sprung setzten. Hier kann einem der Assembler bei der Berechnung helfen. Steht direkt vor dem Sprung mit Label ein PAGE mit Argument :? berechnet der Assembler selber die PAGE Nummer. Beispiel:

PAGE :?
JMP :loop2
:loop
PORT #0x0F
WAIT 200ms
:loop2
PORT #0x00
WAIT 200ms
RJMP :loop

Hier wird automatisch die richtige Seite in den ersten PAGE Befehl eingetragen.

Makros

Es können auch zur Vereinfachung von Programmen Makros verwendet werden. Zunächst muss man ein Makro definieren. Beispiel hier mal die Blink Funktion:

PORT #0B1111
WAIT 200ms
PORT #0B0000
WAIT 200ms

Um diese als Makro an verschiedenen Stellen einsetzen zu können, muss man zunächst die Stelle als Makro markieren. Das macht man, indem man ein mit .macro und .endmacro Anfang und Ende markiert. Hinter .macro muss man dann den Namen des Makros schreiben, unter dem man das Makro dann verwenden möchte. Ergebnis:

.macro blink
PORT #0B1111
WAIT 200ms
PORT #0B0000
WAIT 200ms
.endmacro

Jetzt kann man bestimmte Werte auch noch variabel gestalten. Also hier z.B. welche LEDs blinken sollen und wie lange die Wartezeit sein soll.

.macro blink output delay
PORT output
WAIT delay
PORT #0B0000
WAIT delay
.endmacro

Im eigentlichen Programm kann man das Makro mit z.B.

.blink #0B1010 200ms

aufrufen.

Makros müssen vor dem ersten Aufruf definiert sein. Das gesamte Programm lautet also nun:

.macro blink output delay
PORT output
WAIT delay
PORT #0B0000
WAIT delay
.endmacro

:loop
.blink #0B1111 200ms
RJMP :loop

Direktriven

Der Assembler unterstützt verschiedene Direktriven. Einige haben wir schon kennengelernt, die Macros, Loops…

Weitere:

  • .include
  • .arduinosps: damit wird die Hardware auf ArduinoSPS festgelegt. Diese Directrive sollte vor dem eigentlichen Code erscheinen.
  • .tinysps: legt die Hardware auf die TinySPS fest.
  • .atmega8: legt die Hardware auf die ATMega8 fest.
  • .holtek: legt die Hardware auf Holtek fest.

.include

Mit dieser Direktrive kann man ein Unterprogramm aus einer anderen Datei einbinden. (Dieses Feature wird im Webmodus nicht unterstützt) mit ``.include Blink `` wird eine Datei Blink.tps an dieser Stelle mit ins Programm eingebunden. Damit steht der gesamte Code dieser Datei an dieser Stelle als wäre er dort in der originalen Datei hingeschrieben worden.

Programmgrößen

Jede Hardware hat andere max. Programmgrößen.

  • HOLTEK: 128 Byte
  • ATmega8: 256 Byte
  • ArduinoSPS: 1024 Byte*
  • TinySPS: 512 Byte*

* Alles über 256Byte kann nur durch die Verwendung von Unterroutinen (DFSB und CASB) benutzt werden.

Upload der Daten

Mit dem Online SPSAssembler kannst du deinen Quellcode eingeben und kompilieren lassen. Als Ausgabeformat gibt es ein einfaches Textfile mit den entsprechenden Darstellung zur direkten Eingabe in die TPS.
Oder aber du kannst dir das ganze auch als Intel HEX File ausgeben lassen. Meine ArduinoSPS und die TinySPS können beide das Format über die serielle Schnittstelle empfangen. Dazu sind folgende Schritte nötig:

  • HEX Datei erzeugen und herunter laden.
  • Handy mit FTDI (über ein USB OTG Kabel) verbinden.
  • Den FTDI mit der seriellen Schnittstelle der Hardware verbinden.
  • Terminalprogramm auf dem Handy starten und mit dem FTDI verbinden. 9600Baud.
  • Auf der TPS Reset und SEL gleichzeitig drücken. SEL muss man gedrückt lassen, bis sich die TPS im Terminalprogramm meldet.
  • Mit „w“ in den Programmsendemodus wechseln und schauen, ob sich die TPS im Programmiermodus befindet.
  • Dann die HEX Datei mit dem Terminalprogramm auf die TPS laden.
  • Jetzt noch ein Reset oder „E“ und fertig.

Serieller Anschluss

Beim ATTiny84 werden dazu die Pins 11 (RX) und Pin 12 (TX) verwendet.
Benutzt wird ein Protokoll mit 9600Baud, 0 Parität, 1 Stoppbit.

Der Anschluss erfolgt bei der TinySPS Platine an der Input Leiste.

Pin Signal FTDI
2 Din.2, TX RX
3 Din.3, RX TX
5 GND GND
arduino/arduinosps/tpsass.1599490837.txt.gz · Zuletzt geändert: 2020/09/07 15:00 von admin
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0