#define TIM0OCB_PIN 5
#define TRIGGER_PIN 6
#define OUT_TRUE 13 // Number of 16MHz clock-cycles to hold HIGH for a neopixel 1
#define OUT_FALSE 7 // Number of 16MHz clock-cycles to hold MIGH for a neopixel 0
#define OUT_PERIOD 14 // Number of 16MHz clock-cycles per neopixel full bit-timing
// (may need reduced to account for looping time)
#define MILLISECOND 249 // Number of 16MHz/64 (250KHz) clock-cycles to wait for 1 millisecond
// accessory functions (should probably move to a library later)
void pauseMS(uint16_t milliSec);
void sendBit(bool bit);
void sendPixel(uint32_t pixel); // NOTE: *pixel* must be in GGGGGGGG-RRRRRRRR-BBBBBBBB-xxxxxxxx format
uint32_t buildPixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t flux = 255);
void setup() {
delay(2000); // "Safety Delay" to avoid possibility of bricking a board & having to ICSP-reflash
uint8_t prevSREG = SREG; // Save status register
SREG &= 0b01111111; // disable global interrupts
pinMode(TIM0OCB_PIN, OUTPUT);
pinMode(TRIGGER_PIN, OUTPUT);
TIMSK0 = 0; // Disable all timer0 interrupts, since we just smashed the timer anywise
TCCR0A = 0b00000011; // No OC0x output to pins
TCCR0B = 0; // Fast PWM TOP=0xFF, timer disabled
SREG = prevSREG; // Restore status register
Serial.begin(115200);
}
void loop() {
// Send a few basic test pixel values, just to see how badly I screwed this up:
sendPixel(buildPixel(0, 0, 0, 0));
pauseMS(2);
for (uint16_t i = 0; i < 300; i++) {
sendPixel(buildPixel(255, 255, 255));
}
pauseMS(500);
for (uint16_t i = 0; i < 275; i++) {
sendPixel(buildPixel(255, 255, 255, 192));
}
pauseMS(500);
for (uint16_t i = 0; i < 250; i++) {
sendPixel(buildPixel(255, 255, 255, 128));
}
pauseMS(500);
for (uint16_t i = 0; i < 225; i++) {
sendPixel(buildPixel(255, 255, 255, 64));
}
pauseMS(500);
for (uint16_t i = 0; i < 200; i++) {
sendPixel(buildPixel(255, 255, 255, 32));
}
pauseMS(500);
for (uint16_t i = 0; i < 175; i++) {
sendPixel(buildPixel(255, 0, 0));
}
pauseMS(500);
for (uint16_t i = 0; i < 150; i++) {
sendPixel(buildPixel(255, 255, 0));
}
pauseMS(500);
for (uint16_t i = 0; i < 125; i++) {
sendPixel(buildPixel(0, 255, 0));
}
pauseMS(500);
for (uint16_t i = 0; i < 100; i++) {
sendPixel(buildPixel(0, 255, 255));
}
pauseMS(500);
for (uint16_t i = 0; i < 75; i++) {
sendPixel(buildPixel(0, 0, 255));
}
pauseMS(500);
for (uint16_t i = 0; i < 50; i++) {
sendPixel(buildPixel(255, 0, 255));
}
pauseMS(500);
for (uint16_t i = 0; i < 25; i++) {
sendPixel(buildPixel(0, 0, 0, 0));
}
pauseMS(2000);
}
// Handles building a proper pixel value from R+G+B "hue" bytes, pluse flux "intensity" byte
uint32_t buildPixel(uint8_t red, uint8_t green, uint8_t blue, uint8_t flux) {
uint32_t pixel = 0; // output buffer
// Bits 0-7 = GREEN channel
uint32_t tempVal = green * flux & 0xff00;
pixel |= tempVal << 16;
// Bits 8-15 = RED channel
tempVal = red * flux & 0xff00;
pixel |= tempVal << 8;
// Bits 16-23 = BLUE channel, and 24-31 = 0
tempVal = blue * flux & 0xff00;
pixel |= tempVal;
return pixel;
}
// Handles sequencing of the pixel data to sendBit()
void sendPixel(uint32_t pixel) {
OCR0A = OUT_PERIOD; // Set link speed
TCCR0A = 0b00100011; // No OC0A output, OC0B HIGH until match
bool bit;
for (int bitPos = 0; bitPos < 24; bitPos++) {
bit = 0;
if (pixel & 0x80000000) {
bit = 1;
}
sendBit(bit);
pixel <<= 1;
}
TCCR0A &= 0b00000011; // Disconnect timer from output pins
}
// Sends a single bit to the neopixel, using pin5 (Timer0 OutputB)
void sendBit(bool bit) {
if (bit) { // Send a '1' value
OCR0B = OUT_TRUE;
} else { // Send a '0' value
OCR0B = OUT_FALSE;
}
TIFR0 = 2; // Clear timer flag
TCNT0 = 254; // Reset timer to 0
TCCR0B = 1; // Enable timer, prescale = clk/1
while (!(TIFR0 & 2)) {} // wait for bit to "fully send"
TCCR0B = 0; // Disable Timer0
}
// Replace *delay()*, since we clobbered it by taking over timer0
void pauseMS(uint16_t milliSec) {
OCR0A = MILLISECOND;
TIFR0 = 2;
TCNT0 = 0;
TCCR0A = 0b00000011;
TCCR0B = 0b00001011; // Enable timer, TOP=OCR0A, prescale = clk/64
for (uint16_t elapsed = 0; elapsed < milliSec; elapsed++) {
while (!(TIFR0 & 2)) {} // Wait for millisecond timer
TIFR0 = 2; // Clear timer flag
}
TCCR0B = 0; // Disable timer
}