diff --git a/.github/workflows/custom.yml b/.github/workflows/custom.yml index 1b90a0b1e..a8e1be814 100644 --- a/.github/workflows/custom.yml +++ b/.github/workflows/custom.yml @@ -19,7 +19,7 @@ jobs: env: GH_COMMENT: ${{ github.event.comment.body }} run: | - echo "OC_ARTIFACT_TAG=custom_${GITHUB_ACTOR}-$(sh software/res/oc_build_tag.sh)" | tr '/' '_' >> $GITHUB_ENV + echo "OC_ARTIFACT_TAG=custom_${GITHUB_ACTOR}-$(git rev-parse --short HEAD)" | tr '/' '_' >> $GITHUB_ENV echo "CUSTOM_BUILD_FLAGS=$(python software/res/parse_build_request.py)" >> $GITHUB_ENV - name: Cache PlatformIO diff --git a/TODO.md b/TODO.md index 2c7d1b20e..beb4c4626 100644 --- a/TODO.md +++ b/TODO.md @@ -2,12 +2,13 @@ TODO (Roadmap) === # v1.9 +* Config files on LittleFS / SD for T4.x * Unipolar variant of SequenceX? * T4.1 - expand to 8 channels: Piqued, Quadraturia, Captain MIDI # v2.0 * **Fully merge "abandoned/refactoring" branch from pld** - - this was mostly done on the dev/2.0 branch, needs rebase + - this is partially done on the dev/2.0 branch * Auto-tuner with floor/ceiling detection (fail gracefully) * generalized AppletParams for flexible assignment, extra virtual I/O - MIDI Learn for anything @@ -15,10 +16,11 @@ TODO (Roadmap) * Integrate Calibr8or with DAC for global tracking adjustments # ??? -* MIDI looper applet! * Update Boilerplates - I just assume this needs attention -* better Polyphonic MIDI input tracking -* MIDI output for all apps? +* MORE MIDI STUFF: + - MIDI looper applet! + - better Polyphonic MIDI input tracking + - MIDI output for all apps? # APP IDEAS * Two Spheres (two applets in series on each side) diff --git a/software/platformio.ini b/software/platformio.ini index 72684678c..9613ddc53 100644 --- a/software/platformio.ini +++ b/software/platformio.ini @@ -54,7 +54,7 @@ build_flags = [env:T4] build_flags = ${env.build_flags} - -DPEWPEWPEW +; -DPEWPEWPEW -DDRUMMAP_GRIDS2 -DENABLE_APP_CALIBR8OR -DENABLE_APP_SCENES @@ -85,11 +85,6 @@ lib_ignore = Audio, Wire build_flags = ${env:T4.build_flags} -DOC_VERSION_EXTRA="\"_T40\"" -[env:T40_flipped] -extends = env:T40 -build_flags = ${env:T40.build_flags} - -DFLIP_180 - [env:T41] board = teensy41 board_build.f_cpu = 600000000 @@ -105,6 +100,7 @@ build_flags = ${env.build_flags} ; -DENABLE_APP_POLYLFO ; -DENABLE_APP_BYTEBEATGEN ; -DENABLE_APP_BBGEN + -DENABLE_APP_PASSENCORE -DOC_VERSION_EXTRA="\"_T41\"" [env:antihem] @@ -133,7 +129,30 @@ build_flags = ; -DENABLE_APP_AUTOMATONNETZ -DENABLE_APP_BBGEN -DENABLE_APP_REFERENCES - -DPEWPEWPEW + +[env:prod] +extends = env:T3 +build_flags = + ${env.build_flags} + -DENABLE_APP_CALIBR8OR + -DENABLE_APP_SCENES +; -DENABLE_APP_ENIGMA +; -DENABLE_APP_MIDI + -DENABLE_APP_PONG + -DENABLE_APP_PIQUED + -DENABLE_APP_POLYLFO + -DENABLE_APP_PASSENCORE +; -DENABLE_APP_H1200 + -DENABLE_APP_BYTEBEATGEN + -DENABLE_APP_BBGEN +; -DENABLE_APP_REFERENCES + -DDRUMMAP_GRIDS2 + +[env:prod_vor] +extends = env:T3 +build_flags = + ${env:prod.build_flags} + -DVOR [env:pewpewpew] extends = env:T3 @@ -155,23 +174,12 @@ build_flags = -DPEWPEWPEW -DDRUMMAP_GRIDS2 -DOC_VERSION_EXTRA="\"_phz\"" -[env:wepwepwep] -extends = env:T3 -build_flags = - ${env:pewpewpew.build_flags} - -DFLIP_180 [env:pewpewvor] extends = env:T3 build_flags = ${env:pewpewpew.build_flags} -DVOR -[env:wepwepvor] -extends = env:T3 -build_flags = - ${env:pewpewpew.build_flags} - -DVOR - -DFLIP_180 ; --- Builds for Northern Light Modular 4U Buchla format ; default apps for NLM builds @@ -190,7 +198,6 @@ build_flags = ; -DENABLE_APP_H1200 -DENABLE_APP_BYTEBEATGEN -DENABLE_APP_BBGEN - -DPEWPEWPEW -DNORTHERNLIGHT ; for old versions without inverted outputs @@ -202,7 +209,6 @@ build_flags = -DOC_VERSION_EXTRA="\"_NLM-DIY\"" ; for the 2OC 4U module, right side -; also for the Easel cOC card [env:nlm_right] extends = env:nlm build_flags = @@ -225,6 +231,14 @@ build_flags = -DNLM_hOC -DOC_VERSION_EXTRA="\"_NLM-hOC\"" +; for the cOC Easel card +[env:nlm_cardoc] +extends = env:nlm +build_flags = + ${env:nlm.build_flags} + -DNLM_cardOC + -DOC_VERSION_EXTRA="\"_NLM-cardOC\"" + [env:oc_dev] extends = env:T3 build_flags = ${env.build_flags} -DOC_DEV diff --git a/software/res/parse_build_request.py b/software/res/parse_build_request.py index c2acdc2cc..cbcc67008 100644 --- a/software/res/parse_build_request.py +++ b/software/res/parse_build_request.py @@ -16,6 +16,12 @@ custom_defines += " -DVOR" if f.startswith('BUCHLA') or f.startswith('NLM') or f.startswith('NORTHERN'): custom_defines += " -DNORTHERNLIGHT" + if f.startswith('NLM_HOC'): + custom_defines += " -DNLM_hOC" + if f.startswith('NLM_CARDOC'): + custom_defines += " -DNLM_cardOC" + if f.startswith('NLM_2OC_L'): + custom_defines += " -DNORTHERNLIGHT_2OC_LEFTSIDE" if f.startswith('CALIBR8'): custom_defines += " -DENABLE_APP_CALIBR8OR" if f.startswith('SCENE'): diff --git a/software/src/APP_ASR.h b/software/src/APP_ASR.h index ffafc7b91..2ffe94f02 100644 --- a/software/src/APP_ASR.h +++ b/software/src/APP_ASR.h @@ -25,6 +25,7 @@ #include "util/util_turing.h" #include "util/util_ringbuffer.h" #include "util/util_integer_sequences.h" +#include "OC_ADC.h" #include "OC_DAC.h" #include "OC_menus.h" #include "OC_scales.h" @@ -106,15 +107,15 @@ class ASRApp : public settings::SettingsBase { static constexpr size_t kHistoryDepth = 5; #ifdef ARDUINO_TEENSY41 - static constexpr ADC_CHANNEL CVInput1 = ADC_CHANNEL_5; - static constexpr ADC_CHANNEL CVInput2 = ADC_CHANNEL_6; - static constexpr ADC_CHANNEL CVInput3 = ADC_CHANNEL_7; - static constexpr ADC_CHANNEL CVInput4 = ADC_CHANNEL_8; + static constexpr ADC_CHANNEL &CVInput1 = ADC_CHANNEL_5; + static constexpr ADC_CHANNEL &CVInput2 = ADC_CHANNEL_6; + static constexpr ADC_CHANNEL &CVInput3 = ADC_CHANNEL_7; + static constexpr ADC_CHANNEL &CVInput4 = ADC_CHANNEL_8; #else - static constexpr ADC_CHANNEL CVInput1 = ADC_CHANNEL_1; - static constexpr ADC_CHANNEL CVInput2 = ADC_CHANNEL_2; - static constexpr ADC_CHANNEL CVInput3 = ADC_CHANNEL_3; - static constexpr ADC_CHANNEL CVInput4 = ADC_CHANNEL_4; + static constexpr ADC_CHANNEL &CVInput1 = ADC_CHANNEL_1; + static constexpr ADC_CHANNEL &CVInput2 = ADC_CHANNEL_2; + static constexpr ADC_CHANNEL &CVInput3 = ADC_CHANNEL_3; + static constexpr ADC_CHANNEL &CVInput4 = ADC_CHANNEL_4; #endif int get_scale(uint8_t dummy) const { diff --git a/software/src/APP_CALIBR8OR.h b/software/src/APP_CALIBR8OR.h index 26a7ef331..7d60aefa0 100644 --- a/software/src/APP_CALIBR8OR.h +++ b/software/src/APP_CALIBR8OR.h @@ -202,7 +202,7 @@ Calibr8orPreset cal8_presets[NR_OF_PRESETS]; class Calibr8or : public HSApplication { public: Calibr8or() { - for (int i = DAC_CHANNEL_A; i < DAC_CHANNEL_LAST; ++i) { + for (int i = 0; i < DAC_CHANNEL_LAST; ++i) { channel[i].chan_ = DAC_CHANNEL(i); } } diff --git a/software/src/APP_HEMISPHERE.h b/software/src/APP_HEMISPHERE.h index 08989af2c..ce9332c17 100644 --- a/software/src/APP_HEMISPHERE.h +++ b/software/src/APP_HEMISPHERE.h @@ -78,7 +78,7 @@ static constexpr int HEM_NR_OF_PRESETS = 16; #elif defined(PEWPEWPEW) static constexpr int HEM_NR_OF_PRESETS = 8; #else -static constexpr int HEM_NR_OF_PRESETS = 4; +static constexpr int HEM_NR_OF_PRESETS = 8; #endif /* Hemisphere Preset @@ -541,11 +541,6 @@ class HemisphereManager : public HSApplication { if (HS::q_edit) PokePopup(QUANTIZER_POPUP); - if (draw_applets && clock_setup) { - ClockSetup_instance.View(); - draw_applets = false; - } - if (draw_applets) { if (help_hemisphere > -1) { int index = my_applet[help_hemisphere]; @@ -571,6 +566,11 @@ class HemisphereManager : public HSApplication { } else if (HS::clock_m.IsPaused()) { gfxIcon(56, 1, PAUSE_ICON); } + + // clock screen is an overlay + if (clock_setup) { + ClockSetup_instance.View(); + } } // Overlay popup window last @@ -657,7 +657,7 @@ class HemisphereManager : public HSApplication { void DelegateSelectButtonPush(const UI::Event &event) { bool down = (event.type == UI::EVENT_BUTTON_DOWN); - const int hemisphere = (event.control == OC::CONTROL_BUTTON_UP) ? LEFT_HEMISPHERE : RIGHT_HEMISPHERE; + const int hemisphere = (event.control == OC::CONTROL_BUTTON_A) ? LEFT_HEMISPHERE : RIGHT_HEMISPHERE; if (!down && (preset_cursor || view_state != APPLETS)) { // cancel preset select, or config screen on select button release @@ -675,7 +675,7 @@ class HemisphereManager : public HSApplication { // -- button down if (down) { // dual press for Clock Setup... check first_click, so we only process the 2nd button event - if (event.mask == (OC::CONTROL_BUTTON_UP | OC::CONTROL_BUTTON_DOWN) && hemisphere != first_click) { + if (event.mask == (OC::CONTROL_BUTTON_A | OC::CONTROL_BUTTON_B) && hemisphere != first_click) { clock_setup = 1; SetHelpScreen(-1); select_mode = -1; @@ -784,9 +784,9 @@ class HemisphereManager : public HSApplication { break; } if (HS::q_edit) { - if (event.control == OC::CONTROL_BUTTON_UP) + if (event.control == OC::CONTROL_BUTTON_A) HS::NudgeOctave(HS::qview, 1); - else if (event.control == OC::CONTROL_BUTTON_DOWN) + else if (event.control == OC::CONTROL_BUTTON_B) HS::NudgeOctave(HS::qview, -1); else HS::q_edit = false; @@ -797,7 +797,7 @@ class HemisphereManager : public HSApplication { // most button-down events fall through here case UI::EVENT_BUTTON_PRESS: - if (event.control == OC::CONTROL_BUTTON_UP || event.control == OC::CONTROL_BUTTON_DOWN) { + if (event.control == OC::CONTROL_BUTTON_A || event.control == OC::CONTROL_BUTTON_B) { DelegateSelectButtonPush(event); } else if (event.control == OC::CONTROL_BUTTON_L || event.control == OC::CONTROL_BUTTON_R) { DelegateEncoderPush(event); @@ -810,7 +810,7 @@ class HemisphereManager : public HSApplication { break; case UI::EVENT_BUTTON_LONG_PRESS: - if (event.control == OC::CONTROL_BUTTON_DOWN) ToggleConfigMenu(); + if (event.control == OC::CONTROL_BUTTON_B) ToggleConfigMenu(); if (event.control == OC::CONTROL_BUTTON_L) ToggleClockRun(); break; diff --git a/software/src/APP_QUADRANTS.h b/software/src/APP_QUADRANTS.h index 46475c36a..c1d1b9991 100644 --- a/software/src/APP_QUADRANTS.h +++ b/software/src/APP_QUADRANTS.h @@ -480,10 +480,6 @@ class QuadAppletManager : public HSApplication { // OC::AudioDSP::DrawAudioSetup(); draw_applets = false; } - else if (view_state == CLOCK_SETUP) { - ClockSetup_instance.View(); - draw_applets = false; - } } if (draw_applets) { @@ -517,6 +513,11 @@ class QuadAppletManager : public HSApplication { } else if (HS::clock_m.IsPaused()) { gfxIcon(56, 1, PAUSE_ICON); } + + // Clock setup is an overlay + if (view_state == CLOCK_SETUP) { + ClockSetup_instance.View(); + } } // Overlay popup window last @@ -549,21 +550,15 @@ class QuadAppletManager : public HSApplication { } const HEM_SIDE ButtonToSlot(const UI::Event &event) { - switch (event.control) { - default: - case OC::CONTROL_BUTTON_A: - return LEFT_HEMISPHERE; - break; - case OC::CONTROL_BUTTON_X: - return LEFT2_HEMISPHERE; - break; - case OC::CONTROL_BUTTON_B: - return RIGHT_HEMISPHERE; - break; - case OC::CONTROL_BUTTON_Y: - return RIGHT2_HEMISPHERE; - break; - } + if (event.control == OC::CONTROL_BUTTON_X) + return LEFT2_HEMISPHERE; + if (event.control == OC::CONTROL_BUTTON_B) + return RIGHT_HEMISPHERE; + if (event.control == OC::CONTROL_BUTTON_Y) + return RIGHT2_HEMISPHERE; + + //if (event.control == OC::CONTROL_BUTTON_A) + return LEFT_HEMISPHERE; // default } // returns true if combo detected and button release should be ignored @@ -734,8 +729,8 @@ class QuadAppletManager : public HSApplication { break; } - switch (event.control) { - case OC::CONTROL_BUTTON_Z: + if (event.control == OC::CONTROL_BUTTON_Z) + { // X or Y + Z == go fullscreen if (select_mode) { bool h = (event.mask & OC::CONTROL_BUTTON_Y); // left or right @@ -749,53 +744,44 @@ class QuadAppletManager : public HSApplication { // Z-button - Zap the CLOCK!! ToggleClockRun(); OC::ui.SetButtonIgnoreMask(); - break; - - case OC::CONTROL_BUTTON_L: - case OC::CONTROL_BUTTON_R: + } else if ( + event.control == OC::CONTROL_BUTTON_L || + event.control == OC::CONTROL_BUTTON_R) + { if (event.mask == (OC::CONTROL_BUTTON_L | OC::CONTROL_BUTTON_R)) { // TODO: how to go to app menu? } DelegateEncoderPush(event); // ignore long-press to prevent Main Menu >:) //OC::ui.SetButtonIgnoreMask(); - break; - - case OC::CONTROL_BUTTON_A: - case OC::CONTROL_BUTTON_B: - case OC::CONTROL_BUTTON_X: - case OC::CONTROL_BUTTON_Y: + } else if ( + event.control == OC::CONTROL_BUTTON_A || + event.control == OC::CONTROL_BUTTON_B || + event.control == OC::CONTROL_BUTTON_X || + event.control == OC::CONTROL_BUTTON_Y) + { if (CheckButtonCombos(event)) { select_mode = false; OC::ui.SetButtonIgnoreMask(); // ignore release and long-press } - break; - - default: - break; } break; case UI::EVENT_BUTTON_PRESS: - switch (event.control) { - // A/B/X/Y switch to corresponding applet on release - case OC::CONTROL_BUTTON_A: - case OC::CONTROL_BUTTON_B: - case OC::CONTROL_BUTTON_X: - case OC::CONTROL_BUTTON_Y: - { - HEM_SIDE slot = ButtonToSlot(event); - if (view_state == APPLET_FULLSCREEN && slot == zoom_slot) - view_state = APPLETS; - - SwitchToSlot(slot); - break; - } + // A/B/X/Y switch to corresponding applet on release + if (event.control == OC::CONTROL_BUTTON_A || + event.control == OC::CONTROL_BUTTON_B || + event.control == OC::CONTROL_BUTTON_X || + event.control == OC::CONTROL_BUTTON_Y) + { + HEM_SIDE slot = ButtonToSlot(event); + if (view_state == APPLET_FULLSCREEN && slot == zoom_slot) + view_state = APPLETS; - // ignore all other button release events - default: break; + SwitchToSlot(slot); } + // ignore all other button release events break; case UI::EVENT_BUTTON_LONG_PRESS: diff --git a/software/src/APP_REFS.h b/software/src/APP_REFS.h index c3c841360..64f6986b2 100644 --- a/software/src/APP_REFS.h +++ b/software/src/APP_REFS.h @@ -227,7 +227,7 @@ class ReferencesApp { OC::Autotuner autotuner; void Init() { - int dac_channel = DAC_CHANNEL_A; + int dac_channel = 0; for (auto &channel : channels_) channel.Init(static_cast(dac_channel++)); diff --git a/software/src/APP_SEQ.h b/software/src/APP_SEQ.h index ab646c566..cd09ae70b 100644 --- a/software/src/APP_SEQ.h +++ b/software/src/APP_SEQ.h @@ -1836,14 +1836,14 @@ class SEQ_Channel : public settings::SettingsBase + template void update_main_channel() { int32_t _output = OC::DAC::pitch_to_scaled_voltage_dac(dacChannel, get_step_pitch(), 0, OC::DAC::get_voltage_scaling(dacChannel)); OC::DAC::set(_output); } - template + template void update_aux_channel() { diff --git a/software/src/APP_SETTINGS.h b/software/src/APP_SETTINGS.h index 81796249d..9633322d7 100644 --- a/software/src/APP_SETTINGS.h +++ b/software/src/APP_SETTINGS.h @@ -20,10 +20,17 @@ #include #include +#include "HSUtils.h" #include "OC_apps.h" +#include "OC_calibration.h" +#include "OC_core.h" #include "OC_ui.h" #include "HSApplication.h" #include "OC_strings.h" +#include "src/drivers/display.h" +#ifdef PEWPEWPEW +#include "util/pewpewsplash.h" +#endif extern "C" void _reboot_Teensyduino_(); @@ -32,6 +39,7 @@ class Settings : public HSApplication { bool reflash = false; bool calibration_mode = false; bool calibration_complete = true; + bool cal_save_q = false; OC::DigitalInputDisplay digital_input_displays[4]; OC::TickCount tick_count; @@ -48,6 +56,12 @@ class Settings : public HSApplication { Calibration(); } } + void Suspend() { + if (cal_save_q) { + OC::calibration_save(); + cal_save_q = false; + } + } void Controller() { @@ -165,6 +179,11 @@ class Settings : public HSApplication { return; } + if (CORE::ticks % 3200 == 0) { + pick_left = random(8); + pick_right = random(8); + } + #ifdef PEWPEWPEW HS::frame.Load();PewPewTime.PEWPEW(Clock(3)<<1|Clock(0));} struct{bool go=0;int idx=0;struct{uint8_t x,y;int x_v,y_v;}pewpews[8]; @@ -176,13 +195,23 @@ class Settings : public HSApplication { #endif } + const uint8_t *iconography[8] = { + PhzIcons::voltage, ZAP_ICON, + PhzIcons::pigeons, PhzIcons::camels, + PhzIcons::legoFace, PhzIcons::tb3P0, + PhzIcons::drLoFi, PhzIcons::umbrella, + }; + int pick_left = 0, pick_right = 0; + void View() { if (calibration_mode) { OC::calibration_draw(calibration_state); return; } - gfxHeader("Setup / About"); + gfxHeader("Setup/About"); + gfxIcon(80, 0, OC::calibration_data.flipscreen() ? DOWN_ICON : UP_ICON); + gfxIcon(90, 0, OC::calibration_data.flipcontrols() ? LEFT_ICON : RIGHT_ICON); #if defined(ARDUINO_TEENSY40) gfxPrint(100, 0, "T4.0"); @@ -193,15 +222,19 @@ class Settings : public HSApplication { gfxPrint(100, 0, "T3.2"); #endif - gfxIcon(0, 15, ZAP_ICON); - gfxIcon(120, 15, ZAP_ICON); + gfxIcon(0, 15, iconography[pick_left]); + gfxIcon(120, 15, iconography[pick_right]); #ifdef PEWPEWPEW gfxPrint(21, 15, "PEW! PEW! PEW!"); #else gfxPrint(12, 15, "Phazerville Suite"); #endif - gfxPrint(0, 25, OC::Strings::VERSION); - gfxPrint(0, 35, "github.com/djphazer"); + gfxIcon(0, 25, PhzIcons::full_book); + gfxPrint(10, 25, OC::Strings::VERSION); + gfxIcon(0, 35, PhzIcons::runglBook); + gfxPrint(10, 35, OC::Strings::BUILD_TAG); + gfxIcon(0, 45, PhzIcons::frontBack); + gfxPrint(10, 45, "github.com/djphazer"); gfxPrint(0, 55, reflash ? "[Reflash]" : "[CALIBRATE] [RESET]"); } @@ -223,6 +256,15 @@ class Settings : public HSApplication { Calibration(); } if (event.control == OC::CONTROL_BUTTON_R && event.type == UI::EVENT_BUTTON_PRESS) FactoryReset(); + + // dual-press UP + DOWN to flip screen + if ( event.type == UI::EVENT_BUTTON_DOWN && + (event.mask == (OC::CONTROL_BUTTON_A | OC::CONTROL_BUTTON_B)) ) { + OC::calibration_data.cycle_flipmode(); + display::SetFlipMode(OC::calibration_data.flipscreen()); + cal_save_q = true; + } + return; } @@ -288,6 +330,8 @@ class Settings : public HSApplication { first += interval; OC::calibration_data.dac.calibrated_octaves[ch][i] = first; } + + calibration_state.auto_scale_set[ch] = true; } break; } @@ -310,6 +354,7 @@ class Settings : public HSApplication { if (calibration_state.encoder_value) { SERIAL_PRINTLN("Calibration complete"); OC::calibration_save(); + cal_save_q = false; } else { SERIAL_PRINTLN("Calibration complete (but don't save)"); } @@ -386,12 +431,16 @@ void Settings_isr() { } void Settings_handleAppEvent(OC::AppEvent event) { - if (event == OC::APP_EVENT_RESUME) { - Settings_instance.Resume(); - } + if (event == OC::APP_EVENT_RESUME) { + Settings_instance.Resume(); + } + if (event == OC::APP_EVENT_SUSPEND) { + Settings_instance.Suspend(); + } } -void Settings_loop() {} // Deprecated +void Settings_loop() { +} // Deprecated void Settings_menu() { Settings_instance.BaseView(); @@ -399,6 +448,11 @@ void Settings_menu() { void Settings_screensaver() { #ifdef PEWPEWPEW + for (int i = 0; i < (pewpew_width * pewpew_height / 64); ++i) { + // TODO: the problem here is that one byte in XBM is a row of 8 pixels, + // while one byte in the framebuffer is a column of 8 pixels + gfxBitmap((i & 0x1)*64, (i>>1)*8, 64, pewpew_bits + i*64); + } Settings_instance.PEWPEW(); #endif } diff --git a/software/src/HSApplication.h b/software/src/HSApplication.h index 86e62f796..d7aa2cbca 100644 --- a/software/src/HSApplication.h +++ b/software/src/HSApplication.h @@ -200,7 +200,7 @@ class HSApplication { // Buffered I/O functions for use in Views int ViewIn(int ch) {return frame.inputs[ch];} int ViewOut(int ch) {return frame.outputs[ch];} - int ClockCycleTicks(int ch) {return frame.cycle_ticks[ch];} + uint32_t ClockCycleTicks(int ch) {return frame.cycle_ticks[ch];} /* ADC Lag: There is a small delay between when a digital input can be read and when an ADC can be * read. The ADC value lags behind a bit in time. So StartADCLag() and EndADCLag() are used to diff --git a/software/src/HSIOFrame.h b/software/src/HSIOFrame.h index 785aeb1d0..5ae76952a 100644 --- a/software/src/HSIOFrame.h +++ b/software/src/HSIOFrame.h @@ -34,6 +34,7 @@ typedef struct IOFrame { int output_diff[DAC_CHANNEL_LAST]; int outputs_smooth[DAC_CHANNEL_LAST]; int clock_countdown[DAC_CHANNEL_LAST]; + uint8_t clockskip[DAC_CHANNEL_LAST] = {0}; bool clockout_q[DAC_CHANNEL_LAST]; // for loopback int adc_lag_countdown[ADC_CHANNEL_LAST]; // Time between a clock event and an ADC read event uint32_t last_clock[ADC_CHANNEL_LAST]; // Tick number of the last clock observed by the child class @@ -358,9 +359,15 @@ typedef struct IOFrame { outputs[channel] = value; } void ClockOut(DAC_CHANNEL ch, const int pulselength = HEMISPHERE_CLOCK_TICKS * HS::trig_length) { + // short circuit if skip probability is zero to avoid consuming random numbers + if (0 == clockskip[ch] || random(100) >= clockskip[ch]) { clock_countdown[ch] = pulselength; outputs[ch] = PULSE_VOLTAGE * (12 << 7); clockout_q[ch] = true; + } + } + void NudgeSkip(int ch, int dir) { + clockskip[ch] = constrain(clockskip[ch] + dir, 0, 100); } // TODO: Hardware IO should be extracted diff --git a/software/src/HemisphereApplet.cpp b/software/src/HemisphereApplet.cpp index c074699a1..ff77185a0 100644 --- a/software/src/HemisphereApplet.cpp +++ b/software/src/HemisphereApplet.cpp @@ -19,7 +19,7 @@ void HemisphereApplet::BaseController() { void HemisphereApplet::BaseView(bool full_screen) { //if (HS::select_mode == hemisphere) - gfxHeader(applet_name()); + gfxHeader(applet_name(), applet_icon()); // If active, draw the full screen view instead of the application screen if (full_screen) this->DrawFullScreen(); else this->View(); diff --git a/software/src/HemisphereApplet.h b/software/src/HemisphereApplet.h index d84a5dbcc..b73f9b760 100644 --- a/software/src/HemisphereApplet.h +++ b/software/src/HemisphereApplet.h @@ -39,6 +39,7 @@ #include "util/util_math.h" #include "bjorklund.h" #include "HSicons.h" +#include "PhzIcons.h" #include "HSClockManager.h" #include "HSUtils.h" @@ -65,6 +66,7 @@ class HemisphereApplet { static const char* help[HELP_LABEL_COUNT]; virtual const char* applet_name() = 0; // Maximum of 9 characters + virtual const uint8_t* applet_icon() { return nullptr; } const char* const OutputLabel(int ch) { return OC::Strings::capital_letters[ch + io_offset]; } @@ -181,7 +183,7 @@ class HemisphereApplet { // Buffered I/O functions int ViewIn(int ch) {return frame.inputs[io_offset + ch];} int ViewOut(int ch) {return frame.outputs[io_offset + ch];} - int ClockCycleTicks(int ch) {return frame.cycle_ticks[io_offset + ch];} + uint32_t ClockCycleTicks(int ch) {return frame.cycle_ticks[io_offset + ch];} bool Changed(int ch) {return frame.changed_cv[io_offset + ch];} //////////////// Offset I/O methods @@ -468,10 +470,14 @@ class HemisphereApplet { } } - void gfxHeader(const char *str) { - gfxPrint(1, 2, str); - gfxDottedLine(0, 10, 62, 10); - //gfxLine(0, 11, 62, 11); + void gfxHeader(const char *str, const uint8_t *icon = nullptr) { + int x = 1; + if (icon) { + gfxIcon(x, 2, icon); + x += 8; + } + gfxPrint(x, 2, str); + gfxDottedLine(0, 10, 62, 10); } void DrawSlider(uint8_t x, uint8_t y, uint8_t len, uint8_t value, uint8_t max_val, bool is_cursor) { diff --git a/software/src/Main.cpp b/software/src/Main.cpp index c31bcd0d2..568974625 100644 --- a/software/src/Main.cpp +++ b/software/src/Main.cpp @@ -113,6 +113,8 @@ void setup() { delay(50); Serial.begin(9600); + Entropy.Initialize(); + #if defined(__IMXRT1062__) if (CrashReport) { while (!Serial && millis() < 3000) ; // wait @@ -145,8 +147,8 @@ void setup() { SERIAL_PRINTLN("* %s", OC::Strings::VERSION); OC::DEBUG::Init(); - OC::DigitalInputs::Init(); - #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) + +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) if (DAC8568_Uses_SPI) { // DAC8568 Vref does not turn on by default like DAC8565 // best to turn on Vref as early as possible for analog @@ -157,24 +159,32 @@ void setup() { // ADC33131D wants calibration for Vref, takes ~1150 ms OC::ADC::ADC33131D_Vref_calibrate(); } else { - #endif +#endif delay(400); - #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) } - #endif - OC::ADC::Init(&OC::calibration_data.adc); // Yes, it's using the calibration_data before it's loaded... + + if (OLED_Uses_SPI1) { + SPI1.begin(); + } +#endif + + OC::calibration_load(); + OC::SetFlipMode(OC::calibration_data.flipcontrols()); + + OC::DigitalInputs::Init(); + + OC::ADC::Init(&OC::calibration_data.adc, OC::calibration_data.flipcontrols()); OC::ADC::Init_DMA(); - OC::DAC::Init(&OC::calibration_data.dac); + OC::DAC::Init(&OC::calibration_data.dac, OC::calibration_data.flipcontrols()); + display::AdjustOffset(OC::calibration_data.display_offset); + display::SetFlipMode( OC::calibration_data.flipscreen() ); display::Init(); GRAPHICS_BEGIN_FRAME(true); GRAPHICS_END_FRAME(); - OC::calibration_load(); - - display::AdjustOffset(OC::calibration_data.display_offset); - OC::menu::Init(); OC::ui.Init(); OC::ui.configure_encoders(OC::calibration_data.encoder_config()); diff --git a/software/src/OC_ADC.cpp b/software/src/OC_ADC.cpp index 65a58c590..5501cbeb3 100644 --- a/software/src/OC_ADC.cpp +++ b/software/src/OC_ADC.cpp @@ -3,6 +3,11 @@ #include "DMAChannel.h" #include +ADC_CHANNEL ADC_CHANNEL_1=0, ADC_CHANNEL_2=1, ADC_CHANNEL_3=2, ADC_CHANNEL_4=3; +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) +ADC_CHANNEL ADC_CHANNEL_5=4, ADC_CHANNEL_6=5, ADC_CHANNEL_7=6, ADC_CHANNEL_8=7; +#endif + namespace OC { #if defined(__MK20DX256__) @@ -84,34 +89,38 @@ static PROGMEM const uint8_t adc2_pin_to_channel[] = { #endif // __IMXRT1062__ -#if defined(__MK20DX256__) -/*static*/ void ADC::Init(CalibrationData *calibration_data) { +/*static*/ void ADC::Init(CalibrationData *calibration_data, bool flip180) +{ + if (flip180) { + ADC_CHANNEL_1=3, ADC_CHANNEL_2=2, ADC_CHANNEL_3=1, ADC_CHANNEL_4=0; +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) + ADC_CHANNEL_5=7, ADC_CHANNEL_6=6, ADC_CHANNEL_7=5, ADC_CHANNEL_8=4; +#endif + } + + calibration_data_ = calibration_data; +#if defined(__MK20DX256__) adc_.setReference(ADC_REF_3V3); adc_.setResolution(kAdcScanResolution); adc_.setConversionSpeed(kAdcConversionSpeed); adc_.setSamplingSpeed(kAdcSamplingSpeed); adc_.setAveraging(kAdcScanAverages); - calibration_data_ = calibration_data; std::fill(raw_, raw_ + ADC_CHANNEL_LAST, 0); std::fill(smoothed_, smoothed_ + ADC_CHANNEL_LAST, 0); std::fill(adcbuffer_0, adcbuffer_0 + DMA_BUF_SIZE, 0); - - adc_.enableDMA(); -} + adc_.enableDMA(); #elif defined(__IMXRT1062__) -/*static*/ void ADC::Init(CalibrationData *calibration_data) { - calibration_data_ = calibration_data; // magic number to yield 0V reading on non-existent inputs // (copied from OC_calibration.cpp) static constexpr uint16_t _ADC_OFFSET = (uint16_t)((float)pow(2,OC::ADC::kAdcResolution)*0.6666667f); // ADC offset @2.2V std::fill(raw_, raw_ + ADC_CHANNEL_LAST, _ADC_OFFSET << kAdcSmoothBits); std::fill(smoothed_, smoothed_ + ADC_CHANNEL_LAST, _ADC_OFFSET << kAdcSmoothBits); +#endif // __IMXRT1062__ } -#endif // __IMXRT1062__ #ifdef OC_ADC_ENABLE_DMA_INTERRUPT /*static*/ void ADC::DMA_ISR() { diff --git a/software/src/OC_ADC.h b/software/src/OC_ADC.h index e3cd1339e..7a4e2fc6a 100644 --- a/software/src/OC_ADC.h +++ b/software/src/OC_ADC.h @@ -11,20 +11,15 @@ // If enabled, use an interrupt to track DMA completion; otherwise use polling //#define OC_ADC_ENABLE_DMA_INTERRUPT +typedef int ADC_CHANNEL; -enum ADC_CHANNEL { - ADC_CHANNEL_1, - ADC_CHANNEL_2, - ADC_CHANNEL_3, - ADC_CHANNEL_4, +extern ADC_CHANNEL ADC_CHANNEL_1, ADC_CHANNEL_2, ADC_CHANNEL_3, ADC_CHANNEL_4; #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) - ADC_CHANNEL_5, - ADC_CHANNEL_6, - ADC_CHANNEL_7, - ADC_CHANNEL_8, +extern ADC_CHANNEL ADC_CHANNEL_5, ADC_CHANNEL_6, ADC_CHANNEL_7, ADC_CHANNEL_8; +static constexpr int ADC_CHANNEL_LAST = 8; +#else +static constexpr int ADC_CHANNEL_LAST = 4; #endif - ADC_CHANNEL_LAST, -}; #define DMA_BUF_SIZE 16 #define DMA_NUM_CH 4 @@ -54,7 +49,7 @@ class ADC { int16_t pitch_cv_offset; }; - static void Init(CalibrationData *calibration_data); + static void Init(CalibrationData *calibration_data, bool flip180 = false); #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) static void ADC33131D_Vref_calibrate(); #endif @@ -62,7 +57,7 @@ class ADC { static void DMA_ISR(); static void Scan_DMA(); - template + template static int32_t value() { return calibration_data_->offset[channel] - (smoothed_[channel] >> kAdcValueShift); } @@ -94,7 +89,7 @@ class ADC { private: - template + template static void update(uint32_t value) { value = (value >> (kAdcScanResolution - kAdcResolution)) << kAdcSmoothBits; raw_[channel] = value; diff --git a/software/src/OC_DAC.cpp b/software/src/OC_DAC.cpp index 4e1ee803e..8087e56e3 100644 --- a/software/src/OC_DAC.cpp +++ b/software/src/OC_DAC.cpp @@ -44,7 +44,11 @@ #include #endif -#define SPICLOCK_30MHz (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR) //(60 / 2) * ((1+1)/2) = 30 MHz (= 24MHz, when F_BUS == 48000000) + +DAC_CHANNEL DAC_CHANNEL_A=0, DAC_CHANNEL_B=1, DAC_CHANNEL_C=2, DAC_CHANNEL_D=3; +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) +DAC_CHANNEL DAC_CHANNEL_E=4, DAC_CHANNEL_F=5, DAC_CHANNEL_G=6, DAC_CHANNEL_H=7; +#endif namespace OC { @@ -53,11 +57,17 @@ int DAC::kOctaveZero = 0; #endif /*static*/ -void DAC::Init(CalibrationData *calibration_data) { +void DAC::Init(CalibrationData *calibration_data, bool flip180) { calibration_data_ = calibration_data; restore_scaling(0x0); + if (flip180) { + DAC_CHANNEL_A=3, DAC_CHANNEL_B=2, DAC_CHANNEL_C=1, DAC_CHANNEL_D=0; +#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) + DAC_CHANNEL_E=7, DAC_CHANNEL_F=6, DAC_CHANNEL_G=5, DAC_CHANNEL_H=4; +#endif + } // set up DAC pins OC::pinMode(DAC_CS, OUTPUT); @@ -274,11 +284,7 @@ void set8565_CHA(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - SPIFIFO.write(0b00010110, SPI_CONTINUE); - #else SPIFIFO.write(0b00010000, SPI_CONTINUE); - #endif SPIFIFO.write16(_data); SPIFIFO.read(); SPIFIFO.read(); @@ -290,11 +296,7 @@ void set8565_CHB(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - SPIFIFO.write(0b00010100, SPI_CONTINUE); - #else SPIFIFO.write(0b00010010, SPI_CONTINUE); - #endif SPIFIFO.write16(_data); SPIFIFO.read(); SPIFIFO.read(); @@ -306,11 +308,7 @@ void set8565_CHC(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - SPIFIFO.write(0b00010010, SPI_CONTINUE); - #else SPIFIFO.write(0b00010100, SPI_CONTINUE); - #endif SPIFIFO.write16(_data); SPIFIFO.read(); SPIFIFO.read(); @@ -322,11 +320,7 @@ void set8565_CHD(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - SPIFIFO.write(0b00010000, SPI_CONTINUE); - #else SPIFIFO.write(0b00010110, SPI_CONTINUE); - #endif SPIFIFO.write16(_data); SPIFIFO.read(); SPIFIFO.read(); @@ -341,11 +335,7 @@ void set8565_CHA(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - _data = (0b00010110 << 16) | (_data & 0xFFFF); - #else _data = (0b00010000 << 16) | (_data & 0xFFFF); - #endif LPSPI4_TDR = _data; } @@ -355,11 +345,7 @@ void set8565_CHB(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - _data = (0b00010100 << 16) | (_data & 0xFFFF); - #else _data = (0b00010010 << 16) | (_data & 0xFFFF); - #endif LPSPI4_TDR = _data; } void set8565_CHC(uint32_t data) { @@ -368,11 +354,7 @@ void set8565_CHC(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - _data = (0b00010010 << 16) | (_data & 0xFFFF); - #else _data = (0b00010100 << 16) | (_data & 0xFFFF); - #endif LPSPI4_TDR = _data; } void set8565_CHD(uint32_t data) { @@ -381,11 +363,7 @@ void set8565_CHD(uint32_t data) { #else uint32_t _data = OC::DAC::MAX_VALUE - data; #endif - #ifdef FLIP_180 - _data = (0b00010000 << 16) | (_data & 0xFFFF); - #else _data = (0b00010110 << 16) | (_data & 0xFFFF); - #endif LPSPI4_SR = LPSPI_SR_TCF; // clear transmit complete flag before last write to FIFO LPSPI4_TDR = _data; } diff --git a/software/src/OC_DAC.h b/software/src/OC_DAC.h index 86ea4faea..ef9fb0e71 100644 --- a/software/src/OC_DAC.h +++ b/software/src/OC_DAC.h @@ -24,13 +24,15 @@ static inline void dac8568_set_channel(uint32_t channel, uint32_t data) { #endif extern void SPI_init(); -enum DAC_CHANNEL { - DAC_CHANNEL_A, DAC_CHANNEL_B, DAC_CHANNEL_C, DAC_CHANNEL_D, +typedef int DAC_CHANNEL; + +extern DAC_CHANNEL DAC_CHANNEL_A, DAC_CHANNEL_B, DAC_CHANNEL_C, DAC_CHANNEL_D; #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) - DAC_CHANNEL_E, DAC_CHANNEL_F, DAC_CHANNEL_G, DAC_CHANNEL_H, +extern DAC_CHANNEL DAC_CHANNEL_E, DAC_CHANNEL_F, DAC_CHANNEL_G, DAC_CHANNEL_H; +static constexpr int DAC_CHANNEL_LAST = 8; +#else +static constexpr int DAC_CHANNEL_LAST = 4; #endif - DAC_CHANNEL_LAST -}; enum OutputVoltageScaling { VOLTAGE_SCALING_1V_PER_OCT, // 0 @@ -66,7 +68,7 @@ class DAC { uint16_t calibrated_octaves[DAC_CHANNEL_LAST][OCTAVES + 1]; }; - static void Init(CalibrationData *calibration_data); + static void Init(CalibrationData *calibration_data, bool flip180 = false); #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) static void DAC8568_Vref_enable(); #endif @@ -86,11 +88,11 @@ class DAC { static void init_Vbias(); static void set_all(uint32_t value) { - for (int i = DAC_CHANNEL_A; i < DAC_CHANNEL_LAST; ++i) + for (int i = 0; i < DAC_CHANNEL_LAST; ++i) values_[i] = USAT16(value); } - template + template static void set(uint32_t value) { values_[channel] = USAT16(value); } @@ -189,13 +191,13 @@ class DAC { } // Set channel to semitone value - template + template static void set_semitone(int32_t semitone, int32_t octave_offset) { set(semitone_to_dac(channel, semitone, octave_offset)); } // Set channel to semitone value - template + template static void set_voltage_scaled_semitone(int32_t semitone, int32_t octave_offset, uint8_t voltage_scaling) { set(semitone_to_scaled_voltage_dac(channel, semitone, octave_offset, voltage_scaling)); } @@ -216,7 +218,7 @@ class DAC { // Set all channels to integer voltage value, where 0 = 0V, 1 = 1V static void set_all_octave(int v) { - for (int i = DAC_CHANNEL_A; i < DAC_CHANNEL_LAST; ++i) + for (int i = 0; i < DAC_CHANNEL_LAST; ++i) set_octave(DAC_CHANNEL(i), v); } @@ -250,13 +252,12 @@ class DAC { #endif size_t tail = history_tail_; - for (int i = DAC_CHANNEL_A; i < DAC_CHANNEL_LAST; ++i) + for (int i = 0; i < DAC_CHANNEL_LAST; ++i) history_[i][tail] = values_[i]; history_tail_ = (tail + 1) % kHistoryDepth; } - template - static void getHistory(uint16_t *dst){ + static void getHistory(int channel, uint16_t *dst){ size_t head = (history_tail_ + 1) % kHistoryDepth; size_t count = kHistoryDepth - head; diff --git a/software/src/OC_calibration.cpp b/software/src/OC_calibration.cpp index 8844c4e97..416487357 100644 --- a/software/src/OC_calibration.cpp +++ b/software/src/OC_calibration.cpp @@ -18,6 +18,7 @@ #include "OC_strings.h" #include "OC_ui.h" #include "OC_options.h" +#include "HSicons.h" #include "src/drivers/display.h" #include "src/drivers/ADC/OC_util_ADC.h" #include "util/util_debugpins.h" @@ -92,22 +93,6 @@ FLASHMEM void calibration_reset() { } } -#ifdef FLIP_180 -FLASHMEM void calibration_flip() { - uint16_t flip_dac[OCTAVES + 1]; - uint16_t flip_adc; - for (int i = 0; i < 2; ++i) { - flip_adc = OC::calibration_data.adc.offset[i]; - OC::calibration_data.adc.offset[i] = OC::calibration_data.adc.offset[ADC_CHANNEL_LAST-1 - i]; - OC::calibration_data.adc.offset[ADC_CHANNEL_LAST-1 - i] = flip_adc; - - memcpy(flip_dac, OC::calibration_data.dac.calibrated_octaves[i], sizeof(flip_dac)); - memcpy(OC::calibration_data.dac.calibrated_octaves[i], OC::calibration_data.dac.calibrated_octaves[DAC_CHANNEL_LAST-1 - i], sizeof(flip_dac)); - memcpy(OC::calibration_data.dac.calibrated_octaves[DAC_CHANNEL_LAST-1 - i], flip_dac, sizeof(flip_dac)); - } -} -#endif - FLASHMEM void calibration_load() { SERIAL_PRINTLN("Cal.Storage: PAGESIZE=%u, PAGES=%u, LENGTH=%u", OC::CalibrationStorage::PAGESIZE, OC::CalibrationStorage::PAGES, OC::CalibrationStorage::LENGTH); @@ -117,9 +102,6 @@ FLASHMEM void calibration_load() { if (!calibration_data_loaded) { SERIAL_PRINTLN("No calibration data, using defaults"); } else { -#ifdef FLIP_180 - calibration_flip(); -#endif SERIAL_PRINTLN("Calibration data loaded..."); } @@ -135,13 +117,7 @@ FLASHMEM void calibration_load() { FLASHMEM void calibration_save() { SERIAL_PRINTLN("Saving calibration data"); -#ifdef FLIP_180 - calibration_flip(); -#endif OC::calibration_storage.Save(OC::calibration_data); -#ifdef FLIP_180 - calibration_flip(); -#endif uint32_t start = millis(); while(millis() < start + SETTINGS_SAVE_TIMEOUT_MS) { @@ -161,6 +137,7 @@ const char * const start_footer = "[CANCEL] [OK]"; const char * const end_footer = "[PREV] [EXIT]"; const char * const default_footer = "[PREV] [NEXT]"; const char * const default_help_r = "[R] => Adjust"; +const char * const long_press_hint = "Hold [DOWN] to set"; const char * const select_help = "[R] => Select"; const CalibrationStep calibration_steps[CALIBRATION_STEP_LAST] = { @@ -169,229 +146,65 @@ const CalibrationStep calibration_steps[CALIBRATION_STEP_LAST] = { #if defined(NORTHERNLIGHT) && !defined(IO_10V) { DAC_A_VOLT_3m, "DAC A 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2m, "DAC A 1.2 volts", "-> 1.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1m, "DAC A 2.4 volts", "-> 2.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_0, "DAC A 3.6 volts", "-> 3.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1, "DAC A 4.8 volts", "-> 4.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2, "DAC A 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_3, "DAC A 7.2 volts", "-> 7.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_4, "DAC A 8.4 volts", "-> 8.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_5, "DAC A 9.6 volts", "-> 9.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_6, "DAC A 10.8 volts", "-> 10.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_A_VOLT_6, "DAC A 10.8 volts", "-> 10.800V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_B_VOLT_3m, "DAC B 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2m, "DAC B 1.2 volts", "-> 1.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1m, "DAC B 2.4 volts", "-> 2.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_0, "DAC B 3.6 volts", "-> 3.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1, "DAC B 4.8 volts", "-> 4.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2, "DAC B 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_3, "DAC B 7.2 volts", "-> 7.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_4, "DAC B 8.4 volts", "-> 8.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_5, "DAC B 9.6 volts", "-> 9.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_6, "DAC B 10.8 volts", "-> 10.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_B_VOLT_6, "DAC B 10.8 volts", "-> 10.800V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_C_VOLT_3m, "DAC C 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2m, "DAC C 1.2 volts", "-> 1.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1m, "DAC C 2.4 volts", "-> 2.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_0, "DAC C 3.6 volts", "-> 3.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1, "DAC C 4.8 volts", "-> 4.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2, "DAC C 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_3, "DAC C 7.2 volts", "-> 7.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_4, "DAC C 8.4 volts", "-> 8.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_5, "DAC C 9.6 volts", "-> 9.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_6, "DAC C 10.8 volts", "-> 10.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_C_VOLT_6, "DAC C 10.8 volts", "-> 10.800V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_D_VOLT_3m, "DAC D 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2m, "DAC D 1.2 volts", "-> 1.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1m, "DAC D 2.4 volts", "-> 2.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_0, "DAC D 3.6 volts", "-> 3.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1, "DAC D 4.8 volts", "-> 4.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2, "DAC D 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_3, "DAC D 7.2 volts", "-> 7.200V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_4, "DAC D 8.4 volts", "-> 8.400V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_5, "DAC D 9.6 volts", "-> 9.600V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_6, "DAC D 10.8 volts", "-> 10.800V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_D_VOLT_6, "DAC D 10.8 volts", "-> 10.800V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, #elif defined(IO_10V) && !defined(VOR) { DAC_A_VOLT_3m, "DAC A 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2m, "DAC A 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1m, "DAC A 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_0, "DAC A 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1, "DAC A 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2, "DAC A 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_3, "DAC A 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_4, "DAC A 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_5, "DAC A 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_6, "DAC A 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_A_VOLT_6, "DAC A 9.0 volts", "-> 9.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_B_VOLT_3m, "DAC B 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2m, "DAC B 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1m, "DAC B 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_0, "DAC B 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1, "DAC B 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2, "DAC B 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_3, "DAC B 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_4, "DAC B 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_5, "DAC B 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_6, "DAC B 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_B_VOLT_6, "DAC B 9.0 volts", "-> 9.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_C_VOLT_3m, "DAC C 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2m, "DAC C 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1m, "DAC C 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_0, "DAC C 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1, "DAC C 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2, "DAC C 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_3, "DAC C 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_4, "DAC C 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_5, "DAC C 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_6, "DAC C 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_C_VOLT_6, "DAC C 9.0 volts", "-> 9.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, { DAC_D_VOLT_3m, "DAC D 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2m, "DAC D 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1m, "DAC D 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_0, "DAC D 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1, "DAC D 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2, "DAC D 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_3, "DAC D 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_4, "DAC D 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_5, "DAC D 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_6, "DAC D 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, + { DAC_D_VOLT_6, "DAC D 9.0 volts", "-> 9.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, #elif defined(VOR) { DAC_A_VOLT_3m, "DAC A 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2m, "DAC A 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1m, "DAC A 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_0, "DAC A 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1, "DAC A 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2, "DAC A 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_3, "DAC A 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_4, "DAC A 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_5, "DAC A 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_6, "DAC A 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_7, "DAC A 10.0 volts", "-> 10.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, + { DAC_A_VOLT_7, "DAC A 10.0 volts", "-> 10.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, { DAC_B_VOLT_3m, "DAC B 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2m, "DAC B 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1m, "DAC B 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_0, "DAC B 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1, "DAC B 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2, "DAC B 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_3, "DAC B 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_4, "DAC B 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_5, "DAC B 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_6, "DAC B 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_7, "DAC B 10.0 volts", "-> 10.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, + { DAC_B_VOLT_7, "DAC B 10.0 volts", "-> 10.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, { DAC_C_VOLT_3m, "DAC C 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2m, "DAC C 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1m, "DAC C 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_0, "DAC C 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1, "DAC C 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2, "DAC C 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_3, "DAC C 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_4, "DAC C 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_5, "DAC C 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_6, "DAC C 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_7, "DAC C 10.0 volts", "-> 10.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, + { DAC_C_VOLT_7, "DAC C 10.0 volts", "-> 10.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, { DAC_D_VOLT_3m, "DAC D 0.0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2m, "DAC D 1.0 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1m, "DAC D 2.0 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_0, "DAC D 3.0 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1, "DAC D 4.0 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2, "DAC D 5.0 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_3, "DAC D 6.0 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_4, "DAC D 7.0 volts", "-> 7.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 7, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_5, "DAC D 8.0 volts", "-> 8.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 8, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_6, "DAC D 9.0 volts", "-> 9.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 9, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_7, "DAC D 10.0 volts", "-> 10.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, + { DAC_D_VOLT_7, "DAC D 10.0 volts", "-> 10.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 10, nullptr, 0, DAC::MAX_VALUE }, #else { DAC_A_VOLT_3m, "DAC A -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2m, "DAC A -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1m, "DAC A -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_0, "DAC A 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_1, "DAC A 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_2, "DAC A 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_3, "DAC A 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_4, "DAC A 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_5, "DAC A 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_A_VOLT_6, "DAC A 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_A_VOLT_6, "DAC A 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_B_VOLT_3m, "DAC B -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2m, "DAC B -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1m, "DAC B -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_0, "DAC B 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_1, "DAC B 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_2, "DAC B 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_3, "DAC B 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_4, "DAC B 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_5, "DAC B 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_B_VOLT_6, "DAC B 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_B_VOLT_6, "DAC B 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_C_VOLT_3m, "DAC C -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2m, "DAC C -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1m, "DAC C -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_0, "DAC C 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_1, "DAC C 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_2, "DAC C 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_3, "DAC C 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_4, "DAC C 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_5, "DAC C 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_C_VOLT_6, "DAC C 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_C_VOLT_6, "DAC C 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_D_VOLT_3m, "DAC D -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2m, "DAC D -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1m, "DAC D -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_0, "DAC D 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_1, "DAC D 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_2, "DAC D 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_3, "DAC D 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_4, "DAC D 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_5, "DAC D 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_D_VOLT_6, "DAC D 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_D_VOLT_6, "DAC D 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, #ifdef ARDUINO_TEENSY41 { DAC_E_VOLT_3m, "DAC E -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_2m, "DAC E -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_1m, "DAC E -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_0, "DAC E 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_1, "DAC E 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_2, "DAC E 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_3, "DAC E 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_4, "DAC E 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_5, "DAC E 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_E_VOLT_6, "DAC E 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_E_VOLT_6, "DAC E 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_F_VOLT_3m, "DAC F -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_2m, "DAC F -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_1m, "DAC F -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_0, "DAC F 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_1, "DAC F 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_2, "DAC F 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_3, "DAC F 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_4, "DAC F 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_5, "DAC F 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_F_VOLT_6, "DAC F 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_F_VOLT_6, "DAC F 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_G_VOLT_3m, "DAC G -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_2m, "DAC G -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_1m, "DAC G -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_0, "DAC G 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_1, "DAC G 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_2, "DAC G 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_3, "DAC G 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_4, "DAC G 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_5, "DAC G 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_G_VOLT_6, "DAC G 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_G_VOLT_6, "DAC G 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, { DAC_H_VOLT_3m, "DAC H -3 volts", "-> -3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_2m, "DAC H -2 volts", "-> -2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_1m, "DAC H -1 volts", "-> -1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, -1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_0, "DAC H 0 volts", "-> 0.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 0, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_1, "DAC H 1 volts", "-> 1.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 1, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_2, "DAC H 2 volts", "-> 2.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 2, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_3, "DAC H 3 volts", "-> 3.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 3, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_4, "DAC H 4 volts", "-> 4.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 4, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_5, "DAC H 5 volts", "-> 5.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 5, nullptr, 0, DAC::MAX_VALUE }, - { DAC_H_VOLT_6, "DAC H 6 volts", "-> 6.000V ", default_help_r, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, + { DAC_H_VOLT_6, "DAC H 6 volts", "-> 6.000V ", long_press_hint, default_footer, CALIBRATE_OCTAVE, 6, nullptr, 0, DAC::MAX_VALUE }, #endif #endif @@ -412,11 +225,11 @@ const CalibrationStep calibration_steps[CALIBRATION_STEP_LAST] = { #endif #if defined(NORTHERNLIGHT) && !defined(IO_10V) - { ADC_PITCH_C2, "ADC cal. octave #1", "CV1: Input 1.2V", "Hold [DOWN] to set", default_footer, CALIBRATE_ADC_1V, 0, nullptr, 0, 0 }, - { ADC_PITCH_C4, "ADC cal. octave #3", "CV1: Input 3.6V", "Hold [DOWN] to set", default_footer, CALIBRATE_ADC_3V, 0, nullptr, 0, 0 }, + { ADC_PITCH_C2, "ADC cal. octave #1", "CV1: Input 1.2V", long_press_hint, default_footer, CALIBRATE_ADC_1V, 0, nullptr, 0, 0 }, + { ADC_PITCH_C4, "ADC cal. octave #3", "CV1: Input 3.6V", long_press_hint, default_footer, CALIBRATE_ADC_3V, 0, nullptr, 0, 0 }, #else - { ADC_PITCH_C2, "CV Scaling 1V", "CV1: Input 1V (C2)", "Hold [DOWN] to set", default_footer, CALIBRATE_ADC_1V, 0, nullptr, 0, 0 }, - { ADC_PITCH_C4, "CV Scaling 3V", "CV1: Input 3V (C4)", "Hold [DOWN] to set", default_footer, CALIBRATE_ADC_3V, 0, nullptr, 0, 0 }, + { ADC_PITCH_C2, "CV Scaling 1V", "CV1: Input 1V (C2)", long_press_hint, default_footer, CALIBRATE_ADC_1V, 0, nullptr, 0, 0 }, + { ADC_PITCH_C4, "CV Scaling 3V", "CV1: Input 3V (C4)", long_press_hint, default_footer, CALIBRATE_ADC_3V, 0, nullptr, 0, 0 }, #endif // Changing screensaver to screen blank, and seconds to minutes @@ -444,6 +257,10 @@ FLASHMEM void calibration_draw(const CalibrationState &state) { graphics.setPrintPos(menu::kIndentDx, y + 2); switch (step->calibration_type) { case CALIBRATE_OCTAVE: + if (state.auto_scale_set[step_to_channel(step->step)]) { + graphics.drawBitmap8(menu::kDisplayWidth - 10, y + 13, 8, CHECK_ICON); + graphics.setPrintPos(menu::kIndentDx, y + 2); + } case CALIBRATE_SCREENSAVER: #ifdef VOR case CALIBRATE_VBIAS_BIPOLAR: diff --git a/software/src/OC_calibration.h b/software/src/OC_calibration.h index 7a49b42f8..e49ff5a2d 100644 --- a/software/src/OC_calibration.h +++ b/software/src/OC_calibration.h @@ -30,29 +30,29 @@ static constexpr uint16_t DAC_OFFSET = 4890; // DAC offset, initial approx., ish static constexpr unsigned kCalibrationAdcSmoothing = 4; -enum CALIBRATION_STEP { +enum CALIBRATION_STEP { HELLO, CENTER_DISPLAY, - + #ifdef VOR - DAC_A_VOLT_3m, DAC_A_VOLT_2m, DAC_A_VOLT_1m, DAC_A_VOLT_0, DAC_A_VOLT_1, DAC_A_VOLT_2, DAC_A_VOLT_3, DAC_A_VOLT_4, DAC_A_VOLT_5, DAC_A_VOLT_6, DAC_A_VOLT_7, - DAC_B_VOLT_3m, DAC_B_VOLT_2m, DAC_B_VOLT_1m, DAC_B_VOLT_0, DAC_B_VOLT_1, DAC_B_VOLT_2, DAC_B_VOLT_3, DAC_B_VOLT_4, DAC_B_VOLT_5, DAC_B_VOLT_6, DAC_B_VOLT_7, - DAC_C_VOLT_3m, DAC_C_VOLT_2m, DAC_C_VOLT_1m, DAC_C_VOLT_0, DAC_C_VOLT_1, DAC_C_VOLT_2, DAC_C_VOLT_3, DAC_C_VOLT_4, DAC_C_VOLT_5, DAC_C_VOLT_6, DAC_C_VOLT_7, - DAC_D_VOLT_3m, DAC_D_VOLT_2m, DAC_D_VOLT_1m, DAC_D_VOLT_0, DAC_D_VOLT_1, DAC_D_VOLT_2, DAC_D_VOLT_3, DAC_D_VOLT_4, DAC_D_VOLT_5, DAC_D_VOLT_6, DAC_D_VOLT_7, + DAC_A_VOLT_3m, DAC_A_VOLT_7, + DAC_B_VOLT_3m, DAC_B_VOLT_7, + DAC_C_VOLT_3m, DAC_C_VOLT_7, + DAC_D_VOLT_3m, DAC_D_VOLT_7, V_BIAS_BIPOLAR, V_BIAS_ASYMMETRIC, #else - DAC_A_VOLT_3m, DAC_A_VOLT_2m, DAC_A_VOLT_1m, DAC_A_VOLT_0, DAC_A_VOLT_1, DAC_A_VOLT_2, DAC_A_VOLT_3, DAC_A_VOLT_4, DAC_A_VOLT_5, DAC_A_VOLT_6, - DAC_B_VOLT_3m, DAC_B_VOLT_2m, DAC_B_VOLT_1m, DAC_B_VOLT_0, DAC_B_VOLT_1, DAC_B_VOLT_2, DAC_B_VOLT_3, DAC_B_VOLT_4, DAC_B_VOLT_5, DAC_B_VOLT_6, - DAC_C_VOLT_3m, DAC_C_VOLT_2m, DAC_C_VOLT_1m, DAC_C_VOLT_0, DAC_C_VOLT_1, DAC_C_VOLT_2, DAC_C_VOLT_3, DAC_C_VOLT_4, DAC_C_VOLT_5, DAC_C_VOLT_6, - DAC_D_VOLT_3m, DAC_D_VOLT_2m, DAC_D_VOLT_1m, DAC_D_VOLT_0, DAC_D_VOLT_1, DAC_D_VOLT_2, DAC_D_VOLT_3, DAC_D_VOLT_4, DAC_D_VOLT_5, DAC_D_VOLT_6, + DAC_A_VOLT_3m, DAC_A_VOLT_6, + DAC_B_VOLT_3m, DAC_B_VOLT_6, + DAC_C_VOLT_3m, DAC_C_VOLT_6, + DAC_D_VOLT_3m, DAC_D_VOLT_6, #ifdef ARDUINO_TEENSY41 - DAC_E_VOLT_3m, DAC_E_VOLT_2m, DAC_E_VOLT_1m, DAC_E_VOLT_0, DAC_E_VOLT_1, DAC_E_VOLT_2, DAC_E_VOLT_3, DAC_E_VOLT_4, DAC_E_VOLT_5, DAC_E_VOLT_6, - DAC_F_VOLT_3m, DAC_F_VOLT_2m, DAC_F_VOLT_1m, DAC_F_VOLT_0, DAC_F_VOLT_1, DAC_F_VOLT_2, DAC_F_VOLT_3, DAC_F_VOLT_4, DAC_F_VOLT_5, DAC_F_VOLT_6, - DAC_G_VOLT_3m, DAC_G_VOLT_2m, DAC_G_VOLT_1m, DAC_G_VOLT_0, DAC_G_VOLT_1, DAC_G_VOLT_2, DAC_G_VOLT_3, DAC_G_VOLT_4, DAC_G_VOLT_5, DAC_G_VOLT_6, - DAC_H_VOLT_3m, DAC_H_VOLT_2m, DAC_H_VOLT_1m, DAC_H_VOLT_0, DAC_H_VOLT_1, DAC_H_VOLT_2, DAC_H_VOLT_3, DAC_H_VOLT_4, DAC_H_VOLT_5, DAC_H_VOLT_6, + DAC_E_VOLT_3m, DAC_E_VOLT_6, + DAC_F_VOLT_3m, DAC_F_VOLT_6, + DAC_G_VOLT_3m, DAC_G_VOLT_6, + DAC_H_VOLT_3m, DAC_H_VOLT_6, #endif #endif - + CV_OFFSET_0, CV_OFFSET_1, CV_OFFSET_2, CV_OFFSET_3, #ifdef ARDUINO_TEENSY41 CV_OFFSET_4, CV_OFFSET_5, CV_OFFSET_6, CV_OFFSET_7, @@ -62,7 +62,7 @@ enum CALIBRATION_STEP { CALIBRATION_EXIT, CALIBRATION_STEP_LAST, CALIBRATION_STEP_FINAL = ADC_PITCH_C4 -}; +}; enum CALIBRATION_TYPE { CALIBRATE_NONE, @@ -92,7 +92,7 @@ struct CalibrationStep { int min, max; }; -constexpr DAC_CHANNEL step_to_channel(int step) { +static constexpr DAC_CHANNEL &step_to_channel(const int step) { #ifdef ARDUINO_TEENSY41 if (step >= DAC_H_VOLT_3m) return DAC_CHANNEL_H; if (step >= DAC_G_VOLT_3m) return DAC_CHANNEL_G; @@ -117,6 +117,7 @@ struct CalibrationState { uint16_t adc_3v; bool used_defaults; + bool auto_scale_set[DAC_CHANNEL_LAST]; }; // Originally, this was a single bit that would reverse both encoders. @@ -132,7 +133,10 @@ enum EncoderConfig : uint32_t { }; enum CalibrationFlags : uint32_t { - CALIBRATION_FLAG_ENCODER_MASK = 0x3 + CALIBRATION_FLAG_ENCODER_MASK = 0x3, // mask for the first two bits + CALIBRATION_FLAG_FLIP_MASK = 0xc, // mask + CALIBRATION_FLAG_FLIPSCREEN = 2, // bit index + CALIBRATION_FLAG_FLIPCONTROLS = 3, // bit index }; struct CalibrationData { @@ -152,6 +156,25 @@ struct CalibrationData { uint32_t reserved1; #endif + bool flipcontrols() const { + return (flags >> CALIBRATION_FLAG_FLIPCONTROLS) & 1; + } + bool flipscreen() const { + return (flags >> CALIBRATION_FLAG_FLIPSCREEN) & 1; + } + uint32_t flipmode() const { + return (flags & CALIBRATION_FLAG_FLIP_MASK) >> CALIBRATION_FLAG_FLIPSCREEN; + } + void cycle_flipmode() { + flags = (flags & ~CALIBRATION_FLAG_FLIP_MASK) | + ( ((flags & CALIBRATION_FLAG_FLIP_MASK) + (1 << CALIBRATION_FLAG_FLIPSCREEN)) + & CALIBRATION_FLAG_FLIP_MASK ); + } + bool toggle_flipscreen() { + flags ^= (1 << CALIBRATION_FLAG_FLIPSCREEN); + + return flipscreen(); + } EncoderConfig encoder_config() const { return static_cast(flags & CALIBRATION_FLAG_ENCODER_MASK); } diff --git a/software/src/OC_config.h b/software/src/OC_config.h index 193a6ae40..ee966d71c 100644 --- a/software/src/OC_config.h +++ b/software/src/OC_config.h @@ -19,7 +19,7 @@ static constexpr int OC_CORE_TIMER_PRIO = 80; // yet higher static constexpr int OC_GPIO_ISR_PRIO = 112; // higher static constexpr int OC_UI_TIMER_PRIO = 128; // default -static constexpr unsigned long REDRAW_TIMEOUT_MS = 1; +static constexpr unsigned long REDRAW_TIMEOUT_MS = 2; static constexpr uint32_t SCREENSAVER_TIMEOUT_S = 25; // default time out menu (in s) static constexpr uint32_t SCREENSAVER_TIMEOUT_MAX_S = 120; diff --git a/software/src/OC_core.h b/software/src/OC_core.h index 734f3455f..eade5d2a3 100644 --- a/software/src/OC_core.h +++ b/software/src/OC_core.h @@ -3,6 +3,8 @@ #include #include +#include + #include "OC_config.h" #include "OC_strings.h" #include "OC_ui.h" diff --git a/software/src/OC_debug.cpp b/software/src/OC_debug.cpp index d421a8584..3f43b69bf 100644 --- a/software/src/OC_debug.cpp +++ b/software/src/OC_debug.cpp @@ -92,16 +92,15 @@ static void debug_menu_version() graphics.print(Strings::NAME); graphics.setPrintPos(2, 22); graphics.print(Strings::VERSION); - - weegfx::coord_t y = 32; - graphics.setPrintPos(2, y); y += 10; + graphics.setPrintPos(2, 32); + graphics.print(Strings::BUILD_TAG); #ifdef OC_DEV - graphics.print("DEV"); + graphics.print(" DEV"); #else - graphics.print("PROD"); + graphics.print(" PROD"); #endif #ifdef USB_SERIAL - graphics.setPrintPos(2, y); y += 10; + graphics.setPrintPos(2, 42); graphics.print("USB_SERIAL"); #endif } diff --git a/software/src/OC_digital_inputs.cpp b/software/src/OC_digital_inputs.cpp index 175edab30..5495e859b 100644 --- a/software/src/OC_digital_inputs.cpp +++ b/software/src/OC_digital_inputs.cpp @@ -67,17 +67,6 @@ void OC::DigitalInputs::Init() { // Defaults is 0, or set OC_GPIO_ISR_PRIO for all ports } -void OC::DigitalInputs::reInit() { - // re-init TR4, to avoid conflict with the FTM - #ifdef FLIP_180 - pinMode(TR1, OC_GPIO_TRx_PINMODE); - attachInterrupt(TR1, tr1_ISR, FALLING); - #else - pinMode(TR4, OC_GPIO_TRx_PINMODE); - attachInterrupt(TR4, tr4_ISR, FALLING); - #endif -} - /*static*/ void OC::DigitalInputs::Scan() { clocked_mask_ = diff --git a/software/src/OC_digital_inputs.h b/software/src/OC_digital_inputs.h index 894df6ec4..4105fece7 100644 --- a/software/src/OC_digital_inputs.h +++ b/software/src/OC_digital_inputs.h @@ -25,12 +25,6 @@ static constexpr uint32_t DIGITAL_INPUT_4_MASK = DIGITAL_INPUT_MASK(DIGITAL_INPU #if defined(__MK20DX256__) // Teensy 3.2 -template struct InputPinDesc { }; -template <> struct InputPinDesc { static constexpr int PIN = TR1; }; -template <> struct InputPinDesc { static constexpr int PIN = TR2; }; -template <> struct InputPinDesc { static constexpr int PIN = TR3; }; -template <> struct InputPinDesc { static constexpr int PIN = TR4; }; - void tr1_ISR(); void tr2_ISR(); void tr3_ISR(); @@ -40,9 +34,7 @@ class DigitalInputs { public: static void Init(); - - static void reInit(); - + static void reInit() { Init(); } static void Scan(); // @return mask of all pins cloked since last call (does not reset state) @@ -61,7 +53,7 @@ class DigitalInputs { } template static inline bool read_immediate() { - return !digitalReadFast(InputPinDesc::PIN); + return !digitalReadFast(InputPinMap(input)); } static inline bool read_immediate(DigitalInput input) { @@ -82,10 +74,10 @@ class DigitalInputs { inline static int InputPinMap(DigitalInput input) { switch (input) { - case DIGITAL_INPUT_1: return InputPinDesc::PIN; - case DIGITAL_INPUT_2: return InputPinDesc::PIN; - case DIGITAL_INPUT_3: return InputPinDesc::PIN; - case DIGITAL_INPUT_4: return InputPinDesc::PIN; + case DIGITAL_INPUT_1: return TR1; + case DIGITAL_INPUT_2: return TR2; + case DIGITAL_INPUT_3: return TR3; + case DIGITAL_INPUT_4: return TR4; default: break; } return 0; @@ -105,7 +97,6 @@ class DigitalInputs { } }; - #elif defined(__IMXRT1062__) // Teensy 4.0 or 4.1 class DigitalInputs { @@ -151,7 +142,6 @@ class DigitalInputs { #endif - // Helper class for visualizing digital inputs with decay // Uses 4 bits for decay class DigitalInputDisplay { diff --git a/software/src/OC_gpio.cpp b/software/src/OC_gpio.cpp index 5bb2710c4..24d12aa3a 100644 --- a/software/src/OC_gpio.cpp +++ b/software/src/OC_gpio.cpp @@ -3,18 +3,14 @@ #include "OC_digital_inputs.h" #include "OC_gpio.h" #include "OC_ADC.h" -//#include "OC_options.h" - -// custom pins only for Teensy 4.1 -#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) // default settings for traditional O_C hardware wired for Teensy 3.2 -uint8_t CV1=19, CV2=18, CV3=20, CV4=17; +//uint8_t CV1=19, CV2=18, CV3=20, CV4=17; uint8_t TR1=0, TR2=1, TR3=2, TR4=3; uint8_t OLED_DC=6, OLED_RST=7, OLED_CS=8; uint8_t DAC_CS=10, DAC_RST=9; uint8_t encL1=22, encL2=21, butL=23, encR1=16, encR2=15, butR=14; -uint8_t but_top=5, but_bot=4, but_mid=255, but_top2=255, but_bot2=255; +uint8_t but_top=5, but_bot=4, but_mid=9, but_top2=255, but_bot2=255; uint8_t OC_GPIO_DEBUG_PIN1=24, OC_GPIO_DEBUG_PIN2=25; bool ADC33131D_Uses_FlexIO=false; bool OLED_Uses_SPI1=false; @@ -23,20 +19,120 @@ bool I2S2_Audio_ADC=false; bool I2S2_Audio_DAC=false; bool I2C_Expansion=false; bool MIDI_Uses_Serial8=false; +float id_voltage = 0.0; +bool flip_mode = false; + +FLASHMEM +void OC::SetFlipMode(bool flip_180) { + flip_mode = flip_180; + if (flip_180) { + // reversed + if (id_voltage >= 0.05f) { + // new hardware + but_top = 15; + but_top2 = 14; + but_bot = 28; + but_bot2 = 29; + + encR1 = 30; + encR2 = 31; + butR = 24; + encL1 = 36; + encL2 = 37; + butL = 25; + + TR4 = 0; + TR3 = 1; + TR2 = 23; + TR1 = 22; + } else { + // old hardware +#ifdef NLM_hOC + but_top = 5; + but_bot = 4; +#else + but_top = 4; + but_bot = 5; +#endif + + encR2 = 22; + encR1 = 21; + butR = 23; + encL2 = 16; + encL1 = 15; + butL = 14; + + //CV4 = 19; + //CV3 = 18; + //CV2 = 20; + //CV1 = 17; + TR4 = 0; + TR3 = 1; + TR2 = 2; + TR1 = 3; + } + } else { + // default orientation + if (id_voltage >= 0.05f) { + // new hardware + but_top = 29; + but_top2 = 28; + but_bot = 14; + but_bot2 = 15; + + encL1 = 30; + encL2 = 31; + butL = 24; + encR1 = 36; + encR2 = 37; + butR = 25; + + TR1 = 0; + TR2 = 1; + TR3 = 23; + TR4 = 22; + } else { + // old hardware +#ifdef NLM_hOC + but_top = 4; + but_bot = 5; +#else + but_top = 5; + but_bot = 4; +#endif + + encL1 = 22; + encL2 = 21; + butL = 23; + encR1 = 16; + encR2 = 15; + butR = 14; + + //CV1 = 19; + //CV2 = 18; + //CV3 = 20; + //CV4 = 17; + TR1 = 0; + TR2 = 1; + TR3 = 2; + TR4 = 3; + } + } +} FLASHMEM void OC::Pinout_Detect() { - float id_voltage = OC::ADC::Read_ID_Voltage(); + id_voltage = OC::ADC::Read_ID_Voltage(); const int f = int(floor(id_voltage * 1000)); const int value = f / 1000; const int cents = f % 1000; Serial.printf("ID voltage (pin A17) = %1d.%03d\n", value, cents); if (id_voltage >= 0.05f && id_voltage < 0.15f) { - CV1 = 255; - CV2 = 255; // CV inputs with ADC33131D - CV3 = 255; - CV4 = 255; + //CV1 = 255; + //CV2 = 255; // CV inputs with ADC33131D + //CV3 = 255; + //CV4 = 255; TR1 = 0; TR2 = 1; TR3 = 23; @@ -69,5 +165,3 @@ void OC::Pinout_Detect() { } } - -#endif // __IMXRT1062__ && ARDUINO_TEENSY41 diff --git a/software/src/OC_gpio.h b/software/src/OC_gpio.h index 3d6a5f421..eefc40319 100644 --- a/software/src/OC_gpio.h +++ b/software/src/OC_gpio.h @@ -3,86 +3,11 @@ #include "OC_options.h" -// Teensy 3.2 or Teensy 4.0 have fixed pinout -// -#if defined(__MK20DX256__) || (defined(__IMXRT1062__) && defined(ARDUINO_TEENSY40)) - -#ifdef FLIP_180 - #define CV4 19 - #define CV3 18 - #define CV2 20 - #define CV1 17 - - #define TR4 0 - #define TR3 1 - #define TR2 2 - #define TR1 3 - - #define but_top 4 - #define but_bot 5 -#else - #define CV1 19 - #define CV2 18 - #define CV3 20 - #define CV4 17 - - #define TR1 0 - #define TR2 1 - #define TR3 2 - #define TR4 3 - -#ifdef NLM_hOC - #define but_top 4 - #define but_bot 5 -#else - #define but_top 5 - #define but_bot 4 -#endif - -#endif - -#define OLED_DC 6 -#define OLED_RST 7 -#define OLED_CS 8 - -#define DAC_CS 10 - -#ifdef VOR - #define but_mid 9 -#else - #define DAC_RST 9 -#endif - -// NOTE: encoder pins R1/R2 changed for rev >= 2c -#ifdef FLIP_180 - #define encL1 16 - #define encL2 15 - #define butL 14 - - #define encR1 22 - #define encR2 21 - #define butR 23 -#else - #define encR1 16 - #define encR2 15 - #define butR 14 - - #define encL1 22 - #define encL2 21 - #define butL 23 -#endif - -// NOTE: back side :( -#define OC_GPIO_DEBUG_PIN1 24 -#define OC_GPIO_DEBUG_PIN2 25 - -#endif // Teensy 3.2 or Teensy 4.0 - +// All platforms now have dynamic pinouts. +// Controls can be remapped, and flip_180 is a calibration flag. // Teensy 4.1 has different pinouts depending on voltage at pin 41/A17 -// -#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) -extern uint8_t CV1, CV2, CV3, CV4; +//extern uint8_t CV1, CV2, CV3, CV4; extern uint8_t TR1, TR2, TR3, TR4; extern uint8_t OLED_DC, OLED_RST, OLED_CS; extern uint8_t DAC_CS, DAC_RST; @@ -97,8 +22,6 @@ extern bool I2S2_Audio_DAC; extern bool I2C_Expansion; extern bool MIDI_Uses_Serial8; -#endif - // OLED CS is active low #define OLED_CS_ACTIVE LOW #define OLED_CS_INACTIVE HIGH @@ -108,6 +31,7 @@ extern bool MIDI_Uses_Serial8; #define OC_GPIO_ENC_PINMODE INPUT_PULLUP /* local copy of pinMode (cf. cores/pins_teensy.c), using faster slew rate */ +// TODO: is this necessary? -NJM namespace OC { @@ -154,6 +78,7 @@ void inline pinMode(uint8_t pin, uint8_t mode) { } void Pinout_Detect(); +void SetFlipMode(bool flip_180); } diff --git a/software/src/OC_menus.cpp b/software/src/OC_menus.cpp index 142ec06aa..7db24823a 100644 --- a/software/src/OC_menus.cpp +++ b/software/src/OC_menus.cpp @@ -95,7 +95,7 @@ static const size_t kScopeDepth = 64; uint16_t scope_history[DAC::kHistoryDepth]; uint16_t averaged_scope_history[DAC_CHANNEL_LAST][kScopeDepth]; size_t averaged_scope_tail = 0; -DAC_CHANNEL scope_update_channel = DAC_CHANNEL_A; +int scope_update_channel = 0; template inline uint16_t calc_average(const uint16_t *data) { @@ -108,29 +108,13 @@ inline uint16_t calc_average(const uint16_t *data) { template void scope_averaging() { - switch (scope_update_channel) { - case DAC_CHANNEL_A: - DAC::getHistory(scope_history); - averaged_scope_history[DAC_CHANNEL_A][averaged_scope_tail] = ((65535U - calc_average(scope_history)) >> rshift) & bitmask; - scope_update_channel = DAC_CHANNEL_B; - break; - case DAC_CHANNEL_B: - DAC::getHistory(scope_history); - averaged_scope_history[DAC_CHANNEL_B][averaged_scope_tail] = ((65535U - calc_average(scope_history)) >> rshift) & bitmask; - scope_update_channel = DAC_CHANNEL_C; - break; - case DAC_CHANNEL_C: - DAC::getHistory(scope_history); - averaged_scope_history[DAC_CHANNEL_C][averaged_scope_tail] = ((65535U - calc_average(scope_history)) >> rshift) & bitmask; - scope_update_channel = DAC_CHANNEL_D; - break; - case DAC_CHANNEL_D: - DAC::getHistory(scope_history); - averaged_scope_history[DAC_CHANNEL_D][averaged_scope_tail] = ((65535U - calc_average(scope_history)) >> rshift) & bitmask; - scope_update_channel = DAC_CHANNEL_A; - averaged_scope_tail = (averaged_scope_tail + 1) % kScopeDepth; - break; - default: break; + DAC::getHistory(scope_update_channel, scope_history); + averaged_scope_history[scope_update_channel][averaged_scope_tail] = ((65535U - calc_average(scope_history)) >> rshift) & bitmask; + + ++scope_update_channel %= DAC_CHANNEL_LAST; + + if (0 == scope_update_channel) { + averaged_scope_tail = (averaged_scope_tail + 1) % kScopeDepth; } } @@ -140,15 +124,15 @@ void scope_render() { for (weegfx::coord_t x = 0; x < (weegfx::coord_t)kScopeDepth - 1; ++x) { size_t index = (x + averaged_scope_tail + 1) % kScopeDepth; #ifdef NORTHERNLIGHT - graphics.setPixel(x, 0 + averaged_scope_history[DAC_CHANNEL_C][index]); - graphics.setPixel(64 + x, 0 + averaged_scope_history[DAC_CHANNEL_D][index]); - graphics.setPixel(x, 32 + averaged_scope_history[DAC_CHANNEL_A][index]); - graphics.setPixel(64 + x, 32 + averaged_scope_history[DAC_CHANNEL_B][index]); + graphics.setPixel(x, 0 + averaged_scope_history[2][index]); + graphics.setPixel(64 + x, 0 + averaged_scope_history[3][index]); + graphics.setPixel(x, 32 + averaged_scope_history[0][index]); + graphics.setPixel(64 + x, 32 + averaged_scope_history[1][index]); #else - graphics.setPixel(x, 0 + averaged_scope_history[DAC_CHANNEL_A][index]); - graphics.setPixel(64 + x, 0 + averaged_scope_history[DAC_CHANNEL_B][index]); - graphics.setPixel(x, 32 + averaged_scope_history[DAC_CHANNEL_C][index]); - graphics.setPixel(64 + x, 32 + averaged_scope_history[DAC_CHANNEL_D][index]); + graphics.setPixel(x, 0 + averaged_scope_history[0][index]); + graphics.setPixel(64 + x, 0 + averaged_scope_history[1][index]); + graphics.setPixel(x, 32 + averaged_scope_history[2][index]); + graphics.setPixel(64 + x, 32 + averaged_scope_history[3][index]); #endif } } @@ -158,8 +142,8 @@ void vectorscope_render() { for (weegfx::coord_t x = 0; x < (weegfx::coord_t)kScopeDepth - 1; ++x) { size_t index = (x + averaged_scope_tail + 1) % kScopeDepth; - graphics.setPixel(averaged_scope_history[DAC_CHANNEL_A][index], averaged_scope_history[DAC_CHANNEL_B][index]); - graphics.setPixel(64 + averaged_scope_history[DAC_CHANNEL_C][index], averaged_scope_history[DAC_CHANNEL_D][index]); + graphics.setPixel(averaged_scope_history[0][index], averaged_scope_history[1][index]); + graphics.setPixel(64 + averaged_scope_history[2][index], averaged_scope_history[3][index]); } } diff --git a/software/src/OC_options.h b/software/src/OC_options.h index bfab3cca5..d8fe00176 100644 --- a/software/src/OC_options.h +++ b/software/src/OC_options.h @@ -11,12 +11,14 @@ /* ------------ uncomment for use with all Northernlight 4U modules - cOC, 2OC, hOC ---------- */ //#define NORTHERNLIGHT +/* ------------ uncomment for use with Northernlight 2OC on the left side: -------------------------- */ +//#define NORTHERNLIGHT_2OC_LEFTSIDE /* ------------ uncomment for the Northernlight hOC (to correct button mappings) ----- */ //#define NLM_hOC +/* ------------ uncomment for the Northernlight cardOC (to correct button mappings) ----- */ +//#define NLM_cardOC /* ------------ uncomment for older hardware revisions of the Northernlight 2OC ----- */ //#define NLM_DIY -/* ------------ uncomment for use with Northernlight 2OC on the left side: -------------------------- */ -//#define NORTHERNLIGHT_2OC_LEFTSIDE /* ------------ print debug messages to USB serial -------------------------------------------------- */ //#define PRINT_DEBUG diff --git a/software/src/OC_scale_edit.h b/software/src/OC_scale_edit.h index 3edddf913..be5ed35b2 100644 --- a/software/src/OC_scale_edit.h +++ b/software/src/OC_scale_edit.h @@ -413,7 +413,7 @@ void ScaleEditor::HandleEncoderEvent(const UI::Event &event) { case _SCALING: { int item = scaling_cursor_pos_ + event.value; - CONSTRAIN(item, DAC_CHANNEL_A, DAC_CHANNEL_LAST - 0x1); + CONSTRAIN(item, 0, DAC_CHANNEL_LAST - 0x1); scaling_cursor_pos_ = item; } break; diff --git a/software/src/OC_scales.h b/software/src/OC_scales.h index eeac48a70..4673563ef 100644 --- a/software/src/OC_scales.h +++ b/software/src/OC_scales.h @@ -35,7 +35,7 @@ class Scales { // They still need some hysteresis though class SemitoneQuantizer { public: - static constexpr int32_t kHysteresis = 16; + static constexpr int32_t kHysteresis = 32; SemitoneQuantizer() { } ~SemitoneQuantizer() { } diff --git a/software/src/OC_strings.cpp b/software/src/OC_strings.cpp index 7215c0453..756dd20d8 100644 --- a/software/src/OC_strings.cpp +++ b/software/src/OC_strings.cpp @@ -9,17 +9,21 @@ namespace OC { #ifdef OC_VERSION_EXTRA OC_VERSION_EXTRA #endif + ; #ifdef OC_BUILD_TAG - "-" - OC_BUILD_TAG + const char * const BUILD_TAG = OC_BUILD_TAG; +#else + const char * const BUILD_TAG = ""; #endif - ; #ifdef NORTHERNLIGHT const char * const NAME = "NLM cOC/hOC/2OC"; const char * const SHORT_NAME = "xOC"; #elif defined(VOR) const char * const NAME = "Plum Audio O_C+"; const char * const SHORT_NAME = "OC+"; +#elif defined(__IMXRT1062__) + const char * const NAME = "O_C T4.x"; + const char * const SHORT_NAME = "o_C"; #else const char * const NAME = "Ornaments & Crimes"; const char * const SHORT_NAME = "o_C"; diff --git a/software/src/OC_strings.h b/software/src/OC_strings.h index 9dfb45025..782a13c95 100644 --- a/software/src/OC_strings.h +++ b/software/src/OC_strings.h @@ -13,6 +13,7 @@ namespace OC { extern const char * const NAME; extern const char * const SHORT_NAME; extern const char * const VERSION; + extern const char * const BUILD_TAG; extern const char * const seq_playmodes[]; extern const char * const channel_trigger_sources[]; diff --git a/software/src/OC_ui.cpp b/software/src/OC_ui.cpp index da8f2d4c5..ec7ed0699 100644 --- a/software/src/OC_ui.cpp +++ b/software/src/OC_ui.cpp @@ -12,6 +12,7 @@ #include "OC_menus.h" #include "OC_ui.h" #include "OC_options.h" +#include "PhzIcons.h" #include "src/drivers/display.h" #ifdef VOR @@ -25,6 +26,19 @@ namespace OC { Ui ui; +// Runtime aliases for UI remapping +#if defined(NLM_hOC) || defined(NLM_cardOC) +// hack to swap Hemisphere left/right just for hOC/cOC +UiControl CONTROL_BUTTON_A = CONTROL_BUTTON_DOWN; +UiControl CONTROL_BUTTON_B = CONTROL_BUTTON_UP; +#else +UiControl CONTROL_BUTTON_A = CONTROL_BUTTON_UP; +UiControl CONTROL_BUTTON_B = CONTROL_BUTTON_DOWN; +#endif +UiControl CONTROL_BUTTON_X = CONTROL_BUTTON_UP2; +UiControl CONTROL_BUTTON_Y = CONTROL_BUTTON_DOWN2; +UiControl CONTROL_BUTTON_Z = CONTROL_BUTTON_M; + void Ui::Init() { ticks_ = 0; set_screensaver_timeout(SCREENSAVER_TIMEOUT_S); @@ -217,7 +231,7 @@ UiMode Ui::Splashscreen(bool &reset_settings) { GRAPHICS_BEGIN_FRAME(true); menu::DefaultTitleBar::Draw(); - graphics.print("Welcome 2 Phazerville"); + graphics.print(OC::Strings::NAME); weegfx::coord_t y = menu::CalcLineY(0); graphics.setPrintPos(menu::kIndentDx, y + menu::kTextDy); @@ -240,9 +254,19 @@ UiMode Ui::Splashscreen(bool &reset_settings) { } graphics.print(OC::Strings::VERSION); + const uint8_t *iconroulette[] = { + PhzIcons::clockDivider, PhzIcons::clockSkip, + PhzIcons::clock_warp_A, PhzIcons::clock_warp_B, + PhzIcons::polyDiv, + ZAP_ICON + }; + + static int pick = 0; + if (now % 50 == 0) pick = random(6); // pew pew? for (int i = 0; i < 124; i+=8) - graphics.drawBitmap8(i, 56, 8, ZAP_ICON); + graphics.drawBitmap8(i, 56, 8, iconroulette[pick]); + // chargin mah lazerrrr weegfx::coord_t w = (now-start)*128 / (SPLASHSCREEN_DELAY_MS/6); w %= 256; diff --git a/software/src/OC_ui.h b/software/src/OC_ui.h index 719ad09b0..10fbb2d51 100644 --- a/software/src/OC_ui.h +++ b/software/src/OC_ui.h @@ -48,14 +48,15 @@ enum UiControl { CONTROL_BUTTON_LAST = 4, #endif - // Some aliases for new hardware - CONTROL_BUTTON_A = CONTROL_BUTTON_UP, - CONTROL_BUTTON_B = CONTROL_BUTTON_DOWN, - CONTROL_BUTTON_X = CONTROL_BUTTON_UP2, - CONTROL_BUTTON_Y = CONTROL_BUTTON_DOWN2, - CONTROL_BUTTON_Z = CONTROL_BUTTON_M, }; +// Runtime aliases for UI remapping +extern UiControl CONTROL_BUTTON_A; +extern UiControl CONTROL_BUTTON_B; +extern UiControl CONTROL_BUTTON_X; +extern UiControl CONTROL_BUTTON_Y; +extern UiControl CONTROL_BUTTON_Z; + static inline uint16_t control_mask(unsigned i) { return 1 << i; } diff --git a/software/src/OC_version.h b/software/src/OC_version.h index 1fead340d..71957783c 100644 --- a/software/src/OC_version.h +++ b/software/src/OC_version.h @@ -1,2 +1,2 @@ // NOTE: DO NOT INCLUDE DIRECTLY, USE OC::Strings::VERSION -"v1.8.2" +"v1.8.3" diff --git a/software/src/PhzIcons.h b/software/src/PhzIcons.h new file mode 100644 index 000000000..9917c9471 --- /dev/null +++ b/software/src/PhzIcons.h @@ -0,0 +1,123 @@ +// Icons! Made with http://beigemaze.com/bitmap8x8.html +// Retouched with https://goatama.ridiculousglitch.com/bitca/ (Column major MSB Bottom) + +#ifndef PHAZER_ICON_SET +#define PHAZER_ICON_SET + +namespace PhzIcons { + +// external +const uint8_t calibr8[8] = { 0x7e, 0x81, 0x81, 0x00, 0x76, 0x89, 0x89, 0x76 }; +const uint8_t tuner[8] = { 0x00, 0x00, 0x1e, 0xe0, 0x1e, 0x00, 0x00, 0x00 }; + +// global +const uint8_t metronome[8] = {0xf3,0x8c,0x9a,0xa2,0x82,0x8c,0xf0,0x00}; +const uint8_t midiIn[8] = { 0x18, 0x7e, 0x81, 0xb9, 0x89, 0xb1, 0x89, 0xb1 }; +const uint8_t midiOut[8] = { 0xb9, 0xa9, 0xbd, 0x81, 0xb5, 0x81, 0x7e, 0x18 }; + +// utilities +const uint8_t dualAttenuverter[8] = { 0x26, 0x20, 0x1c, 0x00, 0x00, 0x1c, 0x02, 0x32 }; +const uint8_t button2[8] = { 0x06, 0x0f, 0x0f, 0x06, 0x60, 0xf0, 0xf0, 0x60 }; +const uint8_t scope[8] = { 0x00, 0x20, 0xee, 0x29, 0xee, 0x24, 0x88, 0x70 }; +const uint8_t switchApp[8] = { 0x00, 0x00, 0x14, 0x18, 0x10, 0x00, 0x00, 0x00 }; + +//triggers +const uint8_t burst[8] = { 0x00, 0x18, 0x00, 0x34, 0x00, 0x5a, 0x00, 0xad }; +const uint8_t clockSkip[8] = { 0x12, 0x80, 0x24, 0x01, 0x80, 0x24, 0x01, 0x48 }; +const uint8_t drumMap[8] = { 0x00, 0x6a, 0x54, 0x2a, 0x54, 0x2a, 0x56, 0x00 }; +const uint8_t euclidX[8] = { 0x42, 0x00, 0x02, 0x40, 0x02, 0x00, 0x47, 0x00 }; +const uint8_t trigseq[8] = { 0xb1, 0xd7, 0x01, 0xf0, 0xb7, 0x01, 0x33, 0xf4 }; +const uint8_t trigseq16[8] = { 0x6f, 0xaa, 0x00, 0xe2, 0x6f, 0x00, 0x6e, 0xed }; +const uint8_t shuffle[8] = { 0x30, 0x81, 0x3c, 0x42, 0xf9, 0x41, 0x39, 0x86 }; +const uint8_t palimpsest[8] = { 0xf0, 0x8e, 0xa1, 0xa9, 0xa9, 0x81, 0x79, 0x06 }; + +//gated +const uint8_t compare[8] = { 0x55, 0x55, 0x00, 0xff, 0xff, 0x00, 0x49, 0x92 }; +const uint8_t gateDelay[8] = { 0xfe, 0x81, 0xbc, 0x22, 0x22, 0xbc, 0x81, 0xfe }; +const uint8_t gateVca[8] = { 0x80, 0xa0, 0x2e, 0x8f, 0x8f, 0x2e, 0xa0, 0x80 }; +const uint8_t shiftGate[8] = { 0x48, 0x05, 0x40, 0xde, 0x51, 0x51, 0xde, 0x40 }; +const uint8_t voltage[8] = { 0x00, 0x00, 0x04, 0x4a, 0x29, 0x10, 0x00, 0x00 }; + +//logic +const uint8_t calculate[8] = { 0x00, 0x00, 0x42, 0x30, 0x0c, 0x42, 0x00, 0x00 }; +const uint8_t cumulus[8] = {0x70,0x88,0xa7,0x80,0x80,0x97,0x88,0x70}; +const uint8_t logic[8] = { 0x15, 0x44, 0x15, 0x40, 0x40, 0x05, 0x11, 0x44 }; +const uint8_t trending[8] = { 0xff, 0x80, 0x88, 0x84, 0x84, 0x98, 0x90, 0x88 }; +const uint8_t thresholdLogicNeuron[8] = { 0x7c, 0x63, 0x04, 0x18, 0xd8, 0xc1, 0x36, 0x0e }; +const uint8_t schmitt[8] = { 0x42, 0xff, 0x81, 0x42, 0x42, 0x24, 0x24, 0x18 }; +const uint8_t mixerBal[8] = { 0x5c, 0x80, 0x9f, 0x80, 0x90, 0x80, 0x5e, 0x00 }; + +//dividers +const uint8_t brancher[8] = { 0x06, 0x18, 0x26, 0x41, 0xa0, 0x2c, 0x10, 0x0e }; +const uint8_t binaryCounter[8] = { 0x3c, 0x42, 0x3c, 0x00, 0x10, 0x00, 0x04, 0x7e }; +const uint8_t divSeq[8] = { 0x45, 0x00, 0xa5, 0x00, 0x4a, 0x00, 0x44, 0x00 }; +const uint8_t polyDiv[8] = { 0xa5, 0x00, 0xa5, 0x00, 0x00, 0xa5, 0x00, 0xa5 }; +const uint8_t probDiv[8] = { 0x21, 0x00, 0x84, 0x00, 0x00, 0x20, 0x00, 0x81 }; +const uint8_t resetClk[8] = { 0x3b, 0x01, 0x39, 0x01, 0x39, 0x01, 0x11, 0x1f }; +const uint8_t clockDivider[8] = { 0x24, 0x00, 0x81, 0x00, 0x00, 0x81, 0x00, 0x24 }; + +// envelopes +const uint8_t ADSR_EG[8] = {0x90, 0x8c, 0x62, 0x11, 0x11, 0x11, 0x66, 0x88}; +const uint8_t AD_EG[8] = { 0x88, 0x88, 0x46, 0x21, 0x12, 0x64, 0x88, 0x88 }; +const uint8_t envelopeFollower[8] = { 0x5f, 0x1d, 0x5b, 0x17, 0x57, 0x5b, 0x5d, 0x1f }; +const uint8_t slew[8] = { 0xf0, 0x8c, 0x86, 0x9e, 0xbc, 0xf8, 0xf0, 0xe0 }; +const uint8_t vectorEG[8] = { 0x08, 0x06, 0x01, 0x22, 0x64, 0xf5, 0x64, 0x28 }; + +//LFOs +const uint8_t ebb_n_Flow[8] = { 0x84, 0x82, 0x41, 0x41, 0x49, 0x59, 0x2a, 0x24 }; +const uint8_t lowerenz[8] = { 0x1e, 0x3f, 0x37, 0x1c, 0x38, 0xec, 0xfc, 0x78 }; +const uint8_t vectorLFO[8] = { 0x02, 0x01, 0x22, 0x64, 0xf5, 0x64, 0x28, 0x04 }; +const uint8_t vectorMod[8] = { 0x08, 0x07, 0x22, 0x64, 0xf5, 0x64, 0x28, 0x06 }; +const uint8_t vectorMorph[8] = { 0x20, 0x1c, 0x08, 0x91, 0xd7, 0x91, 0x20, 0x18 }; +const uint8_t runglBook[8] = { 0x38, 0x54, 0x52, 0x51, 0x51, 0x49, 0x25, 0x12 }; + +//quantizers +const uint8_t dualQuantizer[8] = { 0x0f, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x0f, 0x00 }; +const uint8_t scaleDuet[8] = { 0x05, 0xa7, 0xe5, 0xa7, 0xe5, 0xa7, 0xe5, 0xa0 }; +const uint8_t squanch[8] = {0x3f, 0x41, 0x92, 0x84, 0x84, 0x92, 0x41, 0x3f}; +const uint8_t chordinate[8] = { 0x06, 0xc7, 0xf0, 0x00, 0x0c, 0x0f, 0xc0, 0xf0 }; +const uint8_t multiscale[8] = { 0xfc, 0xfc, 0x00, 0xff, 0x81, 0xff, 0x00, 0x80 }; + +//sequencers +const uint8_t carpeggio[8] = { 0x83, 0x78, 0x07, 0xf0, 0x0f, 0xe0, 0x1f, 0xc0 }; +const uint8_t cvRec[8] = { 0x3d, 0xfc, 0x05, 0xa4, 0xa5, 0x05, 0xfc, 0x3d }; +const uint8_t ASR[8] = { 0x10, 0x08, 0x08, 0x04, 0x10, 0x08, 0x08, 0x04 }; +const uint8_t DualTM[8] = { 0x06, 0x29, 0x49, 0x06, 0x60, 0x92, 0x94, 0x60 }; +const uint8_t enigmaJr[8] = { 0x00, 0x3c, 0x7e, 0x7a, 0x6e, 0x7e, 0x3c, 0x00 }; +const uint8_t gameOfLife[8] = { 0x00, 0x60, 0x60, 0x66, 0x66, 0x78, 0x78, 0x00 }; +const uint8_t pigeons[8] = { 0x3c, 0xc2, 0x01, 0x0d, 0x01, 0x02, 0x48, 0x94 }; +const uint8_t probMeloD[8] = { 0x0c, 0x0f, 0x61, 0x78, 0x08, 0xc0, 0xf0, 0x10 }; +const uint8_t seq32[8] = { 0x01, 0x88, 0xa9, 0xf8, 0x01, 0xc9, 0xa8, 0x91 }; +const uint8_t seqPlay7[8] = { 0x01, 0xf8, 0xf9, 0x70, 0x21, 0x09, 0xe8, 0x19 }; +const uint8_t shredder[8] = { 0xe6, 0x07, 0xb4, 0x05, 0x05, 0x74, 0x07, 0xd6 }; +const uint8_t sequenceX[8] = { 0x01, 0x00, 0x45, 0x6c, 0x11, 0x6d, 0x44, 0x01 }; +const uint8_t switchSeq[8] = { 0x10, 0x18, 0x14, 0x00, 0x50, 0x65, 0x46, 0x04 }; +const uint8_t stairs[8] = { 0x70, 0x90, 0x1c, 0x24, 0x47, 0x49, 0x89, 0x12 }; +const uint8_t tb3P0[8] = { 0x3e, 0x63, 0xe3, 0xff, 0xff, 0xe3, 0x63, 0x3e }; +const uint8_t strum[8] = { 0x00, 0x3a, 0x46, 0x40, 0x40, 0x46, 0x3a, 0x00}; +const uint8_t rndWalk[8] = { 0x02, 0x24, 0xd6, 0x11, 0x31, 0x42, 0x26, 0x18 }; + +//audio +const uint8_t boots_n_cats[8] = { 0x1f, 0x11, 0x17, 0x18, 0x00, 0x70, 0x88, 0x90 }; +const uint8_t bugCrack[8] = { 0x10, 0xc8, 0x3d, 0x4a, 0x4a, 0x3d, 0xc8, 0x10 }; +const uint8_t drLoFi[8] = { 0x00, 0x3e, 0x2a, 0xae, 0x2e, 0xaa, 0xbe, 0x00 }; + + +// BONUS +const uint8_t full_book[8] = { 0x38, 0x5c, 0x5e, 0x5f, 0x5f, 0x4f, 0x27, 0x12 }; +const uint8_t nova[8] = {0x7c, 0x82, 0x39, 0x42, 0x9c, 0x41, 0x3e, 0x00}; +const uint8_t half_a_book[8] = {0x38, 0x54, 0x52, 0x51, 0x51, 0x51, 0x49, 0x25}; +const uint8_t camels[8] = { 0x60, 0x92, 0x4d, 0x51, 0x8a, 0x69, 0x12, 0x0c }; +const uint8_t frontBack[8] = { 0xfc, 0x84, 0xb4, 0x87, 0xfd, 0x21, 0x21, 0x3f }; +const uint8_t enigmaSr[8] = { 0x3c, 0x7e, 0xf7, 0xff, 0xdd, 0xff, 0x7e, 0x3c }; +const uint8_t legoFace[8] = { 0xfc, 0x82, 0xaa, 0xa2, 0xa2, 0xaa, 0x82, 0xfc }; +const uint8_t evilTopo[8] = { 0x83, 0xaf, 0x2f, 0x1a, 0x1a, 0x2f, 0xaf, 0x83 }; +const uint8_t pergamen[8] = { 0x01, 0x01, 0x39, 0x45, 0x55, 0x49, 0x22, 0x1c }; +const uint8_t clock_warp_B[8] = { 0x91, 0x20, 0x26, 0x01, 0x80, 0x64, 0x04, 0x89 }; +const uint8_t clock_warp_A[8] = { 0x91, 0x00, 0x24, 0x01, 0x80, 0x24, 0x00, 0x89 }; +const uint8_t umbrella[8] = { 0x18, 0x15, 0x14, 0xf2, 0x94, 0x15, 0x18, 0x02 }; +const uint8_t coatButton[8] = { 0xc3, 0x81, 0x10, 0x04, 0x20, 0x08, 0x81, 0xc3 }; + +} // namespace PhzIcons + +#endif // PHAZER_ICON_SET diff --git a/software/src/UI/ui_encoder.h b/software/src/UI/ui_encoder.h index a4db42384..30cca9743 100644 --- a/software/src/UI/ui_encoder.h +++ b/software/src/UI/ui_encoder.h @@ -28,11 +28,7 @@ namespace UI { -#if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) // Teensy 4.1 has dynamic pins template -#else // default are macros (Teensy 3.2 or 4.0) -template -#endif class Encoder { public: diff --git a/software/src/applets/ADEG.h b/software/src/applets/ADEG.h index f631fb4ec..58bc310e2 100644 --- a/software/src/applets/ADEG.h +++ b/software/src/applets/ADEG.h @@ -30,6 +30,7 @@ class ADEG : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "AD EG"; } + const uint8_t* applet_icon() { return PhzIcons::AD_EG; } void Start() { signal = 0; diff --git a/software/src/applets/ADSREG.h b/software/src/applets/ADSREG.h index 6ef0edb52..46a6da3e4 100644 --- a/software/src/applets/ADSREG.h +++ b/software/src/applets/ADSREG.h @@ -66,6 +66,7 @@ class ADSREG : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "ADSR EG"; } + const uint8_t* applet_icon() { return PhzIcons::ADSR_EG; } void Start() { edit_stage = 0; @@ -80,10 +81,10 @@ class ADSREG : public HemisphereApplet { stage[ch] = HEM_EG_NO_STAGE; //-ghostils:Initialize ADSR channels independently - attack[ch] = 20; + attack[ch] = 10 + ch * 10; decay[ch] = 30; sustain[ch] = 120; - release[ch] = 25; + release[ch] = 25 + ch * 10; release_mod[ch] = 0; } @@ -146,7 +147,7 @@ class ADSREG : public HemisphereApplet { if (++edit_stage > HEM_EG_RELEASE) { edit_stage = HEM_EG_ATTACK; curEG ^= 1; - } + } } void OnEncoderMove(int direction) { @@ -160,23 +161,25 @@ class ADSREG : public HemisphereApplet { } uint64_t OnDataRequest() { - //-ghostils:Update to use an array and snapshot the values using curEG as the index uint64_t data = 0; - Pack(data, PackLocation {0,8}, attack[curEG]); - Pack(data, PackLocation {8,8}, decay[curEG]); - Pack(data, PackLocation {16,8}, sustain[curEG]); - Pack(data, PackLocation {24,8}, release[curEG]); + for(size_t ch = 0; ch < 2; ++ch) { + Pack(data, PackLocation {ch*32 + 0,8}, attack[ch]); + Pack(data, PackLocation {ch*32 + 8,8}, decay[ch]); + Pack(data, PackLocation {ch*32 + 16,8}, sustain[ch]); + Pack(data, PackLocation {ch*32 + 24,8}, release[ch]); + } return data; } void OnDataReceive(uint64_t data) { - //-ghostils:Update to use an array and snapshot the values using curEG as the index - attack[curEG] = Unpack(data, PackLocation {0,8}); - decay[curEG] = Unpack(data, PackLocation {8,8}); - sustain[curEG] = Unpack(data, PackLocation {16,8}); - release[curEG] = Unpack(data, PackLocation {24,8}); - - if (attack[curEG] == 0) Start(); // If empty data, initialize + for(size_t ch = 0; ch < 2; ++ch) { + attack[ch] = Unpack(data, PackLocation {ch*32 + 0,8}); + decay[ch] = Unpack(data, PackLocation {ch*32 + 8,8}); + sustain[ch] = Unpack(data, PackLocation {ch*32 + 16,8}); + release[ch] = Unpack(data, PackLocation {ch*32 + 24,8}); + } + + if (attack[curEG] == 0) Start(); // If empty data, initialize } protected: @@ -203,12 +206,11 @@ class ADSREG : public HemisphereApplet { //-ghostils:TODO Modify to adjust independently for each envelope, we won't be able to do both Attack and Release simultaneously so we either have to build a menu or just do Release. //-Parameterize - int attack_mod; // Modification to attack from CV1 - + //int attack_mod; // Modification to attack from CV1 int release_mod[2]; // Modification to release from CV2 //-ghostils:Additions for tracking multiple ADSR's in each Hemisphere: - int curEG; + bool curEG; // Stage management int stage[2]; // The current ASDR stage of the current envelope diff --git a/software/src/applets/ASR.h b/software/src/applets/ASR.h index ee94a5a16..2cae51fb5 100644 --- a/software/src/applets/ASR.h +++ b/software/src/applets/ASR.h @@ -31,6 +31,7 @@ class ASR : public HemisphereApplet { const char* applet_name() { return "\"A\"SR"; } + const uint8_t* applet_icon() { return PhzIcons::ASR; } void Start() { buffer_m.SetIndex(1); diff --git a/software/src/applets/AttenuateOffset.h b/software/src/applets/AttenuateOffset.h index bdca550f4..b0a617bc0 100644 --- a/software/src/applets/AttenuateOffset.h +++ b/software/src/applets/AttenuateOffset.h @@ -30,6 +30,7 @@ class AttenuateOffset : public HemisphereApplet { const char* applet_name() { return "AttenOff"; } + const uint8_t* applet_icon() { return PhzIcons::dualAttenuverter; } void Start() { ForEachChannel(ch) level[ch] = ATTENOFF_MAX_LEVEL; diff --git a/software/src/applets/Binary.h b/software/src/applets/Binary.h index 5789cad78..c706daa37 100644 --- a/software/src/applets/Binary.h +++ b/software/src/applets/Binary.h @@ -29,6 +29,7 @@ class Binary : public HemisphereApplet { const char* applet_name() { return "BinaryCtr"; } + const uint8_t* applet_icon() { return PhzIcons::binaryCounter; } void Start() { segment.Init(SegmentSize::BIG_SEGMENTS); diff --git a/software/src/applets/BootsNCat.h b/software/src/applets/BootsNCat.h index 6522d8671..5405c8aae 100644 --- a/software/src/applets/BootsNCat.h +++ b/software/src/applets/BootsNCat.h @@ -31,6 +31,7 @@ class BootsNCat : public HemisphereApplet { const char* applet_name() { return "BootsNCat"; } + const uint8_t* applet_icon() { return PhzIcons::boots_n_cats; } void Start() { tone[0] = 32; // Bass drum freq diff --git a/software/src/applets/Brancher.h b/software/src/applets/Brancher.h index bbbcc1c16..cc1b41b38 100644 --- a/software/src/applets/Brancher.h +++ b/software/src/applets/Brancher.h @@ -27,6 +27,7 @@ class Brancher : public HemisphereApplet { const char* applet_name() { return "Brancher"; } + const uint8_t* applet_icon() { return PhzIcons::brancher; } void Start() { p = 50; diff --git a/software/src/applets/BugCrack.h b/software/src/applets/BugCrack.h index 1d70605b5..085c5be51 100644 --- a/software/src/applets/BugCrack.h +++ b/software/src/applets/BugCrack.h @@ -40,6 +40,7 @@ class BugCrack : public HemisphereApplet { const char* applet_name() { return "BugCrack"; } + const uint8_t* applet_icon() { return PhzIcons::bugCrack; } void Start() { tone_kick = 32; diff --git a/software/src/applets/Burst.h b/software/src/applets/Burst.h index f3bd90051..1eaf2d095 100644 --- a/software/src/applets/Burst.h +++ b/software/src/applets/Burst.h @@ -34,6 +34,7 @@ class Burst : public HemisphereApplet { const char* applet_name() { return "Burst"; } + const uint8_t* applet_icon() { return PhzIcons::burst; } void Start() { cursor = 0; diff --git a/software/src/applets/Button.h b/software/src/applets/Button.h index 1557bb510..dbf756e3d 100644 --- a/software/src/applets/Button.h +++ b/software/src/applets/Button.h @@ -24,6 +24,7 @@ class Button : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "Button2"; } + const uint8_t* applet_icon() { return PhzIcons::button2; } /* Run when the Applet is selected */ void Start() { diff --git a/software/src/applets/CVRecV2.h b/software/src/applets/CVRecV2.h index a15ba8b22..2426d8279 100644 --- a/software/src/applets/CVRecV2.h +++ b/software/src/applets/CVRecV2.h @@ -31,6 +31,7 @@ class CVRecV2 : public HemisphereApplet { const char* applet_name() { return "CVRec"; } + const uint8_t* applet_icon() { return PhzIcons::cvRec; } void Start() { segment.Init(SegmentSize::BIG_SEGMENTS); diff --git a/software/src/applets/Calculate.h b/software/src/applets/Calculate.h index 4a75cdf85..5ddf8caae 100644 --- a/software/src/applets/Calculate.h +++ b/software/src/applets/Calculate.h @@ -31,8 +31,9 @@ class Calculate : public HemisphereApplet { public: const char* applet_name() { - return "Calculate"; + return "Calc"; } + const uint8_t* applet_icon() { return PhzIcons::calculate; } void Start() { selected = 0; diff --git a/software/src/applets/Calibr8.h b/software/src/applets/Calibr8.h index 5faaf31e4..42ded22d1 100644 --- a/software/src/applets/Calibr8.h +++ b/software/src/applets/Calibr8.h @@ -36,6 +36,7 @@ class Calibr8 : public HemisphereApplet { const char* applet_name() { return "Calibr8"; } + const uint8_t* applet_icon() { return PhzIcons::calibr8; } void Start() { clocked_mode = false; diff --git a/software/src/applets/Carpeggio.h b/software/src/applets/Carpeggio.h index fd941aa34..fe137e713 100644 --- a/software/src/applets/Carpeggio.h +++ b/software/src/applets/Carpeggio.h @@ -37,6 +37,7 @@ class Carpeggio : public HemisphereApplet { const char* applet_name() { return "Carpeggio"; } + const uint8_t* applet_icon() { return PhzIcons::carpeggio; } void Start() { step = 0; diff --git a/software/src/applets/Chordinator.h b/software/src/applets/Chordinator.h index 06a2423bf..a292b76ad 100644 --- a/software/src/applets/Chordinator.h +++ b/software/src/applets/Chordinator.h @@ -23,6 +23,7 @@ class Chordinator : public HemisphereApplet { public: const char *applet_name() { return "Chordnate"; } + const uint8_t* applet_icon() { return PhzIcons::chordinate; } void Start() { continuous[0] = 1; diff --git a/software/src/applets/ClockDivider.h b/software/src/applets/ClockDivider.h index 083473042..997efe0d2 100644 --- a/software/src/applets/ClockDivider.h +++ b/software/src/applets/ClockDivider.h @@ -35,8 +35,9 @@ class ClockDivider : public HemisphereApplet { ClkDivMult divmult[4]; const char* applet_name() { - return "Clock Div"; + return "Clk Div"; } + const uint8_t* applet_icon() { return PhzIcons::clockDivider; } void Start() { divmult[0].steps = 2; diff --git a/software/src/applets/ClockSetup.h b/software/src/applets/ClockSetup.h index 05a43508a..dcf198389 100644 --- a/software/src/applets/ClockSetup.h +++ b/software/src/applets/ClockSetup.h @@ -24,6 +24,8 @@ using HS::clock_m; class ClockSetup : public HemisphereApplet { public: + static constexpr int SLIDEOUT_TIME = 100; + enum ClockSetupCursor { PLAY_STOP, TEMPO, @@ -41,12 +43,17 @@ class ClockSetup : public HemisphereApplet { BOOP2, BOOP3, BOOP4, - LAST_SETTING = BOOP4 + OUTSKIP1, + OUTSKIP2, + OUTSKIP3, + OUTSKIP4, + LAST_SETTING = OUTSKIP4 }; const char* applet_name() { return "ClockSet"; } + const uint8_t* applet_icon() { return CLOCK_ICON; } void Start() { } @@ -84,24 +91,30 @@ class ClockSetup : public HemisphereApplet { } // 4 internal clock flashers + /* for (int i = 0; i < 4; ++i) { if (clock_m.Tock(i)) flash_ticker[i] = HEMISPHERE_PULSE_ANIMATION_TIME; else if (flash_ticker[i]) --flash_ticker[i]; } + */ if (button_ticker) --button_ticker; } void View() { - DrawInterface(); + if (OC::CORE::ticks - view_tick > 1000) { + slide_anim = SLIDEOUT_TIME; + } + view_tick = OC::CORE::ticks; + DrawInterface(); } void OnButtonPress() { if (!EditMode()) { // special cases for toggle buttons if (cursor == PLAY_STOP) PlayStop(); - else if (cursor >= BOOP1) { + else if (cursor >= BOOP1 && cursor <= BOOP4) { clock_m.Boop(cursor-BOOP1); button_ticker = HEMISPHERE_PULSE_ANIMATION_TIME_LONG; } @@ -147,6 +160,13 @@ class ClockSetup : public HemisphereApplet { HS::trigger_mapping[cursor-TRIG1] = constrain( HS::trigger_mapping[cursor-TRIG1] + direction, 0, TRIGMAP_MAX); break; + case OUTSKIP1: + case OUTSKIP2: + case OUTSKIP3: + case OUTSKIP4: + HS::frame.NudgeSkip(cursor-OUTSKIP1, direction); + break; + case BOOP1: case BOOP2: case BOOP3: @@ -251,8 +271,10 @@ class ClockSetup : public HemisphereApplet { private: int cursor; // ClockSetupCursor - int flash_ticker[4]; + //int flash_ticker[4]; int button_ticker; + int slide_anim = 0; + uint32_t view_tick = 0; static const int NR_OF_TAPS = 3; @@ -269,22 +291,40 @@ class ClockSetup : public HemisphereApplet { } } + // This applet is an overlay, drawn on top of the applet view. + // Space must be cleared first, depending on the cursor position. void DrawInterface() { - // Header: This is sort of a faux applet, so its header - // needs to extend across the screen - graphics.setPrintPos(1, 2); - graphics.print("Clocks/Triggers"); - gfxLine(0, 10, 127, 10); + if (slide_anim) { + const int x = 23 - (slide_anim * 23 / SLIDEOUT_TIME); + graphics.clearRect(0, 0, 128, x+1); + gfxDottedLine(0, x, 127, x); + gfxLine(0, x+1, 127, x+1); + + --slide_anim; + return; + } + + if (cursor < OUTSKIP1) { + graphics.clearRect(0, 0, 128, 24); - int y = 14; + gfxDottedLine(0, 21, 127, 21); + gfxLine(0, 22, 127, 22); + } else { + graphics.clearRect(0, 51, 128, 13); + + gfxLine(0, 52, 127, 52); + gfxDottedLine(0, 53, 127, 53); + } + + if (cursor <= MULT4) { + int y = 1; // Clock State - gfxIcon(1, y, CLOCK_ICON); if (clock_m.IsRunning()) { + gfxIcon(1, y, clock_m.cycle ? METRO_R_ICON : METRO_L_ICON ); gfxIcon(12, y, PLAY_ICON); - } else if (clock_m.IsPaused()) { - gfxIcon(12, y, PAUSE_ICON); } else { - gfxIcon(12, y, STOP_ICON); + gfxIcon(1, y, CLOCK_ICON); + gfxIcon(12, y, clock_m.IsPaused()? PAUSE_ICON : STOP_ICON); } // Tempo @@ -312,47 +352,52 @@ class ClockSetup : public HemisphereApplet { gfxPrint(1 + x, y, (mult >= 0) ? "x" : "/"); gfxPrint( (mult >= 0) ? mult : 1 - mult ); } - + } + } else if (cursor <= BOOP4) { + int y = 1; + for (int ch=0; ch<4; ++ch) { + const int x = ch * 32; // Physical trigger input mappings - gfxPrint(1 + x, y + 13, OC::Strings::trigger_input_names_none[ HS::trigger_mapping[ch] ] ); - + gfxPrint(1 + x, y, OC::Strings::trigger_input_names_none[ HS::trigger_mapping[ch] ] ); // Manual trigger buttons - gfxIcon(4 + x, 47, (button_ticker && ch == cursor-BOOP1)?BTN_ON_ICON:BTN_OFF_ICON); - - // Trigger indicators - gfxIcon(4 + x, 54, DOWN_BTN_ICON); - if (flash_ticker[ch]) gfxInvert(3 + x, 56, 9, 8); + gfxIcon(4 + x, y + 10, (button_ticker && ch == cursor-BOOP1)?BTN_ON_ICON:BTN_OFF_ICON); } - - y += 10; - gfxDottedLine(0, y, 127, y, 3); + } else { + int y = 55; + // output trig-skip + for (int ch=0; ch<4; ++ch) { + const int x = ch * 32; + gfxPrint(1 + x, y, HS::frame.clockskip[ch]); + gfxPrint("%"); + } + } switch ((ClockSetupCursor)cursor) { case PLAY_STOP: - gfxFrame(11, 13, 10, 10); + gfxFrame(11, 0, 10, 10); break; case TEMPO: - gfxCursor(22, 22, 19); + gfxCursor(22, 9, 19); break; case SHUFFLE: - gfxCursor(52, 22, 13); + gfxCursor(52, 9, 13); break; case EXT_PPQN: - gfxCursor(109,22, 13); + gfxCursor(109,9, 13); break; case MULT1: case MULT2: case MULT3: case MULT4: - gfxCursor(8 + 32*(cursor-MULT1), 32, 12); + gfxCursor(8 + 32*(cursor-MULT1), 19, 12); break; case TRIG1: case TRIG2: case TRIG3: case TRIG4: - gfxCursor(1 + 32*(cursor-TRIG1), 45, 19); + gfxCursor(1 + 32*(cursor-TRIG1), 9, 19); break; case BOOP1: @@ -360,9 +405,15 @@ class ClockSetup : public HemisphereApplet { case BOOP3: case BOOP4: if (0 == button_ticker) - gfxIcon(12 + 32*(cursor-BOOP1), 49, LEFT_ICON); + gfxIcon(12 + 32*(cursor-BOOP1), 11, LEFT_ICON); break; + case OUTSKIP1: + case OUTSKIP2: + case OUTSKIP3: + case OUTSKIP4: + gfxCursor(1 + 32*(cursor-OUTSKIP1), 63, 19); + break; default: break; } } diff --git a/software/src/applets/ClockSetupT4.h b/software/src/applets/ClockSetupT4.h index 8f6be0933..b73ca6621 100644 --- a/software/src/applets/ClockSetupT4.h +++ b/software/src/applets/ClockSetupT4.h @@ -23,6 +23,7 @@ class ClockSetup : public HemisphereApplet { public: + static constexpr int SLIDEOUT_TIME = 100; enum ClockSetupCursor { PLAY_STOP, @@ -45,12 +46,21 @@ class ClockSetup : public HemisphereApplet { TRIG6, TRIG7, TRIG8, - LAST_SETTING = TRIG8 + OUTSKIP1, + OUTSKIP2, + OUTSKIP3, + OUTSKIP4, + OUTSKIP5, + OUTSKIP6, + OUTSKIP7, + OUTSKIP8, + LAST_SETTING = OUTSKIP8 }; const char* applet_name() { return "ClockSet"; } + const uint8_t* applet_icon() { return CLOCK_ICON; } void Start() { } @@ -104,7 +114,11 @@ class ClockSetup : public HemisphereApplet { } void View() { - DrawInterface(); + if (OC::CORE::ticks - view_tick > 1000) { + slide_anim = SLIDEOUT_TIME; + } + view_tick = OC::CORE::ticks; + DrawInterface(); } void OnButtonPress() { @@ -166,6 +180,17 @@ class ClockSetup : public HemisphereApplet { break; */ + case OUTSKIP1: + case OUTSKIP2: + case OUTSKIP3: + case OUTSKIP4: + case OUTSKIP5: + case OUTSKIP6: + case OUTSKIP7: + case OUTSKIP8: + HS::frame.NudgeSkip(cursor-OUTSKIP1, direction); + break; + case EXT_PPQN: HS::clock_m.SetClockPPQN(HS::clock_m.GetClockPPQN() + direction); break; @@ -255,6 +280,8 @@ class ClockSetup : public HemisphereApplet { int cursor; // ClockSetupCursor int flash_ticker[8]; int button_ticker; + int slide_anim = 0; + uint32_t view_tick = 0; static const int NR_OF_TAPS = 3; @@ -271,40 +298,62 @@ class ClockSetup : public HemisphereApplet { } } + // This applet is an overlay, drawn on top of the applet view. + // Space must be cleared first, depending on the cursor position. void DrawInterface() { - // Header: This is sort of a faux applet, so its header - // needs to extend across the screen + if (slide_anim) { + const int x = 23 - (slide_anim * 23 / SLIDEOUT_TIME); + graphics.clearRect(0, 0, 128, x+1); + gfxDottedLine(0, x, 127, x); + gfxLine(0, x+1, 127, x+1); + + --slide_anim; + return; + } + /* graphics.setPrintPos(1, 2); graphics.print("Clocks/Triggers"); gfxLine(0, 10, 127, 10); + */ + if (cursor < OUTSKIP1) { + graphics.clearRect(0, 0, 128, 24); + + gfxDottedLine(0, 21, 127, 21); + gfxLine(0, 22, 127, 22); + } else { + graphics.clearRect(0, 41, 128, 23); + + gfxLine(0, 42, 127, 42); + gfxDottedLine(0, 43, 127, 43); + } - int y = 14; + if (cursor <= EXT_PPQN) { + int y = 1; // Clock State - gfxIcon(1, y, CLOCK_ICON); - if (HS::clock_m.IsRunning()) { + if (clock_m.IsRunning()) { + gfxIcon(1, y, clock_m.cycle ? METRO_R_ICON : METRO_L_ICON ); gfxIcon(12, y, PLAY_ICON); - } else if (HS::clock_m.IsPaused()) { - gfxIcon(12, y, PAUSE_ICON); } else { - gfxIcon(12, y, STOP_ICON); + gfxIcon(1, y, CLOCK_ICON); + gfxIcon(12, y, clock_m.IsPaused()? PAUSE_ICON : STOP_ICON); } // Tempo - gfxPrint(22 + pad(100, HS::clock_m.GetTempo()), y, HS::clock_m.GetTempo()); + gfxPrint(22 + pad(100, clock_m.GetTempo()), y, clock_m.GetTempo()); if (cursor != SHUFFLE) gfxPrint(" BPM"); else { // Shuffle gfxIcon(44, y, METRO_R_ICON); - gfxPrint(52 + pad(10, HS::clock_m.GetShuffle()), y, HS::clock_m.GetShuffle()); + gfxPrint(52 + pad(10, clock_m.GetShuffle()), y, clock_m.GetShuffle()); gfxPrint("%"); } // Input PPQN gfxPrint(79, y, "Sync="); - gfxPrint(HS::clock_m.GetClockPPQN()); - - y += 10; + gfxPrint(clock_m.GetClockPPQN()); + } else if (cursor <= MULT8) { + int y = 1; for (int ch=0; ch<8; ++ch) { const int x = (ch % 4) * 32; if (ch == 4) y += 10; @@ -315,30 +364,43 @@ class ClockSetup : public HemisphereApplet { gfxPrint(1 + x, y, (mult >= 0) ? "x" : "/"); gfxPrint( (mult >= 0) ? mult : 1 - mult ); } + } + } else if (cursor <= TRIG8) { + int y = 1; + for (int ch=0; ch<8; ++ch) { + const int x = (ch % 4) * 32; + if (ch == 4) y += 10; // Physical trigger input mappings - gfxPrint(1 + x, y + 21, OC::Strings::trigger_input_names_none[ HS::trigger_mapping[ch] ] ); + gfxPrint(1 + x, y, OC::Strings::trigger_input_names_none[ HS::trigger_mapping[ch] ] ); // Trigger indicators - gfxIcon(23 + x, y + 20, DOWN_BTN_ICON); - if (flash_ticker[ch]) gfxInvert(22 + x, y + 22, 9, 8); + gfxIcon(23 + x, y, DOWN_BTN_ICON); + if (flash_ticker[ch]) gfxInvert(22 + x, y, 9, 8); } + } else if (cursor <= OUTSKIP8) { + int y = 45; + for (int ch=0; ch<8; ++ch) { + const int x = (ch % 4) * 32; + if (ch == 4) y += 10; - y += 9; - gfxDottedLine(0, y, 127, y, 3); + gfxPrint(1 + x, y, HS::frame.clockskip[ch] ); + gfxPrint(23 + x, y, "%"); + } + } switch ((ClockSetupCursor)cursor) { case PLAY_STOP: - gfxFrame(11, 13, 10, 10); + gfxFrame(11, 0, 10, 10); break; case TEMPO: - gfxCursor(22, 22, 19); + gfxCursor(22, 9, 19); break; case SHUFFLE: - gfxCursor(52, 22, 13); + gfxCursor(52, 9, 13); break; case EXT_PPQN: - gfxCursor(109,22, 13); + gfxCursor(109,9, 13); break; case MULT1: @@ -351,7 +413,7 @@ class ClockSetup : public HemisphereApplet { case MULT8: { const int x_ = 8 + 32 * ((cursor-MULT1) % 4); - const int y_ = 32 + ((cursor-MULT1) / 4 * 10); + const int y_ = 9 + ((cursor-MULT1) / 4 * 10); gfxCursor(x_, y_, 12); break; } @@ -366,7 +428,22 @@ class ClockSetup : public HemisphereApplet { case TRIG8: { const int x_ = 1 + 32 * ((cursor-TRIG1) % 4); - const int y_ = 53 + ((cursor-TRIG1) / 4 * 10); + const int y_ = 9 + ((cursor-TRIG1) / 4 * 10); + gfxCursor(x_, y_, 19); + break; + } + + case OUTSKIP1: + case OUTSKIP2: + case OUTSKIP3: + case OUTSKIP4: + case OUTSKIP5: + case OUTSKIP6: + case OUTSKIP7: + case OUTSKIP8: + { + const int x_ = 1 + 32 * ((cursor-OUTSKIP1) % 4); + const int y_ = 53 + ((cursor-OUTSKIP1) / 4 * 10); gfxCursor(x_, y_, 19); break; } diff --git a/software/src/applets/ClockSkip.h b/software/src/applets/ClockSkip.h index 87f652f78..84033c052 100644 --- a/software/src/applets/ClockSkip.h +++ b/software/src/applets/ClockSkip.h @@ -22,8 +22,9 @@ class ClockSkip : public HemisphereApplet { public: const char* applet_name() { - return "ClockSkip"; + return "Clk Skip"; } + const uint8_t* applet_icon() { return PhzIcons::clockSkip; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/Compare.h b/software/src/applets/Compare.h index 7646b84da..6b4e665bd 100644 --- a/software/src/applets/Compare.h +++ b/software/src/applets/Compare.h @@ -26,6 +26,7 @@ class Compare : public HemisphereApplet { const char* applet_name() { return "Compare"; } + const uint8_t* applet_icon() { return PhzIcons::compare; } void Start() { level = 128; diff --git a/software/src/applets/Cumulus.h b/software/src/applets/Cumulus.h index 8b6aa3681..67c0a9b8b 100644 --- a/software/src/applets/Cumulus.h +++ b/software/src/applets/Cumulus.h @@ -46,6 +46,7 @@ class Cumulus : public HemisphereApplet { const char* applet_name() { return "Cumulus"; } + const uint8_t* applet_icon() { return PhzIcons::cumulus; } void Start() { cursor = 0; @@ -170,7 +171,7 @@ class Cumulus : public HemisphereApplet { void DrawSelector() { a_display = isEditing ? outmode[0] : a_mod; - gfxBitmap(1, 15, 8, BEAKER_ICON); + gfxIcon(1, 15, CLOCK_ICON); gfxPrint(12, 15, OP_NAMES[accoperator]); char outlabel[] = { (char)('A' + io_offset), ':', '\0' }; diff --git a/software/src/applets/DivSeq.h b/software/src/applets/DivSeq.h index 5161046d4..5487a16ad 100644 --- a/software/src/applets/DivSeq.h +++ b/software/src/applets/DivSeq.h @@ -35,7 +35,8 @@ class DivSeq : public HemisphereApplet { STEP1B, STEP2B, STEP3B, STEP4B, STEP5B, MUTE1A, MUTE2A, MUTE3A, MUTE4A, MUTE5A, MUTE1B, MUTE2B, MUTE3B, MUTE4B, MUTE5B, - LAST_SETTING = MUTE5B + RE_ZAP, + LAST_SETTING = RE_ZAP }; struct DivSequence { @@ -45,6 +46,9 @@ class DivSeq : public HemisphereApplet { uint8_t muted = 0x0; // bitmask uint32_t last_clock = 0; + int Get(int s) { + return divmult[s].steps; + } void Set(int s, int div) { divmult[s].Set(div); } @@ -109,8 +113,21 @@ class DivSeq : public HemisphereApplet { const char* applet_name() { return "DivSeq"; } + const uint8_t* applet_icon() { return PhzIcons::divSeq; } void Start() { + ForEachChannel(ch) { + int total = 16 + ch*16; + for (int i = 0; i < NUM_STEPS - 1; ++i) { + int val = random(total); + if (1 == val) + div_seq[ch].Set(i, -random(7)-1); + else + div_seq[ch].Set(i, val); + total -= val; + } + div_seq[ch].Set(NUM_STEPS - 1, total); + } Reset(); } @@ -166,6 +183,12 @@ class DivSeq : public HemisphereApplet { } void OnButtonPress() { + if (RE_ZAP == cursor) { + Start(); + cursor = 0; + return; + } + if (cursor >= MUTE1A && !EditMode()) { const int ch = (cursor - MUTE1A) / NUM_STEPS; const int s = (cursor - MUTE1A) % NUM_STEPS; @@ -191,7 +214,7 @@ class DivSeq : public HemisphereApplet { if (ch > 1) // mutes div_seq[ch-2].ToggleStep(s); else { - const int div = div_seq[ch].divmult[s].steps + direction; + const int div = div_seq[ch].Get(s) + direction; div_seq[ch].Set(s, div); } } @@ -256,6 +279,15 @@ class DivSeq : public HemisphereApplet { ProbLoopLinker *loop_linker = loop_linker->get(); void DrawInterface() { + if (RE_ZAP == cursor) { + gfxIcon(28, 22, DOWN_ICON); + gfxIcon(18, 32, RIGHT_ICON); + gfxIcon(28, 32, ZAP_ICON); + gfxIcon(38, 32, LEFT_ICON); + gfxIcon(28, 42, UP_ICON); + return; + } + // divisions ForEachChannel(ch) { const size_t x = 31*ch; diff --git a/software/src/applets/DrLoFi.h b/software/src/applets/DrLoFi.h index 5f267a336..3b76c9905 100644 --- a/software/src/applets/DrLoFi.h +++ b/software/src/applets/DrLoFi.h @@ -17,6 +17,10 @@ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/* + * heavily modified from the original LoFi Tape applet by Chysn, + * with concepts from armandvedel, implementation by djphazer + */ #define HEM_LOFI_PCM_BUFFER_SIZE 2048 #define HEM_LOFI_PCM_SPEED 4 @@ -36,6 +40,7 @@ class DrLoFi : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "Dr. LoFi"; } + const uint8_t* applet_icon() { return PhzIcons::drLoFi; } void Start() { countdown = HEM_LOFI_PCM_SPEED; @@ -67,7 +72,7 @@ class DrLoFi : public HemisphereApplet { // mix input into the buffer ahead, respecting feedback int fbmix = PCM_TO_CV(lofi_pcm_buffer[head]) * fdbk_g / 100 + cv; lofi_pcm_buffer[head_w] = CV_TO_PCM(fbmix); - + rate_mod = rate; Modulate(rate_mod, 1, 1, 64); diff --git a/software/src/applets/DrumMap.h b/software/src/applets/DrumMap.h index 4cd1ea755..ecff53ac3 100644 --- a/software/src/applets/DrumMap.h +++ b/software/src/applets/DrumMap.h @@ -36,8 +36,9 @@ class DrumMap : public HemisphereApplet { static constexpr int MAX_VAL = 255; const char* applet_name() { - return "DrumMap"; + return "DruMap"; } + const uint8_t* applet_icon() { return PhzIcons::drumMap; } void Start() { step = 0; diff --git a/software/src/applets/DualQuant.h b/software/src/applets/DualQuant.h index e55962257..62a7de868 100644 --- a/software/src/applets/DualQuant.h +++ b/software/src/applets/DualQuant.h @@ -26,6 +26,7 @@ class DualQuant : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "DualQuant"; } + const uint8_t* applet_icon() { return PhzIcons::dualQuantizer; } void Start() { cursor = 0; diff --git a/software/src/applets/DualTM.h b/software/src/applets/DualTM.h index e961fb9bf..89e04cd64 100644 --- a/software/src/applets/DualTM.h +++ b/software/src/applets/DualTM.h @@ -91,10 +91,13 @@ class DualTM : public HemisphereApplet { const char* applet_name() { return "DualTM"; } + const uint8_t* applet_icon() { + return PhzIcons::DualTM; + } void Start() { - reg[0] = random(0xFFFFFFFF); - reg[1] = random(0xFFFFFFFF); + reg[0] = Entropy.random(0xFFFFFFFF); + reg[1] = Entropy.random(0xFFFFFFFF); qselect[0] = io_offset; qselect[1] = io_offset + 1; } @@ -171,7 +174,7 @@ class DualTM : public HemisphereApplet { // Advance the register on clock, flipping bits as necessary if (clk) { // If the cursor is not on the p value, and Digital 2 is not gated, the sequence remains the same - int prob = (cursor == PROB || (!reset_active && Gate(1))) ? p_mod : 0; + uint8_t prob = (cursor == PROB || (!reset_active && Gate(1))) ? p_mod : 0; if (rotate_right) ShiftRight(prob); @@ -408,8 +411,8 @@ class DualTM : public HemisphereApplet { int qselect_mod[2]; int length = 16; // Sequence length int len_mod; // actual length after CV mod - int p = 0; // Probability of bit flipping on each cycle - int p_mod; + uint8_t p = 0; // Probability of bit flipping on each cycle + uint8_t p_mod; int range = 24; int range_mod; int smoothing = 0; @@ -427,25 +430,25 @@ class DualTM : public HemisphereApplet { old_val = (old_val * (s - 1) + new_val) / s; } - void ShiftLeft(int prob) { + void ShiftLeft(uint8_t prob) { ForEachChannel(i) { // Grab the bit that's about to be shifted away uint32_t last = (reg[i] >> (len_mod - 1)) & 0x01; // Does it change? - if (random(0, 99) < prob) last = 1 - last; + if (Entropy.random(0, 99) < prob) last = 1 - last; // Shift left, then potentially add the bit from the other side reg[i] = (reg[i] << 1) + last; } } - void ShiftRight(int prob) { + void ShiftRight(uint8_t prob) { ForEachChannel(i) { // Grab the bit that's about to be shifted away uint32_t last = reg[i] & 0x01; // Does it change? - if (random(0, 99) < prob) last = 1 - last; + if (Entropy.random(0, 99) < prob) last = 1 - last; last = last << (len_mod - 1); // Shift right, then potentially add the bit from the other side @@ -594,12 +597,23 @@ class DualTM : public HemisphereApplet { void DrawIndicator() { gfxLine(0, 45, 63, 45); gfxLine(0, 62, 63, 62); - for (int b = 0; b < 32; ++b) + + const int ii = (length <= 16) ? 16 : 32; + for (int b = 0; b < ii; ++b) { int v = (reg[0] >> b) & 0x01; int v2 = (reg[1] >> b) & 0x01; - if (v) gfxRect(62 - (2 * b), 47, 1, 7); - if (v2) gfxRect(62 - (2 * b), 54, 1, 7); + if (v) gfxRect(62 - (64/ii * b) - 16/ii, 47, 32/ii, 7); + if (v2) gfxRect(62 - (64/ii * b) - 16/ii, 54, 32/ii, 7); + } + + // I'm sure these two can be combined with more math. + if (length < 16) { + const int x_ = 4 * (16 - length); + gfxDottedLine(x_, 45, x_, 62); + } else if (length > 16 && length < 32) { + const int x_ = 2 * (32 - length) - 1; + gfxDottedLine(x_, 45, x_, 62); } } diff --git a/software/src/applets/EbbAndLfo.h b/software/src/applets/EbbAndLfo.h index 9a42df661..a4d561c0c 100644 --- a/software/src/applets/EbbAndLfo.h +++ b/software/src/applets/EbbAndLfo.h @@ -17,7 +17,8 @@ class EbbAndLfo : public HemisphereApplet { MAX_CURSOR = ONESHOT_MODE }; - const char *applet_name() { return "Ebb & LFO"; } + const char *applet_name() { return "Ebb&LFO"; } + const uint8_t *applet_icon() { return PhzIcons::ebb_n_Flow; } void Start() { phase = 0; diff --git a/software/src/applets/EnigmaJr.h b/software/src/applets/EnigmaJr.h index 7a15ecfa5..27a0ec453 100644 --- a/software/src/applets/EnigmaJr.h +++ b/software/src/applets/EnigmaJr.h @@ -32,6 +32,7 @@ class EnigmaJr : public HemisphereApplet { const char* applet_name() { return "Enigma Jr"; } + const uint8_t* applet_icon() { return PhzIcons::enigmaJr; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/EnvFollow.h b/software/src/applets/EnvFollow.h index 8c80c502b..14e5b9b64 100644 --- a/software/src/applets/EnvFollow.h +++ b/software/src/applets/EnvFollow.h @@ -34,6 +34,7 @@ class EnvFollow : public HemisphereApplet { const char* applet_name() { return "EnvFollow"; } + const uint8_t* applet_icon() { return PhzIcons::envelopeFollower; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/EuclidX.h b/software/src/applets/EuclidX.h index e2c468db3..62c7c51a9 100644 --- a/software/src/applets/EuclidX.h +++ b/software/src/applets/EuclidX.h @@ -41,7 +41,8 @@ class EuclidX : public HemisphereApplet { LENGTH2, BEATS2, OFFSET2, PADDING2, CV_DEST1, CV_DEST2, - LAST_SETTING = CV_DEST2 + GATE_MODE, + LAST_SETTING = GATE_MODE }; static constexpr const char* const cv_labels[] = { "Length1", "Fill 1", "Rotate1", "Pad 1", @@ -51,6 +52,7 @@ class EuclidX : public HemisphereApplet { const char* applet_name() { return "EuclidX"; } + const uint8_t* applet_icon() { return PhzIcons::euclidX; } void Start() { ForEachChannel(ch) @@ -118,8 +120,12 @@ class EuclidX : public HemisphereApplet { // actually output the triggers int sb = step % (actual_length[ch] + actual_padding[ch]); if ((pattern[ch] >> sb) & 0x01) { + if (gate_mode) + GateOut(ch, true); + else ClockOut(ch); - } + } else + GateOut(ch, 0); } // Plan for the thing to run forever and ever @@ -170,6 +176,9 @@ class EuclidX : public HemisphereApplet { case CV_DEST2: cv_dest[cursor - CV_DEST1] = (EuclidXParam) constrain(cv_dest[cursor - CV_DEST1] + direction, LENGTH1, PADDING2); break; + case GATE_MODE: + gate_mode = !gate_mode; + break; } } @@ -183,6 +192,7 @@ class EuclidX : public HemisphereApplet { Pack(data, PackLocation {idx++ * PARAM_SIZE, PARAM_SIZE}, padding[ch]); Pack(data, PackLocation {idx++ * PARAM_SIZE, PARAM_SIZE}, cv_dest[ch]); } + Pack(data, PackLocation {60, 1}, gate_mode); return data; } @@ -195,6 +205,7 @@ class EuclidX : public HemisphereApplet { actual_padding[ch] = padding[ch] = Unpack(data, PackLocation {idx++ * PARAM_SIZE, PARAM_SIZE}); cv_dest[ch] = (EuclidXParam) Unpack(data, PackLocation {idx++ * PARAM_SIZE, PARAM_SIZE}); } + gate_mode = Unpack(data, PackLocation{60, 1}); step = 0; // reset } @@ -216,6 +227,7 @@ class EuclidX : public HemisphereApplet { int step; int cursor = LENGTH1; // EuclidXParam uint32_t pattern[2]; + bool gate_mode = false; // Settings uint8_t length[2]; @@ -256,11 +268,11 @@ class EuclidX : public HemisphereApplet { const int spacing = 16; const int pad_left = 5; - if (cursor < CV_DEST1) { - gfxBitmap(pad_left + 0 * spacing, 15, 8, LENGTH_ICON); + if (cursor < CV_DEST1 || cursor > CV_DEST2) { + gfxIcon(pad_left + 0 * spacing, 15, LENGTH_ICON); } - gfxBitmap(pad_left + 1 * spacing, 15, 8, PULSES_ICON); - gfxBitmap(pad_left + 2 * spacing, 15, 8, ROTATE_ICON); + gfxIcon(pad_left + 1 * spacing, 15, gate_mode ? GATE_ICON : PULSES_ICON); + gfxIcon(pad_left + 2 * spacing, 15, ROTATE_ICON); gfxIcon(pad_left + 3 * spacing, 15, OFFSET_ICON); int y = 15; @@ -301,6 +313,9 @@ class EuclidX : public HemisphereApplet { gfxBitmap(8, 15, 3, (cursor - CV_DEST1)? SUB_TWO : SUP_ONE); gfxCursor(0, 19 + (cursor - CV_DEST1)*5, 11, 7); break; + case GATE_MODE: + gfxCursor(pad_left + 1 * spacing - 1, 23, 10); + break; } } }; diff --git a/software/src/applets/GameOfLife.h b/software/src/applets/GameOfLife.h index 4b95e65dd..47e81b996 100644 --- a/software/src/applets/GameOfLife.h +++ b/software/src/applets/GameOfLife.h @@ -6,6 +6,7 @@ class GameOfLife : public HemisphereApplet { const char* applet_name() { return "Game/Life"; } + const uint8_t* applet_icon() { return PhzIcons::gameOfLife; } void Start() { for (int i = 0; i < 80; i++) board[i] = 0; diff --git a/software/src/applets/GateDelay.h b/software/src/applets/GateDelay.h index 11ae5b44f..22c542b8b 100644 --- a/software/src/applets/GateDelay.h +++ b/software/src/applets/GateDelay.h @@ -24,6 +24,7 @@ class GateDelay : public HemisphereApplet { const char* applet_name() { return "GateDelay"; } + const uint8_t* applet_icon() { return PhzIcons::gateDelay; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/GatedVCA.h b/software/src/applets/GatedVCA.h index d68d317ea..998c5de7a 100644 --- a/software/src/applets/GatedVCA.h +++ b/software/src/applets/GatedVCA.h @@ -24,6 +24,7 @@ class GatedVCA : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "Gated VCA"; } + const uint8_t* applet_icon() { return PhzIcons::gateVca; } void Start() { amp_offset_pct = 0; diff --git a/software/src/applets/Logic.h b/software/src/applets/Logic.h index 5ebd8d9a1..1b00bb0b8 100644 --- a/software/src/applets/Logic.h +++ b/software/src/applets/Logic.h @@ -44,6 +44,7 @@ class Logic : public HemisphereApplet { const char* applet_name() { return "Logic"; } + const uint8_t* applet_icon() { return PhzIcons::logic; } void Start() { selected = 0; diff --git a/software/src/applets/LowerRenz.h b/software/src/applets/LowerRenz.h index e36fe0036..6b933063b 100644 --- a/software/src/applets/LowerRenz.h +++ b/software/src/applets/LowerRenz.h @@ -31,6 +31,7 @@ class LowerRenz : public HemisphereApplet { const char* applet_name() { return "LowerRenz"; } + const uint8_t* applet_icon() { return PhzIcons::lowerenz; } void Start() { freq = 128; diff --git a/software/src/applets/Metronome.h b/software/src/applets/Metronome.h index 3b15ff18f..d05386e45 100644 --- a/software/src/applets/Metronome.h +++ b/software/src/applets/Metronome.h @@ -24,6 +24,7 @@ class Metronome : public HemisphereApplet { const char* applet_name() { return "Metronome"; } + const uint8_t* applet_icon() { return PhzIcons::metronome; } void Start() { } diff --git a/software/src/applets/MixerBal.h b/software/src/applets/MixerBal.h index b2c9785cf..d1ef06e27 100644 --- a/software/src/applets/MixerBal.h +++ b/software/src/applets/MixerBal.h @@ -26,6 +26,7 @@ class MixerBal : public HemisphereApplet { const char* applet_name() { return "Mixer:Bal"; } + const uint8_t* applet_icon() { return PhzIcons::mixerBal; } void Start() { balance = 127; diff --git a/software/src/applets/MultiScale.h b/software/src/applets/MultiScale.h index 0f0dc4a01..9005fd423 100644 --- a/software/src/applets/MultiScale.h +++ b/software/src/applets/MultiScale.h @@ -28,13 +28,18 @@ class MultiScale : public HemisphereApplet { const char* applet_name() { return "MultiScale"; } + const uint8_t* applet_icon() { return PhzIcons::multiscale; } + void SetMask(uint16_t &mask) { + quant.Configure(OC::Scales::GetScale(OC::Scales::SCALE_SEMI), mask); + } void Start() { // init all scales no all notes for (uint8_t i = 0; i < MS_QUANT_SCALES_COUNT; i++) { scale_mask[i] = 0x0001; } - QuantizerConfigure(0, 5, scale_mask[0]); + quant.Init(); + SetMask(scale_mask[0]); } void Controller() { @@ -47,7 +52,7 @@ class MultiScale : public HemisphereApplet { } if (scale != current_scale) { current_scale = scale; - QuantizerConfigure(0, 5, scale_mask[current_scale]); + SetMask(scale_mask[current_scale]); ClockOut(1); // send clock at second output } @@ -63,7 +68,7 @@ class MultiScale : public HemisphereApplet { } if (continuous || EndOfADCLag(0)) { - int32_t quantized = Quantize(0, In(0), 0, 0); + int32_t quantized = quant.Process(In(0), 0, 0); Out(0, quantized); } } @@ -76,7 +81,7 @@ class MultiScale : public HemisphereApplet { void ToggleBit(const uint8_t bit) { scale_mask[scale_page] ^= (0x01 << bit); // togle bit at position if (scale_page == current_scale) { - QuantizerConfigure(0, 5, scale_mask[current_scale]); + SetMask(scale_mask[current_scale]); } } void OnButtonPress() { @@ -114,7 +119,7 @@ class MultiScale : public HemisphereApplet { scale_mask[1] = Unpack(data, PackLocation {12, 12}); scale_mask[2] = Unpack(data, PackLocation {24, 12}); scale_mask[3] = Unpack(data, PackLocation {36, 12}); - QuantizerConfigure(0, 5, scale_mask[0]); + SetMask(scale_mask[0]); } protected: @@ -134,6 +139,7 @@ class MultiScale : public HemisphereApplet { private: int cursor = 0; bool continuous = true; + braids::Quantizer quant; uint16_t scale_mask[MS_QUANT_SCALES_COUNT]; int8_t current_scale = 0; diff --git a/software/src/applets/Palimpsest.h b/software/src/applets/Palimpsest.h index af99aa34f..10a173177 100644 --- a/software/src/applets/Palimpsest.h +++ b/software/src/applets/Palimpsest.h @@ -26,6 +26,7 @@ class Palimpsest : public HemisphereApplet { const char* applet_name() { return "Palimpsest"; } + const uint8_t* applet_icon() { return PhzIcons::palimpsest; } void Start() { brush = 0; diff --git a/software/src/applets/Pigeons.h b/software/src/applets/Pigeons.h index b16bc72bd..57dff6743 100644 --- a/software/src/applets/Pigeons.h +++ b/software/src/applets/Pigeons.h @@ -41,6 +41,7 @@ class Pigeons : public HemisphereApplet { const char* applet_name() { return "Pigeons"; } + const uint8_t* applet_icon() { return PhzIcons::pigeons; } void Start() { ForEachChannel(ch) { diff --git a/software/src/applets/PolyDiv.h b/software/src/applets/PolyDiv.h index 667a22ede..47c0333a0 100644 --- a/software/src/applets/PolyDiv.h +++ b/software/src/applets/PolyDiv.h @@ -57,6 +57,7 @@ class PolyDiv : public HemisphereApplet { const char* applet_name() { return "PolyDiv"; } + const uint8_t* applet_icon() { return PhzIcons::polyDiv; } void Start() { } diff --git a/software/src/applets/ProbabilityDivider.h b/software/src/applets/ProbabilityDivider.h index acf8e07a7..aed36ce0c 100644 --- a/software/src/applets/ProbabilityDivider.h +++ b/software/src/applets/ProbabilityDivider.h @@ -35,6 +35,7 @@ class ProbabilityDivider : public HemisphereApplet { const char* applet_name() { return "ProbDiv"; } + const uint8_t* applet_icon() { return PhzIcons::probDiv; } void Start() { weight_1 = 0; diff --git a/software/src/applets/ProbabilityMelody.h b/software/src/applets/ProbabilityMelody.h index 4c4ca35ef..82885b255 100644 --- a/software/src/applets/ProbabilityMelody.h +++ b/software/src/applets/ProbabilityMelody.h @@ -36,6 +36,7 @@ class ProbabilityMelody : public HemisphereApplet { const char* applet_name() { return "ProbMeloD"; } + const uint8_t* applet_icon() { return PhzIcons::probMeloD; } void Start() { down = 1; diff --git a/software/src/applets/ResetClock.h b/software/src/applets/ResetClock.h index 91b62ee0a..5235bb2b1 100644 --- a/software/src/applets/ResetClock.h +++ b/software/src/applets/ResetClock.h @@ -27,6 +27,7 @@ class ResetClock : public HemisphereApplet { const char* applet_name() { return "ResetClk"; } + const uint8_t* applet_icon() { return PhzIcons::resetClk; } void Start() { cursor = 0; diff --git a/software/src/applets/RndWalk.h b/software/src/applets/RndWalk.h index 341d9a334..fc164d8d2 100644 --- a/software/src/applets/RndWalk.h +++ b/software/src/applets/RndWalk.h @@ -35,6 +35,7 @@ class RndWalk : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "RndWalk"; } + const uint8_t* applet_icon() { return PhzIcons::rndWalk; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/RunglBook.h b/software/src/applets/RunglBook.h index 2a4380e59..db19ea50c 100644 --- a/software/src/applets/RunglBook.h +++ b/software/src/applets/RunglBook.h @@ -26,6 +26,7 @@ class RunglBook : public HemisphereApplet { const char* applet_name() { return "RunglBook"; } + const uint8_t* applet_icon() { return PhzIcons::runglBook; } void Start() { threshold = (12 << 7) * 2; diff --git a/software/src/applets/ScaleDuet.h b/software/src/applets/ScaleDuet.h index 3d483cbb1..66d0ac7fa 100644 --- a/software/src/applets/ScaleDuet.h +++ b/software/src/applets/ScaleDuet.h @@ -26,13 +26,18 @@ class ScaleDuet : public HemisphereApplet { const char* applet_name() { return "ScaleDuet"; } + const uint8_t* applet_icon() { return PhzIcons::scaleDuet; } + void SetMask(uint16_t &mask) { + quant.Configure(OC::Scales::GetScale(OC::Scales::SCALE_SEMI), mask); + } void Start() { ForEachChannel(scale) { mask[scale] = 0xffff; } - QuantizerConfigure(0, OC::Scales::SCALE_SEMI, mask[0]); + quant.Init(); + SetMask(mask[0]); last_scale = 0; } @@ -52,11 +57,11 @@ class ScaleDuet : public HemisphereApplet { if (continuous || EndOfADCLag()) { uint8_t scale = Gate(1); if (scale != last_scale) { - QuantizerConfigure(0, OC::Scales::SCALE_SEMI, mask[scale]); + SetMask(mask[scale]); last_scale = scale; } - int new_pitch = Quantize(0, In(0), 0, 0); + int new_pitch = quant.Process(In(0), 0, 0); if (q_pitch != new_pitch) ClockOut(1); q_pitch = new_pitch; @@ -78,7 +83,7 @@ class ScaleDuet : public HemisphereApplet { // Toggle the mask bit at the cursor position mask[scale] ^= (0x01 << bit); if (scale == last_scale) - QuantizerConfigure(0, OC::Scales::SCALE_SEMI, mask[scale]); + SetMask(mask[scale]); } void OnEncoderMove(int direction) { @@ -99,7 +104,7 @@ class ScaleDuet : public HemisphereApplet { mask[1] = Unpack(data, PackLocation {12,12}); last_scale = 0; - QuantizerConfigure(0, OC::Scales::SCALE_SEMI, mask[last_scale]); + SetMask(mask[last_scale]); } protected: @@ -123,6 +128,8 @@ class ScaleDuet : public HemisphereApplet { int q_pitch; bool continuous = 1; + braids::Quantizer quant; + void DrawKeyboard() { // Border gfxFrame(0, 27, 63, 32); diff --git a/software/src/applets/Schmitt.h b/software/src/applets/Schmitt.h index 036588515..98ea50f93 100644 --- a/software/src/applets/Schmitt.h +++ b/software/src/applets/Schmitt.h @@ -26,6 +26,7 @@ class Schmitt : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "SchmittTr"; } + const uint8_t* applet_icon() { return PhzIcons::schmitt; } void Start() { low = 3200; // ~2.1V diff --git a/software/src/applets/Scope.h b/software/src/applets/Scope.h index 66571faed..2542c6742 100644 --- a/software/src/applets/Scope.h +++ b/software/src/applets/Scope.h @@ -35,6 +35,7 @@ class Scope : public HemisphereApplet { const char* applet_name() { return "Scope"; } + const uint8_t* applet_icon() { return PhzIcons::scope; } void Start() { last_bpm_tick = OC::CORE::ticks; diff --git a/software/src/applets/Seq32.h b/software/src/applets/Seq32.h index 6fac7a9fb..f06198c0b 100644 --- a/software/src/applets/Seq32.h +++ b/software/src/applets/Seq32.h @@ -56,6 +56,7 @@ class Seq32 : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "Seq32"; } + const uint8_t* applet_icon() { return PhzIcons::seq32; } void Start() { } diff --git a/software/src/applets/SeqPlay7.h b/software/src/applets/SeqPlay7.h index 6e51d8e85..52c2f5c84 100644 --- a/software/src/applets/SeqPlay7.h +++ b/software/src/applets/SeqPlay7.h @@ -77,6 +77,7 @@ class SeqPlay7 : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "SeqPlay7"; } + const uint8_t* applet_icon() { return PhzIcons::seqPlay7; } void Start() { seq_player[0].repeats = 1; diff --git a/software/src/applets/SequenceX.h b/software/src/applets/SequenceX.h index 104b887dc..8184f492e 100644 --- a/software/src/applets/SequenceX.h +++ b/software/src/applets/SequenceX.h @@ -30,8 +30,9 @@ class SequenceX : public HemisphereApplet { static constexpr int SEQX_MAX_VALUE = 30; const char* applet_name() { // Maximum 10 characters - return "SequenceX"; + return "Seq8"; } + const uint8_t* applet_icon() { return PhzIcons::sequenceX; } void Start() { Randomize(); diff --git a/software/src/applets/ShiftGate.h b/software/src/applets/ShiftGate.h index 4e5e4bf09..e10abae38 100644 --- a/software/src/applets/ShiftGate.h +++ b/software/src/applets/ShiftGate.h @@ -4,6 +4,7 @@ class ShiftGate : public HemisphereApplet { const char* applet_name() { return "ShiftGate"; } + const uint8_t* applet_icon() { return PhzIcons::shiftGate; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/ShiftReg.h b/software/src/applets/ShiftReg.h index 9234f4a33..1e9346c7a 100644 --- a/software/src/applets/ShiftReg.h +++ b/software/src/applets/ShiftReg.h @@ -37,6 +37,7 @@ static constexpr int MAX_LENGTH = 16; const char* applet_name() { return "ShiftReg"; } + const uint8_t* applet_icon() { return PhzIcons::DualTM; } void Start() { reg = random(0, 65535); diff --git a/software/src/applets/Shredder.h b/software/src/applets/Shredder.h index 64071bfa5..9c9444db2 100644 --- a/software/src/applets/Shredder.h +++ b/software/src/applets/Shredder.h @@ -29,6 +29,7 @@ class Shredder : public HemisphereApplet { const char* applet_name() { return "Shredder"; } + const uint8_t* applet_icon() { return PhzIcons::shredder; } void Start() { step = 0; @@ -99,6 +100,13 @@ class Shredder : public HemisphereApplet { } void OnButtonPress() { + if (cursor < 2) { + if (OC::CORE::ticks - click_tick < HS::HEMISPHERE_DOUBLE_CLICK_TIME) + Shred(cursor); + else + click_tick = OC::CORE::ticks; + } + if (cursor == 3) HS::QuantizerEdit(io_offset); else @@ -178,6 +186,7 @@ class Shredder : public HemisphereApplet { private: int cursor; + uint32_t click_tick = 0; // Sequencer state uint8_t step; // Current step number diff --git a/software/src/applets/Shuffle.h b/software/src/applets/Shuffle.h index 07bb8e9fc..b210908fb 100644 --- a/software/src/applets/Shuffle.h +++ b/software/src/applets/Shuffle.h @@ -26,6 +26,7 @@ class Shuffle : public HemisphereApplet { const char* applet_name() { return "Shuffle"; } + const uint8_t* applet_icon() { return PhzIcons::shuffle; } void Start() { delay[0] = 0; diff --git a/software/src/applets/Slew.h b/software/src/applets/Slew.h index 33d24c51a..b42e37a84 100644 --- a/software/src/applets/Slew.h +++ b/software/src/applets/Slew.h @@ -27,6 +27,7 @@ class Slew : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "Slew"; } + const uint8_t* applet_icon() { return PhzIcons::slew; } void Start() { ForEachChannel(ch) signal[ch] = 0; diff --git a/software/src/applets/Squanch.h b/software/src/applets/Squanch.h index b13e8c5b5..eb5c19454 100644 --- a/software/src/applets/Squanch.h +++ b/software/src/applets/Squanch.h @@ -32,6 +32,7 @@ class Squanch : public HemisphereApplet { const char* applet_name() { return "Squanch"; } + const uint8_t* applet_icon() { return PhzIcons::squanch; } void Start() { } diff --git a/software/src/applets/Stairs.h b/software/src/applets/Stairs.h index 85b3dcc4c..ce4c105c5 100644 --- a/software/src/applets/Stairs.h +++ b/software/src/applets/Stairs.h @@ -41,6 +41,7 @@ class Stairs : public HemisphereApplet { const char* applet_name() { return "Stairs"; } + const uint8_t* applet_icon() { return PhzIcons::stairs; } void Start() { steps = 1; diff --git a/software/src/applets/Strum.h b/software/src/applets/Strum.h index b26bfcd50..de75d5ecc 100644 --- a/software/src/applets/Strum.h +++ b/software/src/applets/Strum.h @@ -21,6 +21,7 @@ class Strum : public HemisphereApplet { public: const char *applet_name() { return "Strum"; } + const uint8_t *applet_icon() { return PhzIcons::strum; } void Start() { qselect = io_offset; diff --git a/software/src/applets/Switch.h b/software/src/applets/Switch.h index 025089108..f2631607b 100644 --- a/software/src/applets/Switch.h +++ b/software/src/applets/Switch.h @@ -24,6 +24,7 @@ class Switch : public HemisphereApplet { const char* applet_name() { return "Switch"; } + const uint8_t* applet_icon() { return PhzIcons::switchApp; } void Start() { active[0] = 1; diff --git a/software/src/applets/SwitchSeq.h b/software/src/applets/SwitchSeq.h index 3909fdeba..9ca90c7b9 100644 --- a/software/src/applets/SwitchSeq.h +++ b/software/src/applets/SwitchSeq.h @@ -45,10 +45,8 @@ class SwitchSeq : public HemisphereApplet { public: - const char *applet_name() - { - return "SwitchSeq"; - } + const char *applet_name() { return "SwitchSeq"; } + const uint8_t* applet_icon() { return PhzIcons::switchSeq; } void Start() { diff --git a/software/src/applets/TB3PO.h b/software/src/applets/TB3PO.h index 66f081448..d070fb531 100644 --- a/software/src/applets/TB3PO.h +++ b/software/src/applets/TB3PO.h @@ -45,6 +45,7 @@ class TB_3PO: public HemisphereApplet { const char * applet_name() { // Maximum 10 characters return "TB-3PO"; } + const uint8_t* applet_icon() { return PhzIcons::tb3P0; } void Start() { manual_reset_flag = 0; @@ -59,7 +60,7 @@ class TB_3PO: public HemisphereApplet { num_steps = 16; - gate_off_clock = 0; + gate_off_tick = 0; cycle_time = 0; curr_gate_cv = 0; @@ -82,7 +83,7 @@ class TB_3PO: public HemisphereApplet { } void Controller() { - const int this_tick = OC::CORE::ticks; + const uint32_t this_tick = OC::CORE::ticks; if (Clock(1) || manual_reset_flag) { Reset(); @@ -129,16 +130,16 @@ class TB_3PO: public HemisphereApplet { if (step_is_gated(step) || step_is_slid(step_pv)) { curr_gate_cv = step_is_accent(step) ? HEMISPHERE_MAX_CV : HEMISPHERE_3V_CV; - int gate_time = (cycle_time / 2); // multiplier of 2 - gate_off_clock = this_tick + gate_time; + uint32_t gate_time = (cycle_time / 2); // multiplier of 2 + gate_off_tick = this_tick + gate_time; } curr_step_semitone = get_semitone_for_step(step); } - if (curr_gate_cv > 0 && gate_off_clock > 0 && this_tick >= gate_off_clock) { - gate_off_clock = 0; + if (curr_gate_cv > 0 && gate_off_tick > 0 && this_tick >= gate_off_tick) { + gate_off_tick = 0; if (!step_is_slid(step)) { curr_gate_cv = 0; @@ -249,6 +250,8 @@ class TB_3PO: public HemisphereApplet { //Pack(data, PackLocation { 0, 8 }, GetScale(0)); //Pack(data, PackLocation { 8, 4 }, GetRootNote(0)); + Pack(data, PackLocation { 0, 1 }, lock_seed); + Pack(data, PackLocation { 12, 4 }, density_encoder); Pack(data, PackLocation { 16, 16 }, seed); // old octave setting was here: @@ -261,6 +264,7 @@ class TB_3PO: public HemisphereApplet { } void OnDataReceive(uint64_t data) { + lock_seed = Unpack(data, PackLocation { 0, 1 }); density_encoder = Unpack(data, PackLocation { 12, 4 }); seed = Unpack(data, PackLocation { 16, 16 }); @@ -341,8 +345,8 @@ class TB_3PO: public HemisphereApplet { uint8_t current_pattern_scale_size; // Track what size scale was used to render the current pattern (for change detection) // For gate timing as ~32nd notes at tempo, detect clock rate like a clock multiplier - int gate_off_clock; // Scheduled cycle at which the gate should be turned off (when applicable) - int cycle_time; // Cycle time between the last two clock inputs + uint32_t gate_off_tick; // Scheduled cycle at which the gate should be turned off (when applicable) + uint32_t cycle_time; // Cycle time between the last two clock inputs // CV output values int32_t curr_gate_cv = 0; diff --git a/software/src/applets/TLNeuron.h b/software/src/applets/TLNeuron.h index bc533a399..394678f6c 100644 --- a/software/src/applets/TLNeuron.h +++ b/software/src/applets/TLNeuron.h @@ -27,6 +27,7 @@ class TLNeuron : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "TL Neuron"; } + const uint8_t* applet_icon() { return PhzIcons::thresholdLogicNeuron; } void Start() { selected = 0; diff --git a/software/src/applets/Trending.h b/software/src/applets/Trending.h index 635364654..816588831 100644 --- a/software/src/applets/Trending.h +++ b/software/src/applets/Trending.h @@ -34,6 +34,7 @@ class Trending : public HemisphereApplet { const char* applet_name() { return "Trending"; } + const uint8_t* applet_icon() { return PhzIcons::trending; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/TrigSeq.h b/software/src/applets/TrigSeq.h index 1efb10b2f..9abeabbfb 100644 --- a/software/src/applets/TrigSeq.h +++ b/software/src/applets/TrigSeq.h @@ -22,8 +22,9 @@ class TrigSeq : public HemisphereApplet { public: const char* applet_name() { // Maximum 10 characters - return "TrigSeq"; + return "Trig8x2"; } + const uint8_t* applet_icon() { return PhzIcons::trigseq; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/TrigSeq16.h b/software/src/applets/TrigSeq16.h index 9be57ea4f..77a2d3f0b 100644 --- a/software/src/applets/TrigSeq16.h +++ b/software/src/applets/TrigSeq16.h @@ -22,8 +22,9 @@ class TrigSeq16 : public HemisphereApplet { public: const char* applet_name() { // Maximum 10 characters - return "TrigSeq16"; + return "Trig16"; } + const uint8_t* applet_icon() { return PhzIcons::trigseq16; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/Tuner.h b/software/src/applets/Tuner.h index 2f61e9a63..c94f582a8 100644 --- a/software/src/applets/Tuner.h +++ b/software/src/applets/Tuner.h @@ -32,21 +32,15 @@ #include "../src/drivers/FreqMeasure/OC_FreqMeasure.h" #if defined(ARDUINO_TEENSY41) + #define TUNER_ENABLED 1 // TR2 on left, TR4 on right #define TUNER_PIN (hemisphere == 0 ? 1 : 22) #elif defined(ARDUINO_TEENSY40) -# ifdef FLIP_180 -#define TUNER_ENABLED (hemisphere == 1) -# else -#define TUNER_ENABLED (hemisphere == 0) -# endif - -#elif defined(FLIP_180) -#define TUNER_ENABLED (hemisphere == 0) +#define TUNER_ENABLED (hemisphere == OC::calibration_data.flipcontrols()) #else -#define TUNER_ENABLED (hemisphere == 1) +#define TUNER_ENABLED (hemisphere == 1 - OC::calibration_data.flipcontrols()) #endif static constexpr double HEM_TUNER_AaboveMidCtoC0 = 0.03716272234383494188492; @@ -57,6 +51,7 @@ class Tuner : public HemisphereApplet { const char* applet_name() { return "Tuner"; } + const uint8_t* applet_icon() { return PhzIcons::tuner; } void Start() { A4_Hz = 440; @@ -70,8 +65,10 @@ class Tuner : public HemisphereApplet { AllowRestart(); } void Unload() { + if (TUNER_ENABLED) { freq_measure.end(); OC::DigitalInputs::reInit(); + } } void Controller() { @@ -123,13 +120,17 @@ class Tuner : public HemisphereApplet { help[HELP_OUT2] = ""; if (TUNER_ENABLED) { // "-------" <-- Label size guide -#if (defined(FLIP_180) && !defined(ARDUINO_TEENSY40)) || (defined(ARDUINO_TEENSY40) && !defined(FLIP_180)) - help[HELP_DIGITAL1] = "Input"; - help[HELP_DIGITAL2] = ""; +#ifdef ARDUINO_TEENSY40 + if (!OC::calibration_data.flipcontrols()) { #else - help[HELP_DIGITAL1] = ""; - help[HELP_DIGITAL2] = "Input"; + if (OC::calibration_data.flipcontrols()) { #endif + help[HELP_DIGITAL1] = "Input"; + help[HELP_DIGITAL2] = ""; + } else { + help[HELP_DIGITAL1] = ""; + help[HELP_DIGITAL2] = "Input"; + } help[HELP_EXTRA1] = "Enc: Adjust A4 Hz,"; help[HELP_EXTRA2] = " Push to Reset"; } else { @@ -207,22 +208,24 @@ class Tuner : public HemisphereApplet { gfxPrint(" Hz"); gfxCursor(25, 23, 36); } - -#if (defined(FLIP_180) && !defined(ARDUINO_TEENSY40)) || (defined(ARDUINO_TEENSY40) && !defined(FLIP_180)) + void DrawWarning() { +#ifdef ARDUINO_TEENSY40 + if (!OC::calibration_data.flipcontrols()) { +#else + if (OC::calibration_data.flipcontrols()) { +#endif gfxPrint(1, 15, "Tuner goes"); gfxPrint(1, 25, "in left"); gfxPrint(1, 35, "hemisphere"); gfxPrint(1, 45, "<--"); - } -#else - void DrawWarning() { + } else { gfxPrint(1, 15, "Tuner goes"); gfxPrint(1, 25, "in right"); gfxPrint(1, 35, "hemisphere"); gfxPrint(1, 45, " -->"); + } } -#endif float get_frequency() {return frequency_;} diff --git a/software/src/applets/VectorEG.h b/software/src/applets/VectorEG.h index 9c27c01c4..333899d2b 100644 --- a/software/src/applets/VectorEG.h +++ b/software/src/applets/VectorEG.h @@ -30,6 +30,7 @@ class VectorEG : public HemisphereApplet { const char* applet_name() { return "VectorEG"; } + const uint8_t* applet_icon() { return PhzIcons::vectorEG; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/VectorLFO.h b/software/src/applets/VectorLFO.h index 647ffe541..e199b2acb 100644 --- a/software/src/applets/VectorLFO.h +++ b/software/src/applets/VectorLFO.h @@ -29,6 +29,7 @@ class VectorLFO : public HemisphereApplet { const char* applet_name() { return "VectorLFO"; } + const uint8_t* applet_icon() { return PhzIcons::vectorLFO; } static constexpr int min_freq = 8; static constexpr int max_freq = 100000; diff --git a/software/src/applets/VectorMod.h b/software/src/applets/VectorMod.h index 219ee4bcc..3550fac9a 100644 --- a/software/src/applets/VectorMod.h +++ b/software/src/applets/VectorMod.h @@ -27,6 +27,7 @@ class VectorMod : public HemisphereApplet { const char* applet_name() { return "VectorMod"; } + const uint8_t* applet_icon() { return PhzIcons::vectorMod; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/VectorMorph.h b/software/src/applets/VectorMorph.h index e37fa1ec5..5c5685965 100644 --- a/software/src/applets/VectorMorph.h +++ b/software/src/applets/VectorMorph.h @@ -27,6 +27,7 @@ class VectorMorph : public HemisphereApplet { const char* applet_name() { return "VectMorph"; } + const uint8_t* applet_icon() { return PhzIcons::vectorMorph; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/Voltage.h b/software/src/applets/Voltage.h index 5c670ff24..14a77c1ad 100644 --- a/software/src/applets/Voltage.h +++ b/software/src/applets/Voltage.h @@ -26,6 +26,7 @@ class Voltage : public HemisphereApplet { const char* applet_name() { return "Voltage"; } + const uint8_t* applet_icon() { return PhzIcons::voltage; } void Start() { voltage[0] = (5 * (12 << 7)) / VOLTAGE_INCREMENTS; // 5V diff --git a/software/src/applets/hMIDIIn.h b/software/src/applets/hMIDIIn.h index 64024f5bd..c0b14f8c1 100644 --- a/software/src/applets/hMIDIIn.h +++ b/software/src/applets/hMIDIIn.h @@ -39,6 +39,7 @@ class hMIDIIn : public HemisphereApplet { const char* applet_name() { return "MIDIIn"; } + const uint8_t* applet_icon() { return PhzIcons::midiIn; } void Start() { ForEachChannel(ch) diff --git a/software/src/applets/hMIDIOut.h b/software/src/applets/hMIDIOut.h index f18597a38..ab8eafd47 100644 --- a/software/src/applets/hMIDIOut.h +++ b/software/src/applets/hMIDIOut.h @@ -39,6 +39,7 @@ class hMIDIOut : public HemisphereApplet { const char* applet_name() { // Maximum 10 characters return "MIDIOut"; } + const uint8_t* applet_icon() { return PhzIcons::midiOut; } void Start() { channel = 0; // Default channel 1 diff --git a/software/src/src/drivers/SH1106_128x64_driver.cpp b/software/src/src/drivers/SH1106_128x64_driver.cpp index 23ffdf230..41a5d197d 100644 --- a/software/src/src/drivers/SH1106_128x64_driver.cpp +++ b/software/src/src/drivers/SH1106_128x64_driver.cpp @@ -31,6 +31,7 @@ #if defined(__IMXRT1062__) #include #endif +#include "../../util/util_SPIFIFO.h" // NOTE: Don't disable DMA unless you absolutely know what you're doing. It will hurt you. #if defined(__MK20DX256__) @@ -52,11 +53,12 @@ static int sendpage_count; static const uint32_t *sendpage_src; #endif +static uint8_t disp_offset = 0; static uint8_t SH1106_data_start_seq[] = { // u8g_dev_ssd1306_128x64_data_start 0x10, /* set upper 4 bit of the col adr to 0 */ - 0x02, /* set lower 4 bit of the col adr to 0 */ - 0x00 /* 0xb0 | page */ + 0x00, /* set lower 4 bit of the col adr to 0 */ + 0x00 /* 0xb0 | page */ }; static uint8_t SH1106_init_seq[] = { @@ -68,22 +70,23 @@ static uint8_t SH1106_init_seq[] = { 0x0d3, 0x000, /* set display offset */ 0x040, /* start line */ - 0x08d, 0x014, /* [2] charge pump setting (p62): 0x014 enable, 0x010 disable */ + // Charge pump setting - only for SSD1306 + //0x8d, 0x14, /* [2] charge pump setting (p62): 0x014 enable, 0x010 disable */ - 0x020, 0x000, /* 2012-05-27: page addressing mode */ // PLD: Seems to work in conjuction with lower 4 bits of column data? - #ifdef FLIP_180 + 0x020, 0x002, /* Memory addressing mode: 0x00 horiz, 0x01 vert, 0x02 page */ +#ifdef FLIP_180 0x0a0, /* segment remap a0/a1*/ 0x0c0, /* c0: scan dir normal, c8: reverse */ - #else +#else 0x0a1, /* segment remap a0/a1*/ 0x0c8, /* c0: scan dir normal, c8: reverse */ - #endif +#endif 0x0da, 0x012, /* com pin HW config, sequential com pin config (bit 4), disable left/right remap (bit 5) */ 0x081, 0x0cf, /* [2] set contrast control */ 0x0d9, 0x0f1, /* [2] pre-charge period 0x022/f1*/ 0x0db, 0x040, /* vcomh deselect level */ - - 0x02e, /* 2012-05-27: Deactivate scroll */ + + 0x02e, /* 2012-05-27: Deactivate scroll */ 0x0a4, /* output ram to display */ #ifdef INVERT_DISPLAY 0x0a7, /* inverted display mode */ @@ -93,16 +96,49 @@ static uint8_t SH1106_init_seq[] = { //0x0af, /* display on */ }; +// indexes to above sequence +static constexpr int CONTRAST_VALUE = 15; +static constexpr int FLIP_CMD_A = 10; +static constexpr int FLIP_CMD_B = 11; + static uint8_t SH1106_display_on_seq[] = { 0xaf }; +/*static*/ +void SH1106_128x64_Driver::Reinit() { + // u8g_dev_ssd1306_128x64_adafruit3_init_seq + digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0), +#if defined(__MK20DX256__) + ChangeSpeed(SPICLOCK_30MHz); +#endif + digitalWriteFast(OLED_DC, LOW); // U8G_ESC_ADR(0), /* instruction mode */ + + digitalWriteFast(OLED_RST, LOW); // U8G_ESC_RST(1), /* do reset low pulse with (1*16)+2 milliseconds */ + delay(20); + digitalWriteFast(OLED_RST, HIGH); + delay(20); +#if defined(__MK20DX256__) + ChangeSpeed(SPI_CLOCK_8MHz); +#endif + digitalWriteFast(OLED_CS, OLED_CS_ACTIVE); // U8G_ESC_CS(1), /* enable chip */ + + // assumes SPI bus is already initialized !! + SPI_send(SH1106_init_seq, sizeof(SH1106_init_seq)); + + digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0), /* disable chip */ + delayMicroseconds(1); +#if defined(__MK20DX256__) + ChangeSpeed(SPICLOCK_30MHz); +#endif +} + /*static*/ void SH1106_128x64_Driver::Init() { OC::pinMode(OLED_CS, OUTPUT); OC::pinMode(OLED_RST, OUTPUT); OC::pinMode(OLED_DC, OUTPUT); - //SPI_init(); + //SPI_init(); // u8g_teensy::U8G_COM_MSG_INIT digitalWriteFast(OLED_RST, HIGH); @@ -112,30 +148,35 @@ void SH1106_128x64_Driver::Init() { digitalWriteFast(OLED_RST, HIGH); // u8g_dev_ssd1306_128x64_adafruit3_init_seq - digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0), /* disable chip */ + digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0), +#if defined(__MK20DX256__) + ChangeSpeed(SPICLOCK_30MHz); +#endif digitalWriteFast(OLED_DC, LOW); // U8G_ESC_ADR(0), /* instruction mode */ digitalWriteFast(OLED_RST, LOW); // U8G_ESC_RST(1), /* do reset low pulse with (1*16)+2 milliseconds */ delay(20); digitalWriteFast(OLED_RST, HIGH); delay(20); - +#if defined(__MK20DX256__) + ChangeSpeed(SPI_CLOCK_8MHz); +#endif digitalWriteFast(OLED_CS, OLED_CS_ACTIVE); // U8G_ESC_CS(1), /* enable chip */ - // assumes OC::DAC:Init already initialized SPI, called SPI.begin() if Teensy 4.x - #if defined(__IMXRT1062__) && defined(ARDUINO_TEENSY41) - if (OLED_Uses_SPI1) SPI1.begin(); - #endif + // assumes SPI bus is already initialized !! SPI_send(SH1106_init_seq, sizeof(SH1106_init_seq)); digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0), /* disable chip */ delayMicroseconds(1); +#if defined(__MK20DX256__) + ChangeSpeed(SPICLOCK_30MHz); +#endif #if defined(__MK20DX256__) #ifdef DMA_PAGE_TRANSFER page_dma.destination((volatile uint8_t&)SPI0_PUSHR); page_dma.transferSize(1); - page_dma.transferCount(kPageSize); + page_dma.transferCount(kSubpageSize); page_dma.disableOnCompletion(); page_dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPI0_TX); page_dma.disable(); @@ -144,7 +185,6 @@ void SH1106_128x64_Driver::Init() { #elif defined(__IMXRT1062__) #if defined(ARDUINO_TEENSY41) if (OLED_Uses_SPI1) { - SPI1.begin(); LPSPI3_IER = 0; LPSPI3_SR = 0x3F00; // clear any prior pending interrupt flags LPSPI3_FCR = LPSPI_FCR_RXWATER(0) | LPSPI_FCR_TXWATER(3); @@ -191,6 +231,7 @@ void SH1106_128x64_Driver::Flush() { page_dma_active = false; digitalWriteFast(OLED_CS, OLED_CS_INACTIVE); // U8G_ESC_CS(0) + ChangeSpeed(SPICLOCK_30MHz); page_dma.clearComplete(); page_dma.disable(); // DmaSpi.h::post_finishCurrentTransfer_impl @@ -212,6 +253,9 @@ void SH1106_128x64_Driver::Clear() { SH1106_data_start_seq[2] = 0xb0 | 0; digitalWriteFast(OLED_DC, LOW); +#if defined(__MK20DX256__) + ChangeSpeed(SPI_CLOCK_8MHz); +#endif digitalWriteFast(OLED_CS, OLED_CS_ACTIVE); SPI_send(SH1106_data_start_seq, sizeof(SH1106_data_start_seq)); digitalWriteFast(OLED_DC, HIGH); @@ -229,9 +273,15 @@ void SH1106_128x64_Driver::Clear() { #if defined(__MK20DX256__) /*static*/ -void SH1106_128x64_Driver::SendPage(uint_fast8_t index, const uint8_t *data) { - SH1106_data_start_seq[2] = 0xb0 | index; +void SH1106_128x64_Driver::SendPage(uint_fast8_t index, uint_fast8_t subpage, const uint8_t *data) { + uint8_t startCol = subpage * kSubpageSize; + const uint8_t* startData = data + startCol; + startCol += disp_offset; + SH1106_data_start_seq[0] = 0x10 | (startCol >> 4); + SH1106_data_start_seq[1] = 0x00 | (startCol & 0x0F); + SH1106_data_start_seq[2] = 0xb0 | index; + ChangeSpeed(SPI_CLOCK_8MHz); digitalWriteFast(OLED_DC, LOW); // U8G_ESC_ADR(0), /* instruction mode */ digitalWriteFast(OLED_CS, OLED_CS_ACTIVE); // U8G_ESC_CS(1), /* enable chip */ SPI_send(SH1106_data_start_seq, sizeof(SH1106_data_start_seq)); // u8g_WriteEscSeqP(u8g, dev, u8g_dev_ssd1306_128x64_data_start); @@ -242,7 +292,7 @@ void SH1106_128x64_Driver::SendPage(uint_fast8_t index, const uint8_t *data) { SPI0_SR = 0xFF0F0000; SPI0_RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS; - page_dma.sourceBuffer(data, kPageSize); + page_dma.sourceBuffer(startData, kSubpageSize); page_dma.enable(); // go page_dma_active = true; #else // not DMA_PAGE_TRANSFER @@ -253,11 +303,17 @@ void SH1106_128x64_Driver::SendPage(uint_fast8_t index, const uint8_t *data) { #elif defined(__IMXRT1062__) /*static*/ -void SH1106_128x64_Driver::SendPage(uint_fast8_t index, const uint8_t *data) { +void SH1106_128x64_Driver::SendPage(uint_fast8_t index, uint_fast8_t subpage, const uint8_t *data) { + uint8_t startCol = subpage * kSubpageSize; + const uint8_t* startData = data + startCol; + startCol += disp_offset; + + SH1106_data_start_seq[0] = 0x10 | (startCol >> 4); + SH1106_data_start_seq[1] = 0x00 | (startCol & 0x0F); SH1106_data_start_seq[2] = 0xb0 | index; sendpage_state = 1; - sendpage_src = (const uint32_t *)data; // frame buffer is 32 bit aligned - sendpage_count = kPageSize >> 2; // number of 32 bit words to write into FIFO + sendpage_src = (const uint32_t *)startData; // frame buffer is 32 bit aligned + sendpage_count = kSubpageSize >> 2; // number of 32 bit words to write into FIFO #if defined(ARDUINO_TEENSY41) if (OLED_Uses_SPI1) { // DAC does not use SPI1, so we must forcibly trigger first interrupt @@ -297,7 +353,7 @@ static void spi_sendpage_isr() { digitalWriteFast(OLED_DC, HIGH); lpspi->CR |= LPSPI_CR_RRF | LPSPI_CR_RTF; // clear FIFO lpspi->IER = LPSPI_IER_TDIE; // run spi_sendpage_isr() when FIFO wants data - const size_t nbits = SH1106_128x64_Driver::kPageSize * 8; + const size_t nbits = SH1106_128x64_Driver::kSubpageSize * 8; lpspi->TCR = (lpspi->TCR & 0xF8000000) | LPSPI_TCR_FRAMESZ(nbits-1) | LPSPI_TCR_PCS(3) | LPSPI_TCR_RXMSK | LPSPI_TCR_BYSW; sendpage_state = 3; @@ -383,13 +439,13 @@ void SH1106_128x64_Driver::SPI_send(void *bufr, size_t n) { void SH1106_128x64_Driver::SPI_send(void *bufr, size_t n) { #if defined(ARDUINO_TEENSY41) if (OLED_Uses_SPI1) { - SPI1.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0)); + SPI1.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); SPI1.transfer(bufr, NULL, n); SPI1.endTransaction(); return; } #endif - SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0)); + SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); LPSPI4_TCR |= LPSPI_TCR_PCS(3); // do not interfere with DAC's CS pin SPI.transfer(bufr, NULL, n); SPI.endTransaction(); @@ -398,5 +454,27 @@ void SH1106_128x64_Driver::SPI_send(void *bufr, size_t n) { /*static*/ void SH1106_128x64_Driver::AdjustOffset(uint8_t offset) { - SH1106_data_start_seq[1] = offset; // lower 4 bits of col adr + disp_offset = offset; } + +/*static*/ +void SH1106_128x64_Driver::SetFlipMode(bool flip180) { + // TODO: swap bytes for flip screen + SH1106_init_seq[FLIP_CMD_A] = flip180 ? 0xa0 : 0xa1; + SH1106_init_seq[FLIP_CMD_B] = flip180 ? 0xc0 : 0xc8; +} +/*static*/ +void SH1106_128x64_Driver::SetContrast(uint8_t contrast) { + SH1106_init_seq[CONTRAST_VALUE] = contrast; +} + +#if defined(__MK20DX256__) +/*static*/ +void SH1106_128x64_Driver::ChangeSpeed(uint32_t speed) { + uint32_t ctar = speed; + ctar = speed; + ctar |= (ctar & 0x0F) << 12; + KINETISK_SPI0.CTAR0 = ctar | SPI_CTAR_FMSZ(7); + KINETISK_SPI0.CTAR1 = ctar | SPI_CTAR_FMSZ(15); +} +#endif diff --git a/software/src/src/drivers/SH1106_128x64_driver.h b/software/src/src/drivers/SH1106_128x64_driver.h index e8cab210c..7c966ea44 100644 --- a/software/src/src/drivers/SH1106_128x64_driver.h +++ b/software/src/src/drivers/SH1106_128x64_driver.h @@ -29,19 +29,25 @@ struct SH1106_128x64_Driver { static constexpr size_t kFrameSize = 128 * 64 / 8; static constexpr size_t kNumPages = 8; + static constexpr size_t kNumSubpages = 4; static constexpr size_t kPageSize = kFrameSize / kNumPages; + static constexpr size_t kSubpageSize = kPageSize / 4; static constexpr uint8_t kDefaultOffset = 2; static void Init(); + static void Reinit(); static void Clear(); static void Flush(); - static void SendPage(uint_fast8_t index, const uint8_t *data); + static void SendPage(uint_fast8_t index, uint_fast8_t subpage, const uint8_t *data); static void SPI_send(void *bufr, size_t n); // SH1106 ram is 132x64, so it needs an offset to center data in display. // However at least one display (mine) uses offset 0 so it's minimally // configurable static void AdjustOffset(uint8_t offset); + static void ChangeSpeed(uint32_t speed); + static void SetFlipMode(bool flip180); + static void SetContrast(uint8_t contrast); }; #endif // SH1106_128X64_DRIVER_H_ diff --git a/software/src/src/drivers/display.cpp b/software/src/src/drivers/display.cpp index a2d9f7096..eb628af4b 100644 --- a/software/src/src/drivers/display.cpp +++ b/software/src/src/drivers/display.cpp @@ -38,5 +38,11 @@ void Init() { void AdjustOffset(uint8_t offset) { SH1106_128x64_Driver::AdjustOffset(offset); } +void SetFlipMode(bool flip180) { + SH1106_128x64_Driver::SetFlipMode(flip180); +} +void SetContrast(uint8_t contrast) { + SH1106_128x64_Driver::SetContrast(contrast); +} }; diff --git a/software/src/src/drivers/display.h b/software/src/src/drivers/display.h index 2506b3517..626614853 100644 --- a/software/src/src/drivers/display.h +++ b/software/src/src/drivers/display.h @@ -36,6 +36,8 @@ extern PagedDisplayDriver driver; void Init(); void AdjustOffset(uint8_t offset); +void SetFlipMode(bool flip180); +void SetContrast(uint8_t contrast); static inline void Flush() __attribute__((always_inline)); static inline void Flush() { diff --git a/software/src/src/drivers/page_display_driver.h b/software/src/src/drivers/page_display_driver.h index ff950a75b..cf6842b33 100644 --- a/software/src/src/drivers/page_display_driver.h +++ b/software/src/src/drivers/page_display_driver.h @@ -13,26 +13,37 @@ class PagedDisplayDriver { PagedDisplayDriver() { } + void Reinit() { + display_driver::Reinit(); + } + void Init() { display_driver::Init(); current_page_index_ = 0; current_page_data_ = NULL; + current_subpage_index_ = 0; } void Begin(const uint8_t *frame) { current_page_data_ = frame; current_page_index_ = 0; + current_subpage_index_ = 0; } void Update() { uint_fast8_t page = current_page_index_; + uint_fast8_t subpage = current_subpage_index_; if (page < display_driver::kNumPages) { const uint8_t *data = current_page_data_; - display_driver::SendPage(page, data); - current_page_index_ = page + 1; - current_page_data_ = data + display_driver::kPageSize; + display_driver::SendPage(page, subpage, data); + current_subpage_index_ = subpage + 1; + if (current_subpage_index_ >= display_driver::kNumSubpages) { + current_subpage_index_ = 0; + current_page_index_ += 1; + current_page_data_ = data + display_driver::kPageSize; + } } } @@ -42,6 +53,7 @@ class PagedDisplayDriver { return false; } else { current_page_index_ = 0; + current_subpage_index_ = 0; current_page_data_ = NULL; return true; } @@ -53,6 +65,7 @@ class PagedDisplayDriver { private: uint_fast8_t current_page_index_; + uint_fast8_t current_subpage_index_; const uint8_t *current_page_data_; DISALLOW_COPY_AND_ASSIGN(PagedDisplayDriver); diff --git a/software/src/util/pewpew.xcf b/software/src/util/pewpew.xcf new file mode 100644 index 000000000..c225949ed Binary files /dev/null and b/software/src/util/pewpew.xcf differ diff --git a/software/src/util/pewpewsplash.h b/software/src/util/pewpewsplash.h new file mode 100644 index 000000000..31e7a56cf --- /dev/null +++ b/software/src/util/pewpewsplash.h @@ -0,0 +1,89 @@ +#define pewpew_width 8 +#define pewpew_height 1024 +static unsigned char pewpew_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, + 0xf0, 0xf8, 0xf8, 0xfc, 0xfc, 0xfe, 0xfe, 0x3f, 0x0f, 0x07, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x80, 0xc0, 0xe0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xfe, 0xff, + 0xdf, 0xc3, 0xc0, 0xc0, 0xc0, 0xc0, 0x40, 0x00, 0x00, 0x00, 0xc0, 0xe0, + 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xef, 0xe1, 0xe0, 0x60, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0xc0, 0xe0, 0x00, 0x00, + 0x00, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0xf0, 0xf8, 0x00, 0x00, 0x00, 0xf0, + 0x3c, 0x1c, 0x3c, 0xf0, 0x00, 0xfe, 0x00, 0xfc, 0xde, 0xc0, 0xfe, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x3e, 0x3f, + 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xbf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x3f, 0x3f, 0x1f, 0x0f, 0x07, + 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0xc3, 0xff, 0xff, 0x7f, 0x3f, 0x3f, 0x1f, 0xcf, 0xc7, 0x03, + 0x01, 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0, 0xd8, 0x1e, 0x0f, + 0x07, 0xc3, 0xf1, 0x78, 0x1c, 0x1c, 0x7c, 0xf8, 0xe0, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x80, 0xc0, 0xff, 0x1f, 0x00, 0x00, 0x7f, 0xfc, 0x9c, 0x9e, + 0xfe, 0x3e, 0x07, 0x00, 0xff, 0x7f, 0x38, 0x38, 0xbc, 0xff, 0x03, 0x00, + 0x80, 0x80, 0xff, 0xc7, 0xc0, 0x00, 0xff, 0xff, 0x00, 0x00, 0x78, 0x7f, + 0x70, 0x7f, 0x3c, 0x07, 0x00, 0x1f, 0x1f, 0x07, 0x00, 0x00, 0xc0, 0xf0, + 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe0, + 0xf8, 0xfc, 0x7f, 0x3f, 0x3f, 0xff, 0xff, 0xef, 0xf7, 0xf7, 0xf3, 0xf1, + 0xf0, 0xf8, 0xf8, 0xf8, 0x78, 0x38, 0x00, 0x38, 0xfc, 0xfc, 0xc0, 0xe0, + 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xfe, 0x0f, 0x07, 0x00, 0x07, 0x7f, 0xff, + 0xd8, 0x00, 0x80, 0xc0, 0xfc, 0xff, 0x1f, 0x00, 0x00, 0x02, 0x0f, 0x07, + 0x03, 0x00, 0x00, 0x81, 0xff, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x03, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0x07, 0x00, 0xc0, 0xc0, 0xc3, 0xe3, 0xe3, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x39, 0x3f, 0x07, 0xc0, 0xc0, 0xdc, + 0xdf, 0x00, 0x00, 0x00, 0x07, 0x07, 0xf0, 0xfa, 0xfb, 0xfb, 0xff, 0xff, + 0xfd, 0x7c, 0xff, 0xfe, 0xfe, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xe3, 0xe7, 0x63, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x01, 0x81, 0xe0, 0xf0, 0x7c, 0x0f, 0x07, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0x11, 0x81, 0xf9, 0xfd, 0x1f, + 0x03, 0x01, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x77, 0x1f, 0x0f, 0x03, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0x08, 0x1e, 0x7f, 0x7c, 0x78, 0x38, 0x3e, 0x0f, + 0x03, 0x01, 0xc0, 0xc8, 0xcf, 0x07, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, + 0x00, 0x08, 0x7b, 0x79, 0x61, 0x01, 0x01, 0x00, 0x06, 0x3f, 0xff, 0xff, + 0xfe, 0xe0, 0x80, 0x80, 0x00, 0xc7, 0xff, 0xff, 0x1f, 0x3f, 0xf0, 0xe0, + 0xc0, 0x00, 0x00, 0x01, 0xc1, 0xc0, 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xff, + 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xfc, 0xfc, 0x7c, 0x7e, + 0x3e, 0x3e, 0x3f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf8, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x80, 0xe0, 0xf0, 0xfc, 0xfe, + 0xe7, 0xe3, 0xe1, 0xe0, 0xf0, 0xf0, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x3f, 0x3f, 0xbc, 0x1f, 0x03, 0x01, 0x40, 0xc0, 0xc0, 0x80, 0x06, + 0x07, 0x33, 0xf0, 0xf0, 0xc0, 0x80, 0x00, 0x1d, 0x3d, 0x3c, 0x3e, 0x3e, + 0x3e, 0x1e, 0x3c, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x83, + 0x8f, 0xff, 0xf8, 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x7e, 0xfc, 0xe0, 0x00, 0x07, 0x3f, 0xff, 0xf8, 0xc3, 0x1f, 0xff, + 0xfc, 0xc0, 0x8f, 0xff, 0xff, 0x70, 0x03, 0x7f, 0xff, 0xfc, 0xe0, 0x80, + 0x07, 0x07, 0x83, 0x83, 0x83, 0xc3, 0xc1, 0xc1, 0xef, 0xff, 0xff, 0xfc, + 0x00, 0x00, 0x00, 0x3f, 0x7f, 0xff, 0xf8, 0xf8, 0xf8, 0xf8, 0x7c, 0x7f, + 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x9f, 0x83, 0x00, + 0x00, 0x60, 0xcf, 0x87, 0x07, 0x37, 0x73, 0x73, 0x7b, 0x73, 0x61, 0xc1, + 0x81, 0x00, 0x00, 0x02, 0x0e, 0x1c, 0x30, 0xe0, 0xc0, 0x00, 0x00, 0x01, + 0x03, 0x06, 0x10, 0x30, 0xe1, 0xc3, 0x1f, 0x3f, 0xe4, 0xd8, 0x38, 0xe7, + 0xdf, 0x3b, 0xf6, 0xce, 0xbc, 0x70, 0xe0, 0x84, 0x1c, 0x3c, 0x1e, 0x1f, + 0x1f, 0x1f, 0x3e, 0xf8, 0xe0, 0x80, 0x1f, 0x7f, 0xff, 0xc7, 0x87, 0xc7, + 0xdf, 0xfe, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0f, + 0xff, 0xf8, 0x60, 0x19, 0x3f, 0x7f, 0x00, 0x00, 0x00, 0x07, 0x1f, 0x1f, + 0x1f, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x0f, 0x0f, 0x06, 0x00, 0x07, 0x87, + 0xc7, 0xe7, 0xe3, 0xf3, 0xfb, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0x1f, 0x07, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x11, 0x23, 0x4e, 0x94, 0x28, 0x5b, + 0xb7, 0x6a, 0xd4, 0xec, 0xd8, 0xb0, 0x6c, 0xdc, 0x1d, 0x1f, 0x1e, 0x1c, + 0xb0, 0xe2, 0xcf, 0x9f, 0x37, 0x67, 0xc6, 0xcc, 0xf8, 0xe0, 0xc0, 0x80, + 0x00, 0x01, 0x07, 0x0e, 0x18, 0x71, 0x67, 0x1e, 0x31, 0x03, 0x06, 0x0f, + 0x1f, 0x38, 0x11, 0x03, 0x02, 0x0e, 0x1c, 0x02, 0x8e, 0xcf, 0xef, 0xff, + 0x7f, 0x1f, 0x06, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x81, 0xc1, + 0xe0, 0xf0, 0xf8, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xc3, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xe0, + 0xf0, 0xf8, 0xfc, 0xfc, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, + 0x7c, 0x3c, 0x3c, 0x1c, 0x1c, 0x0c, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x09, 0x06, 0x08, + 0x01, 0x03, 0x06, 0x04, 0x01, 0x03, 0x02, 0x05, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x06, 0x07, 0x87, 0xf7, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x03, 0x03, 0x03, 0x03, 0xc3, 0xfb, 0xff, + 0x7f, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xe0, 0xf0, 0xfc, 0x7f, 0x7f, 0x3f, 0x3f, 0x1f, 0x1f, 0x0f, 0x0f, + 0x07, 0x07, 0x03, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; diff --git a/software/src/util/util_SPIFIFO.h b/software/src/util/util_SPIFIFO.h index fe8298c95..f4e5b5b72 100644 --- a/software/src/util/util_SPIFIFO.h +++ b/software/src/util/util_SPIFIFO.h @@ -5,6 +5,8 @@ #include +#define SPICLOCK_30MHz (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR) //(60 / 2) * ((1+1)/2) = 30 MHz (= 24MHz, when F_BUS == 48000000) + #ifdef KINETISK #if F_BUS == 120000000