From 7c56be8a724e2b0647e5a6cbd55c7b0eb948206b Mon Sep 17 00:00:00 2001 From: Sebastian Muszytowski Date: Fri, 3 Jun 2016 15:48:16 +0200 Subject: initial commit of all data --- src/system.cc | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 src/system.cc (limited to 'src/system.cc') diff --git a/src/system.cc b/src/system.cc new file mode 100644 index 0000000..535702b --- /dev/null +++ b/src/system.cc @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2016 by Daniel Friesel + * + * License: You may use, redistribute and/or modify this file under the terms + * of either: + * * The GNU LGPL v3 (see COPYING and COPYING.LESSER), or + * * The 3-clause BSD License (see COPYING.BSD) + * + */ + +#include +#include +#include +#include +#include +#include + +#include "display.h" +#include "fecmodem.h" +#include "storage.h" +#include "system.h" +#include "static_patterns.h" + +#define SHUTDOWN_THRESHOLD 2048 + +System rocket; + +animation_t active_anim; + +uint8_t disp_buf[132]; // 4 byte header + 128 byte data +uint8_t *rx_buf = disp_buf + sizeof(disp_buf) - 33; + +void System::initialize() +{ + // disable ADC to save power + PRR |= _BV(PRADC); + + // dito + wdt_disable(); + + // Enable pull-ups on PC3 and PC7 (button pins) + PORTC |= _BV(PC3) | _BV(PC7); + + display.enable(); + modem.enable(); + storage.enable(); + + //storage.reset(); + //storage.save((uint8_t *)"\x10\x0a\x11\x00nootnoot"); + //storage.save((uint8_t *)"\x10\x09\x20\x00" "fnordor"); + //storage.save((uint8_t *)"\x10\x05\x20\x00 \x01 "); + //storage.save((uint8_t *)"\x20\x22\x08\x02" + // "\x00\x04\x22\x02\x22\x04\x00\x00" + // "\x00\x00\x00\x00\x00\x00\x00\x00" + // "\x00\x04\x22\x02\x22\x04\x00\x00" + // "\x00\x00\x00\x00"); + //storage.append((uint8_t *)"\x00\x00\x00\x00"); + + sei(); + + current_anim_no = 0; + loadPattern(0); +} + +void System::loadPattern_P(const uint8_t *pattern_ptr) +{ + uint8_t i; + + for (i = 0; i < 4; i++) + disp_buf[i] = pgm_read_byte(pattern_ptr + i); + + for (i = 0; i < disp_buf[1]; i++) + disp_buf[i+4] = pgm_read_byte(pattern_ptr + i + 4); + + loadPattern_buf(disp_buf); +} + +void System::loadPattern_buf(uint8_t *pattern) +{ + active_anim.type = (AnimationType)(pattern[0] >> 4); + active_anim.length = (pattern[0] & 0x0f) << 8; + active_anim.length += pattern[1]; + + if (active_anim.type == AnimationType::TEXT) { + active_anim.speed = 250 - (pattern[2] & 0xf0); + active_anim.delay = (pattern[2] & 0x0f ); + active_anim.direction = pattern[3] >> 4; + } else if (active_anim.type == AnimationType::FRAMES) { + active_anim.speed = 250 - ((pattern[2] & 0x0f) << 4); + active_anim.delay = (pattern[3] & 0x0f); + active_anim.direction = 0; + } + + active_anim.data = pattern + 4; + display.show(&active_anim); +} + +void System::loadPattern(uint8_t anim_no) +{ + if (storage.hasData()) { + storage.load(anim_no, disp_buf); + loadPattern_buf(disp_buf); + } else { + loadPattern_P(emptyPattern); + } +} + +void System::receive(void) +{ + static uint8_t rx_pos = 0; + static uint16_t remaining_bytes = 0; + uint8_t rx_byte = modem.buffer_get(); + + /* + * START* and PATTERN* are sync signals, everything else needs to be + * stored on the EEPROM. + * (Note that the C++ standard guarantees "rxExpect > PATTERN2" to match + * for HEADER*, META* and DATA since they are located after PATTERN2 + * in the RxExpect enum declaration) + */ + if (rxExpect > PATTERN2) { + rx_buf[rx_pos++] = rx_byte; + /* + * HEADER and META are not included in the length + * -> only count bytes for DATA. + */ + if (rxExpect > META2) { + remaining_bytes--; + } + } + + switch(rxExpect) { + case START1: + if (rx_byte == BYTE_START) + rxExpect = START2; + else + rxExpect = NEXT_BLOCK; + break; + case START2: + if (rx_byte == BYTE_START) { + rxExpect = PATTERN1; + storage.reset(); + loadPattern_P(flashingPattern); + MCUSR &= ~_BV(WDRF); + cli(); + // watchdog interrupt after 4 seconds + WDTCSR = _BV(WDCE) | _BV(WDE); + WDTCSR = _BV(WDIE) | _BV(WDP3); + sei(); + } else { + rxExpect = NEXT_BLOCK; + } + break; + case NEXT_BLOCK: + if (rx_byte == BYTE_START) + rxExpect = START2; + else if (rx_byte == BYTE_PATTERN) + rxExpect = PATTERN2; + else if (rx_byte == BYTE_END) { + storage.sync(); + current_anim_no = 0; + loadPattern(0); + rxExpect = START1; + wdt_disable(); + } + break; + case PATTERN1: + if (rx_byte == BYTE_PATTERN) + rxExpect = PATTERN2; + else + rxExpect = NEXT_BLOCK; + break; + case PATTERN2: + rx_pos = 0; + if (rx_byte == BYTE_PATTERN) + rxExpect = HEADER1; + else + rxExpect = NEXT_BLOCK; + break; + case HEADER1: + rxExpect = HEADER2; + remaining_bytes = (rx_byte & 0x0f) << 8; + break; + case HEADER2: + rxExpect = META1; + remaining_bytes += rx_byte; + wdt_reset(); + break; + case META1: + rxExpect = META2; + break; + case META2: + rxExpect = DATA_FIRSTBLOCK; + /* + * skip empty patterns (would bork because of remaining_bytes-- + * otherwise + */ + if (remaining_bytes == 0) + rxExpect = NEXT_BLOCK; + break; + case DATA_FIRSTBLOCK: + if (remaining_bytes == 0) { + rxExpect = NEXT_BLOCK; + storage.save(rx_buf); + } else if (rx_pos == 32) { + rxExpect = DATA; + rx_pos = 0; + storage.save(rx_buf); + } + break; + case DATA: + if (remaining_bytes == 0) { + rxExpect = NEXT_BLOCK; + storage.append(rx_buf); + } else if (rx_pos == 32) { + rx_pos = 0; + storage.append(rx_buf); + wdt_reset(); + } + break; + } +} + +void System::loop() +{ + // First, check for a shutdown request (long press on both buttons) + if ((PINC & (_BV(PC3) | _BV(PC7))) == 0) { + /* + * Naptime! + * (But not before both buttons have been pressed for at least + * SHUTDOWN_THRESHOLD * 0.256 ms) + */ + if (want_shutdown < SHUTDOWN_THRESHOLD) { + want_shutdown++; + } + else { + shutdown(); + want_shutdown = 0; + } + } + else { + want_shutdown = 0; + } + + if (btn_debounce == 0) { + if ((PINC & _BV(PC3)) == 0) { + btnMask = (ButtonMask)(btnMask | BUTTON_RIGHT); + } + if ((PINC & _BV(PC7)) == 0) { + btnMask = (ButtonMask)(btnMask | BUTTON_LEFT); + } + /* + * Only handle button presses when they are released to avoid + * double actions, such as switching to the next/previous pattern + * when the user actually wants to press the shutdown combo. + */ + if ((PINC & (_BV(PC3) | _BV(PC7))) == (_BV(PC3) | _BV(PC7))) { + if (btnMask == BUTTON_RIGHT) { + current_anim_no = (current_anim_no + 1) % storage.numPatterns(); + loadPattern(current_anim_no); + } else if (btnMask == BUTTON_LEFT) { + if (current_anim_no == 0) + current_anim_no = storage.numPatterns() - 1; + else + current_anim_no--; + loadPattern(current_anim_no); + } + btnMask = BUTTON_NONE; + /* + * Ignore keypresses for 25ms to work around bouncing buttons + */ + btn_debounce = 100; + } + } else { + btn_debounce--; + } + + while (modem.buffer_available()) { + receive(); + } + + display.update(); +} + +void System::shutdown() +{ + uint8_t i; + + modem.disable(); + + // show power down image + loadPattern_P(shutdownPattern); + + // wait until both buttons are released + while (!((PINC & _BV(PC3)) && (PINC & _BV(PC7)))) + display.update(); + + // and some more to debounce the buttons (and finish powerdown animation) + for (i = 0; i < 200; i++) { + display.update(); + _delay_ms(1); + } + + // turn off display to indicate we're about to shut down + display.disable(); + + // actual naptime + + // enable PCINT on PC3 (PCINT11) and PC7 (PCINT15) for wakeup + PCMSK1 |= _BV(PCINT15) | _BV(PCINT11); + PCICR |= _BV(PCIE1); + + // go to power-down mode + SMCR = _BV(SM1) | _BV(SE); + asm("sleep"); + + // execution will resume here - disable PCINT again. + // Don't disable PCICR, something else might need it. + PCMSK1 &= ~(_BV(PCINT15) | _BV(PCINT11)); + + // turn on display + loadPattern(current_anim_no); + display.enable(); + + /* + * Wait for wakeup button(s) to be released to avoid accidentally + * going back to sleep again or switching the active pattern. + */ + while (!((PINC & _BV(PC3)) && (PINC & _BV(PC7)))) + display.update(); + + // debounce + for (i = 0; i < 100; i++) { + display.update(); + _delay_ms(1); + } + + // finally, turn on the modem... + modem.enable(); + + // ... and reset the receive state machine + rxExpect = START1; +} + +void System::handleTimeout() +{ + modem.disable(); + modem.enable(); + rxExpect = START1; + current_anim_no = 0; + loadPattern_P(timeoutPattern); +} + +ISR(PCINT1_vect) +{ + // we use PCINT1 for wakeup, so we need an (empty) ISR for it +} + +ISR(WDT_vect) +{ + /* + * Modem transmission was interrupted without END byte. Reset state + * machine and show timeout message. + */ + wdt_disable(); + rocket.handleTimeout(); +} -- cgit v1.2.3