/* ProffieOS: Control software for lightsabers and other props. http://fredrik.hubbe.net/lightsaber/teensy_saber.html Copyright (c) 2016-2019 Fredrik Hubinette Additional copyright holders listed inline below. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /*-----------------------------------------------------------------*\ | You can have multiple configuration files, and specify which one | | to use here by removing the two slashes at the beginning. | | **NOTE** Only ONE line should be left uncommented at a time! | | Add the slashes to any that you are not using. | \*-----------------------------------------------------------------*/ #define CONFIG_FILE "config/Pathfinder.h" // #define CONFIG_FILE "config/default_proffieboard_config.h" // #define CONFIG_FILE "config/default_v3_config.h" // #define CONFIG_FILE "config/crossguard_config.h" // #define CONFIG_FILE "config/graflex_v1_config.h" // #define CONFIG_FILE "config/prop_shield_fastled_v1_config.h" // #define CONFIG_FILE "config/owk_v2_config.h" // #define CONFIG_FILE "config/test_bench_config.h" // #define CONFIG_FILE "config/toy_saber_config.h" // #define CONFIG_FILE "config/proffieboard_v1_test_bench_config.h" // #define CONFIG_FILE "config/proffieboard_v2_testing_config.h" // #define CONFIG_FILE "config/td_proffieboard_config.h" // #define CONFIG_FILE "config/proffieboard_v1_graflex.h" // #define CONFIG_FILE "config/teensy_audio_shield_micom.h" // #define CONFIG_FILE "config/proffieboard_v2_ob4.h" // #define CONFIG_FILE "config/testconfig.h" // #define CONFIG_FILE "config/test_bench_config.h" #ifdef CONFIG_FILE_TEST #undef CONFIG_FILE #define CONFIG_FILE CONFIG_FILE_TEST #endif #ifndef CONFIG_FILE #error Please set CONFIG_FILE as shown above. #endif #define CONFIG_TOP #include CONFIG_FILE #undef CONFIG_TOP #ifndef BOOT_VOLUME #define BOOT_VOLUME VOLUME #endif #ifdef SAVE_STATE #define SAVE_VOLUME #define SAVE_PRESET #define SAVE_COLOR_CHANGE #define SAVE_BLADE_DIMMING #endif #ifdef ENABLE_ALL_EDIT_OPTIONS #define DYNAMIC_BLADE_LENGTH #define DYNAMIC_BLADE_DIMMING #define DYNAMIC_CLASH_THRESHOLD #define SAVE_VOLUME #define SAVE_BLADE_DIMMING #define SAVE_CLASH_THRESHOLD #define SAVE_COLOR_CHANGE #endif #define ENABLE_DEBUG #ifdef KEEP_SAVEFILES_WHEN_PROGRAMMING #warning Your config file has KEEP_SAVEFILES_WHEN_PROGRAMMING in it. If you experience problems, please remove it and try again before asking for help. For more information, see: https://pod.hubbe.net/config/keeping-edits-when-uploading.html #endif // // OVERVIEW // // Here explain some general code concepts to make it easier // to understand the code below. // // Most things start with the ProbBase class. Depending on the // configuration, this class is extended by the Saber class, // the Detonator class, or some other class. The extended class // is instantiated as "prop", and is responsible for handling // button clicks, clashes, swings and other events. These events // are then send to all registered SaberBase classes. /// // Generally speaking, there are usually two registered SaberBase // classes listening for events. One for sound and one for // the blade. Sound and blade effects are generally executed // separately by separate clases. // // Blades are generally handled by one of the child-classes of // BladeBase. These classes know how many LEDs the current // blade has, and how to set those LEDs to a given color, but // they don't actually decide what the blade should look like. // Instead they just call the current BladeStyle class and // asks it to set the colors. The BladeStyle classes don't // need to know what kind of blade is attached, although // some combinations of BladeBases and BladeStyles just don't // make any sense. // // Sounds are also abstracted. It starts with scanning a directory // on the SD card for files that match known patterns of file names. // The Effect class is responsible for keeping track of all numbered // files that for a particular filename prefix. // // Once the directory has been scanned, we'll decide how to play // sounds. In the past, there was one class for handling NEC style // fonts and another for handling Plecter style fonts. However, // both of those have now been merged into the HybridFont class // which can do both. It is also capable of doing some mix and matching, // so you can use a plecter style hum together with a NEC style // swing if you so desire. The HybridFont class inherit from // SaberBase and listen on on/off/clash/etc. events, just like // BladeBase classes do. // // HybridFont tells the audio subsystem // to trigger and mix sounds as aproperiate. The sound subsystem // starts with an DMA channel which feeds data to a digital-to-analog // converter. Once the data buffer is half-gone, and interrupt is // triggered in the DAC class, which tries to fill it up by // reading data from a int16_t AudioStream. Generally, that data // stream is hooked up to the AudioDynamicMixer class. This // class is responsible for taking multiple audio inputs, // summing them up and then adjusting the volume to minimize // clipping. // TODO LIST: // stab detect/effect // // Audio work items: // select clash from force // stab effect // Blade stuff // better clash // Allow several blades to share power pins. // If defined, DAC vref will be 3 volts, resulting in louder sound. (teensy only) #define LOUD // You can get better SD card performance by // activating the USE_TEENSY3_OPTIMIZED_CODE define // in SD.h in the teensy library, however, my sd card // did not work with that define. #include #ifdef TEENSYDUINO #include #include #ifndef USE_TEENSY4 #include #include #else // This is a hack to let me access the internal stuff.. #define private public #include #undef private #endif #include #include #ifdef abs #undef abs namespace { template constexpr auto abs(T x) -> decltype(-x) { return x < 0 ? -x : x; } } #endif #else // TEENSYDUINO #define digitalWriteFast digitalWrite #endif // TEENSYDUINO #ifdef ARDUINO_ARCH_STM32L4 // This is a hack to let me access the internal stuff.. #define private public #include #undef private #include #include #include #include #include #include #include #include #include #include #define DMAChannel stm32l4_dma_t #define DMAMEM #define NVIC_SET_PRIORITY(X,Y) NVIC_SetPriority((X), (IRQn_Type)(Y)) #else // ARDUINO_ARCH_STM32L4 #define INPUT_ANALOG INPUT #endif // ARDUINO_ARCH_STM32L4 #include #include #ifdef ENABLE_SERIALFLASH // This is a hack to let me access the internal stuff.. #define private public #define protected public #include #undef private #undef protected #endif #ifdef ENABLE_SNOOZE #define startup_early_hook DISABLE_startup_early_hook #include #undef startup_early_hook SnoozeTimer snooze_timer; SnoozeDigital snooze_digital; SnoozeTouch snooze_touch; SnoozeBlock snooze_config(snooze_touch, snooze_digital, snooze_timer); #endif const char version[] = "v7.14"; #include "common/common.h" #include "common/state_machine.h" #include "common/monitoring.h" #include "common/stdout.h" #include "common/errors.h" Monitoring monitor; DEFINE_COMMON_STDOUT_GLOBALS; void PrintQuotedValue(const char *name, const char* str) { STDOUT.print(name); STDOUT.write('='); if (str) { while (*str) { switch (*str) { case '\n': STDOUT.print("\\n"); break; case '\t': STDOUT.print("\\t"); break; case '\\': STDOUT.write('\\'); default: STDOUT.write(*str); } ++str; } } STDOUT.write('\n'); } #ifdef ENABLE_DEBUG // This class is really useful for finding crashes // basically, the pin you give it will be held high // while this function is running. After that it will // be set to low. If a crash occurs in this function // it will stay high. class ScopedPinTracer { public: explicit ScopedPinTracer(int pin) : pin_(pin) { pinMode(pin_, OUTPUT); digitalWriteFast(pin, HIGH); } ~ScopedPinTracer() { digitalWriteFast(pin_, LOW); } private: int pin_; }; class ScopedTracer3 { public: explicit ScopedTracer3(int code) { pinMode(bladePowerPin1, OUTPUT); pinMode(bladePowerPin2, OUTPUT); pinMode(bladePowerPin3, OUTPUT); digitalWriteFast(bladePowerPin1, !!(code & 1)); digitalWriteFast(bladePowerPin2, !!(code & 2)); digitalWriteFast(bladePowerPin3, !!(code & 4)); } ~ScopedTracer3() { digitalWriteFast(bladePowerPin1, LOW); digitalWriteFast(bladePowerPin2, LOW); digitalWriteFast(bladePowerPin3, LOW); } }; #endif #include "common/scoped_cycle_counter.h" #include "common/profiling.h" uint64_t audio_dma_interrupt_cycles = 0; uint64_t pixel_dma_interrupt_cycles = 0; uint64_t motion_interrupt_cycles = 0; uint64_t wav_interrupt_cycles = 0; uint64_t loop_cycles = 0; #include "common/loop_counter.h" #if defined(ENABLE_SSD1306) || defined(INCLUDE_SSD1306) #define ENABLE_DISPLAY_CODE #endif #ifdef DOSFS_CONFIG_STARTUP_DELAY #define PROFFIEOS_SD_STARTUP_DELAY DOSFS_CONFIG_STARTUP_DELAY #else #define PROFFIEOS_SD_STARTUP_DELAY 1000 #endif #ifndef CONFIG_STARTUP_DELAY #define CONFIG_STARTUP_DELAY 0 #endif #if PROFFIEOS_SD_STARTUP_DELAY > CONFIG_STARTUP_DELAY #define PROFFIEOS_STARTUP_DELAY PROFFIEOS_SD_STARTUP_DELAY #else #define PROFFIEOS_STARTUP_DELAY CONFIG_STARTUP_DELAY #endif #include "common/linked_list.h" #include "common/looper.h" #include "common/command_parser.h" #include "common/monitor_helper.h" CommandParser* parsers = NULL; MonitorHelper monitor_helper; #include "common/vec3.h" #include "common/quat.h" #include "common/ref.h" #include "common/events.h" #include "common/saber_base.h" #include "common/saber_base_passthrough.h" SaberBase* saberbases = NULL; SaberBase::LockupType SaberBase::lockup_ = SaberBase::LOCKUP_NONE; SaberBase::ColorChangeMode SaberBase::color_change_mode_ = SaberBase::COLOR_CHANGE_MODE_NONE; bool SaberBase::on_ = false; uint32_t SaberBase::last_motion_request_ = 0; uint32_t SaberBase::current_variation_ = 0; float SaberBase::sound_length = 0.0; int SaberBase::sound_number = -1; float SaberBase::clash_strength_ = 0.0; #ifdef DYNAMIC_BLADE_DIMMING int SaberBase::dimming_ = 16384; #endif #include "common/box_filter.h" // Returns the decimals of a number, ie 12.2134 -> 0.2134 float fract(float x) { return x - floorf(x); } // clamp(x, a, b) makes sure that x is between a and b. float clamp(float x, float a, float b) { if (x < a) return a; if (x > b) return b; return x; } float Fmod(float a, float b) { return a - floorf(a / b) * b; } int32_t clampi32(int32_t x, int32_t a, int32_t b) { if (x < a) return a; if (x > b) return b; return x; } int16_t clamptoi16(int32_t x) { return clampi32(x, -32768, 32767); } int32_t clamptoi24(int32_t x) { return clampi32(x, -8388608, 8388607); } #include "common/sin_table.h" void EnableBooster(); void EnableAmplifier(); bool AmplifierIsActive(); void MountSDCard(); const char* GetSaveDir(); #include "common/lsfs.h" #include "common/strfun.h" // Double-zero terminated array of search paths. // No trailing slashes! char current_directory[128]; const char* next_current_directory(const char* dir) { dir += strlen(dir); dir ++; if (!*dir) return NULL; return dir; } const char* last_current_directory() { const char* ret = current_directory; while (true) { const char* tmp = next_current_directory(ret); if (!tmp) return ret; ret = tmp; } } const char* previous_current_directory(const char* dir) { if (dir == current_directory) return nullptr; dir -= 2; while (true) { if (dir == current_directory) return current_directory; if (!*dir) return dir + 1; dir--; } } #include "sound/sound.h" #include "common/battery_monitor.h" #include "common/color.h" #include "common/range.h" #include "common/fuse.h" #include "common/config_file.h" #include "blades/blade_base.h" #include "blades/blade_wrapper.h" class MicroEventTime { void SetToNow() { micros_ = micros(); millis_ = millis(); } uint32_t millis_since() { return millis() - millis_; } uint32_t micros_since() { if (millis_since() > (0xFFFF0000UL / 1000)) return 0xFFFFFFFFUL; return micros() - micros_; } private: uint32_t millis_; uint32_t micros_; }; template struct is_same_type { static const bool value = false; }; template struct is_same_type { static const bool value = true; }; // This really ought to be a typedef, but it causes problems I don't understand. #define StyleAllocator class StyleFactory* #include "styles/rgb.h" #include "styles/rgb_arg.h" #include "styles/charging.h" #include "styles/fire.h" #include "styles/sparkle.h" #include "styles/gradient.h" #include "styles/random_flicker.h" #include "styles/random_per_led_flicker.h" #include "styles/audio_flicker.h" #include "styles/brown_noise_flicker.h" #include "styles/hump_flicker.h" #include "styles/rainbow.h" #include "styles/color_cycle.h" #include "styles/cylon.h" #include "styles/ignition_delay.h" #include "styles/retraction_delay.h" #include "styles/pulsing.h" #include "styles/blinking.h" #include "styles/on_spark.h" #include "styles/rgb_cycle.h" #include "styles/clash.h" #include "styles/lockup.h" // Also does "drag" #include "styles/blast.h" #include "styles/strobe.h" #include "styles/inout_helper.h" #include "styles/inout_sparktip.h" #include "styles/colors.h" #include "styles/mix.h" #include "styles/style_ptr.h" #include "styles/file.h" #include "styles/stripes.h" #include "styles/random_blink.h" #include "styles/sequence.h" #include "styles/byteorder.h" #include "styles/rotate_color.h" #include "styles/colorchange.h" #include "styles/transition_pulse.h" #include "styles/transition_effect.h" #include "styles/transition_loop.h" #include "styles/effect_sequence.h" #include "styles/color_select.h" #include "styles/remap.h" #include "styles/edit_mode.h" // functions #include "functions/ifon.h" #include "functions/change_slowly.h" #include "functions/int.h" #include "functions/int_arg.h" #include "functions/int_select.h" #include "functions/sin.h" #include "functions/scale.h" #include "functions/battery_level.h" #include "functions/trigger.h" #include "functions/bump.h" #include "functions/smoothstep.h" #include "functions/swing_speed.h" #include "functions/sound_level.h" #include "functions/blade_angle.h" #include "functions/variation.h" #include "functions/twist_angle.h" #include "functions/layer_functions.h" #include "functions/islessthan.h" #include "functions/circular_section.h" #include "functions/marble.h" #include "functions/slice.h" #include "functions/mult.h" #include "functions/wavlen.h" #include "functions/wavnum.h" #include "functions/effect_position.h" #include "functions/time_since_effect.h" #include "functions/sum.h" #include "functions/ramp.h" #include "functions/center_dist.h" #include "functions/linear_section.h" #include "functions/hold_peak.h" #include "functions/clash_impact.h" #include "functions/effect_increment.h" #include "functions/increment.h" #include "functions/subtract.h" #include "functions/divide.h" #include "functions/isbetween.h" #include "functions/clamp.h" #include "functions/alt.h" #include "functions/volume_level.h" #include "functions/mod.h" // transitions #include "transitions/fade.h" #include "transitions/join.h" #include "transitions/concat.h" #include "transitions/instant.h" #include "transitions/delay.h" #include "transitions/wipe.h" #include "transitions/join.h" #include "transitions/boing.h" #include "transitions/random.h" #include "transitions/colorcycle.h" #include "transitions/wave.h" #include "transitions/select.h" #include "transitions/extend.h" #include "transitions/center_wipe.h" #include "transitions/sequence.h" #include "transitions/blink.h" #include "transitions/doeffect.h" #include "transitions/loop.h" #include "styles/legacy_styles.h" //responsive styles #include "styles/responsive_styles.h" #include "styles/pov.h" class NoLED; #include "blades/power_pin.h" #include "blades/drive_logic.h" #include "blades/pwm_pin.h" #include "blades/ws2811_blade.h" #include "blades/fastled_blade.h" #include "blades/simple_blade.h" #include "blades/saviblade.h" #include "blades/sub_blade.h" #include "blades/dim_blade.h" #include "blades/leds.h" #include "blades/blade_id.h" #include "common/preset.h" #include "common/blade_config.h" #include "common/current_preset.h" #include "common/status_led.h" #include "styles/style_parser.h" #include "styles/length_finder.h" #include "styles/show_color.h" #include "styles/blade_shortener.h" BladeConfig* current_config = nullptr; class BladeBase* GetPrimaryBlade() { #if NUM_BLADES == 0 return nullptr; #else return current_config->blade1; #endif } const char* GetSaveDir() { if (!current_config) return ""; if (!current_config->save_dir) return ""; return current_config->save_dir; } ArgParserInterface* CurrentArgParser; #define CONFIG_STYLES #include CONFIG_FILE #undef CONFIG_STYLES #define CONFIG_PRESETS #include CONFIG_FILE #undef CONFIG_PRESETS #define CONFIG_PROP #include CONFIG_FILE #undef CONFIG_PROP #ifndef PROP_TYPE #include "props/saber.h" #endif PROP_TYPE prop; #ifdef BLADE_ID_SCAN_MILLIS bool ScanBladeIdNow() { return prop.ScanBladeIdNow(); } #endif #if 0 #include "scripts/test_motion_timeout.h" #warning MOTION TEST SCRIPT ACTIVE MotionTimeoutScript script; #endif #if 0 #include "scripts/v3_test_script.h" #warning !!! V3 TEST SCRIPT ACTIVE !!! V3TestScript script; #endif #include "buttons/floating_button.h" #include "buttons/latching_button.h" #include "buttons/button.h" #ifdef TEENSYDUINO #include "buttons/touchbutton.h" #endif #ifdef ARDUINO_ARCH_STM32L4 #include "buttons/stm32l4_touchbutton.h" #endif #include "buttons/rotary.h" #include "buttons/pots.h" #include "ir/ir.h" #include "ir/receiver.h" #include "ir/blaster.h" #include "ir/print.h" #include "ir/nec.h" #include "ir/rc6.h" #include "ir/stm32_ir.h" #ifndef TEENSYDUINO uint32_t startup_AHB1ENR; uint32_t startup_AHB2ENR; uint32_t startup_AHB3ENR; uint32_t startup_APB1ENR1; uint32_t startup_APB1ENR2; uint32_t startup_APB2ENR; uint32_t startup_MODER[4]; #endif #define CONFIG_BUTTONS #include CONFIG_FILE #undef CONFIG_BUTTONS #ifdef BLADE_DETECT_PIN LatchingButtonTemplate> BladeDetect(BUTTON_BLADE_DETECT, BLADE_DETECT_PIN, "blade_detect"); #endif #include "common/sd_test.h" class I2CDevice; class Commands : public CommandParser { public: enum PinType { PinTypeFloating, PinTypePulldown, PinTypeCap, PinTypeOther, }; bool TestPin(int pin, PinType t) { int ret = 0; pinMode(pin, OUTPUT); digitalWrite(pin, LOW); delayMicroseconds(20); ret <<= 1; ret |= digitalRead(pin); digitalWrite(pin, HIGH); delayMicroseconds(20); ret <<= 1; ret |= digitalRead(pin); // Discharge time pinMode(pin, INPUT_PULLDOWN); uint32_t start = micros(); uint32_t end; while (digitalRead(pin)) { end = micros(); if (end - start > 32768) break; // 32 millis } ret <<= 16; ret |= (end - start); pinMode(pin, INPUT_PULLUP); delayMicroseconds(20); ret <<= 1; ret |= digitalRead(pin); pinMode(pin, INPUT); return ret; } bool Parse(const char* cmd, const char* e) override { #ifdef ENABLE_SERIALFLASH if (!strcmp(cmd, "ls")) { char tmp[128]; SerialFlashChip::opendir(); uint32_t size; while (SerialFlashChip::readdir(tmp, sizeof(tmp), size)) { STDOUT.print(tmp); STDOUT.print(" "); STDOUT.println(size); } STDOUT.println("Done listing files."); return true; } if (!strcmp(cmd, "rm")) { if (SerialFlashChip::remove(e)) { STDOUT.println("Removed.\n"); } else { STDOUT.println("No such file.\n"); } return true; } if (!strcmp(cmd, "format")) { STDOUT.print("Erasing ... "); SerialFlashChip::eraseAll(); while (!SerialFlashChip::ready()); STDOUT.println("Done"); return true; } #endif #ifdef ENABLE_SD #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "dir")) { LOCK_SD(true); if (!e || LSFS::Exists(e)) { for (LSFS::Iterator dir(e ? e : ""); dir; ++dir) { STDOUT.print(dir.name()); STDOUT.print(" "); STDOUT.println(dir.size()); } STDOUT.println("Done listing files."); } else { STDOUT.println("No such directory."); } LOCK_SD(false); return true; } #endif #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "cat") && e) { LOCK_SD(true); File f = LSFS::Open(e); while (f.available()) { STDOUT.write(f.read()); } f.close(); LOCK_SD(false); return true; } #endif if (!strcmp(cmd, "del") && e) { LOCK_SD(true); LSFS::Remove(e); LOCK_SD(false); return true; } #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "readalot")) { uint8_t tmp[10]; LOCK_SD(true); File f = LSFS::Open(e); for (int i = 0; i < 10000; i++) { f.seek(0); f.read(tmp, 10); f.seek(1000); f.read(tmp, 10); } f.close(); LOCK_SD(false); STDOUT.println("Done"); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "sdtest")) { SDTestHelper sdtester; if (e && !strcmp(e, "all")) { sdtester.TestDir(""); } else { sdtester.TestFont(); } return true; } #endif #endif // ENABLE_SD #if defined(ENABLE_SD) && defined(ENABLE_SERIALFLASH) if (!strcmp(cmd, "cache")) { LOCK_SD(true); File f = LSFS::Open(e); if (!f) { STDOUT.println("File not found."); return true; } int bytes = f.size(); if (!SerialFlashChip::create(e, bytes)) { STDOUT.println("Not enough space on serial flash chip."); return true; } SerialFlashFile o = SerialFlashChip::open(e); while (bytes) { char tmp[256]; int b = f.read(tmp, min(bytes, (int)NELEM(tmp))); o.write(tmp, b); bytes -= b; } LOCK_SD(false); STDOUT.println("Cached!"); return true; } #endif #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "effects")) { Effect::ShowAll(); return true; } #endif #if 0 if (!strcmp(cmd, "df")) { STDOUT.print(SerialFlashChip::capacity()); STDOUT.println(" bytes available."); return true; } #endif #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "high") && e) { pinMode(atoi(e), OUTPUT); digitalWrite(atoi(e), HIGH); STDOUT.println("Ok."); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "low") && e) { pinMode(atoi(e), OUTPUT); digitalWrite(atoi(e), LOW); STDOUT.println("Ok."); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #if VERSION_MAJOR >= 4 if (!strcmp(cmd, "booster")) { if (!strcmp(e, "on")) { digitalWrite(boosterPin, HIGH); STDOUT.println("Booster on."); return true; } if (!strcmp(e, "off")) { digitalWrite(boosterPin, LOW); STDOUT.println("Booster off."); return true; } } #endif #ifdef ENABLE_AUDIO #if 0 if (!strcmp(cmd, "ton")) { EnableAmplifier(); dac.SetStream(&saber_synth); saber_synth.on_ = true; return true; } if (!strcmp(cmd, "tof")) { saber_synth.on_ = false; return true; } #endif #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "dumpwav")) { int16_t tmp[32]; wav_players[0].Stop(); wav_players[0].read(tmp, NELEM(tmp)); wav_players[0].Play(e); for (int j = 0; j < 64; j++) { int k = wav_players[0].read(tmp, NELEM(tmp)); for (int i = 0; i < k; i++) { STDOUT.print(tmp[i]); STDOUT.print(" "); } STDOUT.println(""); } wav_players[0].Stop(); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "dumpwavplayer")) { for (size_t i = 0; i < NELEM(wav_players); i++) { if (e && atoi(e) != (int)i) continue; wav_players[i].dump(); } return true; } #endif // ENABLE_DEVELOPER_COMMANDS #endif #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "sleep") && e) { delay(atoi(e)); return true; } #endif #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "twiddle")) { int pin = strtol(e, NULL, 0); STDOUT.print("twiddling "); STDOUT.println(pin); pinMode(pin, OUTPUT); for (int i = 0; i < 1000; i++) { digitalWrite(pin, HIGH); delay(10); digitalWrite(pin, LOW); delay(10); } STDOUT.println("done"); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "twiddle2")) { int pin = strtol(e, NULL, 0); STDOUT.print("twiddling "); STDOUT.println(pin); pinMode(pin, OUTPUT); for (int i = 0; i < 1000; i++) { for (int i = 0; i < 500; i++) { digitalWrite(pin, HIGH); delayMicroseconds(1); digitalWrite(pin, LOW); delayMicroseconds(1); } delay(10); } STDOUT.println("done"); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "malloc")) { STDOUT.print("alloced: "); STDOUT.println(mallinfo().uordblks); STDOUT.print("Free: "); STDOUT.println(mallinfo().fordblks); return true; } #endif if (!strcmp(cmd, "make_default_console")) { default_output = stdout_output; return true; } #if 0 // Not finished yet if (!strcmp(cmd, "selftest")) { struct PinDefs { int8_t pin; PinType type; }; static PinDefs pin_defs[] = { { bladePowerPin1, PinTypePulldown }, { bladePowerPin2, PinTypePulldown }, { bladePowerPin3, PinTypePulldown }, { bladePowerPin4, PinTypePulldown }, { bladePowerPin5, PinTypePulldown }, { bladePowerPin6, PinTypePulldown }, { bladePin, PinTypeOther }, { blade2Pin, PinTypeFloating }, { blade3Pin, PinTypeFloating }, { blade4Pin, PinTypeFloating }, { blade5Pin, PinTypeFloating }, { amplifierPin, PinTypeFloating }, { boosterPin, PinTypeFloating }, { powerButtonPin, PinTypeFloating }, { auxPin, PinTypeFloating }, { aux2Pin, PinTypeFloating }, { rxPin, PinTypeOther }, { txPin, PinTypeFloating }, }; for (size_t test_index = 0; test_index < NELEM(pin_defs); test_index++) { int pin = pin_defs[test_index].pin; for (size_t i = 0; i < NELEM(pin_defs); i++) pinMode(pin_defs[i].pin, INPUT); // test for (size_t i = 0; i < NELEM(pin_defs); i++) { pinMode(pin_defs[i].pin, OUTPUT); digitalWrite(pin_defs[i].pin, HIGH); // test digitalWrite(pin_defs[i].pin, LOW); // test pinMode(pin_defs[i].pin, INPUT); } } } #endif #ifndef DISABLE_DIAGNOSTIC_COMMANDS if (!strcmp(cmd, "top")) { #ifdef TEENSYDUINO if (!(ARM_DWT_CTRL & ARM_DWT_CTRL_CYCCNTENA)) { ARM_DEMCR |= ARM_DEMCR_TRCENA; ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA; STDOUT.println("Cycle counting enabled, top will work next time."); return true; } #endif #ifdef ARDUINO_ARCH_STM32L4 if (!(DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk)) { CoreDebug->DEMCR |= 1<<24; // DEMCR_TRCENA_Msk; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; STDOUT.println("Cycle counting enabled, top will work next time."); return true; } #endif // TODO: list cpu usage for various objects. float total_cycles = (float)(audio_dma_interrupt_cycles + pixel_dma_interrupt_cycles + motion_interrupt_cycles + wav_interrupt_cycles + Looper::CountCycles() + CountProfileCycles()); STDOUT.print("Audio DMA: "); STDOUT.print(audio_dma_interrupt_cycles * 100.0f / total_cycles); STDOUT.println("%"); STDOUT.print("Wav reading: "); STDOUT.print(wav_interrupt_cycles * 100.0f / total_cycles); STDOUT.println("%"); STDOUT.print("Pixel DMA: "); STDOUT.print(pixel_dma_interrupt_cycles * 100.0f / total_cycles); STDOUT.println("%"); STDOUT.print("LOOP: "); STDOUT.print(loop_cycles * 100.0f / total_cycles); STDOUT.println("%"); STDOUT.print("Motion: "); STDOUT.print(motion_interrupt_cycles * 100.0f / total_cycles); STDOUT.println("%"); STDOUT.print("Global loops / second: "); global_loop_counter.Print(); STDOUT.println(""); STDOUT.print("High frequency loops / second: "); hf_loop_counter.Print(); STDOUT.println(""); SaberBase::DoTop(total_cycles); Looper::LoopTop(total_cycles); DumpProfileLocations(total_cycles); noInterrupts(); audio_dma_interrupt_cycles = 0; pixel_dma_interrupt_cycles = 0; motion_interrupt_cycles = 0; wav_interrupt_cycles = 0; interrupts(); return true; } #endif if (!strcmp(cmd, "version")) { STDOUT << version << "\n" CONFIG_FILE "\nprop: " TOSTRING(PROP_TYPE) "\nbuttons: " TOSTRING(NUM_BUTTONS) "\ninstalled: " << install_time << "\n"; return true; } if (!strcmp(cmd, "reset")) { #ifdef TEENSYDUINO SCB_AIRCR = 0x05FA0004; #endif #ifdef ARDUINO_ARCH_STM32L4 STM32.reset(); #endif STDOUT.println("Reset failed."); return true; } #ifdef ARDUINO_ARCH_STM32L4 if (!strcmp(cmd, "shutdown")) { STDOUT.println("Sleeping 10 seconds.\n"); STM32.stop(100000); return true; } if (!strcmp(cmd, "RebootDFU")) { stm32l4_system_dfu(); return true; } #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "dumpfusor")) { fusor.dump(); return true; } #endif #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "stm32info")) { STDOUT.print("VBAT: "); STDOUT.println(STM32.getVBAT()); STDOUT.print("VREF: "); STDOUT.println(STM32.getVREF()); STDOUT.print("TEMP: "); STDOUT.println(STM32.getTemperature()); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "i2cstate")) { extern void DumpI2CState(); DumpI2CState(); SaberBase::DumpMotionRequest(); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "portstates")) { GPIO_TypeDef *GPIO; for (int i = 0; i < 4; i++) { switch (i) { case 0: GPIO = (GPIO_TypeDef *)GPIOA_BASE; STDOUT.print("PORTA: "); break; case 1: GPIO = (GPIO_TypeDef *)GPIOB_BASE; STDOUT.print("PORTB: "); break; case 2: GPIO = (GPIO_TypeDef *)GPIOC_BASE; STDOUT.print("PORTC: "); break; case 3: GPIO = (GPIO_TypeDef *)GPIOH_BASE; STDOUT.print("PORTH: "); break; } for (int j = 15; j >= 0; j--) { uint32_t now = (GPIO->MODER >> (j * 2)) & 3; uint32_t saved = (startup_MODER[i] >> (j * 2)) & 3; STDOUT.print((now == saved ? "ioga" : "IOGA")[now]); if (!(j & 3)) STDOUT.print(" "); } STDOUT.print(" "); for (int j = 15; j >= 0; j--) { uint32_t now = (GPIO->PUPDR >> (j * 2)) & 3; STDOUT.print("-ud?"[now]); if (!(j & 3)) STDOUT.print(" "); } STDOUT.print(" "); for (int j = 15; j >= 0; j--) { uint32_t now = ((GPIO->IDR >> j) & 1) | (((GPIO->ODR >> j) & 1) << 1); STDOUT.print("lhLH"[now]); if (!(j & 3)) STDOUT.print(" "); } STDOUT.print(" "); for (int j = 15; j >= 0; j--) { int afr = 0xf & (GPIO->AFR[j >> 3] >> ((j & 7) * 4)); STDOUT.print("0123456789ABCDEF"[afr]); if (!(j & 3)) STDOUT.print(" "); } STDOUT.println(""); } return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "CLK")) { if (e) { uint32_t c = atoi(e) * 1000000; stm32l4_system_sysclk_configure(c, c/2, c/2); } STDOUT.print("Clocks: hse="); STDOUT.print(stm32l4_system_hseclk()); STDOUT.print(" lse="); STDOUT.print(stm32l4_system_lseclk()); STDOUT.print(" sys="); STDOUT.print(stm32l4_system_sysclk()); STDOUT.print(" f="); STDOUT.print(stm32l4_system_fclk()); STDOUT.print(" h="); STDOUT.print(stm32l4_system_hclk()); STDOUT.print(" p1="); STDOUT.print(stm32l4_system_pclk1()); STDOUT.print(" p2="); STDOUT.print(stm32l4_system_pclk2()); STDOUT.print(" sai="); STDOUT.println(stm32l4_system_saiclk()); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS if (!strcmp(cmd, "whatispowered")) { STDOUT.print("ON: "); #define PRINTIFON(REG, BIT) do { \ if (RCC->REG & RCC_##REG##_##BIT##EN) { \ STDOUT.print(" " #BIT); \ if (!(startup_##REG & RCC_##REG##_##BIT##EN)) STDOUT.print("+"); \ } \ } while(0) PRINTIFON(AHB1ENR,FLASH); PRINTIFON(AHB1ENR,DMA1); PRINTIFON(AHB1ENR,DMA2); PRINTIFON(AHB2ENR,GPIOA); PRINTIFON(AHB2ENR,GPIOB); #ifdef GPIOC_BASE PRINTIFON(AHB2ENR,GPIOC); #endif #ifdef GPIOD_BASE PRINTIFON(AHB2ENR,GPIOD); #endif #ifdef GPIOE_BASE PRINTIFON(AHB2ENR,GPIOE); #endif #if defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(AHB2ENR,GPIOF); PRINTIFON(AHB2ENR,GPIOG); #endif PRINTIFON(AHB2ENR,GPIOH); #if defined(STM32L496xx) PRINTIFON(AHB2ENR,GPIOI); #endif PRINTIFON(AHB2ENR,ADC); PRINTIFON(APB1ENR1,DAC1); #if defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(AHB2ENR,OTGFS); #else PRINTIFON(APB1ENR1,USBFS); #endif PRINTIFON(APB2ENR,USART1); PRINTIFON(APB1ENR1,USART2); #if defined(STM32L433xx) || defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB1ENR1,USART3); #endif #if defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB1ENR1,UART4); PRINTIFON(APB1ENR1,UART5); #endif PRINTIFON(APB1ENR2,LPUART1); PRINTIFON(APB1ENR1,I2C1); #if defined(STM32L433xx) || defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB1ENR1,I2C2); #endif PRINTIFON(APB1ENR1,I2C3); #if defined(STM32L496xx) PRINTIFON(APB1ENR2,I2C4); #endif PRINTIFON(APB2ENR,SPI1); #if defined(STM32L433xx) || defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB1ENR1,SPI2); #endif PRINTIFON(APB1ENR1,SPI3); PRINTIFON(APB1ENR1,CAN1); #if defined(STM32L496xx) PRINTIFON(APB1ENR1,CAN2); #endif PRINTIFON(AHB3ENR,QSPI); #if defined(STM32L433xx) || defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB2ENR,SDMMC1); #endif PRINTIFON(APB2ENR,SAI1); #if defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB2ENR,SAI2); PRINTIFON(APB2ENR,DFSDM1); #endif PRINTIFON(APB2ENR,TIM1); PRINTIFON(APB1ENR1,TIM2); #ifdef TIM3_BASE PRINTIFON(APB1ENR1,TIM3); #endif #ifdef TIM4_BASE PRINTIFON(APB1ENR1,TIM4); #endif #ifdef TIM5_BASE PRINTIFON(APB1ENR1,TIM5); #endif PRINTIFON(APB1ENR1,TIM6); #ifdef TIM7_BASE PRINTIFON(APB1ENR1,TIM7); #endif #ifdef TIM8_BASE PRINTIFON(APB2ENR,TIM8); #endif PRINTIFON(APB2ENR,TIM15); PRINTIFON(APB2ENR,TIM16); #if defined(STM32L476xx) || defined(STM32L496xx) PRINTIFON(APB2ENR,TIM17); #endif PRINTIFON(APB1ENR1,LPTIM1); PRINTIFON(APB1ENR2,LPTIM2); // Not sure what CPUs implement these PRINTIFON(AHB1ENR, CRC); PRINTIFON(AHB1ENR, TSC); PRINTIFON(AHB2ENR, RNG); #ifdef LCD_BASE PRINTIFON(APB1ENR1, LCD); #endif PRINTIFON(APB1ENR1, RTCAPB); PRINTIFON(APB1ENR1, WWDG); PRINTIFON(APB1ENR1, CRS); PRINTIFON(APB1ENR1, CAN1); PRINTIFON(APB1ENR1, PWR); PRINTIFON(APB1ENR1, OPAMP); #ifdef SWPMI1_BASE PRINTIFON(APB1ENR2, SWPMI1); #endif PRINTIFON(APB2ENR, SYSCFG); PRINTIFON(APB2ENR, FW); STDOUT.println(""); STDOUT.print("VBUS: "); STDOUT.println(stm32l4_gpio_pin_read(GPIO_PIN_PB2)); STDOUT.print("USBD connected: "); STDOUT.println(USBD_Connected()); return true; } #endif // ENABLE_DEVELOPER_COMMANDS #ifdef ENABLE_DEVELOPER_COMMANDS #ifdef HAVE_STM32L4_DMA_GET if (!strcmp(cmd, "dmamap")) { for (int channel = 0; channel < 16; channel++) { stm32l4_dma_t *dma = stm32l4_dma_get(channel); if (dma) { STDOUT.print(" DMA"); STDOUT.print( 1 +(channel / 8) ); STDOUT.print("_CH"); STDOUT.print( channel % 8 ); STDOUT.print(" = "); STDOUT.println(dma->channel >> 4, HEX); } } return true; } #endif // HAVE_STM32L4_DMA_GET #endif // ENABLE_DEVELOPER_COMMANDS #endif // TEENSYDUINO return false; } }; StaticWrapper commands; #include "common/serial.h" #if defined(ENABLE_MOTION) || defined(ENABLE_DISPLAY_CODE) #include "common/i2cdevice.h" I2CBus i2cbus; #endif #ifdef ENABLE_SSD1306 #include "display/ssd1306.h" #ifndef DISPLAY_POWER_PINS #define DISPLAY_POWER_PINS PowerPINS<> #endif StandardDisplayController<128, uint32_t> display_controller; SSD1306Template<128, uint32_t, DISPLAY_POWER_PINS> display(&display_controller); #endif #ifdef INCLUDE_SSD1306 #include "display/ssd1306.h" #endif #ifdef ENABLE_MOTION #include "motion/motion_util.h" #include "motion/mpu6050.h" #include "motion/lsm6ds3h.h" #include "motion/fxos8700.h" #include "motion/fxas21002.h" // Define this to record clashes to sd card as CSV files // #define CLASH_RECORDER #ifdef GYRO_CLASS // Can also be gyro+accel. StaticWrapper gyroscope; #endif #ifdef ACCEL_CLASS StaticWrapper accelerometer; #endif #endif // ENABLE_MOTION #include "sound/amplifier.h" #include "common/sd_card.h" #include "common/booster.h" void setup() { #if VERSION_MAJOR >= 4 #define SAVE_RCC(X) startup_##X = RCC->X SAVE_RCC(AHB1ENR); SAVE_RCC(AHB2ENR); SAVE_RCC(AHB3ENR); SAVE_RCC(APB1ENR1); SAVE_RCC(APB1ENR2); SAVE_RCC(APB2ENR); #define SAVE_MODER(PORT, X) startup_MODER[X] = ((GPIO_TypeDef *)GPIO##PORT##_BASE)->MODER SAVE_MODER(A, 0); SAVE_MODER(B, 1); SAVE_MODER(C, 2); SAVE_MODER(H, 3); // TODO enable/disable as needed pinMode(boosterPin, OUTPUT); digitalWrite(boosterPin, HIGH); #endif Serial.begin(115200); #if VERSION_MAJOR >= 4 // TODO: Figure out if we need this. // Serial.blockOnOverrun(false); #endif // Wait for all voltages to settle. // Accumulate some entrypy while we wait. uint32_t now = millis(); while (millis() - now < PROFFIEOS_STARTUP_DELAY) { #ifndef NO_BATTERY_MONITOR srand((rand() * 917823) ^ LSAnalogRead(batteryLevelPin)); #endif #ifdef BLADE_DETECT_PIN // Figure out if blade is connected or not. // Note that if PROFFIEOS_STARTUP_DELAY is smaller than // the settle time for BladeDetect, this won't work properly. BladeDetect.Warmup(); #endif } #ifdef ENABLE_SERIALFLASH SerialFlashChip::begin(serialFlashSelectPin); #endif #ifdef ENABLE_SD bool sd_card_found = LSFS::Begin(); if (!sd_card_found) { if (sdCardSelectPin >= 0 && sdCardSelectPin < 255) { STDOUT.println("No sdcard found."); pinMode(sdCardSelectPin, OUTPUT); digitalWrite(sdCardSelectPin, 0); delayMicroseconds(2); pinMode(sdCardSelectPin, INPUT); delayMicroseconds(2); if (digitalRead(sdCardSelectPin) != HIGH) { STDOUT.println("SD select not pulled high!"); } } #if VERSION_MAJOR >= 4 stm32l4_gpio_pin_configure(GPIO_PIN_PA5, (GPIO_PUPD_PULLUP | GPIO_OSPEED_HIGH | GPIO_MODE_INPUT)); delayMicroseconds(10); if (!stm32l4_gpio_pin_read(GPIO_PIN_PA5)) { STDOUT.println("SCK won't go high!"); } stm32l4_gpio_pin_configure(GPIO_PIN_PA5, (GPIO_PUPD_PULLDOWN | GPIO_OSPEED_HIGH | GPIO_MODE_INPUT)); delayMicroseconds(10); if (stm32l4_gpio_pin_read(GPIO_PIN_PA5)) { STDOUT.println("SCK won't go low!"); } #endif } else { STDOUT.println("Sdcard found.."); } #endif Looper::DoSetup(); // Time to identify the blade. prop.FindBlade(); SaberBase::DoBoot(); #if defined(ENABLE_SD) if (!sd_card_found) ProffieOSErrors::sd_card_not_found(); #endif // ENABLE_SD } #ifdef MTP_RX_ENDPOINT void mtp_yield() { Looper::DoLoop(); } void mtp_lock_storage(bool lock) { AudioStreamWork::LockSD(lock); } #include "mtp/mtpd.h" MTPD mtpd; #ifdef ENABLE_SD #include "mtp/mtp_storage_sd.h" MTPStorage_SD sd_storage(&mtpd); #endif #ifdef ENABLE_SERIALFLASH #include "mtp/mtp_storage_serialflash.h" MTPStorage_SerialFlash serialflash_storage(&mtpd); #endif #endif // MTP_RX_ENDPOINT #include "common/clock_control.h" void loop() { #ifdef MTP_RX_ENDPOINT mtpd.loop(); #endif Looper::DoLoop(); } #define CONFIG_BOTTOM #include CONFIG_FILE #undef CONFIG_BOTTOM #define PROFFIEOS_DEFINE_FUNCTION_STAGE #include "common/errors.h"