1
\$\begingroup\$

I'm trying to use the light_ws2812 library to drive WS2812 LEDs from an ATTiny414. The core of that library is an inline assembly snippet that bitbangs the serial line. here it is with the timing-nops stripped out:

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; } 

An obvious difference between the 'regular AVR' and the ATTiny*14 series C definitions is that the IO registers aren't called DDRx and PORTx but PORTx.DIR and PORTx. I already fixed that in the light_ws2812 header and it seems to work fine.

After this I am however getting an error with the assembly operand 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 

I also tried using a lowercase i as the constraint instead of the I, which changes the error to

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

Looking at the datasheet this value makes sense, PORTA starts at 0x400 (1024) and the PORTx.OUT register has an additional offset of 4 bytes, placing PORTA.OUT at 0x404 = 1028.

with a 16-bit register constraint like x or w it compiles but then I get a linker error:

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

I am compiling this using avr-gcc and avr-ld, with a current avr-libc that I manually added the relevant files from the Atmel ATtiny Series Device Support package to.

\$\endgroup\$
1
  • \$\begingroup\$ perhaps I should be using the VPORTA.OUT register instead? It starts at 0x0 so unlike PORTA.OUT it should fit into i... \$\endgroup\$ Commented Jul 26, 2018 at 13:40

2 Answers 2

2
\$\begingroup\$

I know this is an old post, but there was never any clear answer, so here goes.

What worked for me was to replace _SFR_IO_ADDR(ws2812_PORTREG) with the following: _SFR_ADDR(VPORTA.OUT)

Why not _SFR_IO_ADDR? Well, that macro deducts the old offset of 0x20, which is not relevant in the new series. _SFR_ADDR(x) just converts x into the address of x, which is what we want in this case.

Edit: I happened to use PORTA in this case, but obviously you can use whatever port you want :)

\$\endgroup\$
2
\$\begingroup\$

The ATTiny 1-series has the GPIO registers mapped outside of the io port register space, so they cannot be accessed using out or in. You will either need to use the VPORT registers, or load the address of the io port into a register and use an st instruction to write the data. If you decide to use a regular store instruction, it may change the timing of the loop so you may have to adjust the number of nops.

\$\endgroup\$
6
  • \$\begingroup\$ Great, I had also come to a similar conclusion after I finally understood how to read the overall memory-layout from the datasheet. Would there be an advantage in using the bit-addressing instructions to set the pin? \$\endgroup\$ Commented Jul 26, 2018 at 13:55
  • \$\begingroup\$ I'm not sure about the tiny414, but the overall AVR arch defines st as taking 2 cycles, whereas out takes 1 cycle. \$\endgroup\$ Commented Jul 26, 2018 at 13:59
  • \$\begingroup\$ I think it would SBI and CBI. Since they are all single-cycle instructions I suppose there is no difference (except in that if there was a peripheral active we wouldn't/couldn't overwrite their values in the port register, but I'm not sure if that is actually a possible problem). I will check if this works as expected when I get home and accept the answer :) \$\endgroup\$ Commented Jul 26, 2018 at 14:01
  • \$\begingroup\$ okay, I couldn't get my code to work yet but thats likely unrelated to the question since the original code with the "I" constraint now works with VPORT*. \$\endgroup\$ Commented Jul 27, 2018 at 12:56
  • \$\begingroup\$ I know this topic is old, but I am trying to do the same thing (control WS2812 with ATtiny414) and I was wondering what solution did you finally find and what modifications did you do to the library? \$\endgroup\$ Commented Jan 13, 2023 at 18:40

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.