|
| 1 | +/* |
| 2 | + * Copyright 2012 Alan Burlison, alan@bleaklow.com. All rights reserved. |
| 3 | + * Use is subject to license terms. |
| 4 | + */ |
| 5 | + |
| 6 | +/* |
| 7 | + * WS2811 RGB LED driver. |
| 8 | + */ |
| 9 | + |
| 10 | +#ifndef WS2811_h |
| 11 | +#define WS2811_h |
| 12 | + |
| 13 | +// RGB value structure. |
| 14 | +typedef struct __attribute__ ((__packed__)) { |
| 15 | + uint8_t r; |
| 16 | + uint8_t g; |
| 17 | + uint8_t b; |
| 18 | +} RGB_t; |
| 19 | + |
| 20 | +#ifndef ARRAYLEN |
| 21 | +#define ARRAYLEN(A) (sizeof(A) / sizeof(A[0])) |
| 22 | +#endif |
| 23 | + |
| 24 | +/* |
| 25 | + * Inline asm macro to output 24-bit RGB value in (G,R,B) order, MSBit first. |
| 26 | + * 0 bits are 250ns hi, 1000ns lo, 1 bits are 1000ns hi, 250ns lo. |
| 27 | + * r18 = red byte to be output |
| 28 | + * r19 = green byte to be output |
| 29 | + * r20 = blue byte to be output |
| 30 | + * r26 = saved SREG |
| 31 | + * r27 = inner loop counter |
| 32 | + */ |
| 33 | +#define WS2811(PORT, PIN, RGB, LEN) \ |
| 34 | +asm volatile( \ |
| 35 | +/* initialise */ \ |
| 36 | +" cp %A[len], r1 ; check len > 0, return immediately if it is\n" \ |
| 37 | +" cpc %B[len], r1\n" \ |
| 38 | +" brne 1f\n" \ |
| 39 | +" rjmp 16f\n" \ |
| 40 | +"1: ld r18, Z+ ; load in first red byte to be output\n" \ |
| 41 | +" ld r19, Z+ ; load in first green byte to be output\n" \ |
| 42 | +" ld r20, Z+ ; load in first blue byte to be output\n" \ |
| 43 | +" ldi r27, 8 ; load inner loop counter\n" \ |
| 44 | +" in r26, __SREG__ ; timing-critical, so no interrupts\n" \ |
| 45 | +" cli\n" \ |
| 46 | +/* green - loop over 8 bits */ \ |
| 47 | +"2: sbi %[port], %[pin] ; pin lo -> hi\n" \ |
| 48 | +" sbrc r19, 7 ; test hi bit clear\n" \ |
| 49 | +" rjmp 3f ; true, skip pin hi -> lo\n" \ |
| 50 | +" cbi %[port], %[pin] ; false, pin hi -> lo\n" \ |
| 51 | +"3: sbrc r19, 7 ; equalise delay of both code paths\n" \ |
| 52 | +" rjmp 4f\n" \ |
| 53 | +"4: nop ; pulse timing delay\n" \ |
| 54 | +" nop\n" \ |
| 55 | +" nop\n" \ |
| 56 | +" nop\n" \ |
| 57 | +" nop\n" \ |
| 58 | +" nop\n" \ |
| 59 | +" lsl r19 ; shift to next bit\n" \ |
| 60 | +" dec r27 ; decrement loop counter\n" \ |
| 61 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 62 | +" brne 2b\n ; loop if required\n" \ |
| 63 | +" ldi r27, 7 ; reload inner loop counter\n" \ |
| 64 | +/* red - loop over first 7 bits */ \ |
| 65 | +"5: sbi %[port], %[pin] ; pin lo -> hi\n" \ |
| 66 | +" sbrc r18, 7 ; test hi bit clear\n" \ |
| 67 | +" rjmp 6f ; true, skip pin hi -> lo\n" \ |
| 68 | +" cbi %[port], %[pin] ; false, pin hi -> lo\n" \ |
| 69 | +"6: sbrc r18, 7 ; equalise delay of both code paths\n" \ |
| 70 | +" rjmp 7f\n" \ |
| 71 | +"7: nop ; pulse timing delay\n" \ |
| 72 | +" nop\n" \ |
| 73 | +" nop\n" \ |
| 74 | +" nop\n" \ |
| 75 | +" nop\n" \ |
| 76 | +" nop\n" \ |
| 77 | +" lsl r18 ; shift to next bit\n" \ |
| 78 | +" dec r27 ; decrement inner loop counter\n" \ |
| 79 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 80 | +" brne 5b ; inner loop, if required\n" \ |
| 81 | +" nop ; equalise delay of both code paths\n" \ |
| 82 | +/* red, 8th bit - output & fetch next values */ \ |
| 83 | +" sbi %[port], %[pin] ; pin lo -> hi\n" \ |
| 84 | +" sbrc r18, 7 ; test hi bit clear\n" \ |
| 85 | +" rjmp 8f ; true, skip pin hi -> lo\n" \ |
| 86 | +" cbi %[port], %[pin] ; false, pin hi -> lo\n" \ |
| 87 | +"8: sbrc r18, 7 ; equalise delay of both code paths\n" \ |
| 88 | +" rjmp 9f\n" \ |
| 89 | +"9: nop ; pulse timing delay\n" \ |
| 90 | +" nop\n" \ |
| 91 | +" nop\n" \ |
| 92 | +" ld r18, Z+ ; load next red byte\n" \ |
| 93 | +" ld r19, Z+ ; load next green byte\n" \ |
| 94 | +" ldi r27, 7 ; reload inner loop counter\n" \ |
| 95 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 96 | +" nop ; pulse timing delay\n" \ |
| 97 | +" nop\n" \ |
| 98 | +/* blue - loop over first 7 bits */ \ |
| 99 | +"10: sbi %[port], %[pin] ; pin lo -> hi\n" \ |
| 100 | +" sbrc r20, 7 ; test hi bit clear\n" \ |
| 101 | +" rjmp 11f ; true, skip pin hi -> lo\n" \ |
| 102 | +" cbi %[port], %[pin] ; false, pin hi -> lo\n" \ |
| 103 | +"11: sbrc r20, 7 ; equalise delay of both code paths\n" \ |
| 104 | +" rjmp 12f\n" \ |
| 105 | +"12: nop ; pulse timing delay\n" \ |
| 106 | +" nop\n" \ |
| 107 | +" nop\n" \ |
| 108 | +" nop\n" \ |
| 109 | +" nop\n" \ |
| 110 | +" nop\n" \ |
| 111 | +" lsl r20 ; shift to next bit\n" \ |
| 112 | +" dec r27 ; decrement inner loop counter\n" \ |
| 113 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 114 | +" brne 10b ; inner loop, if required\n" \ |
| 115 | +" nop ; equalise delay of both code paths\n" \ |
| 116 | +/* blue, 8th bit - output & handle outer loop */ \ |
| 117 | +" sbi %[port], %[pin] ; pin lo -> hi\n" \ |
| 118 | +" sbrc r20, 7 ; test hi bit clear\n" \ |
| 119 | +" rjmp 13f ; true, skip pin hi -> lo\n" \ |
| 120 | +" cbi %[port], %[pin] ; false, pin hi -> lo\n" \ |
| 121 | +"13: sbrc r20, 7 ; equalise delay of both code paths\n" \ |
| 122 | +" rjmp 14f\n" \ |
| 123 | +"14: nop ; pulse timing delay\n" \ |
| 124 | +" nop\n" \ |
| 125 | +" ldi r27, 8 ; reload inner loop counter\n" \ |
| 126 | +" sbiw %A[len], 1 ; decrement outer loop counter\n" \ |
| 127 | +" breq 15f ; exit outer loop if zero\n" \ |
| 128 | +" ld r20, Z+ ; load in next blue byte\n" \ |
| 129 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 130 | +" rjmp 2b ; outer loop, if required\n" \ |
| 131 | +"15: nop ; pulse timing delay\n" \ |
| 132 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 133 | +" nop ; pulse timing delay\n" \ |
| 134 | +" nop\n" \ |
| 135 | +" out __SREG__, r26 ; reenable interrupts\n" \ |
| 136 | +"16:\n" \ |
| 137 | +" sbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 138 | +" cbi %[port], %[pin] ; pin hi -> lo\n" \ |
| 139 | +: \ |
| 140 | +: [rgb] "z" (RGB), \ |
| 141 | + [len] "w" (LEN), \ |
| 142 | + [port] "I" (_SFR_IO_ADDR(PORT)), \ |
| 143 | + [pin] "I" (PIN) \ |
| 144 | +: "r18", "r19", "r20", "r26", "r27", "cc", "memory" \ |
| 145 | +) |
| 146 | + |
| 147 | +/* |
| 148 | + * Define a C function to wrap the inline WS2811 macro for a given port and pin. |
| 149 | + */ |
| 150 | +#define DEFINE_WS2811_FN(NAME, PORT, PIN) \ |
| 151 | +extern void NAME(const RGB_t *rgb, uint16_t len) __attribute__((noinline)); \ |
| 152 | +void NAME(const RGB_t *rgb, uint16_t len) { WS2811(PORT, PIN, rgb, len); } |
| 153 | + |
| 154 | +#endif /* WS2811_h */ |
0 commit comments