Hallo,
Ich habe einen Quelltext aus einem Beispielprogramm doch verstehe dieses
nicht wirklich.
1
y=BIT_5&~LATB;/* push LED state */
2
LATBSET=BIT_5;/* set LED pin high */
3
TRISBSET=BIT_5;/* set LED pin as input */
4
LATBCLR=y;
5
TRISBCLR=BIT_5;/* set LED pin as output */
Kann mir jemand erklären, was hier passiert?
Für mich sieht es so aus als ob nur des Status des PINS ausgelesen wird.
Liege ich da richtig? Aber warum so kompliziert?
Alex
alex schrieb:> Für mich sieht es so aus als ob nur des Status des PINS ausgelesen wird.
Das ist so ungefähr das einzige, was da nicht passiert. ;-)
> Aber warum so kompliziert?
Bist du sicher, dass der Code Sinn ergeben soll?
Also da soll eine Status LED an PB5 sein.
Oder soll das einfach nur die LED toggeln?
Ich verstehe die erste Zeile schon nicht.
Es könnte sein, dass LATB ein Macro in der plib.h ist welches die
Startadresse des PortB angibt und BIT_5 ist ein Offset.
Würde das Sinn machen?
Allerdings verstehe ich dann immer noch nicht warum ein Offset mit AND
NOT Adresse verknüpft wird?
alex schrieb:> Es könnte sein, dass LATB ein Macro in der plib.h ist welches die> Startadresse des PortB angibt und BIT_5 ist ein Offset.
Offset ist das falsche Wort.
BIT_5 ist hier eher eine Bitmaske.
> Würde das Sinn machen?
Das Statement invertiert den Inhalt von LATB und löscht danach alle Bits
(setzt sie auf 0), die in der Bitmaske BIT_5 nicht auf 1 stehen.
Wenn ich mal davon ausgehe, dass in BIT_5 nur ein einziges gesetztes Bit
existiert, und zwar das an der Bitposition 5, dann in a Nutshell:
y hat danach den invertierten Zustand des Bits 5 von LATB.
Was immer dieser Wert dann aussagt, kann ich nicht sagen. Da ich kein
PIC 'spreche', weiss ich nicht was sich hinter LATB verbirgt.
> Allerdings verstehe ich dann immer noch nicht warum ein Offset mit AND> NOT Adresse verknüpft wird?
LATB ist ziemlich sicher nicht eine Adresse, sondern der Inhalt eines
Registers.
Aber warum siehst du nicht einfach nach? Die Schreibweise mit alles
komplett in Grossbuchstaben ist ein Hinweis darauf, dass es sich dabei
um C Makros handelt. Die müssen irgendwo existieren. Und dort kann man
nachsehen.
Bei den PIC32 wird das nicht anders als bei den übrigen PICs sein:
LATx = Ausgangsregister, vgl. PORTx bei AVRs.
TRIx = Richtungsregister, vgl. DDRx bei AVRs, aber 1=in/0=out.
Die seltsam anmutenden Bezeichnungen erwuchsen aus der langen Geschichte
der PICs, die immerhin in die 70er zurückreicht. TRI steht hier für
"tristate" und LAT für "latch". Die ersten PICs hatten implizit
bidirektionale Ports ähnlich 8051 und folglich keines dieser Register.
Analog zu ähnlichen Ansätzen in diversen anderen RISC µCs werden die
YYYxSET/CLR alle YYYx Bits setzen/löschen, wo die geschriebenen Daten 1
sind, und den Rest in Ruhe lassen.
alex schrieb:> Ich verstehe die erste Zeile schon nicht.
y = BIT_5 & ~LATB; /* push LED state */
Aktuellen Zustand von Bit 5 des Port-Ausgangsregisters in
komplementiertem Zustand in y sichern.
LATBCLR = y;
Bit 5 des Port-Ausgangsregisters löschen, falls der Zustand anfangs 0
war. Andernfalls nichts tun. Am Pin selbst ändert das (noch) nichts, da
der Pin zu diesem Zeitpunkt ein Input ist (TRIB.5 = 1).
A. K. schrieb:> alex schrieb:>> Ich verstehe die erste Zeile schon nicht.>> y = BIT_5 & ~LATB; /* push LED state */>> Aktuellen Zustand von Bit 5 des Port-Ausgangsregisters in> komplementiertem Zustand in y sichern.>> LATBCLR = y;>> Bit 5 des Port-Ausgangsregisters löschen, falls der Zustand anfangs 0> war. Andernfalls nichts tun. Am Pin selbst ändert das (noch) nichts, da> der Pin zu diesem Zeitpunkt ein Input ist (TRIB.5 = 1).
Was ich dabei nicht kapiere:
Warum macht man nicht einfach folgendes:
Nachdem sich Microchip soviel Mühe gemacht hat, die ganzen Bitfelder zu
deklarieren, warum die nicht hernehmen?
Das #define kann man in einen eigenen Header packen. Damit lässt sich
der Code leichter portieren, weil im eigentlichen Code nicht
projektspezifisches mehr steht, sondern nur allgemeines.
Die Zuweisungen port <> Funktion kann man an einer zentralen Stelle
übersichtlich machen.
>Was ich dabei nicht kapiere:
Da ist erstmal nix zu kapieren.
Wer hat dieses Stück Software geschrieben?
Zu welche Zweck?
Ist der Programmierer gut?
Weiß er was er tut?
Warum schaltet er Ports kurzzeitig von Output auf Input und dann wieder
zurück?
Warum sollte sich hier Leute mit Software auseinander setzen, von der
nix bekannt ist? Nicht einmal, ob sie läuft.
alex schrieb:> Kann mir jemand erklären, was hier passiert?
Bei AVR und PIC12/16/18/24 hast Du Bit-Befehle, die atomar Bits in
Prozessor- und Peripherieregistern ändern können. Atomar heißt
wortwörtlich "unteilbar", d.h. es ist unter allen Umständen gesichert,
dass dieser Befehl an einem Stück in einem Zyklus ausgeführt wird. Das
geht genau deswegen, weil Peripherieregister oder SFRs quasi das gleiche
wie Prozessorregister sind.
Bei ARM und MIPS (PIC32 ist MIPS) geht das nicht. Der zugekaufte
Prozessorkern hat definierte Schnittstellen, und alles, was darüber
geht, sind Zugriffe auf externen Speicher. Das Setzen eines Bits
erfordert also mindestens drei Befehle:
1. Wert aus Speicher in Register laden
2. Bit in Register setzen
3. Wert aus Register in Speicher schreiben
Es ist offensichtlich, dass das jetzt nicht mehr atomar ist, weil die
ganze Prozedur durch Interrupts, DMA-Zugriffe etc unterbrochen werden
kann.
Um das doch realisieren zu können, gibts das sogenannte Bit-Banding.
Heißt also: Schreibzugriffe auf bestimmte Adressräume bedeuten nicht
mehr *a=x in C-Notation, sondern *a|=x oder *a&=(~x) oder *a^=x.
Deswegen gibts nicht nur das Ausgabelatch LAT? und das
Datenrichtungsregister TRIS?, sondern auch LAT?CLR und LAT?SET bzw
TRIS?CLR und TRIS?SET.
Das Datenrichtungsregister ist bei PIC gegenüber AVR genau invertiert.
0=o für output und 1=i für input.
fchk
Frank K. schrieb:> Um das doch realisieren zu können, gibts das sogenannte Bit-Banding.
Bitbanding (ARM Bitbanding) ist eine spezielle Eigenschaft
bestimmter ARM Cores der Cortex M Reihe. Aber auch da nicht aller Cores,
so hat der Cortex M0 es nicht und beim M0+ ist optional.
Die SET/CLR-Register sind kein Bitbanding, sondern eine besondere Form
von I/O-Registern, wie sie bei vielen µCs mit RISC Architektur
vorzufinden sind.
WehOhWeh schrieb:> Nachdem sich Microchip soviel Mühe gemacht hat, die ganzen Bitfelder zu> deklarieren, warum die nicht hernehmen?
Wenn du um die Nebenwirkungen des entstehenden Codes weisst, dann kannst
du das tun. Nur ist die Wahrscheinlichkeit recht gross, dass bei einem
32 Bits breiten Port irgendwelche Pins dabei sind, die in einem
Interrupt-Handler direkt manipuliert werden. Und dann musst du im
Hauptprogramm entweder mittels der SET/CLR-Register atomaren Code
verwenden, oder jedesmal die Sequenz der Maschinenbefehle gegen
Interrupts sperren.
Besonders interessant ist dieses Thema, wenn man nicht einfach bloss
einen Pin aktiviert, sondern beispielsweise die 4 Datenbits eines
Text-LCD auf den Port schmeisst. Wenn man das auf klassische Weise mit
PORT = (PORT & ~MASK) | ((data << SHIFT) & MASK);
macht, dann hat man das beschriebene Problem mit ISRs bei jedem
Prozessor, egal ob 8051, 8-Bit oder 32-Bit PIC. Bitfeldoperationen
erzeugen auch solchen Code, falls der Prozessor dafür keine speziellen
Befehle kennt.
Effiziente Lösungen dieses Problems unterscheiden sich stark von µC zu
µC. Bei nicht zu alten AVRs kann dazu man in etwas hinterfotziger Weise
auf PINx schreiben, bei den STM32 gibts ein kombiniertes
SET/CLR-Register (genau deshalb sind deren Ports nur 16 Bits breit),
manche haben ein separates Maskenregister für die Ports (NXP ARMs) und
bei manchen fährt man besser, wenn man die Einsen und die Nullen
getrennt über SET/CLR Register steuert.