avr/ws2812/direct_write_assembly.c

53 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__ ( \
; count out eight bits \
"ldi %z, 8" "\n\t" \
; load the current byte into a temporary register \
"ld __tmp_reg__, %[byte]" "\n\t" \
; shift the temporary register left, saving the msb in SREG (1 cycle) \
"L_%=: " "lsl __tmp_reg__" "\n\t" \
; if SREG is set, branch to I_%= (2 cycles if true, 1 cycle if false) \
"brbs I_%=" "\n\t" \
; SREG was zero, so write a zero to the port \
write_zero(port, bit) \
; Jump to J_%=, the loop cleanup (2 cycles) \
"rjmp J_%=" "\n\t" \
; SREG was one, so write a one to the port \
"I_%=: " write_one(port, bit) \
; Decrement the bits counter (1 cycle) \
"J_%=: " "dec %z" "\n\t" \
; are there any bits left to send? (1 cycle) \
"cpi %z, 0" "\n\t" \
; there are, so go back to L_%= (2 cycles) \
"brne L_%=" "\n\t" \
: /* no outputs */ \
: [byte] "I" (byte) \
)