summaryrefslogtreecommitdiff
path: root/src/system.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/system.cc')
-rw-r--r--src/system.cc185
1 files changed, 185 insertions, 0 deletions
diff --git a/src/system.cc b/src/system.cc
new file mode 100644
index 0000000..9b04f69
--- /dev/null
+++ b/src/system.cc
@@ -0,0 +1,185 @@
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/wdt.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+#include <stdlib.h>
+
+// Debian's avr-libc lacks the ATTiny2313A WDTCSR definition
+#define WDTCSR _SFR_IO8(0x21)
+
+#include "system.h"
+
+/*
+ * Charge Status IN -> PD3 (PCINT14)
+ * Piezo IN -X
+ * Charge Status LED OUT -> PD1
+ * Vcc -> VCC
+ * GND -> GND
+ * Warm White OUT -> PD5 (OC0B)
+ * Blue OUT -> PB2 (OC0A)
+ * Button IN -> PD2 (PCINT13)
+ * Red OUT -> PB3 (OC1A)
+ * Green OUT -> PB4 (OC1B)
+ *
+ */
+
+System blinkencat;
+
+void System::initialize()
+{
+
+ // disable analog comparator
+ ACSR |= _BV(ACD);
+
+ // disable unused modules
+ PRR |= _BV(PRUSI) | _BV(PRUSART);
+
+ wdt_disable();
+
+ DDRA = 0;
+ DDRB = _BV(DDB2) | _BV(DDB3) | _BV(DDB4);
+ DDRD = _BV(DDD1) | _BV(DDD5);
+ PORTD = _BV(PD2);
+
+ /*
+ * Pin change interrupts on PD2 and PD3.
+ * They are also used to wake the chip from sleep, hence we can't use
+ * INT0/INT1 directly. For those, only a level interrupt is detected
+ * asynchronously.
+ */
+
+ PCMSK2 = _BV(PCINT13) | _BV(PCINT14);
+ GIMSK = _BV(PCIE2);
+
+ sei();
+}
+
+void System::idle()
+{
+ MCUCR &= ~_BV(SM0);
+ MCUCR |= _BV(SE);
+ asm("sleep");
+ MCUCR &= ~_BV(SE);
+}
+
+void System::sleep()
+{
+ MCUCR |= _BV(SM0) | _BV(SE);
+ asm("sleep");
+ MCUCR &= ~_BV(SE);
+}
+
+void System::set_outputs()
+{
+ if (warmwhite) {
+ PORTD |= _BV(PD5);
+ } else {
+ PORTD &= ~_BV(PD5);
+ }
+ if (red) {
+ PORTB |= _BV(PB3);
+ } else {
+ PORTB &= ~_BV(PB3);
+ }
+ if (green) {
+ PORTB |= _BV(PB4);
+ } else {
+ PORTB &= ~_BV(PB4);
+ }
+ if (blue) {
+ PORTB |= _BV(PB2);
+ } else {
+ PORTB &= ~_BV(PB2);
+ }
+}
+
+void System::loop()
+{
+ switch (mode) {
+ case OFF:
+ warmwhite = red = green = blue = 0;
+ break;
+ case WARMWHITE:
+ warmwhite = 255;
+ red = green = blue = 0;
+ break;
+ case RED:
+ red = 255;
+ warmwhite = green = blue = 0;
+ break;
+ case GREEN:
+ green = 255;
+ warmwhite = red = blue = 0;
+ break;
+ case BLUE:
+ blue = 255;
+ warmwhite = red = green = 0;
+ break;
+ case YELLOW:
+ red = green = 255;
+ warmwhite = blue = 0;
+ break;
+ case MAGENTA:
+ red = blue = 255;
+ warmwhite = green = 0;
+ break;
+ case CYAN:
+ green = blue = 255;
+ warmwhite = red = 0;
+ break;
+ case SUN:
+ warmwhite = red = green = blue = 255;
+ break;
+ default:
+ break;
+ }
+ set_outputs();
+
+ if (mode == OFF && !btn_debounce) {
+ sleep();
+ } else {
+ idle();
+ }
+}
+
+void System::next_mode(void)
+{
+ if (!btn_debounce) {
+ mode = (BCMode)((mode + 1) % MODE_ENUM_MAX);
+ }
+}
+
+void System::debounce_start(void)
+{
+ btn_debounce = 1;
+ wdt_reset();
+ WDTCSR = _BV(WDE) | _BV(WDCE);
+ WDTCSR = _BV(WDIE) | _BV(WDP1) | _BV(WDP0);
+}
+
+void System::debounce_done(void)
+{
+ btn_debounce = 0;
+ wdt_disable();
+}
+
+ISR(WDT_OVERFLOW_vect)
+{
+ blinkencat.debounce_done();
+}
+
+ISR(PCINT2_vect)
+{
+ if (!(PIND & _BV(PD2))) {
+ blinkencat.next_mode();
+ }
+ if (PIND & _BV(PD3)) {
+ blinkencat.is_charging = 1;
+ PORTD |= _BV(PD1);
+ } else {
+ blinkencat.is_charging = 0;
+ PORTD &= ~_BV(PD1);
+ }
+ blinkencat.debounce_start();
+}