I started looking at how to drive ws2812's manually rather than just downloading the neopixel library
I have used the neopixel lib and it works really well
But, I am writing for ATTiny85/13 at the moment and need something that (1) works on ~8mhz (2) very small code to drive ONE tricolour LED
so i read this...
it works well at 16mhz (800khz driving cycle), so I started modifying it
I changed the asm section to speed things up a fair bit, its not quite 800khz but is enough to be within the acceptable limits of the ws2812
asm volatile(
// Instruction Description
"startbit:\n\t" // label
"ldi %5, 8\n\t" // reset bitcount
"nextbit:\n\t" // label
"sbi %0, %1\n\t" // SET OUTPUT HIGH
"sbrc %4, 7\n\t" // Skip if HiBit in value is clear
"rjmp bitset\n\t" // jump if HiBit is set
"cbi %0, %1\n\t" // clear output bit
"dec %5\n\t" // decrement nBits
"rol %4\n\t" // shift value left to get to next bit
"brne nextbit\n\t" // branch if bits not finished
"rjmp nextbyte\n\t" // jump to next byte
"bitset:\n\t" // label
"rol %4\n\t" // shift value left to get to next bit
"dec %5\n\t" // decrement nBits
"cbi %0, %1\n\t" // clear output bit
"brne nextbit\n\t" // branch back if bits not finished
"nextbyte:\n\t" // label -
"ld %4, %a8+\n\t" // val = *p++ a8/a5
"cbi %0, %1\n\t" // clear output bit
"dec %9\n\t" // decrease bytecount
"brne startbit\n\t" // if bytes not finished start again
::
// Input operands Operand Id (w/ constraint)
"I" (_SFR_IO_ADDR(PORT)), // %0
"I" (PORT_PIN), // %1
"e" (&PORT), // %a2
"r" (high), // %3
"r" (val), // %4
"r" (nbits), // %5
"r" (tmp), // %6
"r" (low), // %7
"e" (p), // %a8
"w" (nbytes) // %9
);
some of the input operands are redundant now (like nBits, high, low, tmp) but I dont yet know how to remove them from the list yet - this is my first proper attempt at AVR ASM !!!
Since I was having problems with using ASM with arrays in a volatile manner (not knowing enough about all this), plus as I didnt want to use memset/malloc to remove dynamic memory addressing, I ended up with this sub which works perfectly...
void WS2812(void) {
while((micros() - latch50us) < 50L); // 50us data latch
cli();
volatile uint8_t green = dmxData[1];
volatile uint8_t red = dmxData[2];
volatile uint8_t blue = dmxData[3];
asm volatile(
"rcall dobits\n\t" // do green
"mov %5, %6\n\t"
"rcall dobits\n\t" // do red
"mov %5, %7\n\t"
"rcall dobits\n\t" // do bloo
"rjmp out\n\t"
"dobits:\n\t" // label
"ldi %4, 8\n\t" // reset number of bits
"nextbit:\n\t" // label
"sbi %0, %1\n\t" // SET OUTPUT HIGH
"sbrc %5, 7\n\t" // Skip if HiBit in value is clear
"rjmp bitset\n\t" // jump if HiBit is set
"cbi %0, %1\n\t" // SET OUTPUT LOW
"rol %5\n\t" // shift value left to get to next bit
"dec %4\n\t" // decrement nBits
"brne nextbit\n\t" // branch if bits not finished
"ret\n\t"
"bitset:\n\t" // label
"rol %5\n\t" // shift value left to get to next bit
"cbi %0, %1\n\t" // SET OUTPUT LOW
"dec %4\n\t" // decrement nBits
"brne nextbit\n\t" // branch back if bits not finished
"ret\n\t" // return from sub
"out:\n\t" // exit
::
// Input operands Operand Id (w/ constraint)
"I" (_SFR_IO_ADDR(PORT)), // %0
"I" (PORT_PIN), // %1
"e" (&PORT), // %a2
"w" (NULL), // %3
"r" (8), // %4
"r" (green), // %5
"r" (red), // %6
"r" (blue) // %7
);
sei();
latch50us = micros();
}