Schreiben auf Pins an einem Port, ohne andere Pins an diesem Port zu beeinflussen

Ich gebe eine 6-Bit-Zahl auf PORTA (RA0-RA5) aus, wie schreibe ich in diese Bits, ohne zu verfälschen, was bereits auf RA6 und RA7 ist?

Zum Beispiel, wenn ich 0x3F auf den Bits RA0-RA5 ausgeben möchte. Wenn ich das verwende PORTA = 0x3F;, wird 00111111 ausgegeben, wodurch effektiv gelöscht wird, was bereits in die Bits RA6 und RA7 geschrieben wurde.

Ich codiere in C mit MikroC auf einem PIC18

Zusammengesetzte Zuweisungsoperatoren sind Ihr Freund: &=, |=, ^=sind alles Operatoren, mit denen Sie in der eingebetteten Entwicklung sehr vertraut sein sollten.
Lesen-Ändern-Schreiben.
Verwenden Sie die Single-Bit-Anweisungen (BTFSS, BTFSC oder was auch immer sie für Ihre MCU sind). Ein guter C-Compiler kann sie aus den Operatoren &= |= generieren (gcc kann, unterstützt aber meines Wissens nach PIC nicht). Überprüfen Sie die Assembly-Ausgabe Ihres Compilers, um zu sehen, was er tut. (Ada-Programmierer haben es viel einfacher, für MCUs, die gcc ansprechen kann - definieren Sie einfach ein gepacktes Array von booleschen Werten und Sie können sie direkt setzen/löschen).

Antworten (3)

Eine Prozedur namens "Lesen-Ändern-Schreiben".

Was es damit auf sich hat, steckt schon im Namen. Du liest. Dann änderst du. Dann schreibst du.

Lesen:

//Read in the value of the output register
tempVariable = [output register]

Ändern:

//set all bits you want to modify to be 0.
tempVariable &= [some mask];
//or in the values of the bits with those bits you want unchanged set to 0
tempVariable |= [new value of bits];

Schreiben:

//Write the new value back to the output register
[output register] = tempVariable;

Der Schlüssel liegt im Wesentlichen darin, die Werte der Bits, die Sie unverändert lassen möchten, zusammen mit den neuen Werten der Bits, die Sie ändern möchten, in das Ausgangsregister zurückzuschreiben.

Um festzustellen, was das Ausgangsregister für Ihr Gerät ist, sollten Sie sich auf dessen Datenblatt beziehen.


Wir können nicht einfach direkt in das Register schreiben, da dies auch die Bits betrifft, die wir nicht ändern möchten. Wir brauchen also eine Abfolge von Operationen, die nur die Bits ändern, die wir wollen. Hier kommen bitweise Operatoren ins Spiel.

Es gibt mehrere bitweise Operatoren, aber die beiden wichtigen sind &(und) und |(oder). Bitweise und alles mit einer 0 und es setzt dieses Bit auf 0, bitweise und alles mit 1 und es bleibt gleich. Bitweise oder irgendetwas mit einer 1 und es setzt dieses Bit auf eine 1, bitweise oder irgendetwas mit 0 und es bleibt gleich. Diese beiden Operatoren ermöglichen es uns, die erforderlichen Änderungen vorzunehmen, da wir jetzt eine Möglichkeit haben, nur einige Bits auf 0 zu setzen, und eine Möglichkeit, nur einige Bits auf 1 zu setzen.

Der neue Wert, den Sie schreiben möchten, erfordert, dass einige Bits auf 0 und einige Bits auf 1 gesetzt werden. Wir können dies erreichen, indem wir bitweise und gefolgt von einem bitweisen or ausführen . Das und wird verwendet, um alle Bits, die wir ändern möchten, auf 0 zu setzen, damit wir dann das oder ausführen können, das nur die Bits, die wir auf 1 haben möchten, auf 1 setzt.

Ein Beispiel hilft. Angenommen, Sie möchten die unteren 5 Bits auf einen Wert von ändern 0b01011, die oberen 3 Bits jedoch unverändert lassen. Nehmen wir auch an, der aktuelle Wert ist 0b10111101. Also folgen wir dem Ablauf:

Schritt 1, Maske:

Current: 0b101 11101
Bitmask: 0b111 00000 <- remember a 1 means don't change, a 0 means clear.
Result : 0b101 00000

Schritt 2, ändern:

Masked : 0b101 00000
New Val: 0b000 01011 <- remember a 1 means set to 1, a 0 means unchanged
Result : 0b101 01011

Und da haben Sie es - beachten Sie, dass die oberen 3 Bits in beiden Operationen unverändert blieben, während die unteren Bits aktualisiert wurden, um dem neuen Wert zu entsprechen.


