-
Notifications
You must be signed in to change notification settings - Fork 0
/
neopixel.c
111 lines (92 loc) · 4.56 KB
/
neopixel.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <stdint.h>
#include "neopixel.h"
#include "vfd.h"
uint8_t neopixelFadeCountDown = 0; // Count down for how the Neopixel increase in brightness should be done
volatile bit neopixelUpdate = 0; // A 'update flag' set true at the systick frequency
// Swap the order of the lower 4 bits between each other (the higher 4-bits will be discarded)
uint8_t reverse4bits(uint8_t value) {
return ((value & (1<<0)) << 3) | // Move bit 0 to bit 3
((value & (1<<1)) << 1) | // Move bit 1 to bit 2
((value & (1<<2)) >> 1) | // Move bit 2 to bit 1
((value & (1<<3)) >> 3); // Move bit 3 to bit 0
}
// Fading only the red color only
void neopixelFadeHandler(void) {
if (neopixelUpdate) {
// Consider doing the Neopixel update only once per systick
if (neopixelFadeCountDown) {
// At the first 16 ticks of the systick timer the color will fade from black to red
// only if the neopixelFadeUp is setup. This way the fade will happen only on
// powerup but not any other button presses which reset the stayAwake counter
neopixelFadeCountDown--;
if (neopixelFadeCountDown) {
neopixelSetColor(reverse4bits((NEOPIXEL_START_FADE - neopixelFadeCountDown)/NEOPIXEL_FADE_SPEED) << 4);
} else {
neopixelSetColor(NEOPIXEL_CLOCK_COLOR); // Final color is reached
}
}
if ((NEOPIXEL_START_FADE * NEOPIXEL_FADE_SPEED) > stayAwake) {
// At the last 16 ticks of the systick timer the color will fade from red to black
neopixelSetColor(reverse4bits((stayAwake)/NEOPIXEL_FADE_SPEED) << 4);
}
neopixelUpdate = 0;
}
}
// Set color of one Neopixel WS2812B LED.
// Hardcoded to PB2 pin and timed exactly for an 8MHz clock.
// 12-bit colors instead of 24-bit, (4-bit for each channel, each bit is transmitted twice).
// Order of colors: Blue Red Green (0xBRG)
// MSB is first in the stream, this means that 0x001 will be a brighter green than 0x008
// CodeVisionAVR compiler doesn't support good mixing of C and ASM and
// had to hardcode registers for its ABI. While rewriting this into GCC
// and its "Extended assembly" would make it more portable and robust.
void neopixelSetColor(uint16_t color) {
#asm
cli // Disable IRQ
ldi r31, 12 // counter=12 (12 bits to count down), the ABI promising R31 is free to use in assembly
lsr r17 // color = color >> 1 hardcoding R16 and R17 for CodeVisionAVR ABI :(
ror r16
brcc _bit_low // If the bit shifted from color is 0 then push the low bits, otherwise push the high bits
_bit_high:
sbi 0x5, 2 // PB2 = 1
nop // Timing NOPs
nop
nop
lsr r17 // Shift the color bits again, sooner than it's needed (because later there will be no time)
ror r16
dec r31 // counter--; it doesn't touch the CARRY, so both decrement and right shift are safe to do ahead of time
cbi 0x5, 2 // PB2 = 0
nop
nop
sbi 0x5, 2 // PB2 = 1 (each bit is pushed twice)
nop
nop
nop
breq _neo_end // If (0==counter) then end this loop (the end of the loop will do CBI anyway, so it's safe to do it ahead of time)
nop
cbi 0x5, 2 // PB2 = 0 (but instead of timing NOPs do some decisions)
brcc _bit_low // If the carry from the shifted bit from the color was 0 then go to the low bit branch,
rjmp _bit_high // otherwise repeat this high bit branch
_bit_low:
sbi 0x5, 2 // PB2 = 1
nop // Timing NOP
cbi 0x5, 2 // PB2 = 0
nop
nop
lsr r17 // Shift the color bits again, sooner than it's needed (because later there will be no time)
ror r16
dec r31 // counter--; it doesn't touch the CARRY, so both decrement and right shift are safe to do ahead of time
sbi 0x5, 2 // PB2 = 1 (each bit is pushed twice)
nop // Timing NOP
cbi 0x5, 2 // PB2 = 0
nop
nop
breq _neo_end // If (0==counter) then end this loop
brcc _bit_low // If the carry from the shifted bit from the color was 0 then repeat this low bit branch,
rjmp _bit_high // otherwise go to the high bit branch
_neo_end:
cbi 0x5,2 // PB2 = 0 as it might not have been set yet
// Leave it at 0 as that will send the WS2812B chip a 'reset' command which will start displaying the color
sei // enable IRQs
#endasm
}