ATTiny414 Inline-Assembly-Operandenbeschränkungen für IO-Pin-Parameter

Ich versuche, die light_ws2812- Bibliothek zu verwenden, um WS2812-LEDs von einem ATTiny414 anzusteuern. Der Kern dieser Bibliothek ist ein Inline-Assembly-Snippet, das die serielle Leitung bitbangt. Hier ist es mit den ausgezogenen Timing-Nops:

void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
{
  uint8_t curbyte,ctr,masklo;
  uint8_t sreg_prev;

  ws2812_DDRREG |= maskhi; // Enable output

  masklo  =~maskhi&ws2812_PORTREG;
  maskhi |=        ws2812_PORTREG;

  sreg_prev=SREG;
  cli();  

  while (datlen--) {
    curbyte=*data++;

    asm volatile(
    "       ldi   %0,8  \n\t"
    "loop%=:            \n\t"
    "       out   %2,%3 \n\t"    //  '1' [01] '0' [01] - re
    "       sbrs  %1,7  \n\t"    //  '1' [03] '0' [02]
    "       out   %2,%4 \n\t"    //  '1' [--] '0' [03] - fe-low
    "       lsl   %1    \n\t"    //  '1' [04] '0' [04]
    "       out   %2,%4 \n\t"    //  '1' [+1] '0' [+1] - fe-high

    "       dec   %0    \n\t"    //  '1' [+2] '0' [+2]
    "       brne  loop%=\n\t"    //  '1' [+3] '0' [+4]
    :  "=&d" (ctr)
    :  "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo)
    );
  }

  SREG=sreg_prev;
}

Ein offensichtlicher Unterschied zwischen den 'normalen AVR'- und den C-Definitionen der ATTiny*14-Serie besteht darin, dass die IO-Register nicht DDRx und PORTx, sondern PORTx.DIR und PORTx heißen. Ich habe das bereits in der light_ws2812Kopfzeile korrigiert und es scheint gut zu funktionieren.

Danach erhalte ich jedoch einen Fehler mit dem Assembly-Operanden 2 ( "I" (_SFR_IO_ADDR(ws2812_PORTREG))):

lib/light_ws2812.c: In function 'ws2812_sendarray_mask':
lib/light_ws2812.c:119:5: warning: asm operand 2 probably doesn't match constraints
     asm volatile(
     ^~~
lib/light_ws2812.c:119:5: error: impossible constraint in 'asm'
make: *** [Makefile:28: lib/light_ws2812.o] Error 1

Ich habe auch versucht, ianstelle von , einen Kleinbuchstaben als Einschränkung zu verwenden I, wodurch sich der Fehler in ändert

/tmp/ccBR4JKE.s:48: Error: operand out of range: 1028

Wenn man sich das Datenblatt ansieht , ist dieser Wert sinnvoll, PORTAbeginnt bei 0x400(1024) und das PORTx.OUTRegister hat einen zusätzlichen Offset von 4 Bytes und platziert PORTA.OUTbei 0x404 = 1028.

mit einer 16-Bit-Registerbeschränkung wie xoder wes wird kompiliert, aber dann erhalte ich einen Linker-Fehler:

avr-ld: lib/light_ws2812.o: in function `loop32':
light_ws2812.c:(.text+0x38): undefined reference to `r30'

Ich kompiliere dies mit avr-gccund avr-ldmit einem Strom avr-libc, dem ich die relevanten Dateien aus dem Atmel ATtiny Series Device SupportPaket manuell hinzugefügt habe.

Vielleicht sollte ich VPORTA.OUTstattdessen das Register verwenden? Es fängt 0x0so an, dass PORTA.OUTes nicht hineinpassen sollte i...

Antworten (1)

Bei der ATTiny 1-Serie sind die GPIO-Register außerhalb des io-Port-Registerraums abgebildet, sodass auf sie nicht mit outoder zugegriffen werden kann in. Sie müssen entweder die VPORT-Register verwenden oder die Adresse des io-Ports in ein Register laden und eine stAnweisung verwenden, um die Daten zu schreiben. Wenn Sie sich für die Verwendung einer regulären Speicheranweisung entscheiden, kann dies das Timing der Schleife ändern, sodass Sie möglicherweise die Anzahl der nops anpassen müssen.

Super, ich war auch zu einem ähnlichen Schluss gekommen, nachdem ich endlich verstanden hatte, wie man das gesamte Speicher-Layout aus dem Datenblatt liest. Wäre es ein Vorteil, die Bitadressierungsanweisungen zum Setzen des Pins zu verwenden?
Ich bin mir bei dem tiny414 nicht sicher, aber der gesamte AVR-Bogen definiert st2 Zyklen, während out1 Zyklus benötigt wird.
Ich denke, es würde SBIund CBI. Da es sich bei allen um Einzelzyklusbefehle handelt, gibt es vermutlich keinen Unterschied (außer dass wir bei einem aktiven Peripheriegerät ihre Werte im Portregister nicht überschreiben würden / könnten, aber ich bin mir nicht sicher, ob dies tatsächlich der Fall ist ein mögliches Problem). Ich werde prüfen, ob dies wie erwartet funktioniert, wenn ich nach Hause komme und die Antwort akzeptieren :)
Okay, ich konnte meinen Code noch nicht zum Laufen bringen, aber das hat wahrscheinlich nichts mit der Frage zu tun, da der ursprüngliche Code mit der "I"Einschränkung jetzt mit VPORT * funktioniert.