Forum: Compiler & IDEs GCC: Pin toggeln in einem Takt?


von gf (Gast)


Lesenswert?

Ich arbeite mit GCC auf eine AtMega325P. Das toggeln mittels PIN geht 
bei dem Prozessor.
Wie setzte ich das korrekt in C um? Muß ich das mit inline assembler 
abarbeiten?

von Markus C. (ljmarkus)


Lesenswert?

zb. so:
1
PORTC ^= (1<<PC2);

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


Lesenswert?

1
PINC = 1 << PC2;

von gf (Gast)


Lesenswert?

>PORTC ^= (1<<PC2);

>PINC = 1 << PC2;

Beides benötigt sichern nur 1 Takt?

von Karl H. (kbuchegg)


Lesenswert?

Nimm zweiteres.
Ob der gcc soweit ist, dass er zweiteres als schnelleren Ersatz für 
ersteres erkennt, müsste man durch Studium des erzeugten 
Assembleroutputs rausfinden.

von gf (Gast)


Lesenswert?

>PINC = 1 << PC2;

Wenn ich mich recht erinnere setzt der Compiler das in "sbi" um.
Wie müsste denn das ganze mit inline assembler aussehen?

von Karl H. (kbuchegg)


Lesenswert?

gf schrieb:
>>PINC = 1 << PC2;
>
> Wenn ich mich recht erinnere setzt der Compiler das in "sbi" um.
> Wie müsste denn das ganze mit inline assembler aussehen?

Wozu willst du inline Assembler?
sbi ist doch OK.
Der springende Punkt ist ja nicht die Operation, sondern dass sie auf 
das PIN Register angewendet wird. Und das ist dann so definiert, dass 
eine Zuweisung eines 1-Bits an das Pin Register das entsprechende Bit im 
Port Register toggelt.

von yalu (Gast)


Lesenswert?

gf schrieb:
> Beides benötigt sichern nur 1 Takt?

Nein, es sind 4 bzw. 2. In 1 Zyklus geht es mit:
1
PINC |= PC2;

auch wenn es unlogisch aussieht, aber s. Datenblatt.

von gf (Gast)


Lesenswert?

Hm, im Daten blatt steht halt SBI --> 2 Takte.
Warum sollte das bei einem PIN Register nur einen Takt brauchen?

von gf (Gast)


Lesenswert?

Daten blatt = Datenblatt

von gf (Gast)


Lesenswert?

>PINC |= PC2;

Interessant. Was macht der Compiler da draus?

von Peter D. (peda)


Lesenswert?

gf schrieb:
>>PINC = 1 << PC2;
>
> Wenn ich mich recht erinnere setzt der Compiler das in "sbi" um.

Das wäre dann ein Bug.
SBI macht keine Zuweisnung, sondern ein ODER.

Die erstes Ausführung kostet 2 Zyklen, da ein Register geladen wwerden 
muß.


Peter

von yalu (Gast)


Lesenswert?

> Hm, im Daten blatt steht halt SBI --> 2 Takte.

Upps, stimmt. Dann geht es in einem Takt wohl nicht. Aber immerhin ist
die |=-Methode die Platz sparendste, da sie nur 1 Befehl statt 2 oder 4
braucht.

> Interessant. Was macht der Compiler da draus?

Eben auch SBI :-/

von yalu (Gast)


Lesenswert?

Wenn du Jörgs
1
PINC = 1 << PC2;

in einer Schleife ausführst, ist die Chance groß, dass der Ladebefehl
vor die Schleife gezogen wird, so dass für die Anweisung innerhalb der
Schleife effektiv nur 1 Zyklus benötigt wird. Dasselbe gilt, wenn
mehrere dieser Anweisungen hintereinander geschrieben werden.

von gf (Gast)


Lesenswert?

könnte so was gehen?

