avr/ws2812/np_common.c

90 lines
3.1 KiB
C

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stddef.h>
#include "ws2812.h"
/*
void latch(io_pin_t *addr) {
if (addr->bit >= 8) return;
*(addr->port) &= ~(1<<0);
_delay_us(50);
}
*/
#define T0_HIGH 4
#define T1_HIGH 7
#define T_FRAME 11
#define write_bit(label, bit) \
"out %[port], %[hi]" "\n\t" \
"nop" "\n\t" \
"sbrc %[byte], " #bit "\n\t" \
"rjmp .+0" "\n\t" \
"out %[port], %[low]" "\n\t" \
"rjmp .+0" "\n\t" \
"rjmp .+0" "\n\t" \
/*
#define write_bit(label, bit) \
"bst %[byte], " #bit "\n\t" \
"out %[port], %[hi]" "\n\t" \
"nop" "\n\t" \
"brtc " label "\n\t" \
"rjmp .+0" "\n\t" \
label ": " "out %[port], %[low]" "\n\t" \
"rjmp .+0" "\n\t" \
"rjmp .+0" "\n\t"
*/
inline void write_byte(volatile uint8_t *port, uint8_t hi, uint8_t low, uint8_t byte) {
__asm__ __volatile__ (
write_bit("A_%=", 7)
write_bit("B_%=", 6)
write_bit("C_%=", 5)
write_bit("D_%=", 4)
write_bit("E_%=", 3)
write_bit("F_%=", 2)
write_bit("G_%=", 1)
write_bit("H_%=", 0)
: /* No outputs */
: [port] "I" (_SFR_IO_ADDR(PORTB)),
[hi] "r" (hi),
[low] "r" (low),
[byte] "r" (byte)
);
// __asm__ __volatile__ (
/*
"ldi r24, 8" "\n\t" // count out eight bits. One higher than normal because I do the final check *after* the counter decrements. I'm effectively 1-based
"lsl %[byte]" "\n\t" // output data left, saving the msb in SREG (1 cycle)
"bst sreg, 0" "\n\t" // Save hte carry flag to the transfer bit
"L_%=: " "out %[port], %[hi]" "\n\t" // enable the port
"brtc I_%=" "\n\t" // If we shifted out a 0, if SREG[C] is clear, immediately branch I_%= (2 cycles if true, 1 cycle if false)
"nop" "\n\t" // We shifted out a 1, so wait just a touch longer
"nop" "\n\t"
"nop" "\n\t"
"I_%=: " "out %[port], %[low]" "\n\t" // now clear the pin
// I now have five ticks before I can re-enable the pin
"lsl %[byte]" "\n\t" // Shift out the next bit
"bst sreg, 0" "\n\t"
"dec r24" "\n\t" // Decrement the bit counter
"cpi r24, 0" "\n\t" // Is the bit counter 0?
"brne L_%=" "\n\t" // If we haven't reached 0, we have more data to send
*/
}
void write_pixels(const uint8_t *pixels, uint8_t length) {
uint8_t hi = PORTB | (1 << 3);
uint8_t low = PORTB & ~(1 << 3);
cli();
for (int idx = 0; idx < length; idx++) {
write_byte(&PORTB, hi, low, pixels[idx]);
}
_delay_us(100);
sei();
}