90 lines
3.1 KiB
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();
|
|
}
|
|
|