1
void funktion(void)
2
{
3
   register unsigned char wert;
4
5
   wert = 0x03;
6
   asm volatile ("out PINB, wert"::)
7
}

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


Lesenswert?

gf schrieb:
> könnte so was gehen?

Nein, die Syntax allein krankt hinten und vorn.

Sorry, lass die Finger vom Inline-Assembler, der ist nicht für dich
gemacht, und er ist zum Glück auch gar nicht notwendig.  Wenn du
zwei Pins (und nicht eins, wie du eingangs schriebst) auf einmal
toggeln willst, dann schreibe doch bitte
1
PINC = (1 << PC1) | (1 << PC0);

oder wenn du die symbolische Schreibweise nicht magst
1
PINC = 3;

Schluss, aus, mehr brauchst du nicht (außer dem Einschalten der
Compiler-Optimierung natürlich, aber wenn du die nicht einschaltest,
stellt sich die Frage nach dem Zeitverhalten der Applikation gleich
gar nicht).

yalu's
1
PINC |= PC2;

ist allerdings nicht das, was du willst.

von gf (Gast)


Lesenswert?

>PINC = 3;

Das sind aber wieder 2 Takte oder ??

von Karl H. (kbuchegg)


Lesenswert?

Was hast du eigentlich dauern mit deinen Takten?

Wenn du eine Schwingung erzeugen willst, dann musst du das Ganze sowieso 
in eine Schleife packen und da wird dir die Schleife selbst wieder Takte 
dazubringen. Und höchst wahrscheinlich sogar mehr als das Port toggeln 
ausmacht.

Ansonsten:
Compilier es, schau dir das Assembler Listing an, hol dir das Datenblatt 
und zähl die Takte.

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


Lesenswert?

gf schrieb:
>>PINC = 3;
>
> Das sind aber wieder 2 Takte oder ??

Wie dir yalu schon schrieb, wenn du das in einer Schleife
ausführst, wird das Laden des Registers außerhalb der Schleife
erfolgen:
1
#include <avr/io.h>
2
3
void
4
foo(void)
5
{
6
        for (;;)
7
                PINC = 3;
8
}

ergibt:
1
foo:
2
/* prologue: function */
3
/* frame size = 0 */
4
        ldi r24,lo8(3)
5
.L2:
6
        out 38-32,r24
7
        rjmp .L2

Die Frage ist natürlich, was du genau erreichen willst.  Dafür
müsstest du uns mehr von deinem Kontext erzählen.  Selbst, wenn
du endlos in einer Schleife ein Pin toggeln willst (wie hier
gezeigt), brauchst du natürlich noch zwei Takte für eine Rücksprung
in der Schleife.  Eine limitierte Schleife kann man mit entsprechen-
der Optimierung noch komplett entrollen lassen:
1
#include <avr/io.h>
2
3
void
4
foo(void)
5
{
6
        for (uint8_t i = 0; i < 10; i++)
7
                PINC = 3;
8
}

ergibt mit -O3
1
foo:
2
/* prologue: function */
3
/* frame size = 0 */
4
        ldi r24,lo8(3)
5
        out 38-32,r24
6
        out 38-32,r24
7
        out 38-32,r24
8
        out 38-32,r24
9
        out 38-32,r24
10
        out 38-32,r24
11
        out 38-32,r24
12
        out 38-32,r24
13
        out 38-32,r24
14
        out 38-32,r24
15
/* epilogue start */
16
        ret

Natürlich hättest du das auch alles selbst probieren können...

[Edith: da war Karl Heinz mal wieder schneller.]

von gf (Gast)


Lesenswert?

O.K.
Erst mal zum Hintergund meiner Frage.
Ich versuche mehrere MByte Daten per USB Bus an den Rechner zu 
übertragen. In einer Schleife wird nun ein externer AD-Wandler 
gestartet, dieser ausgelesen, die Umschaltung von analogen Quellen 
getätigt, die Daten in ein FIFO getaktet und noch einige mehr...

