#include <avr/io.h>
#include <util/delay.h>
#define NOINIT __attribute__((section(".noinit")))
typedef unsigned char byte;
struct GRB{
byte g,r,b;
};
// Repeat ist hier „vertikal“, d.h. das Muster wiederholt sich
// nach LEN WS2812-LEDs.
// Für andersherum = nach LEN gleicher Farbe kommt LEN anderer Farbe
// muss die Update-Funktion anders geschrieben werden.
// Ein Armutszeugnis warum das bei Arduino nicht genau so
// (mit Templates und ohne malloc()) gelöst wird. Sind halt Deppen.
template<unsigned PORT,byte BIT,unsigned LEN,unsigned REP> class WS2812 {
public:
GRB data[LEN];
// data[] auf WS2812-kompatible LED-Kette REP-fach ausgeben
// Interrupts müssen gesperrt sein!
void update() const{
byte*workp;
unsigned workl;
asm volatile(
// Takte-Soll: T0H = 6,4 T1H = 12,8, T0L = 13,6, T1L = 7,2, Toleranz = ±2,4
// Bitslot = 20, Toleranz = ±9,6
// Takte-Ist: T0H = 6, T1H = 12, T0L = 14, T1L = 8, Summe (1 Bit) = 20
"1: movw %0,%4 \n" // 0 LL
" movw %1,%5 \n" // 1 LL
"2: ld r0,%a0+ \n" // 2 LL
// 3 LL
"3: sbi %2,%3 \n" // 4 LL
// 5 LL
" lsl r0 \n" // 6 HH
" inc r1 \n" // 7 HH
" nop \n" // 8 HH
" brcs 4f \n" // 9 HH
" cbi %2,%3 \n" // 10 H-
// 11 H-
"4:" // 10 -H
" brcc . \n" // 12 L- 11 -H
// 12 -H
" sbrs r1,3 \n" // 13 LH
" rjmp 8f \n" // 14 LH
" clr r1 \n" // 15 LH
" cbi %2,%3 \n" // 16 LH
// 17 LH
" subi %A1,1 \n" // 18 LL
" sbci %B1,0 \n" // 19 LL
" brne 2b \n" // 20 LL
// 21 LL = kein Extra-Takt fürs nächste Byte
" subi %A6,1 \n" // 22 LL
" sbci %B6,0 \n" // 23 LL
" brne 1b \n" // 24 LL
// 25 LL = 6 Takte mehr bei Wiederholung
" rjmp 9f \n"
// Für das Bit mittendrin ist mehr Zeit, deshalb „nach unten ausgelagert“.
"8:" // 15 LH
" cbi %2,%3 \n" // 16 LH
// 17 LH
" rjmp . \n" // 18 LL
// 19 LL
" rjmp . \n" // 20 LL
// 21 LL
" rjmp 3b \n" // 22 LL
// 23 LL
"9:"
:"=&e"(workp),"=&r"(workl):
"I"(PORT),"I"(BIT),"r"(data),"r"(sizeof data),"r"(REP));
}
};
static WS2812<_SFR_IO_ADDR(PORTD),7,1,800> ws2812 NOINIT;
// bedeutet: 3 Byte Puffer für 800 RGB-LEDs.
// Für unifarbene Lichtbänder sowieso die richtige Lösung!
int main() {
// Kein originaler ATmega328p sondern ein von Arduino verhunzter.
// Vorteil: Taktteiler ist bereits 1
// CLKPR=0x80;
// CLKPR=0;
DDRD|=0x80;
GRB&pixel=ws2812.data[0];
// Bei voller Lichtstärke kommt am Ende des 5-m-Bands nur noch knapp
// 2 V Speisespannung an, und es kommt zu einer Rotverschiebung.
// Deshalb hier extra geringe Lichtstärken zum Test der „Schnecke“.
pixel={8,4,2};
for(;;) {
_delay_ms(300);
ws2812.update();
pixel={pixel.r,pixel.b,pixel.g}; // Farbanteile rotieren
}
}
Detected encoding: UTF-8 | 0
|