156 lines
4.2 KiB
C++
156 lines
4.2 KiB
C++
/*
|
|
hackspacecon wand firmware
|
|
|
|
initially written by true
|
|
rgb programs and further hacking by 000000widow
|
|
|
|
|
|
operation workflow:
|
|
|
|
- at initial reset (battery inserted), set up GPIO and peripherals. this
|
|
includes setting up button wake interrupt. sleep MCU into standby mode.
|
|
|
|
- when button state changes, MCU will wake up and process the interrupt.
|
|
if button is pushed (btn gpio is low), prepare to run next RGB program.
|
|
|
|
- rgb program setup involves turning on power to the LEDs and enabling
|
|
TCB0 periodic timer to interrupt every ~61Hz. this timer interrupt
|
|
does not handle the rgb program, but will wake the CPU which resumes
|
|
processing in the loop() function.
|
|
|
|
- call the rgb program with the `init` parameter set to 1. idle the CPU.
|
|
|
|
- rgb prog timer will interrupt every ~61Hz. this will wake the CPU and
|
|
send the previously rendered LED data, then call the rgb prog function.
|
|
afterward, the CPU idles. process repeats until the rgb prog fn returns 0.
|
|
|
|
- every time the rgb program is run, the rgb data to send is updated.
|
|
the data is not actually sent until next wakeup in less than 16.4ms.
|
|
this allows the functions to have variable processing time, but the
|
|
output to have a consistent ~61Hz (~16.4ms) timing.
|
|
|
|
- once rgb program has finalized, clean up state, disable LED power, and sleep
|
|
the CPU in standby mode. MCU will wake again once the button is activated.
|
|
|
|
|
|
todo:
|
|
- test the code
|
|
- set the RGB output data in the sample rainbow puke program
|
|
- add more programs
|
|
- support incrementing or randomly selecting the next program on each button push
|
|
- run TCB0 in standby mode to save a little more power
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#include "rgbled.h"
|
|
|
|
|
|
#define PIN_LED_PWRENA PIN_PA6
|
|
|
|
|
|
|
|
enum {
|
|
RGB_IDLE,
|
|
RGB_INIT,
|
|
RGB_RUNNING
|
|
};
|
|
|
|
enum {
|
|
PROG_RUN = 0,
|
|
PROG_INIT = 1
|
|
};
|
|
|
|
uint8_t run_rgbprog = RGB_IDLE;
|
|
uint8_t rgbprog_idx = 0;
|
|
|
|
|
|
|
|
void idle_cpu()
|
|
{
|
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc | SLPCTRL_SEN_bm;
|
|
__asm("sleep");
|
|
}
|
|
|
|
void sleep_cpu()
|
|
{
|
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc | SLPCTRL_SEN_bm;
|
|
__asm("sleep");
|
|
}
|
|
|
|
|
|
// mcu init
|
|
void setup() {
|
|
// configure PA2 as falling edge interrupt for button
|
|
// note: only PA2 and PA6 support async wakeup.
|
|
// since we're using PA2, we're good to wakeup from a
|
|
// falling edge (button pushed) event only.
|
|
PORTA.DIRCLR = PIN2_bm;
|
|
PORTA.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
|
|
|
|
// configure other hardware pins as appropriate
|
|
pinMode(PIN_PA3, INPUT_PULLUP); // unused, spare pad on board
|
|
pinMode(PIN_PA7, INPUT_PULLUP); // unused, voltage passthru
|
|
|
|
digitalWrite(PIN_PA6, LOW);
|
|
pinMode(PIN_PA6, OUTPUT); // LED boost regulator enable
|
|
|
|
digitalWrite(PIN_PA1, LOW);
|
|
pinMode(PIN_PA1, OUTPUT); // LED data
|
|
|
|
// set up the RGB ~61Hz periodic timer
|
|
conf_rgb_timer();
|
|
|
|
// enable global interrupts (though they may already be enabled? fuck if I know)
|
|
sei();
|
|
}
|
|
|
|
// mcu program loop
|
|
void loop() {
|
|
switch (run_rgbprog) {
|
|
case RGB_INIT: { // just started running a program
|
|
digitalWrite(PIN_LED_PWRENA, HIGH); // enable LED power supply,
|
|
delay(20); // wait a moment for LEDs to stabilize,
|
|
|
|
rgbprog_idx++; // select the next program in sequence,
|
|
if (rgbprog_idx >= PROG_COUNT) {
|
|
rgbprog_idx = 0;
|
|
}
|
|
|
|
rgb_program[rgbprog_idx](PROG_INIT);// initialize the program,
|
|
run_rgbprog++; // and set to running mode.
|
|
|
|
enable_rgb_timer(); // then start the RGB program timebase.
|
|
|
|
idle_cpu(); // we can idle CPU after running the program
|
|
|
|
break;
|
|
}
|
|
|
|
case RGB_RUNNING: { // continuing to run a program
|
|
rgb.show(); // send updates to the led
|
|
// then process the next program frame
|
|
if (!rgb_program[rgbprog_idx](PROG_RUN)) {
|
|
run_rgbprog = RGB_IDLE; // until the program says it's done
|
|
break;
|
|
}
|
|
|
|
idle_cpu(); // we can idle CPU after running the program
|
|
|
|
break;
|
|
}
|
|
|
|
default: { // no longer running a program
|
|
disable_rgb_timer(); // disable RGB program timer,
|
|
digitalWrite(PIN_LED_PWRENA, LOW); // disable LED power supply,
|
|
run_rgbprog = RGB_IDLE; // and clear run_rgbprog.
|
|
|
|
sleep_cpu(); // finally, go to sleep in standby mode
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|