diff options
-rw-r--r-- | include/arch/arduino-nano/driver/gpio.h | 3 | ||||
-rw-r--r-- | src/app/blinkencat3/Makefile.inc | 1 | ||||
-rw-r--r-- | src/app/blinkencat3/main.cc | 262 | ||||
-rw-r--r-- | src/arch/arduino-nano/arch.cc | 2 | ||||
-rw-r--r-- | src/arch/arduino-nano/driver/gpio.cc | 2 |
5 files changed, 270 insertions, 0 deletions
diff --git a/include/arch/arduino-nano/driver/gpio.h b/include/arch/arduino-nano/driver/gpio.h index 0b4ca26..74aa879 100644 --- a/include/arch/arduino-nano/driver/gpio.h +++ b/include/arch/arduino-nano/driver/gpio.h @@ -148,10 +148,13 @@ class GPIO { if (pin < 8) { } else if (pin < 16) { PCMSK0 |= _BV(pin - 8); + PCICR |= _BV(PCIE0); } else if (pin < 24) { PCMSK1 |= _BV(pin - 16); + PCICR |= _BV(PCIE1); } else if (pin < 32) { PCMSK2 |= _BV(pin - 24); + PCICR |= _BV(PCIE2); } } inline void disable_int(unsigned char const pin) { diff --git a/src/app/blinkencat3/Makefile.inc b/src/app/blinkencat3/Makefile.inc new file mode 100644 index 0000000..2cb8a42 --- /dev/null +++ b/src/app/blinkencat3/Makefile.inc @@ -0,0 +1 @@ +loop ?= 1 diff --git a/src/app/blinkencat3/main.cc b/src/app/blinkencat3/main.cc new file mode 100644 index 0000000..79753b6 --- /dev/null +++ b/src/app/blinkencat3/main.cc @@ -0,0 +1,262 @@ +#include "arch.h" +#include "driver/neopixel.h" +#include "driver/stdout.h" +#include <util/delay.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/wdt.h> + +#define NUM_PIXELS 27 + +Adafruit_NeoPixel np(NUM_PIXELS, GPIO::pb0, NEO_GRB+NEO_KHZ800); + +class Blinkencat { + private: + uint8_t btn_debounce; + + public: + enum Mode : uint8_t { + OFF = 0, + RGBWHEEL_FAST, + RGBWHEEL_SLOW, + RGBFADE_FAST, + RGBFADE_SLOW, + COLD_WHITE, + BEEDOO, + STROBE, + COLOR_STROBE, + MODE_ENUM_MAX + }; + + Mode mode; + + void setup(void); + void next_mode(void); + void debounce_done(void); + void debounce_start(void); + void check_battery(void); + void sleep(void); + void idle(void); + void loop(void); + + Blinkencat() : btn_debounce(0), mode(OFF) {} +}; + +void Blinkencat::setup(void) +{ + np.setup(); + gpio.input(GPIO::pb1, 0); + gpio.input(GPIO::pd3, 1); + gpio.enable_int(GPIO::pd3); + + // One ADC conversion per four seconds + TCCR1A = 0; + TCCR1B = _BV(CS12) | _BV(CS10); + + // Measure internal 1.1V bandgap using VCC as reference on each Timer 1 overflow + ADMUX = _BV(REFS0) | 0x0e; + ADCSRB = _BV(ADTS2) | _BV(ADTS1); + ADCSRA = _BV(ADEN) | _BV(ADATE) | _BV(ADPS2) | _BV(ADPS1); +} + +void Blinkencat::idle(void) +{ + SMCR = _BV(SE); + asm("sleep"); + SMCR = 0; +} + +void Blinkencat::sleep(void) +{ + SMCR = _BV(SM1) | _BV(SE); + asm("sleep"); + SMCR = 0; +} + +void Blinkencat::debounce_start(void) +{ + if (!btn_debounce) { + btn_debounce = 1; + wdt_reset(); + WDTCSR = _BV(WDE) | _BV(WDCE); + WDTCSR = _BV(WDIE) | _BV(WDP2); + } +} + +void Blinkencat::debounce_done(void) +{ + btn_debounce = 0; + wdt_disable(); + // long press? -> turn off + if (!gpio.read(GPIO::pd3)) { + mode = OFF; + } +} + +void Blinkencat::next_mode(void) +{ + if (!btn_debounce) { + mode = (Mode)((mode + 1) % MODE_ENUM_MAX); + } +} + +void Blinkencat::check_battery(void) +{ + if (ADCSRA & _BV(ADIF)) { + uint8_t adcr_l = ADCL; + uint8_t adcr_h = ADCH; + uint16_t adcr = adcr_l + (adcr_h << 8); + uint16_t vcc = 1100L * 1023 / adcr; + + TIFR1 |= _BV(TOV1); + ADCSRA |= _BV(ADIF); + + //kout << "VCC is " << vcc << endl; + // 3 V under load ~~ 3.5 V idle + if (vcc < 3000) { + for (uint8_t i = 0; i < 5; i++) { + for (uint8_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(0, 0, 0)); + } + np.show(); + _delay_ms(400); + for (uint8_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(127 * ((i % 7) == 0), 0, 0)); + } + np.show(); + _delay_ms(400); + } + for (uint8_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(0, 0, 0)); + } + np.show(); + sleep(); + } + } +} + +void Blinkencat::loop(void) +{ + static uint16_t rgbwheel_offset = 0; + static uint16_t rgbfade_hsv = 0; + static uint8_t beedoo_pos = 0; + static uint8_t strobe_on = 0; + + /* + // not working due to bad logic levels + if (gpio.read(GPIO::pb1)) { + // Arduino and WS2812 strip are connected in parallel with the battery, + // which will significantly confuse the charging circuit when the + // strip is active while charging. So we make sure that it isn't. + mode = OFF; + } + */ + + switch (mode) { + case OFF: + // the mode may have been set by an ISR, which may in turn have + // been handled immediately after an np.show() call. So we must + // observe the 300us idle time mandated by WS2812. + _delay_ms(1); + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(0, 0, 0)); + } + np.show(); + sleep(); + break; + case RGBWHEEL_FAST: + case RGBWHEEL_SLOW: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + uint16_t hsv = (i * 252 + rgbwheel_offset) % 6553; + np.setPixelColor(i, np.gamma32(np.ColorHSV(hsv * 10))); + } + rgbwheel_offset = (rgbwheel_offset + 10) % 6553; + np.show(); + _delay_ms(1); + if (mode == RGBWHEEL_SLOW) { + _delay_ms(9); + } + break; + case RGBFADE_FAST: + case RGBFADE_SLOW: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.ColorHSV(rgbfade_hsv * 10)); + } + rgbfade_hsv = (rgbfade_hsv + 10) % 6553; + np.show(); + _delay_ms(1); + if (mode == RGBFADE_SLOW) { + _delay_ms(99); + } + break; + case COLD_WHITE: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(127, 127, 127)); + } + np.show(); + sleep(); + break; + case BEEDOO: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(0, 0, (i+2 - beedoo_pos <= 2) * 255)); + } + np.show(); + beedoo_pos = (beedoo_pos + 1) % NUM_PIXELS; + _delay_ms(20); + break; + case STROBE: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + np.setPixelColor(i, np.Color(strobe_on, strobe_on, strobe_on)); + } + np.show(); + strobe_on = 127 - strobe_on; + _delay_ms(40); + break; + case COLOR_STROBE: + for (uint16_t i = 0; i < NUM_PIXELS; i++) { + if (strobe_on) { + np.setPixelColor(i, np.ColorHSV(rgbfade_hsv * 10)); + } else { + np.setPixelColor(i, np.Color(0, 0, 0)); + } + } + rgbfade_hsv = (rgbfade_hsv + 50) % 6553; + np.show(); + strobe_on = 127 - strobe_on; + _delay_ms(40); + break; + } +} + +Blinkencat blinkencat; + +int main(void) +{ + arch.setup(); + gpio.setup(); + kout.setup(); + + blinkencat.setup(); + + while (1) { + blinkencat.check_battery(); + blinkencat.loop(); + } + + return 0; +} + + +ISR(WDT_vect) +{ + blinkencat.debounce_done(); +} + + +ISR(PCINT2_vect) +{ + if (!gpio.read(GPIO::pd3)) { + blinkencat.next_mode(); + } + blinkencat.debounce_start(); +} diff --git a/src/arch/arduino-nano/arch.cc b/src/arch/arduino-nano/arch.cc index 752b3f4..9103d10 100644 --- a/src/arch/arduino-nano/arch.cc +++ b/src/arch/arduino-nano/arch.cc @@ -1,10 +1,12 @@ #include "arch.h" #include <avr/io.h> #include <avr/interrupt.h> +#include <avr/wdt.h> #include <util/delay.h> void Arch::setup(void) { + wdt_disable(); #if F_CPU == 16000000UL /* default */ diff --git a/src/arch/arduino-nano/driver/gpio.cc b/src/arch/arduino-nano/driver/gpio.cc index 3b870b1..edfc2d0 100644 --- a/src/arch/arduino-nano/driver/gpio.cc +++ b/src/arch/arduino-nano/driver/gpio.cc @@ -13,7 +13,9 @@ ISR(PCINT1_vect) { } +/* ISR(PCINT2_vect) { } +*/ #endif |