Forum: Compiler & IDEs einzelnen Pin invertieren (nicht ganzen Port)


von weinender Megagott (Gast)


Lesenswert?

Guten Abend liebe Experten!

Ich wollte mal fragen ob mir jemand sagen kann wie man einen einzelnen 
Pin invertiert statt dem gesamten Port wie z.B. so:

PORTD ^= 0xFF;
PORTD = ~PORTD;

Also ich möchte z.B. nur PD0 umkippen.

ich hatte es erst mit
if (PD0) {pin einschalten}else{pin ausschalten} und
if (PD0) {pin ausschalten}else{pin einschalten}
ausprobiert aber das geht so gar nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Du warst doch schon nahe dran:

PORTD ^= _BV(PD0);

Neuere AVRs können ein Portpin umschalten, indem man eine 1 auf das
(eigentlich nur für die Eingabe sinnvolle) PINx-Register schreibt:

PIND = _BV(PD0);

Idealerweise würde der Compiler aus der ersten Sequenz oben intern
die zweite Sequenz ableiten wenn er weiß, dass der entsprechende AVR
das kann, aber sowas hat dem AVR-GCC noch niemand beigebracht.

von weinender Megagott (Gast)


Lesenswert?

erste Variante funktionert. Zweite jedoch nicht.
Danke dir für die schnelle Antwort Jörg!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn die zweite nicht geht, benutzt du keinen der moderneren AVRs, bei
denen das implementiert ist.  Du hast nicht dazu geschrieben, was für
einen Controller du hast.

von weinender Megagott (Gast)


Lesenswert?

einen Mega8. aber das spielt ja auch keine Rolle denn es funktioniert ja 
so prima :)

von A.K. (Gast)


Lesenswert?

"Idealerweise würde der Compiler aus der ersten Sequenz oben intern
die zweite Sequenz ableiten wenn er weiß, dass der entsprechende AVR
das kann, aber sowas hat dem AVR-GCC noch niemand beigebracht."

Lieber nicht. Dazu müsste der Compiler die I/O-Ports soweit 
verinnerlichen, dass für jeden neuen AVR ein neuer Compiler fällig wird.

Statt dessen sind sämtliche Ports nur ganz normale C-Makros, und bei 
denen darf der Compiler so etwas nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A.K. wrote:

> Lieber nicht. Dazu müsste der Compiler die I/O-Ports soweit
> verinnerlichen, dass für jeden neuen AVR ein neuer Compiler fällig
> wird.

Nicht zwingend.  Alle neuen AVRs scheinen das auf gleiche Weise zu
implementieren, man bräuchte also nur ein Flag "ist ein neuer AVR".

Der ATmega8 ist zu alt dafür, ein ATmega88 könnte es.

> Statt dessen sind sämtliche Ports nur ganz normale C-Makros, und bei
> denen darf der Compiler so etwas nicht.

Die Optimierung von memory-mapped IO auf den separaten IO space macht
er doch auch, genau wie die Optimierung auf SBI/CBI, wenn diese
möglich ist.

von Hannes (Gast)


Lesenswert?

PD0 toggeln:

PORTD^=(1<<PD0)

73 Hannes

von A.K. (Gast)


Lesenswert?

> "Alle neuen AVRs scheinen das auf gleiche Weise zu implementieren,
> man bräuchte also nur ein Flag "ist ein neuer AVR".

Das ist nicht der Punkt. Woher weiss der Compiler, dass PORTD und PIND 
irgendwie zusammenhängen? Stand heute sind das 2 getrennte #defines in 
einem Include-File, die für den Compiler in keinerlei Zusammenhang 
stehen.

Damit wäre Schluss, man müsste diesem Zusammenhang auf höchst spezielle 
Weite in den Compiler integrieren, per Attribute, Pragma, direkt ins 
config/avr/avr.c, oder so.

> Die Optimierung von memory-mapped IO auf den separaten IO space macht
> er doch auch, genau wie die Optimierung auf SBI/CBI, wenn diese
> möglich ist.

Das ist etwas völlig anderes. Damit wählt der Compiler nur die 
Adressierungsart aus. Das I/O-Register bleibt das gleiche.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A.K. wrote:

> Damit wäre Schluss, man müsste diesem Zusammenhang auf höchst spezielle
> Weite in den Compiler integrieren, per Attribute, Pragma, direkt ins
> config/avr/avr.c, oder so.

Da müsste man mal recherchieren, welche Möglichkeiten dafür in Frage
kommen.  Solange sich das sauber hinter einem #include <avr/io.h>
verstecken ließe, wäre es komplett transparent für den Benutzer.

> Das ist etwas völlig anderes. Damit wählt der Compiler nur die
> Adressierungsart aus. Das I/O-Register bleibt das gleiche.

Es ist etwas anderes, aber etwas völlig anderes?  Nee.  Liegt im
gleichen Bereich, auch wenn SBI/CBI allgemeiner implementierbar sind
(weil der Adressbereich, für den die Optimierung machbar ist, ein
einfacher großer Block ist).

von A.K. (Gast)


Lesenswert?

Ist schon was anderes. Denn ebendieser Zusammenhang zwischen den 
Datenadressen steckt ja im Compiler hardcoded drin. Der Zusammenhang 
zwischen PIND und PORTD hingegen (noch) nicht.

Willst du, dass der Compiler bei einem Mega16 im Fall von
   TIMSK ^= 0x40;
einfach 1 auf die Adresse addiert und
   GIFR = 0x40;
daraus macht, weil er die gleiche Regel wie bei I/O-Ports anwendet? 
Stand heute hat der Compiler ja keinerlei Ahnung, ob es sich bei PORTD 
um einen I/O-Port oder um Interrupt-Flags handelt. Genau diese Kenntnis 
müsstest du ihm erst beibringen.

Also: Erweitere dem GCC so, dass man ihm mitteilen kann
   "PORTD ist der Output zu PIND der Input"
und schon geht's. Vorher nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A.K. wrote:

> Also: Erweitere dem GCC so, dass man ihm mitteilen kann
>    "PORTD ist der Output zu PIND der Input"
> und schon geht's. Vorher nicht.

Genau, auf so etwas müsste es hinauslaufen.  Sei's nun, dass man in
der Definition von PORTD ein _attribute_ dranklebt, das er dann
intern auswertet oder was auch immer.  Kann man im Headerfile ja
bei Bedarf noch von der Compilerversion abhängig machen, damit ist
man rückwärtskompatibel zu einem Compiler, der das noch nicht
versteht.

(Ich glaube, du hast auf mein 30 Sekunden später zurückgezogenes
Posting geantwortet, aber das macht nix. :)

von Karl H. (kbuchegg)


Lesenswert?

Solche Optimierungen werden doch für gewöhnlich in den
Peephole Optimizer gelegt. Und die sind aus der Natur
der Sache heraus sowieso hardwarespezifisch.

von Stefan K. (_sk_)


Lesenswert?

Warum den Compiler ändern? 2 Szenarios:

Fall 1:
Der Programmierer weiss was er tut und kennt die neue Option.
Dann benutzt er sie auch und schreibt den Pin-Toggle selbsst so hin.

Fall 2:
Der Programmierer weiss nichts von der neuen Option. Der Compiler macht 
das zukünftig automatisch für ihn.
Beim Debuggen schaut er sich den Assemblercode an und wundert sich. 
Verstehen tut er leider nichts. Danach postet er hier "gcc hat schon 
wieder einen Bug".
Vorteil wäre natürlich:
Da so ein Post sehr gerne gelesen wird, verbreitet sich die neue Option 
viel schneller in den Köpfen als ohne Compileränderung.

Und nun?

Grüße von Stefan

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Solche Optimierungen werden doch für gewöhnlich in den
> Peephole Optimizer gelegt. Und die sind aus der Natur
> der Sache heraus sowieso hardwarespezifisch.

Aber bislang eben doch auf höherem Niveau.

von A.K. (Gast)


Lesenswert?

> Solche Optimierungen werden doch für gewöhnlich in den
> Peephole Optimizer gelegt. Und die sind aus der Natur
> der Sache heraus sowieso hardwarespezifisch.

Auch der Peehole-Optimizer müsste irgendwoher wissen, dass auf 
I/O-Adresse 0x00 je nach Controller-Modell mal TWI-Register, mal 
Portregister stehen. Er ändert also nichts an der Notwendigkeit, dies 
dem Compiler mitzuteilen.

Ein Peephole Optimizier repariert Dinge, die man auf normalem Weg nicht 
so ohne weiteres gebacken kriegt. Die Optimierung hier liesse sich 
problemlos in der normalen Codegenerierung des Compilers unterbringen, 
als gewöhnliches instruction template für XOR, das eben nur unter 
bestimmten Randbedingungen greift.

von Karl H. (kbuchegg)


Lesenswert?

> Er ändert also nichts an der Notwendigkeit, dies
> dem Compiler mitzuteilen.

Das sicherlich nicht.
Aber wenn man dem P-O je nach Prozessortyp eine andere
Tabelle mit Ersetzungsregeln unterjubelt, ist das doch
eine schöne Lösung, ohne dass man allzusehr in die
generelle Compilerlogik eingreifen müsste.


von A.K. (Gast)


Lesenswert?

Verstehst du unter diesem P-O ein separates Tool, das nach dem Compiler 
aber vor dem Assembler des Code "repariert"?  Ja, so kann man das schon 
machen.  Würde ich aber eher als Hack ansehen, im Compiler gefällt mir 
das sehr viel besser, dafür sind die insns ja da.

Nur: Lohnt das wirklich???

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Aber wenn man dem P-O je nach Prozessortyp eine andere
> Tabelle mit Ersetzungsregeln unterjubelt, ist das doch
> eine schöne Lösung, ohne dass man allzusehr in die
> generelle Compilerlogik eingreifen müsste.

Doch, es wäre ein ziemlicher Eingriff.  Bisher weiß das AVR-Backend
nichts in diesen Details über den Prozessor, sondern es klassifiziert
die einzelnen AVRs nur nach den Fähigkeiten ihres CPU-Cores.  Man muss
zwar immer noch den GCC für jeden einzelnen AVR patchen, aber das sind
zwei oder drei Tabellen bislang.

Die gesamte Belegung der IO-Ports jedoch ist in den durch <avr/io.h>
hereingezogenen Headerdateien festgelegt.

von Karl H. (kbuchegg)


Lesenswert?

> Verstehst du unter diesem P-O ein separates Tool, das nach dem
> Compiler aber vor dem Assembler des Code "repariert"?

In allen Compilern, die ich bisher gesehen habe, war das so.
Ich muss allerdings gestehen, mich noch nie im Detail mit
dem gcc auseinandergesetzt zu haben.

> Bisher weiß das AVR-Backend nichts in diesen Details über
> den Prozessor

Das ist interessant. Bisher bin ich immer naiv davon ausgegangen
dass die ganze sbi/cbi Umsetzung durch den P-O gemacht würde.
Oder auch die Spezialbehandlung für die Extended Register (die
heissen doch so, oder).

Wenn das natürlich nicht so ist, dann wäre es natürlich Blödsinn
da jetzt einen neuen Mechanismus einzuführen.

Ich ziehe mich zurück und lausche der weiteren Unterhaltung mit
grossem Interesse.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger wrote:

> Das ist interessant. Bisher bin ich immer naiv davon ausgegangen
> dass die ganze sbi/cbi Umsetzung durch den P-O gemacht würde.

Ja, sicher.  Aber da braucht's eben auch keinerlei Unterscheidung
zwischen den AVRs.  Wenn die Adresse zwischen 0x20 und 0x5f liegt,
dann kann man sie mittels IN/OUT durch Subtraktion von 0x20
ebenfalls erreichen.  Wenn die Adresse nach dem Subtrahieren zwischen
0 und 0x1f liegt und nur ein einzelnes Bit zu setzen oder zu löschen
ist, kann man CBI/SBI benutzen.

Dafür muss man überhaupt nicht wissen, ob man auf einem uralten
AT90S2333 oder auf dem allerneuesten ATmega644P arbeitet.

von A.K. (Gast)


Lesenswert?

>> Das ist interessant. Bisher bin ich immer naiv davon ausgegangen
>> dass die ganze sbi/cbi Umsetzung durch den P-O gemacht würde.

> Ja, sicher.

Für mich sieht das eher nach einem ganz normalen instruction template 
für &= aus, mit Randbedingung "I/O-Adressraum" (avr_io_address_p) und 
"Einzelbit".

(define_insn "*cbi"
  [(set (mem:QI (match_operand 0 "const_int_operand" "n"))
  (and:QI (mem:QI (match_dup 0))
    (match_operand 1 "const_int_operand" "n")))]
  "avr_io_address_p (operands[0], 1 + 0x20)
   && exact_log2 (~INTVAL (operands[1]) & 0xff) >= 0"
{
  operands[2] = GEN_INT (exact_log2 (~INTVAL (operands[1]) & 0xff));
  return AS2 (cbi,%0-0x20,%2);
}
  [(set_attr "length" "1")
   (set_attr "cc" "none")])

von A.K. (Gast)


Lesenswert?

Ich glaube übrigens nicht, dass es im GCC überhaupt einen P-O gibt, wie 
er oben beschrieben wird. Und was im MD-File "peephole" genannt wird, 
scheint mit eher ein Mechanismus zu sein, bestimmte Sequenzen zu 
erkennen, die im Rahmen von normalen Templates nicht erfasst werden 
können. Insofern nicht direkt vergleichbar zu den oben skizzierten P-Os.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.