diff --git a/Firmware/PocketVoltex/LED.c b/Firmware/PocketVoltex/LED.c index f8e0f6c..306ba8e 100644 --- a/Firmware/PocketVoltex/LED.c +++ b/Firmware/PocketVoltex/LED.c @@ -28,6 +28,8 @@ #define G 1 #define B 0 +static volatile uint8_t leds_frontbuffer[LED_PHYSICAL_COUNT]; + void led_init() { // all GNDs low level for high impedence or gnd GND_PORT &= ~GND_MASK; @@ -39,7 +41,8 @@ void led_init() { // all LEDs output LED_DDR |= LED_MASK; - memset((uint8_t*)leds, 0, LED_PHYSICAL_COUNT); + memset(leds, 0, LED_PHYSICAL_COUNT); + memset((uint8_t*)leds_frontbuffer, 0, LED_PHYSICAL_COUNT); // 64 light levels * 60Hz update * 4 different GND pins = 15360Hz // 520 clock cycles for our interrupt handler @@ -54,6 +57,10 @@ void led_init() { TIFR0 = _BV(OCF0A); } +void led_commit(void) { + memcpy((uint8_t*)leds_frontbuffer, leds, LED_PHYSICAL_COUNT); +} + void led_set(uint8_t num, uint8_t r, uint8_t g, uint8_t b) { uint8_t offset = num * 3; leds[offset+R] = r; @@ -71,6 +78,33 @@ void led_set_max(uint8_t num, uint8_t r, uint8_t g, uint8_t b) { leds[offset+B] = b; } +// Applies a crossfade between the current colour and an overlay colour with a given strength +void led_fade_over(uint8_t num, uint8_t r, uint8_t g, uint8_t b, uint8_t strength) { + uint8_t offset = num * 3; + // going outside max val for a signed int8 + int16_t scales[3]; + // get colour distances + scales[R] = r - leds[offset+R]; + scales[G] = g - leds[offset+G]; + scales[B] = b - leds[offset+B]; + for(uint8_t i = 0; i < 3; i++) { + // perform scaling with div0 check + if(scales[i] == 0) { + scales[i] = BRIGHTNESS_LEVELS; + } else { + // won't ever be 0, don't check later + scales[i] = BRIGHTNESS_LEVELS / scales[i]; + } + int16_t new = leds[offset+i] + strength/scales[i]; + // Integer division strikes again + if(new > BRIGHTNESS_MAX) + new = BRIGHTNESS_MAX; + if(new < 0) + new = 0; + leds[offset+i] = new; + } +} + void led_set_all(uint8_t r, uint8_t g, uint8_t b) { for(uint8_t i = 0; i < LED_COUNT; i++) { led_set(i, r, g, b); @@ -111,7 +145,7 @@ ISR(TIMER0_COMPA_vect) { // This saves us doing a costly dynamic _BV() static uint8_t currentGndMask = 0; static uint8_t brightness = BRIGHTNESS_LEVELS - BRIGHTNESS_INCREMENT; - static volatile uint8_t* offset = &leds[0]; + static volatile uint8_t* offset = &leds_frontbuffer[0]; uint8_t out = 0; @@ -121,7 +155,7 @@ ISR(TIMER0_COMPA_vect) { currentGnd = 0; // Because we work backwards start at the high end and shift down currentGndMask = _BV(7); - offset = &leds[0]; + offset = &leds_frontbuffer[0]; brightness += BRIGHTNESS_INCREMENT; // brightness rolls over cleanly due to being a multiple } diff --git a/Firmware/PocketVoltex/LED.h b/Firmware/PocketVoltex/LED.h index 5b6035b..8f323a9 100644 --- a/Firmware/PocketVoltex/LED.h +++ b/Firmware/PocketVoltex/LED.h @@ -25,7 +25,7 @@ typedef struct { // Arranged left to right, top to bottom // LED order is BGR...BGR -volatile uint8_t leds[LED_PHYSICAL_COUNT]; +uint8_t leds[LED_PHYSICAL_COUNT]; // Maps BT/FX keys to their associated LED // Order is BT-1-4, FX-L, FX-R @@ -37,8 +37,10 @@ static const PROGMEM uint8_t ledLeftCircleMap[] = {0, 2, 4, 6}; static const PROGMEM uint8_t ledRightCircleMap[] = {7, 5, 3, 1}; void led_init(void); +void led_commit(void); void led_set(uint8_t num, uint8_t r, uint8_t g, uint8_t b); void led_set_max(uint8_t num, uint8_t r, uint8_t g, uint8_t b); +void led_fade_over(uint8_t num, uint8_t r, uint8_t g, uint8_t b, uint8_t strength); void led_set_all(uint8_t r, uint8_t g, uint8_t b); void led_set_indiv(uint8_t num, uint8_t val); diff --git a/Firmware/PocketVoltex/LEDPatterns.c b/Firmware/PocketVoltex/LEDPatterns.c index 5e42f20..4592015 100644 --- a/Firmware/PocketVoltex/LEDPatterns.c +++ b/Firmware/PocketVoltex/LEDPatterns.c @@ -41,19 +41,19 @@ typedef struct { // OPTIONS SHOULD BE: blue, pink, green, yellow static KnobLights knobs[2] = { // Aqua, mid left LEDs - {{0,1,1}, {2,3}, {BRIGHTNESS_LEVELS/2, BRIGHTNESS_LEVELS/2}}, + {{0,BRIGHTNESS_MAX,BRIGHTNESS_MAX}, {2,3}, {BRIGHTNESS_LEVELS/2, BRIGHTNESS_LEVELS/2}}, // Pink, mid right LEDs - {{1,0,1}, {0,1}, {BRIGHTNESS_LEVELS/2, BRIGHTNESS_LEVELS/2}} + {{BRIGHTNESS_MAX,0,BRIGHTNESS_MAX}, {0,1}, {BRIGHTNESS_LEVELS/2, BRIGHTNESS_LEVELS/2}} }; void led_knob_light_indiv(KnobLights* knob, const uint8_t* map); +uint8_t led_on_frame(void) { + return ++frameCounter >= LED_MS_PER_FRAME; +} + // Called every 1ms -void led_frame(void) { - if(++frameCounter < LED_MS_PER_FRAME) { - return; - } - +void led_animate(void) { frameCounter = 0; switch(animMode) { @@ -113,19 +113,17 @@ void led_frame(void) { break; } - // Knob lights, what shall I do with you? - led_set_all(0,0,0); led_knob_light_indiv(&knobs[0], ledLeftCircleMap); led_knob_light_indiv(&knobs[1], ledRightCircleMap); } void led_knob_light_indiv(KnobLights* knob, const uint8_t* map) { for(uint8_t led = 0; led < 2; led++) { - uint8_t r = knob->levels[led] * knob->rgb[0]; - uint8_t g = knob->levels[led] * knob->rgb[1]; - uint8_t b = knob->levels[led] * knob->rgb[2]; + uint8_t r = knob->rgb[0]; + uint8_t g = knob->rgb[1]; + uint8_t b = knob->rgb[2]; //led_set_max(ledCircleMap[knobs[i].leds[led]], r, g, b); - led_set(pgm_read_byte(&map[knob->leds[led]]), r, g, b); + led_fade_over(pgm_read_byte(&map[knob->leds[led]]), r, g, b, knob->levels[led]); } } diff --git a/Firmware/PocketVoltex/LEDPatterns.h b/Firmware/PocketVoltex/LEDPatterns.h index f94ffcf..820082a 100644 --- a/Firmware/PocketVoltex/LEDPatterns.h +++ b/Firmware/PocketVoltex/LEDPatterns.h @@ -22,7 +22,8 @@ enum LEDMode { BREATHE, }; -void led_frame(void); +uint8_t led_on_frame(void); +void led_animate(void); void led_anim_flash(void); void led_anim_follower(void); void led_knobs_update(int8_t left, int8_t right); diff --git a/Firmware/PocketVoltex/PocketVoltex.c b/Firmware/PocketVoltex/PocketVoltex.c index c6b7742..ff656e4 100644 --- a/Firmware/PocketVoltex/PocketVoltex.c +++ b/Firmware/PocketVoltex/PocketVoltex.c @@ -38,6 +38,7 @@ static uint8_t PrevInputsHIDReportBuffer[MAX(sizeof(Keyboard_Report_t), sizeof(U static uint8_t PrevLEDHIDReportBuffer[sizeof(LED_Report_t)]; static uint8_t sendKeyboard = 0; +static uint8_t updateLEDs = 1; /** LUFA HID Class driver interface configuration and state information. This structure is * passed to all HID Class driver functions, so that multiple instances of the same class @@ -181,6 +182,12 @@ int main(void) break; } } + + if(updateLEDs) { + updateLEDs = 0; + led_animate(); + led_commit(); + } } } @@ -312,7 +319,7 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI uint8_t offset = i+2; for(uint8_t j = 0; j < 3; j++) { // cast away the volatile for faster ops - ((uint8_t*)leds)[i++] = LEDReport->mainLights[offset--]; + leds[i++] = LEDReport->mainLights[offset--]; } } @@ -343,6 +350,7 @@ void EVENT_USB_Device_Connect(void) void EVENT_USB_Device_Disconnect(void) { led_set_all(0,0,0); + led_commit(); } /** Event handler for the library USB Configuration Changed event. */ @@ -371,7 +379,11 @@ void EVENT_USB_Device_StartOfFrame(void) HID_Device_MillisecondElapsed(&LED_HID_Interface); if(hidTimeout > HID_LED_TIMEOUT) { - led_frame(); + // we use a sentinel since this is actually inside an interrupt! + // less LED flicker if ran outside + if(led_on_frame()) { + updateLEDs = 1; + } } else { hidTimeout++; }