Viele Aktionen bestehen dabei einfach daraus einen CLock für einen 
Baustein (z.B. ADC) zu erzeugen, was im allgemeinen mind. 4 Takte 
benötigt (SBI und CBI). Meine Intension ist die Taktzahl pro 
Schleifendurchgang zu minimieren.
In der Schleife müssen 4 Pins an PortB z.T mehrfach getoggelt werden.

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


Lesenswert?

Aber du willst nicht zufällig eine Standard-SPI in Software zimmern?

von yalu (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> yalu's
> PINC |= PC2;
> ist allerdings nicht das, was du willst.

Wieso nicht (abgesehen von der Zyklenzahl)? Falls du darauf hinaus
wolltest, dass der vom Compiler generierte SBI-Befehl auch andere als
das gewünschte Bit in PORTC toggelt:

  "Writing a logic one to PINxn toggles the value of PORTxn, independent
  on the value of DDRxn. Note that the SBI instruction can be used to
  toggle one single bit in a port."

Deswegen schrieb ich oben, dass das die Anweisung zwar unlogisch
aussieht, man aber ins Datenblatt schauen möge.

Es soll AVRs geben, die zwar das Toggeln über PIN unterstützen, der
SBI-Befehl aber mehr als ein Bit ändert, s. hier (nach "categories"
suchen):

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=38612&start=0

Ich kenne allerdings keinen, auf den das zutrifft.

von Karl H. (kbuchegg)


Lesenswert?

yalu schrieb:
> Jörg Wunsch schrieb:
>> yalu's
>> PINC |= PC2;
>> ist allerdings nicht das, was du willst.
>
> Wieso nicht (abgesehen von der Zyklenzahl)?

Weil du da in dieselbe Falle tappst, die auch beim Rücksetzen der 
Interrupt Flags besteht.
Die von dir vorgeschlagene Anweisung versucht nicht ein einzelnes Bit zu 
schreiben. Du holst dir den momentanen Wert des Pinregisters, setzt noch 
1 Bit dazu und schreibst das wieder auf das Pin Register. Abhängig vom 
tatsächlichen Zustand des Pin Registers vor der Operation, versuchst du 
daher unter Umständen mehrere Bits auf einmal auf 1 zu setzen und 
toggelst daher auch mehrere Bits.

von (prx) A. K. (prx)


Lesenswert?

Genauer gesagt sollte
  PINC |= irgendwas;
zur Folge haben, dass nebenbei alle Bits ausser "irgendwas" gelöscht 
werden. Das zumindest sagt die sich aus der Doku ergebende Logik. Weil 
alle Bits, die vorher gesetzt waren, als 1 wieder zurück geschrieben und 
damit gelöscht werden.

Andererseits wird in der Doku nahegelegt, dass
  sbi PINC,0
nur Bit 0 toggelt und den Rest intakt lässt. Wenn das zutrifft, dann ist 
das eine Spezialbehandlung des SBI Befehls und das Ergebnis von
  PINC |= irgendwas;
hängt nun davon ab, ob der Compiler SBI oder IN/ORI/OUT erzeugt.

Der Compiler müsste deshalb eigentlich darauf verzichten, bei |= mit 
passendem Wert den Befehl SBI statt IN/ORI/OUT zu erzeugen.

Das gefällt mit nicht wirklich.

von gf (Gast)


Lesenswert?

1
void funktion(void)
2
{
3
   register unsigned char wert;
4
5
   wert = 0x01;
6
   asm volatile ("out PINB, wert"::)
7
}


Kann mir mal bitte einer die korrekte Syntax zu obigen Zeilen 
korrigieren? Ich sehe hier die einzige chance einen "1-Takt toggle" zu 
realisieren.

Danke...

von yalu (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Abhängig vom tatsächlichen Zustand des Pin Registers vor der
> Operation, versuchst du daher unter Umständen mehrere Bits auf einmal
> auf 1 zu setzen und toggelst daher auch mehrere Bits.

Das war in früheren Zeiten auch meine Befürchtung bis ich den
entsprechenden Abschnitt im Datenblatt gelesen habe (s. mein letzter
Beitrag), wo explizit geschrieben steht, dass man den SBI-Befehl
verwenden kann. Und da der Compiler ioreg|=1<<x in einen solchen
umsetzt, funktioniert das auch.

Der SBI-Befehl scheint in diesem Fall also keine gewöhnliche Read-
Modify-Write-Sequenz auszuführen, sondern irgendetwas Spezielles zu tun.

A. K. schrieb:
> Der Compiler müsste deshalb eigentlich darauf verzichten, bei |= mit
> passendem Wert den Befehl SBI statt IN/ORI/OUT zu erzeugen.

Eigentlich ja. Auf der anderen Seite fände ich es ganz ok, wenn nichts
geändert wird, weil man sonst tatsächlich Inline-Assembler bräuchte, um
diesem Befehl zu erzwingen. Ich glaube auch nicht, dass dies den
C-Standard verletzen würde: Es könnte ja sein (und vielleicht wird es
auch tatsächlich so), dass die |=-Operation nicht ein komplettes Byte,
sondern nur die zu verändernden Bits zurückschreibt.

@gf:
Bitte entschuldige das Offtopic

von Karl H. (kbuchegg)


Lesenswert?

gf schrieb:
>
1
> void funktion(void)
2
> {
3
>    register unsigned char wert;
4
> 
5
>    wert = 0x01;
6
>    asm volatile ("out PINB, wert"::)
7
> }
8
>
>
>
> Kann mir mal bitte einer die korrekte Syntax zu obigen Zeilen
> korrigieren? Ich sehe hier die einzige chance einen "1-Takt toggle" zu
> realisieren.

Und wie soll dieser 1-Takt toggle denn deiner Meinung nach 
funktionieren? Ob du jetzt den out machst, oder den Compiler den aus 
deinem C-Code generiert, spielt so gut wie keine Rolle, denn um die 
Zuweisung von 0x01 zu Wert kommst auch du nicht vorbei.
Und ob DU jetzt im C-Code ein Register mit einem Wert befüllst, oder ob 
sich der Compiler das aus

    PINB = 0x01;

generiert, ist Jacke wie Hose.

von Rolf Magnus (Gast)


Lesenswert?

> void funktion(void)
> {
>   register unsigned char wert;

register ist nur ein Hinweis, den der Compiler für gewöhnlich ignoriert. 
Der entscheidet selbst, welche Variablen er in Register steckt.

>   wert = 0x03;
>   asm volatile ("out PINB, wert"::)
> }
>
> Kann mir mal bitte einer die korrekte Syntax zu obigen Zeilen
> korrigieren?

Ungefähr so:
1
asm ("out %port, %wert"::[wert] "r" (wert), [port] "I" (_SFR_IO_ADDR(PINB)));

Das garantiert dir aber nur, daß die asm-Zeile selbst in einem Takt 
ausgeführt wird. Der Compiler könnte trotzdem einen Ladebefehl direkt 
davor setzen, um wert aus dem Speicher zu lesen. Allerdings dürfte das 
ziemlich unwahrscheinlich sein.

von gf (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Und ob DU jetzt im C-Code ein Register mit einem Wert befüllst, oder ob
> sich der Compiler das aus
>
>     PINB = 0x01;
>
> generiert, ist Jacke wie Hose.

Nein !!!

Aus deinem PINB = 1; macht der Compiler ein SBI. Damit komm ich nie auf 
eine Takt. Das geht nur mit OUT!

von Karl H. (kbuchegg)


Lesenswert?

gf schrieb:

>>     PINB = 0x01;
>>
>> generiert, ist Jacke wie Hose.
>
> Nein !!!
>
> Aus deinem PINB = 1; macht der Compiler ein SBI.

Dann ist der Compiler fehlerhaft.
Aus einem

    PINB = 0x01;

KANN der Compiler keinen einzelnen sbi machen, weil die C-Anweisung 
offensichtlich den kompletten Port beeinflusst. Die Bits 7 bis 1 auf 0, 
Bit 0 auf 1. Das steht so im C-Code und ich wüsste jetzt nicht, wie der 
Compiler das mit cbi und sbi so umsetzen könnte, dass ein ordinärer out 
performancemässig nicht alles andere in die Tasche stecken würde.

von (prx) A. K. (prx)


Lesenswert?

yalu schrieb:

> weil man sonst tatsächlich Inline-Assembler bräuchte, um
> diesem Befehl zu erzwingen.

Und genau diesen Inline-Assembler benötigt man nun, um die SBI Operation 
zu erzwingen, weil bei IN/ORI/OUT etwas anderes herauskommt und man nur 
in Assembler sicher sein kann, dass wirklich SBI erzeugt wird.

Anders ausgedrückt: Finger weg von PINx |= mask; Ist ohnehin selten 
sinnvoll, PINx = mask; tut es auch und ist eindeutig.

> Ich glaube auch nicht, dass dies den
> C-Standard verletzen würde:

Da
  volatile uint8_t a;
  a |= b;
per Definition identisch ist mit
  a = a | b;
und auch mit
  volatile uint8_t one = 1;
  a = (a * one) | b;
und spätestens darin a geschlossen gelesen und geschrieben wird, würde 
ich schon sagen, dass man hier quer zum Standard liegt.

Aber da 8051 bekanntermassen als Standard gilt, wenn auch nicht für C, 
bewegt man sich so eben vom Standard "C" zum Standard "8051", denn dort 
hatte schon immer dieses Problem und scheinbar stört es dort niemanden.

von gf (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> KANN der Compiler keinen einzelnen sbi machen, weil die C-Anweisung
> offensichtlich den kompletten Port beeinflusst. Die Bits 7 bis 1 auf 0,
> Bit 0 auf 1. Das steht so im C-Code und ich wüsste jetzt nicht, wie der
> Compiler das mit cbi und sbi so umsetzen könnte, dass ein ordinärer out
> performancemässig alles andere in die Tasche stecken würde.

Hm ... da geb ich dir recht.
O.K. ich probier das ganze heute abend noch mal aus.

@Rolf
Danke, ich versuch mal ob ichs damit hin bekommme.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Der Funktionsaufruf und der Funktionsprolog/-epilog fressen doch mehr 
Takte als 1 Takt. Also Funktion weglassen und stattdessen ein Makro 
benutzen.

Generell:
* http://www.nongnu.org/avr-libc/user-manual/inline_asm.html
* http://www.rn-wissen.de/index.php/Inline-Assembler_in_avr-gcc

Mein erster Schuss wäre sowas:
1
#define ONEEYEDSNAKE(lokale_variable)                 \
2
  asm volatile (                                      \
3
                 "out %0, %1"                         \      
4
    : /* output list leer */                          \
5
    : "I" (_SFR_IO_ADDR(PINB)), "d" (lokale_variable) \
6
    : /* clobber list leer */                         \               
7
  )

Testanwendung:
1
#include <avr/io.h>
2
int main(void)
3
{
4
   unsigned char foo = 1<<PB0;
5
   DDRB = foo; // Ausgang
6
   while(1)
7
   {
8
     ONEEYEDSNAKE(foo); // tiggelditoggeldi
9
     // geht so nicht: ONEEYEDSNAKE(1<<PB0);
10
   }
11
}

Unsicher bin ich mir, ob der Compiler so weit vorausschauend kompiliert, 
dass es weiss: "Im Makro brauche ich foo in einem höheren Register, 
deshalb legen ich es beim unsigned char foo = 1<<PB0; schon in einem 
solchen an". Habe keinen AVR-GCC zur hand um es kurzfristig zu testen.

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


Lesenswert?

yalu schrieb:

> Das war in früheren Zeiten auch meine Befürchtung bis ich den
> entsprechenden Abschnitt im Datenblatt gelesen habe (s. mein letzter
> Beitrag), wo explizit geschrieben steht, dass man den SBI-Befehl
> verwenden kann.

Da habe ich irgendwie permanent drüber gelesen.  Ja, das stimmt
natürlich, und damit funktioniert deine Befehlsfolge auch, allerdings
für einen 2-Takte-Toggle, da SBI 2 Takte benötigt.  Der einzige
Vorteil des SBI gegenüber der Folge LDI/OUT ist dann, dass kein
Hilfsregister benötigt wird, aber der wird dann wieder aufgehoben,
wenn man das gleiche Pin mehrmals toggeln will, sodass das Hilfs-
register mit gleichem Wert wieder verwendet werden kann.

Ich habe testhalber folgendes:
1
#include <avr/io.h>
2
#define F_CPU 1E6
3
#include <util/delay.h>
4
5
int
6
main(void)
7
{
8
        DDRD = 0xff;
9
        PORTD = 0xff;
10
11
        for (;;) {
12
                PORTD ^= 1;
13
                PIND = 2;
14
                PIND |= 4;
15
                _delay_ms(300);
16
        }
17
18
        return 0;
19
}

auf einem STK600 programmiert, die drei LEDs blinken alle gleich.
Alle drei Methoden funktionieren also.  Hier ist der generierte
Assemblercode:
1
        in r24,43-32
2
        eor r24,r21
3
        out 43-32,r24
4
        out 41-32,r20
5
        sbi 41-32,2

(r20 und r21 werden natürlich vor der Schleife mit Werten geladen,
die dann konstant bleiben.)

von (prx) A. K. (prx)


Lesenswert?

Und was passiert bei
  PIND |= vier();
mit einer Funktion, die 8 zurückgibt, oder sonst irgendwas von dem der 
Compiler nicht weiss, dass es dem Wert 4 entspricht?

von gf (Gast)


Lesenswert?

Stefan B. schrieb:
> Der Funktionsaufruf und der Funktionsprolog/-epilog fressen doch mehr
> Takte als 1 Takt. Also Funktion weglassen und stattdessen ein Makro
> benutzen.

Beitrag "Re: GCC: Pin toggeln in einem Takt?"

von Michael A. (micha54)


Lesenswert?

Hallo,

also unter toggle verstand ich bisher immer eine Komplementierung....und 
die in 1 Taktzyklus ???

Gruß,
Michael

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


Lesenswert?

A. K. schrieb:

> Und was passiert bei
>   PIND |= vier();
> mit einer Funktion, die 8 zurückgibt, oder sonst irgendwas von dem der
> Compiler nicht weiss, dass es dem Wert 4 entspricht?

Wenn der Compiler es nicht weiß, kann er nur ein komplettes
read-modify-write implementieren, das ist doch sonnenklar.  Die
Optimierung kann nur zuschlagen, wenn sie erkennen kann, dass der
Ausdruck zur Compilezeit konstant ist und nur ein einzelnes Bit
gesetzt hat.

Michael Appelt schrieb:
> also unter toggle verstand ich bisher immer eine Komplementierung....und
> die in 1 Taktzyklus ???

Ja, liest du dir denn wenigstens auch den geposteten Code durch?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

gf schrieb:
> Stefan B. schrieb:
>> Der Funktionsaufruf und der Funktionsprolog/-epilog fressen doch mehr
>> Takte als 1 Takt. Also Funktion weglassen und stattdessen ein Makro
>> benutzen.
>
> Beitrag "Re: GCC: Pin toggeln in einem Takt?"

Deswegen verstehe ich ja nicht, warum du das mit der Funktion funktion() 
machen willst, wie du in in 
Beitrag "Re: GCC: Pin toggeln in einem Takt?" vorschlägst.

Wenn du deine Funktion so
1
#include <avr/io.h>
2
int main(void)
3
{
4
   DDRB = 1<<PB0; // Ausgang
5
   while(1)
6
   {
7
     funktion(); // möglichst schnelles Setzen/Löschen/Toggeln
8
   }
9
}

benutzt, brauchst du bestimmt mehr als einen Takt!

Oder spekulierst du darauf, dass die funktion() auch ohne inline 
Schlüsselword vom Kompiler inline angelegt wird? Vergiss dabei die Takte 
für das Laden des Wertes in das Arbeitsregister nicht.

von yalu (Gast)


Lesenswert?

A. K. schrieb:
> Und was passiert bei
>   PIND |= vier();

Das geht dann ziemlich sicher schief. Du hast schon recht, wenn du
schreibst:

> Finger weg von PINx |= mask; Ist ohnehin selten sinnvoll, PINx = mask;
> tut es auch und ist eindeutig.

Aber es gibt hin und wieder Fälle, wo man gerne den SBI hätte, weil man
damit ein Register und i.Allg. auch ein Programmspeicherwort einspart
oder weil das Toggeln nichtunterbrechbar sein soll. Dass man dafür von
gewissen Annahmen ausgehen muss, wie der Compiler den C-Code übersetzt,
ist zwar nicht schön, aber in der Mikrocontrollerei nicht unüblich. Ohne
solche Annahmen wären bspw. keine Zugriffe auf Doppelregister wie ADC
mit nur einer C-Anweisung möglich, genauso wenig wie die _delay_x-Funk-
tionen aus der AVR-Libc in ihrer aktuellen Form.

von (prx) A. K. (prx)


Lesenswert?

Jörg Wunsch schrieb:

> Wenn der Compiler es nicht weiß, kann er nur ein komplettes
> read-modify-write implementieren, das ist doch sonnenklar.

Das war ja der Sinn der Sache. Mich interessieren die LEDs.

von gf (Gast)


Lesenswert?

Stefan B. schrieb:
> Deswegen verstehe ich ja nicht, warum du das mit der Funktion funktion()
> machen willst, wie du in in

Mir geht es doch gar nicht um die Funktion. Vergiss die einfach.
Mir geht es einzig und allein darum:

gf schrieb:
>Meine Intension ist die Taktzahl pro Schleifendurchgang zu minimieren.

von (prx) A. K. (prx)


Lesenswert?

yalu schrieb:

> oder weil das Toggeln nichtunterbrechbar sein soll.

Das ist es so auch:
  PINx = mask;

Bleibt nur der Unterschied in der Länge und dem Register. Wer deshalb 
auf SBI Wert legt, dem sei inline-assembler statt |= empfohlen, um 
spätere Überrschungen zu vermeiden.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dann braucht du bei 4 Pins bis zu 2^4=16 verschiedene Werte für wert. 
Die kannst du nicht alle lokal in Registern halten, so wie ich es mit 
dem Makro vor hatte. D.h. du musst in der Arbeitsschleife Werte in 
Register nachladen, um sie mit OUT verwenden zu können. Damit scheitert 
die 1-Takt Idee grundsätzlich. Zwei Takte pro Manipulation von 1 bis 4 
Pins gleichzeitig mit LDI/OUT sind IMHO realistisch, wenn alle Pins am 
gleichen Port sind; mehr wenn die Pins zeitversetzt geschaltet werden. 
Irgendwo wird man sich das Optimum zwischen LDI/OUT und SBI/CBI auf den 
genauen Anwendungfall bzw. das gewünschte Timing maßschneidern müssen.

von (prx) A. K. (prx)


Lesenswert?

Stefan B. schrieb:

> Dann braucht du bei 4 Pins bis zu 2^4=16 verschiedene Werte für wert.

Wenn es um mehr als ein Bit geht, klar. Aber damit musst du leben, oder 
eine andere Controllerfamilie verwenden. Beim Bitbanging sind die PIC24H 
ganz flott unterwegs. Propeller und XMOS bieten exaktes nebenläufiges 
Timing ohne Störung durch Interrupts.

von Michael A. (micha54)


Lesenswert?

Jörg Wunsch schrieb:
> Ja, liest du dir denn wenigstens auch den geposteten Code durch?

Was meinst Du denn, wie ich das verlorene ^= gefunden habe ?

Oder wird das hier eine typische ingenieurmäßige Entwicklung an den 
Anforderungen vorbei ?

Gruß,
Michael

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


Lesenswert?

Michael Appelt schrieb:
> Jörg Wunsch schrieb:
>> Ja, liest du dir denn wenigstens auch den geposteten Code durch?
>
> Was meinst Du denn, wie ich das verlorene ^= gefunden habe ?

(Sorry, hier weiß ich nicht, wovon du gerade redest.)

Warum glaubst du dann nicht, dass
1
out 41-32,r20

tatsächlich nur einen Takt braucht und auch funktioniert?  Nicht nur
rein theoretisch, sondern ganz praktisch experimentell nachgewiesen --
OK, das Timing habe ich nicht nachgemessen, da habe ich mich aufs
Atmel-Datenblatt verlessen, dass sie für ein OUT auch wirklich nur
einen Takt brauchen.

von Michael A. (micha54)


Lesenswert?

Hallo,

aber es toggled nicht. Toggeln wäre XOR mit 1 Takt.

Schau Dir die ersten Postings an, da war das noch korrekt, danach wurden 
bits gezielt auf 0 oder 1 gesetzt, was eben kein toggeln mehr ist.

Das Thema an sich fand ich interessant, weil Lösungen mit 1 ASM-Befehl 
eben kein CLI/SEI drum herum brauchen, damit kein Interrupt dazwischen 
haut.

Aber ich will Euren Plauderthread nicht länger stören ;-)

Gruß,
Michael

von (prx) A. K. (prx)


Lesenswert?

Michael Appelt schrieb:

> aber es toggled nicht. Toggeln wäre XOR mit 1 Takt.

Ausgabe auf PINx toggelt auf neueren Devices sehr wohl. Mit OUT in einem 
Takt, wenn die Maske bereits im Register steht.

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


Lesenswert?

A. K. schrieb:

>> aber es toggled nicht. Toggeln wäre XOR mit 1 Takt.
>
> Ausgabe auf PINx toggelt auf neueren Devices sehr wohl.

Zumal ich ja explizit dazu geschrieben habe, dass mein gepostetes
Testprogramm funktioniert...

von gf (Gast)


Lesenswert?

Also ich habs gestern ausprobiert.

"PINX = 0x01;  PINX = 0x02;  PINX = 0x04;" werden vom Compiler jeweils 
in ein "out  p, Rd" umgewandelt. Die Register werden VOR der Schleife 
nur einmal geladen. Somit habe ich ein Toggeln in einem einzigen Takt 
erreicht.
Das ganz funkioniert mit mind. 3 unabhängigen Bits die getoggelt werden 
sollen. Bei mehr Bits werden wohl irgendwann die verfügbaren Register 
knapp werden und das Laden findet dann wieder innerhalb der Schleife 
statt. Ich hab aber nicht ausprobiert wo die Grenze liegt.
Jedenfalls hab ich eine Lösung :-)

Vielen Dank für die rege Anteilnahme an der Diskussion.

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


Lesenswert?

gf schrieb:

> Bei mehr Bits werden wohl irgendwann die verfügbaren Register
> knapp werden

Sei froh, dass du nicht auf einem i386 arbeiten musst mit seinem
chronischen Registermangel. :-)

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.