Um einen in den Kommentaren und der anderen Antwort erwähnten Punkt anzusprechen, sollte dies tatsächlich im Ausgangsregister erfolgen, was die ursprüngliche Absicht meiner Antwort war. Es scheint einige Verwirrung zu geben, wenn ich annehme, dass ich mich mit Port auf die PORTx-Register in PICs beziehe - tatsächlich ist das Ausgangsregister auf einigen Geräten das LATx-Register. Einige PICs haben kein LATx-Register. Bei AVRs ist zum Beispiel PORTx das Ausgangsregister. Das Datenblatt für Ihr Gerät sagt Ihnen, was das Ausgangsregister ist.

Darüber hinaus kann die Technik verwendet werden, um sowohl Variablen als auch Register zu ändern, und kann verwendet werden, wenn die Register für andere Dinge als nur E / A-Ports geändert werden - Sie können auch Dinge wie Steuerregister für serielle Peripheriegeräte und dergleichen ändern.

Aufgrund der Unterschiede in der Benennung von Registern und der Tatsache, dass der Prozess ein sehr universeller Ansatz ist, hatte ich oben versucht, allgemein zu sein, da das Gleiche nicht nur für PICs, sondern für jeden Mikrocontroller gilt - eigentlich für so ziemlich alles, was einige erfordert Bits eines Registers modifiziert werden, aber nicht andere.

Und auf Architekturen und Compilern, die dies unterstützen, können bitweise Operationen an Ports in Bit-Set/Clear-Operationen umgewandelt werden, wenn sie weniger Zeit oder weniger Anweisungen benötigen.
Wer auch immer dies abgelehnt hat, möchten Sie erklären, warum?
Bei Ports funktioniert das nicht. Bei jedem Mikro, das ich je verwendet habe (und ich bin seit über 20 Jahren in diesem Spiel), einschließlich des PIC, liest das Lesen eines Ports den aktuellen Status der Pins des Ports, NICHT den Status der Ausgänge. Dies ist ein sehr wichtiger Unterschied, wenn einige dieser Pins Eingänge oder Open-Collector-Ausgänge sind. Stattdessen müssen Sie eine Variable behalten, um zu speichern, was Sie zuletzt an den Port geschrieben haben, diese Variable ändern und diese Variable dann an den Port schreiben.
@TomCarpenter Ich war nicht der ursprüngliche Downvoter. Aber ich habe Ihre Antwort auch aus dem Grund, den ich gerade angegeben habe, abgelehnt.
@ Graham Ich werde das korrigieren. Es sollte jedoch darauf hingewiesen werden, dass ich mit Port das Ausgangsregister meine - auf einem AVR heißt es PORTx. Während es auf PICs LATx heißt.
@TomCarpenter Auf PICs heißt es nur auf PIC18F-Mikros LATx. Andere 8-Bit-Mikros (PIC10F, PIC12F, PIC16F) haben kein LATx-Register.

Im Allgemeinen sollten Sie in der PIC18-Architektur niemals Read-Modify-Write-Befehle wie verwenden

PORTA |= 0x3F; // Bits 0 bis 5 setzen

Verwenden Sie lieber

LATA |= 0x3F; // Bits 0 bis 5 setzen

oder

LATA &= ~0x80; // Bit 7 löschen

Der Grund dafür ist, dass die Anweisung PORTA |= xx zuerst die Bitpegel an den Pins liest, sie modifiziert und dann das Ergebnis in den Port-Latch schreibt.

Der LATA-Befehl liest die Bits im Port-Latch, modifiziert sie und schreibt dann das Ergebnis in den Port-Latch.

Wenn die Port-Pins aus irgendeinem Grund (z. B. Lade- oder Ausbreitungsverzögerungen) nicht auf den richtigen und gültigen Logikpegeln liegen, kann der Lese-Modifizier-Schreib-Befehl versehentlich Bits modifizieren, die Sie nicht modifizieren wollten. Wenn Sie Pins von Eingang zu Ausgang tauschen, um Open-Drain-Pins zu simulieren, tritt ein ähnliches Problem für Pins auf, die vorübergehend Eingänge sind - der Ausgangslatch eines anderen Pins als der, den Sie absichtlich ändern, und dann, wenn Sie das TRIS-Register zurückschalten 0, um den simulierten offenen Drain einzuschalten, wurde der Latch-Zustand für dieses Bit geändert.

Wenn Sie für ältere PICs ohne LATx RMW verwenden müssen, können Sie ein Schattenregister manuell pflegen, es ändern und das Ergebnis dann in das Portregister übertragen.

Ein bisschen mehr Details zu dem, was ich oben geschrieben habe, von Ihrem Compiler-Lieferanten hier .

Manipuliert nur das niederwertigste Bit (LSB) eines Registers

Satz

GPIO_DATA = GPIO_DATA | 0x01;

Klar

GPIO_DATA = GPIO_DATA & (~0x01);

Unten ist eine Reihe von Notizen, die ich griffbereit habe und die von Nutzen sein könnten. Achten Sie auf die Hinweise in Rot.

Geben Sie hier die Bildbeschreibung ein

Für eine detaillierte Erklärung schlage ich vor, dass Sie hier nachsehen .


Verweise: