avr/ws2812/direct_write_assembly.c

43 lines
2.4 KiB
C

// I have reduced noops at the end of each of these to take into account that there are several additional clock ticks of setup, after. However, I'm not totally sure that I get things right, seeing that there are four possible sequences, and I'm not really accounting for the timing of all four of them.
#define write_zero(port, bit) \
__asm__ __volatile__ ( \
"sbi %0, %1" "\n\t" \
"nop" "\n\t" \
"cbi %0, %1" "\n\t" \
"nop" "\n\t" \
: /* no outputs */ \
: "I" (_SFR_IO_ADDR(port)), \
"I" (bit) \
)
#define write_one(port, bit) \
__asm__ __volatile__ ( \
"sbi %0, %1" "\n\t" \
"nop" "\n\t" \
"nop" "\n\t" \
"nop" "\n\t" \
"nop" "\n\t" \
"nop" "\n\t" \
"cbi %0, %1" "\n\t" \
: /* no outputs */ \
: "I" (_SFR_IO_ADDR(port)), \
"I" (bit) \
)
#define write_byte(port, bit, byte) \
__asm__ __volatile__ ( \
"ldi %z, 8" "\n\t" \ // count out eight bits
"ld __tmp_reg__, %[byte]" "\n\t" \ // load the current byte into a temporary register
"L_%=: " "lsl __tmp_reg__" "\n\t" \ // shift the temporary register left, saving the msb in SREG (1 cycle)
"brbs I_%=" "\n\t" \ // if SREG is set, branch to I_%= (2 cycles if true, 1 cycle if false)
write_zero(port, bit) \ // SREG was zero, so write a zero to the port
"rjmp J_%=" "\n\t" \ // Jump to J_%=, the loop cleanup (2 cycles)
"I_%=: " write_one(port, bit) \ // SREG was one, so write a one to the port
"J_%=: " "dec %z" "\n\t" \ // Decrement the bits counter (1 cycle)
"cpi %z, 0" "\n\t" \ // are there any bits left to send? (1 cycle)
"brne L_%=" "\n\t" \ // there are, so go back to L_%= (2 cycles)
: /* no outputs */ \
: [byte] "I" (byte) \
)