expanded comments, minor cleanups, minor fixes
fixed inconsistent timing when re-enabling TCB0 timer fixed sleep wake on button release causes a fast frame bug
This commit is contained in:
parent
5b73fd93d3
commit
c1d0fef585
|
@ -0,0 +1,5 @@
|
||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
|
@ -1,24 +1,41 @@
|
||||||
/*
|
/*
|
||||||
|
|
||||||
hackspacecon wand firmware
|
hackspacecon wand firmware
|
||||||
|
|
||||||
initially written by true
|
initially written by true
|
||||||
rgb programs and further hacking by 000000widow
|
rgb programs and further hacking by 000000widow
|
||||||
|
|
||||||
|
|
||||||
operation workflow:
|
operation workflow:
|
||||||
- at poweron, set up GPIO and used peripherals, then go into standby mode.
|
|
||||||
- when button interrupt happens, wake up and process. if button is pushed,
|
- at initial reset (battery inserted), set up GPIO and peripherals. this
|
||||||
prepare to run RGB program.
|
includes setting up button wake interrupt. sleep MCU into standby mode.
|
||||||
- enable the TCB0 periodic timer, which will interrupt every ~61Hz.
|
|
||||||
- initialize the rgb program. idle the CPU.
|
- when button state changes, MCU will wake up and process the interrupt.
|
||||||
- timer will interrupt every ~61Hz and the program function will be called
|
if button is pushed (btn gpio is low), prepare to run next RGB program.
|
||||||
again, until prog function returns 0.
|
|
||||||
- run the RGB program. if program returns 0, disable periodic timer, and
|
- rgb program setup involves turning on power to the LEDs and enabling
|
||||||
wait for the next button event to wake us up and run a program again.
|
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:
|
todo:
|
||||||
- test the code
|
- test the code
|
||||||
- actually process the RGB data in the sample rainbow puke program
|
- set the RGB output data in the sample rainbow puke program
|
||||||
- add more programs
|
- add more programs
|
||||||
- support incrementing or randomly selecting the next program on each button push
|
- support incrementing or randomly selecting the next program on each button push
|
||||||
- run TCB0 in standby mode to save a little more power
|
- run TCB0 in standby mode to save a little more power
|
||||||
|
@ -38,12 +55,37 @@ tinyNeoPixel rgb = tinyNeoPixel(RGB_COUNT, PIN_PA1, NEO_GRB, rgbled);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum {
|
||||||
|
RGB_PROG_IDLE,
|
||||||
|
RGB_PROG_INIT,
|
||||||
|
RGB_PROG_RUNNING,
|
||||||
|
RGB_PROG_BTN_RELEASE
|
||||||
|
};
|
||||||
|
|
||||||
uint8_t run_rgb_program = 0;
|
uint8_t run_rgb_program = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void idle_cpu()
|
||||||
|
{
|
||||||
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc | SLPCTRL_SEN_bm;
|
||||||
|
__asm("sleep");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sleep_cpu()
|
||||||
|
{
|
||||||
|
SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc | SLPCTRL_SEN_bm;
|
||||||
|
__asm("sleep");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
// configure PA3 as falling edge interrupt input for button
|
// configure PA3 as both edge interrupt input for button
|
||||||
|
// note: only PA2 and PA6 support async wakeup and thus we can't check for
|
||||||
|
// falling edge on PA3. we're not using PA6 for the button as a
|
||||||
|
// future program may use SPI MISO to drive the LEDs.
|
||||||
|
// because of this, we need to wake up on both edges.
|
||||||
PORTA.DIRCLR = PIN3_bm;
|
PORTA.DIRCLR = PIN3_bm;
|
||||||
PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
|
PORTA.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc;
|
||||||
|
|
||||||
|
@ -66,46 +108,52 @@ void setup() {
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
switch (run_rgb_program) {
|
switch (run_rgb_program) {
|
||||||
case 1: { // just started running a program
|
case RGB_PROG_INIT: { // just started running a program
|
||||||
digitalWrite(PIN_PA6, HIGH); // enable LED power supply,
|
digitalWrite(PIN_PA6, HIGH); // enable LED power supply,
|
||||||
delay(10); // wait a moment for LEDs to stabilize,
|
delay(10); // wait a moment for LEDs to stabilize,
|
||||||
|
|
||||||
rgb_program[0](1); // initialize the program,
|
rgb_program[0](1); // initialize the program,
|
||||||
run_rgb_program++; // and set to running mode.
|
run_rgb_program++; // and set to running mode.
|
||||||
|
|
||||||
enable_rgb_timer(); // then start the RGB program timebase.
|
enable_rgb_timer(); // then start the RGB program timebase.
|
||||||
|
|
||||||
// we can idle the CPU after running the program
|
idle_cpu(); // we can idle CPU after running the program
|
||||||
SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc | SLPCTRL_SEN_bm;
|
|
||||||
__asm("sleep");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 2: { // continuing to run a program
|
|
||||||
|
case RGB_PROG_RUNNING: { // continuing to run a program
|
||||||
// send updates to the led
|
// send updates to the led
|
||||||
rgb.show();
|
rgb.show();
|
||||||
|
|
||||||
// then process the next program frame
|
// then process the next program frame
|
||||||
if (!rgb_program[0](0)) {
|
if (!rgb_program[0](0)) {
|
||||||
// until the program says it's done
|
// until the program says it's done
|
||||||
run_rgb_program = 0;
|
run_rgb_program = RGB_PROG_IDLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can idle the CPU after running the program
|
idle_cpu(); // we can idle CPU after running the program
|
||||||
SLPCTRL.CTRLA = SLPCTRL_SMODE_IDLE_gc | SLPCTRL_SEN_bm;
|
|
||||||
__asm("sleep");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: { // no longer running a program
|
|
||||||
disable_rgb_timer(); // disable RGB program timer,
|
case RGB_PROG_BTN_RELEASE: { // button released wakes MCU, but we're still running a program
|
||||||
digitalWrite(PIN_PA6, LOW); // disable LED power supply,
|
// skip processing and go back to stage 2
|
||||||
run_rgb_program = 0; // and clear run_rgb_program.
|
run_rgb_program = RGB_PROG_RUNNING;
|
||||||
|
|
||||||
|
idle_cpu(); // we can idle the CPU after doing nothing
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: { // no longer running a program
|
||||||
|
disable_rgb_timer(); // disable RGB program timer,
|
||||||
|
digitalWrite(PIN_PA6, LOW); // disable LED power supply,
|
||||||
|
run_rgb_program = RGB_PROG_IDLE; // and clear run_rgb_program.
|
||||||
|
|
||||||
// finally, go to sleep in standby mode
|
// finally, go to sleep in standby mode
|
||||||
SLPCTRL.CTRLA = SLPCTRL_SMODE_STDBY_gc | SLPCTRL_SEN_bm;
|
sleep_cpu();
|
||||||
__asm("sleep");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -125,6 +173,10 @@ ISR(PORTA_PORT_vect)
|
||||||
if (!digitalRead(PIN_PA3)) {
|
if (!digitalRead(PIN_PA3)) {
|
||||||
// start running a program if one isn't running already
|
// start running a program if one isn't running already
|
||||||
if (!run_rgb_program) run_rgb_program = 1;
|
if (!run_rgb_program) run_rgb_program = 1;
|
||||||
|
} else if (run_rgb_program == 2) {
|
||||||
|
// if we're running a program when the button is released (likely),
|
||||||
|
// then skip this interrupt
|
||||||
|
run_rgb_program++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define enable_rgb_timer() {TCB0.CTRLA |= 1;};
|
#define enable_rgb_timer() {TCB0.CNT = 0; TCB0.CTRLA |= 1;};
|
||||||
#define disable_rgb_timer() {TCB0.CTRLA &= ~1;};
|
#define disable_rgb_timer() {TCB0.CTRLA &= ~1;};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue