Tips mit PICs 10: Die Befehle #define und #ifdef
        
      
    
      
	Auch dieser 'Tip' ist ein Hinweis, was man machen könnte, aber nicht unbedingt machen 
	müßte.
	Einschränkung: Wir beziehen uns hier auf das Programmsystem MPLAB IDE von microchip, das kostenlos
	heruntergeladen werden kann.
	
	Wenn man von der Programmierung mit Hochsprachen kommt (man kann auch BASIC dazurechnen), dann nervt die 
	'Bit-Klopferei' der Assemblersprache doch gewaltig. Nur ein Beispiel: Es braucht immer etliche Phantasie, Befehle
	zur  Null-Abfrage richtig zu interpretieren, zumal bei 'Null' das Zero-Bit im STATUS-Register auf '1' abgefragt
	werden muß. Da ist jedesmal eine Art Stolperstein integriert. Wenn Sie lesen
      
      
	
	btfss STATUS,Z
	
      
      
	dann erschließt sich der Sinn des Befehles nicht sofort. Dies ist aber für das Verstehen des Programms 
	immens wichtig, da hier ja eine Verzweigung stattfindet. (Der Autor hat auch nach Jahren intensiver 
	Programmier-Tätigkeit immer noch Probleme!)
	Oder:
	Sie programmieren eine Steuerung, die viele Ausgänge einzeln setzen soll. Später, beim Entwerfen der 
	Platine, stellt sich aber heraus, daß sich viele Leiterbahnen am PIC überkreuzen würden und
	daß es daher sinnvoll wäre, diese zu entwirren. Kein Problem, nur müssen Sie in Ihrem Programm genau
	das Gleiche tun und dürfen dabei nicht einen einzigen Fehler machen. Ansonsten läuft Ihr Programm 'falsch'.
	
	Derartige Probleme kann man auf einfachste Weise lösen, indem man den Befehl
      
      
	
	#define
	
      
      
	einsetzt. Dieser ist 'nur' die Anweisung an das Assemblier-Programm, einen Text durch einen anderen zu ersetzen. So 
	steht es, reichlich dürftig, in der Beschreibung. Was man aber tatsächlich alles damit machen kann, 
	läßt sich kaum erahnen.
	
	Null-Abfrage:
	Sie meinen, mit einem Befehl, der z.B.
      
      
	
	ist_null?
	
      
      
	heißen könnte, kämen Sie besser zurecht als mit dem (oben beschriebenen) btfss-Konstrukt. Dann
	definieren Sie sich doch diesen neuen Befehl, indem Sie schreiben:
      
      
	
	#define ist_null? btfss STATUS,Z
	
      
      
	Sogar das Fragezeichen funktioniert. Damit sagen Sie dem Assemblier-Programm, es soll jedesmal, wenn es die 
	Zeichenfolge 'ist_null?' findet, stattdessen 'btfss STATUS,Z' einsetzen.
	Das war's schon. Nur: Leerzeichen sind nicht erlaubt. Wenn Sie sich selber noch einen Gefallen tun wollen, schreiben
	Sie vor die Definitions-Zeile noch eine Kommentarzeile, etwa so:
      
      
	
	; Ersatz der Null-Abfrage: skip, wenn Null
	
      
      
	Das hilft Ihnen später beim Lesen enorm; und auch anderen, die Ihr Programm lesen und verstehen sollen. Nun
	können Sie an beliebiger Stelle im Programm den neuen Befehl einsetzen, und wenigstens dieser Stolperstein ist
	weg.
	Der hier genannte Ersatz ist natürlich nur ein Vorschlag. Sie können jeden beliebigen Text (außer 
	einem bereits definierten Schlüsselwort) verwenden, Beispiele:
      
      
	
	skip_wenn_null
	sze
	
      
      
	Das Letztere ist aus dem Befehlssatz der alten AEG 60-10 entlehnt. Dort hieß die Nullabfrage 'tze' (test zero 
	equal); hier würde es analog heißen: 'skip zero equal'.
	Natürlich geht das auch mit Eigen-Konstruktionen für das ach so heißgeliebte Carry-Bit (bei dem man 
	immer wieder Überraschungen erlebt), wie z.B. mit:
      
      
	
 	ist_ueberlauf?
	ist_unterlauf?
	
      
      
	Bevor wir hier jetzt etwas Falsches schreiben: Sie wissen ja jetzt, wie es geht ...
	
	Setzen eines Bits an einem E/A-Port:
	Hier sollten Sie jeder Ein- und Ausgabe-Leitung einen sinnvollen Namen geben. Beispiel:
      
      
	
	#define Lampe1 PORTA,4
	
      
      
	Es ist klar erkenntlich, daß Sie mit Leitung 4 von Port A die Lampe 1 schalten wollen. Diese Namensvergabe 
	sollten Sie für jede Leitung vornehmen. Und im Programm können Sie jetzt ohne Kommentar schreiben:
      
      
	
	bsf Lampe1
	
      
      
	oder
      
      
	
	bcf Lampe1
	
      
      
	Nun brauchen Sie bei Änderungen in der Verdrahtung (Lampe 1 wird z.B. nunmehr von Port A, Leitung 5 geschaltet) 
	nur noch ein einziges Mal dieses 'define' zu ändern,
      
      
	
	#define Lampe1 PORTA,5
	
      
      
	und alles läuft wie vorher.
	
	Luxus-Version:
	Wenn Sie schon einmal beim 'Um-Definieren' sind, können Sie auch noch einen Schritt weiter gehen und 
	schreiben:
      
      
	
	#define Lampe1_ein bsf Lampe1
	#define Lampe1_aus bcf Lampe1
	
      
      
	oder
      
      
	
	#define Lampe1_ein bsf PORTA,4
	#define Lampe1_aus bcf PORTA,4
	
      
      
	Auch das doppelte Um-Definieren im ersten Vorschlag wird richtig verarbeitet. Die '#define'-Anweisung dürfen 
	Sie beliebig oft verwenden. Nur muß die Neu-Definition aus einem einzigen Wort bestehen, das bisher nicht 
	verwendet wurde und auch nicht als Kommentar auftritt. Ansonsten kommt der Assembler 'ins Schleudern'. Das zweite 
	Vorschlags-Pärchen ist auch möglich, aber nicht zu empfehlen, da das Ändern bei Um-Verdrahtung (s. 
	oben, PORTA,4 in PORTA,5 ändern) evtl. an mehreren Stellen geschehen muß. Und gerade das wollten 
	wir ja vermeiden!
	
	Falls Sie die Lampe gegen Masse (ein-)schalten wollen, muß der Ausgang natürlich auf 'Low' gesetzt 
	werden. Elegant können Sie dann schreiben:
      
      
	
	#define Lampe1_ein bcf Lampe1
	#define Lampe1_aus bsf Lampe1
	
      
      
	Jedesmal, wenn Sie die Lampe 1 einschalten wollen, schreiben Sie nur noch
      
      
	
	Lampe1_ein
	
      
      
	oder
      
      
	
	Lampe1_aus
	
      
      
	Wie und in welche Richtung das E/A-Bit gesetzt wird, ist hier völlig egal. Das haben Sie an anderer Stelle mit 
	dem '#define' festgelegt. Sie entfernen Sich damit wohltuend vom Bit-Klopfen zur logisch verständlichen 
	Programmierung.
	
	Bei uns wird von diesen hier beschriebenen Um-Definierungen exzessiv Gebrauch gemacht:
	Der Weichendecoder für das Faller-Car-System besteht aus einer ausgetesteten Platine, die nicht mehr
	verändert werden wird. In dem darauf sitzenden PIC existiert ein Rumpf-Programm, in dem alle Zuordnungen
	definiert worden sind. Diese Platine wird maximal 63-mal benötigt, für 63 verschiedene Weichen. Die
	individuellen Programme, eines für jede Weiche, sind noch nicht geschrieben (auch weil die Weichen und der
	Straßenverlauf noch in der Planung sind).
	Wenn sich jemand daran macht, für eine neue Weiche ein Programm zu schreiben, braucht er nur noch anzugeben,
	welchen Ein- oder Ausgangspin der Platine er bearbeiten will. Die sind ihm, auch wegen der Verdrahtung in
	die Anlage, bestens bekannt. Um die Beinchen am PIC braucht er sich nicht mehr zu kümmern: eine erhebliche
	Vereinfachung und Fehlervermeidung.
	Eine weitere Vereinfachung ist das Erstellen eines eigenen Programm-Schnipsels, das nur die Neu-Definierungen
	der Befehle beinhaltet. Dieses wird bei jedem Programm dazugebunden. Dadurch werden diese Befehle quasi zu einem
	Standard beim MEC.
	
	noch eine wichtige Anwendung:
	Eine Änderung der Verdrahtung, wie oben beschrieben, könnte auch den Wechsel des PICs beinhalten. Nehmen
	wir an, infolge gesteigerter Anforderungen (so etwas soll vorkommen!!) muß ein PIC mit mehr Beinchen eingesetzt
	werden. Auch hier genügt es, die neue Konfiguration (wir meinen hier die Zuordnung von Funktion zur Hardware)
	per #define einzugeben. Und wieder ist dieses Problem gelöst.
	
	Ein Nachteil soll hier nicht verschwiegen werden:
	Da das Assemblier-Programm die neu geschriebenen Texte nicht sofort als Befehle erkennt, werden sie auf dem
	Bildschirm nicht in der Befehls-Farbe (blau) dargestellt. Das ist gewöhnungsbedürftig!
	
	Die Befehlsfolge #ifdef - #else - #endif
	ist eine Anweisung an das Assemblier-Programm, die nachfolgenden Befehlszeilen nur dann einzubinden, wenn eine 
	bestimmte Variable vorher definiert wurde (Stichwort: bedingte Assemblierung). Sinnvollerweise muß dieser 
	Programmteil mit der Anweisung '#endif' abgeschlossen werden.
	Beispiel:
	Sie wollen beim Testen Ihres Programms an mehreren Stellen jeweils ein paar Befehle ausführen, die in der 
	endgültigen Version nicht erwünscht sind, z.B irgendwelche Anzeigen setzen. Sie können sich das 
	Schreiben und spätere Löschen vereinfachen und damit Fehler umgehen, wenn Sie das '#define' in Kombination 
	mit '#ifdef' verwenden.
	Als allererste Zeile in Ihrem Programm (muß dort nicht sein, steht aber an herausragender Stelle) schreiben 
	Sie:
      
      
	
	#define testfall 1
	
      
      
	Hiermit erzeugen Sie eine Variable mit den Namen 'testfall' und geben ihr einen beliebigen Wert, hier '1'.
	An beliebiger Stelle im Programm können Sie nun Befehle einfügen, die Sie nur zum Testen 
	benötigen:
      
      
	
	#ifdef testfall
	...
	...
	#endif
	
      
      
	Wenn Sie die Definition in der ersten Programmzeile aus-kommentieren, werden der beschriebene Block (und evtl. 
	noch viele andere Blöcke) nicht mit assembliert, da 'testfall' nun nicht bekannt ist. So ist die einzige 
	Umstellung zum 'Normalfall' das Einfügen eines Semikolons; mit dem Vorteil, daß auch wirklich alle 
	Test-Stellen gelöscht werden.
	Wollen Sie Befehlsfolgen beim Testen durch andere ersetzen, so ist auch dies möglich:
      
      
	
	#ifdef testfall
	... ; Ersatz-Befehle zum Testen
	#else
	... ; Original-Befehle
	#endif
	
	
      
      
	... und jetzt kommt die Kür ... :
	
	Die folgenden Befehle hat der Autor in keiner Beschreibung gefunden, sondern selber ausprobiert:
      
      
	
	#if testfall == 1
	#if testfall > 1
	
      
      
	Hier wird 'testfall' auf ganz bestimmte Werte abgefragt.
	
	Und es geht noch schöner:
	
	Es gibt eine Antwort auf die Ur-Frage 'wo bin ich?'. Dabei wird der Wert eines Labels abgefragt. Nehmen wir an, das 
	Label 'xxx' ist die Ansprungadresse eines Unterprogramms und steht (rein zufällig) auf der Adresse 0x100; dann 
	können Sie abfragen:
      
      
	
	#if xxx == 0x100
	
      
      
	und dann davon abhängig Befehle einfügen oder weglassen. (Merke: wir sind hier nicht mehr beim 
	Testen!) Dies ist durchaus sinnvoll, wenn in einem Adreß-Bereich > 0x7f berechnete Sprünge 
	ausgeführt werden sollen. Hier müssen, abhängig vom Programmzähler, von Hand 
	Adreßerweiterungsbits gesetzt werden, ansonsten springt der Rechner 'in den Wald'. S. dazu auch die sehr gute 
	Dokumentation von 
	sprut.
	
      
      
	Für weitere Fragen stehen gern zur Verfügung:
	- der MEC; Besichtigung und Fachsimpelei z.B. an unseren "Club-Abenden"
	- der Autor: Hans Peter Kastner
	
      
    
      
	Version vom: 19.04.2025; erstellt am: 26.03.2010
	
	Copyright © 2010 - 2025 by Modelleisenbahnclub Castrop-Rauxel 1987 e.V.