diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 23c66744c..127669ccd 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -8,12 +8,16 @@ "${workspaceRoot}/tests/googletest/googletest/include", "${workspaceRoot}/src/sys", "${workspaceRoot}/src/usbd", + "${workspaceRoot}/src/usbh", "${workspaceRoot}/Drivers/CMSIS_5/CMSIS/Core/Include", "${workspaceRoot}/Drivers/CMSIS-Device/ST/STM32H7xx/Include", "${workspaceRoot}/Drivers/STM32H7xx_HAL_Driver/Inc", "${workspaceRoot}/Drivers/STM32H7xx_HAL_Driver/Inc/Legacy", "${workspaceRoot}/Middlewares/ST/STM32_USB_Device_Library/Core/Inc", "${workspaceRoot}/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc", + "${workspaceRoot}/Middlewares/ST/STM32_USB_Host_Library/Core/Inc", + "${workspaceRoot}/Middlewares/ST/STM32_USB_Host_Library/Class/MIDI/Inc", + "${workspaceRoot}/Middlewares/ST/STM32_USB_Host_Library/Class/MSC/Inc", "${workspaceRoot}/Middlewares/Third_Party/FatFs/src" ], "defines": [ diff --git a/CMakeLists.txt b/CMakeLists.txt index 42f15065d..b2bb90a39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,9 @@ add_library(daisy STATIC ${MODULE_DIR}/daisy_pod.cpp ${MODULE_DIR}/daisy_seed.cpp ${MODULE_DIR}/daisy_versio.cpp + ${MODULE_DIR}/desktop_devkit.cpp + ${MODULE_DIR}/eurorack_devkit.cpp + ${MODULE_DIR}/pedal_devkit.cpp ${MODULE_DIR}/dev/codec_ak4556.cpp ${MODULE_DIR}/dev/codec_pcm3060.cpp ${MODULE_DIR}/dev/codec_wm8731.cpp @@ -130,7 +133,6 @@ target_compile_options(daisy PRIVATE $<$:-Wno-missing-attributes> ) - set_target_properties(daisy PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED @@ -138,7 +140,6 @@ set_target_properties(daisy PROPERTIES C_STANDARD_REQUIRED ) - # Don't add examples if we're being included in another project if(PROJECT_IS_TOP_LEVEL) add_subdirectory(examples EXCLUDE_FROM_ALL) diff --git a/Makefile b/Makefile index 03874ca1c..5253e570e 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,9 @@ daisy_field \ daisy_versio \ daisy_legio \ daisy_patch_sm \ +desktop_devkit \ +eurorack_devkit \ +pedal_devkit \ sys/fatfs \ sys/system \ dev/sr_595 \ diff --git a/examples/devkits/Desktop-DevKit-UI-Template/Makefile b/examples/devkits/Desktop-DevKit-UI-Template/Makefile new file mode 100644 index 000000000..a38a007c4 --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/Makefile @@ -0,0 +1,33 @@ +# Project Name +TARGET = Desktop-DevKit-UI-Template + +# Configure for debugging +# common configurations: +# use DEBUG = 1 and OPT = -Og for debugging +# or DEBUG = 0 and OPT = -O3 for performance +DEBUG = 1 +OPT = -Og + +# (optional) use bootloader +APP_TYPE=BOOT_SRAM + +# Sources +CPP_SOURCES = src/main.cpp + +LIBDAISY_DIR = ../../../ + +# (optional) Add DaisySP +# DAISYSP_DIR = ../path/to/DaisySP +# USE_DAISYSP_LGPL=1 + +# (optional) Includes FatFS source files within project. +USE_FATFS = 1 + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core + +# Use the bootloader variant that allows programming DFU over the +# external USB port (used to power the devkit). +BOOT_BIN=$(SYSTEM_FILES_DIR)/dsy_bootloader_v6_4-extdfu-2000ms.bin + +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/devkits/Desktop-DevKit-UI-Template/README.md b/examples/devkits/Desktop-DevKit-UI-Template/README.md new file mode 100644 index 000000000..8e4b0616f --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/README.md @@ -0,0 +1,38 @@ +# Desktop Devkit UI Template + +This example is a bit more complex than the other examples in the repo. + +All inputs for the Desktop DevKit are read, and printed when changed to Serial over the external USB port, also used to power the DevKit. + +The 16 buttons, 8 pots, and LEDs are all managed using libDaisy's UI class from `main_page.h`. + +As usual inputs are read in the AudioCallback. UI Events are then created for the relevant pieces of the interface. +Inside the Low-priority Callback, the events are handled, and the LED UI is redrawn. +The Low-priority Callback is used so that things like SD Card Disk I/O, QSPI saving, etc. that may block the main while() loop do not cause the UI to become unresponsive, or appear stuck. + +## Flashing to the Daisy + +This is designed to use the external USB version of the bootloader. + +This version of the bootloader can be flashed to the daisy via the built-in USB port by running `make program-boot` from this directory, or by selecting the "v6.x (external)" variant from the [web programmer](https://flash.daisy.audio/). + +Once the bootloader has been flashed to the Daisy, you can power connect the USB-C cable from your computer to the Desktop Devkit's USB-C port (located above the Seed3). + +With the device connected, press RESET and then BOOT on the Seed. +You should see the USR LED on the Daisy blink quickly and then start slowly pulsing. + +While in this state you can run: `make program-dfu`, or upload the `Desktop-DevKit-UI-Template.bin` file to the [web programmer](https://flash.daisy.audio/) to program the example to the Seed. + +Once bootloader is programmed onto the Seed it does not need to be reprogrammed unless you need to install a different variant of the bootloader, or program an app to the Seed's internal flash. + +## Connecting to the Program + +Once the program has been flashed to the Daisy, you'll see that the Seed's USR LED is blinking at a steady rate. +You can push each of the tactile switches to see the corresponding LED illuminate. + +The CV outputs will be generating slow ramp and saw waveforms respectively, MIDI note messages to the MIDI Input will emit from the MIDI output, and audio sent to the audio inputs will be copied to the outputs. + +You can connect to the program using a serial monitor, and see print statements that occur whenever any input is changed. + +Within the source code you'll see that many of these print statements are set within on change callbacks in the `main_page.h`. +Other inputs are checked in the Audio Callback, and messages are generated to be sent from the main `while()` loop. diff --git a/examples/devkits/Desktop-DevKit-UI-Template/src/main.cpp b/examples/devkits/Desktop-DevKit-UI-Template/src/main.cpp new file mode 100644 index 000000000..af47c383b --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/src/main.cpp @@ -0,0 +1,230 @@ +/** Desktop DevKit UI Template + * + * UI template program for the Desktop DevKit + * + * This program uses the libDaisy UI framework to generate events from the input + * that can be handled within UI pages. + * This is a bit more complex than the simpler approach to handling UI in other examples, + * but provides a lot of flexibility for more involved applications. + * + * Since the Desktop DevKit is USB powered via the port on the devkit, this build uses the + * external variant of the bootloader so that the seed on the devkit can be reprogrammed + * while connected to the single USB port on the desktop devkit. + * + * This program: + * - initializes the Desktop DevKit hardware + * - starts an audio callback to do pass through, update the controls, and generate UI events + * - starts a low-priority callback to process the UI events + * - prints input data from the `main_page.h` event hooks over USB serial + */ +#include "desktop_devkit.h" +#include "userinterface.h" + +using namespace daisy; +using namespace daisy::desktop_devkit; + +/** Naive hysteresis filter for printing CV input changes + * + * Only update the value if the input has changed by enough to print. +*/ +struct CvChangeFilter +{ + float prev_val; + bool changed; + + /** simple constructor */ + CvChangeFilter() : prev_val(0.f), changed(false) {} + + /** compares the input to the last value output, and updates it if it exceeds a threshold of 0.1 */ + inline float Process(const float in) + { + changed = false; + if(std::abs(in - prev_val) > 0.01f) + { + prev_val = in; + changed = true; + } + return prev_val; + } + + /** returns true the value changed last time `Process` was called */ + inline bool Changed() const { return changed; } +}; + +/** Global Hardware object */ +Hardware hw; + +/** Global UI object */ +Ui ui; + +/** FIFO of strings that we can use to move messages from the Audio callback + * to be printed in the main while() loop. + */ +FIFO, 16> msg_fifo; + +/** state of 3-way toggle for output when changed */ +int prev_toggle_pos = 0; + +/** Simple filters for CV inputs to only output values when they have changed */ +CvChangeFilter cv_change_filt[2]; + +void AudioCallback(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size) +{ + hw.UpdateAllControls(); + ui.GenerateEvents(); + + // Here we'll send some strings based on events not covered in the UI. + // + // Certain things (e.g. gate inputs, CV inputs, etc.) often make more sense to + // handle within the audio callback so that there is a new value once per callback, + // syncronized with the block of audio it was read in. + // + // A few of these (e.g. toggles) could be handled in the UI pages. + + // Gate Inputs + if(hw.gate_in[0].Trig()) + msg_fifo.PushBack("Gate In 1: Trig!"); + if(hw.gate_in[1].Trig()) + msg_fifo.PushBack("Gate In 2: Trig!"); + + // Input detection changes + if(hw.sd_detect.FallingEdge()) + msg_fifo.PushBack("SD card removed"); + else if(hw.sd_detect.RisingEdge()) + msg_fifo.PushBack("SD card inserted"); + + if(hw.left_detect.FallingEdge()) + msg_fifo.PushBack("Left input removed"); + else if(hw.left_detect.RisingEdge()) + msg_fifo.PushBack("Left input inserted"); + + if(hw.right_detect.FallingEdge()) + msg_fifo.PushBack("Right input removed"); + else if(hw.right_detect.RisingEdge()) + msg_fifo.PushBack("Right input inserted"); + + // 2-way Toggle + if(hw.tog_sw17.FallingEdge()) + msg_fifo.PushBack("SW17: Down"); + else if(hw.tog_sw17.RisingEdge()) + msg_fifo.PushBack("SW17: Up"); + + // 3-way Toggle + auto new_tog_pos = hw.tog_sw18.Read(); + if(prev_toggle_pos != new_tog_pos) + { + switch(new_tog_pos) + { + case Switch3::POS_CENTER: msg_fifo.PushBack("SW18: Center"); break; + case Switch3::POS_UP: msg_fifo.PushBack("SW18: Up"); break; + case Switch3::POS_DOWN: msg_fifo.PushBack("SW18: Down"); break; + default: break; + } + prev_toggle_pos = new_tog_pos; + } + + // CV Inputs + auto cv1 = cv_change_filt[0].Process(hw.cv[0].Value()); + if(cv_change_filt[0].Changed()) + { + FixedCapStr<32> msg("CV1: "); + msg.AppendFloat(cv1); + msg_fifo.PushBack(msg); + } + auto cv2 = cv_change_filt[1].Process(hw.cv[1].Value()); + if(cv_change_filt[1].Changed()) + { + FixedCapStr<32> msg("CV2: "); + msg.AppendFloat(cv2); + msg_fifo.PushBack(msg); + } + + // Audio pass through + for(size_t i = 0; i < size; i++) + { + out[0][i] = in[0][i]; + out[1][i] = in[1][i]; + } +} + + +/** This callback happens every 4ms (250Hz) and can be interrupted by + * everything except for the SysTick interrupt. + * The frequency of this callback is set in the `Hardware::StartLowPriorityCallback` + * parameters. + * + * Handling the UI events here means that anything we do in the main while() + * loop that may take an indeterminate time (file system I/O, QSPI Saving, etc.) + * won't cause the UI to stall during those processes +*/ +void LowPriorityCallback(void *data) +{ + // Handles events, and redraws the current UI pages + ui.DoEvents(); +} + +int main(void) +{ + hw.Init(); + System::Delay(50); + Logger::StartLog(false); + ui.Init(&hw); + + hw.StartLowPriorityCallback(LowPriorityCallback, 250); + hw.seed.StartAudio(AudioCallback); + + auto last_dac_write = System::GetNow(); + uint16_t dac_value = 0; + + while(true) + { + auto now = System::GetNow(); + + /** Handle MIDI events */ + while(hw.midi.HasEvents()) + { + auto msg = hw.midi.PopEvent(); + switch(msg.type) + { + case NoteOn: + // Echo note on messages to the MIDI out + { + uint8_t bytes[3] = {0x90, 0x00, 0x00}; + bytes[1] = msg.data[0]; + bytes[2] = msg.data[1]; + hw.midi.SendMessage(bytes, 3); + } + break; + default: break; + } + } + + /** Write a ramp (cv out 1) and saw (cv out 2) waveform to the DAC outputs + * Approx. 2.5s period for waveform (0.005s * (4096 / 8)) = 2.56s + */ + if(now - last_dac_write > 5) + { + dac_value += 8; + if(dac_value > 4095) + { + dac_value = 0; + } + hw.SetDacOut(DacHandle::Channel::ONE, dac_value); + hw.SetDacOut(DacHandle::Channel::TWO, 4095 - dac_value); + last_dac_write = now; + } + + /** Print messages from the audio callback */ + while(!msg_fifo.IsEmpty()) + { + auto msg = msg_fifo.PopFront(); + Logger::PrintLine(msg.Cstr()); + } + + System::Delay(1); + } + + return 0; +} \ No newline at end of file diff --git a/examples/devkits/Desktop-DevKit-UI-Template/src/main_page.h b/examples/devkits/Desktop-DevKit-UI-Template/src/main_page.h new file mode 100644 index 000000000..f9d15444b --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/src/main_page.h @@ -0,0 +1,56 @@ +#pragma once + +#include "daisy.h" + +class MainPage : public daisy::UiPage +{ + public: + /** Simple alias for logging + * StartLog() for the destination should have already been called before the + * page is opened. + */ + using Log = daisy::Logger; + + void Init() {} + + void Draw(const daisy::UiCanvasDescriptor &canvasDescriptor) override + { + auto *hw = reinterpret_cast( + canvasDescriptor.handle_); + auto now = daisy::System::GetNow(); + hw->seed.SetLed((now & 511) < 255); + + // For now we'll just illuminate each LED with the state of the button + for(size_t i = 0; i < daisy::desktop_devkit::kNumLeds; i++) + { + hw->SetLed(i, hw->button[i].Pressed() ? 1.f : 0.f); + } + } + + bool OnButton(uint16_t buttonID, + uint8_t numberOfPresses, + bool isRetriggering) override + { + switch(numberOfPresses) + { + case 0: Log::PrintLine("Released %d", buttonID + 1); break; + case 1: Log::PrintLine("Pressed %d", buttonID + 1); break; + case 2: Log::PrintLine("Long Pressed %d", buttonID + 1); break; + default: break; + } + return false; + } + + bool OnPotMoved(uint16_t potID, float newPosition) + { + // Print Format: "Pot {potID+1}: {newPosition}" + daisy::FixedCapStr<16> valueStr("Pot "); + valueStr.AppendInt(potID + 1); + valueStr.Append(": "); + valueStr.AppendFloat(newPosition, 3); + Log::PrintLine(valueStr.Cstr()); + return false; + } + + void OnShow() { Log::PrintLine("Page Opened!"); } +}; diff --git a/examples/devkits/Desktop-DevKit-UI-Template/src/ui_glue.h b/examples/devkits/Desktop-DevKit-UI-Template/src/ui_glue.h new file mode 100644 index 000000000..a6f1b9ec8 --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/src/ui_glue.h @@ -0,0 +1,34 @@ +#pragma once + +#include "desktop_devkit.h" + +static constexpr size_t kLongPressDuration = 1500; + +enum CanvasIds { canvasLedDisplay = 0, NUM_CANVASES }; + +void FlushLeds(const daisy::UiCanvasDescriptor &canvasDescriptor) { + auto *hw = reinterpret_cast( + canvasDescriptor.handle_); + hw->UpdateLeds(); +} + +void ClearLeds(const daisy::UiCanvasDescriptor &canvasDescriptor) { + auto *hw = reinterpret_cast( + canvasDescriptor.handle_); + hw->ClearLeds(); +} + +/** Interface class used for generating UI events from Pot inputs. */ +class PotListener { +public: + /** Initialize the Object */ + void Init(daisy::desktop_devkit::Hardware *hw) { hw_ = hw; } + + /** Read the value from the hardware + * TODO: update to wrapper if add pot min/max calibration + */ + float GetPotValue(uint16_t potId) { return hw_->pot[potId].Value(); } + +private: + daisy::desktop_devkit::Hardware *hw_; +}; diff --git a/examples/devkits/Desktop-DevKit-UI-Template/src/userinterface.h b/examples/devkits/Desktop-DevKit-UI-Template/src/userinterface.h new file mode 100644 index 000000000..855872f15 --- /dev/null +++ b/examples/devkits/Desktop-DevKit-UI-Template/src/userinterface.h @@ -0,0 +1,99 @@ +#pragma once +#include "desktop_devkit.h" +#include "main_page.h" +#include "ui_glue.h" + +/** User Inteface for the application. + * + * Initializes the mechanisms for updating the LEDs at a regular interval, + * and generating events from tactile inputs (i.e. pots, buttons). + * + * CVs/Gates do not typically generate UI events because we dont' want to flood + * the event fifo since we want to handle those signals as they arrive from the + * audio callback. It is, however, possible to access the hardware class in the + * `Draw()` method of any UIPage if visualizing those signals is important; it + * should be kept in mind that the period that the Draw method is called is + * often much slower than the rate the inputs actually update. + * + */ +class Ui +{ + public: + void Init(daisy::desktop_devkit::Hardware *hw) + { + hw_ = hw; + + daisy::UI::SpecialControlIds specialControlIds; //< None + daisy::UiCanvasDescriptor ledDisplayDescriptor; + ledDisplayDescriptor.id_ = canvasLedDisplay; + ledDisplayDescriptor.handle_ = hw_; + ledDisplayDescriptor.updateRateMs_ = 16; //< 60Hz + ledDisplayDescriptor.clearFunction_ = ClearLeds; + ledDisplayDescriptor.flushFunction_ = FlushLeds; + + pot_listener_.Init(hw_); + pot_monitor_.Init(event_queue_, + pot_listener_, + 125, + (1.f / (1 << 9)), + (1.f / (1 << 12))); + + // Init the pages: + ui_.Init(event_queue_, + specialControlIds, + {ledDisplayDescriptor}, + canvasLedDisplay); + // ui_.OpenPage(splash_page_); + ui_.OpenPage(main_page_); + } + + void GenerateEvents() + { + // Pot events + pot_monitor_.Process(); + + // Button events + for(size_t i = 0; i < daisy::desktop_devkit::kNumButtons; i++) + { + auto *btn = &hw_->button[i]; + if(btn->RisingEdge()) + { + event_queue_.AddButtonPressed(i, 1); + } + else if(btn->Pressed() && btn->TimeHeldMs() > kLongPressDuration) + { + if(!ignore_next_btn_release[i]) + { + event_queue_.AddButtonPressed(i, 2); + ignore_next_btn_release[i] = true; + } + } + else if(btn->FallingEdge()) + { + if(ignore_next_btn_release[i]) + { + ignore_next_btn_release[i] = false; + } + else + { + event_queue_.AddButtonReleased(i); + } + } + } + } + + void DoEvents() { ui_.Process(); } + + private: + daisy::desktop_devkit::Hardware *hw_; + daisy::UI ui_; + + MainPage main_page_; + + /** Event Generation */ + bool ignore_next_btn_release[daisy::desktop_devkit::kNumButtons]; + daisy::UiEventQueue event_queue_; + PotListener pot_listener_; + daisy::PotMonitor + pot_monitor_; +}; diff --git a/examples/devkits/Eurorack-DevKit-Template/Makefile b/examples/devkits/Eurorack-DevKit-Template/Makefile new file mode 100644 index 000000000..574b90f62 --- /dev/null +++ b/examples/devkits/Eurorack-DevKit-Template/Makefile @@ -0,0 +1,31 @@ +# Project Name +TARGET = Eurorack-DevKit-Template + +# Configure for debugging +# common configurations: +# use DEBUG = 1 and OPT = -Og for debugging +# or DEBUG = 0 and OPT = -O3 for performance +DEBUG = 1 +OPT = -Og + +# (optional) Use bootloader +# APP_TYPE=BOOT_SRAM + +# Sources +CPP_SOURCES = src/main.cpp + +LIBDAISY_DIR = ../../../ + +# (optional) Add DaisySP +# DAISYSP_DIR = ../../DaisySP +# USE_DAISYSP_LGPL=1 + +# (optional) Includes FatFS source files within project. +USE_FATFS = 1 + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core + +BOOT_BIN=$(SYSTEM_FILES_DIR)/dsy_bootloader_v6_4-extdfu-2000ms.bin + +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/devkits/Eurorack-DevKit-Template/src/main.cpp b/examples/devkits/Eurorack-DevKit-Template/src/main.cpp new file mode 100644 index 000000000..83aac582a --- /dev/null +++ b/examples/devkits/Eurorack-DevKit-Template/src/main.cpp @@ -0,0 +1,109 @@ +/** Eurorack DevKit Template + * + * Basic template program for the Eurorack DevKit + * + * This program: + * - initializes the Eurorack DevKit hardware + * - starts an audio callback to do pass through and update the controls + * - blinks the LEDs + * - Writes a signal to the DAC outputs + * - Sends MIDI notes received on the MIDI input to the MIDI output + * - Prints all input state over USB serial + */ +#include "eurorack_devkit.h" + +using namespace daisy; +using namespace daisy::eurorack_devkit; + +Hardware hw; + +void AudioCallback(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size) +{ + hw.UpdateAllControls(); + + for(size_t i = 0; i < size; i++) + { + out[0][i] = in[0][i]; + out[1][i] = in[1][i]; + } +} + +int main(void) +{ + hw.Init(); + System::Delay(50); + hw.seed.StartLog(false); + + hw.seed.StartAudio(AudioCallback); + + hw.midi.StartReceive(); + + auto last_print = System::GetNow(); + auto last_dac_write = System::GetNow(); + uint16_t dac_value = 0; + + while(true) + { + auto now = System::GetNow(); + // Blink the Seed's LED + hw.seed.SetLed((now & 511) < 255); + + // Blink each color of the RGB LED at different rates: + auto blink_red = (now & 511) < 255; + auto blink_green = (now & 1023) < 511; + auto blink_blue = (now & 2047) < 1023; + hw.led1.Set(blink_red, blink_green, blink_blue); + hw.led2.Set(blink_red); + hw.led1.Update(); + hw.led2.Update(); + + // Restart MIDI in the event of errors. + hw.midi.Listen(); + + // Handle MIDI events + while(hw.midi.HasEvents()) + { + auto msg = hw.midi.PopEvent(); + switch(msg.type) + { + case NoteOn: + // Echo note on messages to the MIDI out + { + uint8_t bytes[3] = {0x90, 0x00, 0x00}; + bytes[1] = msg.data[0]; + bytes[2] = msg.data[1]; + hw.midi.SendMessage(bytes, 3); + } + break; + default: break; + } + } + + /** Write a ramp (cv out 1) and saw (cv out 2) waveform to the DAC outputs + * Approx. 2.5s period for waveform (0.005s * (4096 / 8)) = 2.56s + */ + if(now - last_dac_write > 5) + { + dac_value += 8; + if(dac_value > 4095) + { + dac_value = 0; + } + hw.SetDacOut(DacHandle::Channel::ONE, dac_value); + hw.SetDacOut(DacHandle::Channel::TWO, 4095 - dac_value); + last_dac_write = now; + } + + if(now - last_print > 100) + { + hw.PrintAllControls(); + last_print = now; + } + + System::Delay(1); + } + + return 0; +} \ No newline at end of file diff --git a/examples/devkits/Pedal-DevKit-Template/Makefile b/examples/devkits/Pedal-DevKit-Template/Makefile new file mode 100644 index 000000000..ff9b8a463 --- /dev/null +++ b/examples/devkits/Pedal-DevKit-Template/Makefile @@ -0,0 +1,29 @@ +# Project Name +TARGET = Pedal-DevKit-Template + +# Configure for debugging +# common configurations: +# use DEBUG = 1 and OPT = -Og for debugging +# or DEBUG = 0 and OPT = -O3 for performance +DEBUG = 1 +OPT = -Og + +#(optional) use the bootloader +# APP_TYPE=BOOT_SRAM + +# Sources +CPP_SOURCES = src/main.cpp + +LIBDAISY_DIR = ../../../ + +# (optional) Add DaisySP +# DAISYSP_DIR = ../path/to/DaisySP +# USE_DAISYSP_LGPL=1 + +# (optional) Includes FatFS source files within project. +USE_FATFS = 1 + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core + +include $(SYSTEM_FILES_DIR)/Makefile diff --git a/examples/devkits/Pedal-DevKit-Template/src/main.cpp b/examples/devkits/Pedal-DevKit-Template/src/main.cpp new file mode 100644 index 000000000..12be2967f --- /dev/null +++ b/examples/devkits/Pedal-DevKit-Template/src/main.cpp @@ -0,0 +1,96 @@ +/** Pedal DevKit Template + * + * Basic template program for the Pedal DevKit + * + * This program: + * - initializes the Pedal DevKit hardware + * - starts an audio callback to do pass through and update the controls + * - blinks the LEDs + * - Sends MIDI notes received on the MIDI input to the MIDI output + * - Prints all input states over USB serial + */ +#include "pedal_devkit.h" + +using namespace daisy; +using namespace daisy::pedal_devkit; + +using Log = Logger; + +Hardware hw; + +void AudioCallback(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size) +{ + hw.UpdateAllControls(); + + for(size_t i = 0; i < size; i++) + { + out[0][i] = in[0][i]; + out[1][i] = in[1][i]; + } +} + +int main(void) +{ + hw.Init(); + System::Delay(50); + Log::StartLog(false); + + hw.seed.StartAudio(AudioCallback); + + hw.midi.StartReceive(); + + auto last_print = System::GetNow(); + + while(true) + { + // Record the current time. + auto now = System::GetNow(); + + // Blink the Seed's LED + hw.seed.SetLed((now & 511) < 255); + + // Blink each color of the RGB LED at different rates: + auto blink_red = (now & 511) < 255; + auto blink_green = (now & 1023) < 511; + auto blink_blue = (now & 2047) < 1023; + hw.led1.Set(blink_red); + hw.led2.Set(blink_red, blink_green, blink_blue); + hw.led1.Update(); + hw.led2.Update(); + + hw.midi.Listen(); // Restart MIDI in the event of errors. + + /** Handle MIDI events */ + while(hw.midi.HasEvents()) + { + auto msg = hw.midi.PopEvent(); + switch(msg.type) + { + case NoteOn: + // Echo note on messages to the MIDI out + { + uint8_t bytes[3] = {0x90, 0x00, 0x00}; + bytes[1] = msg.data[0]; + bytes[2] = msg.data[1]; + hw.midi.SendMessage(bytes, 3); + } + break; + default: break; + } + } + + // Print Everything + if(now - last_print > 50) + { + hw.PrintAllControls(); + last_print = now; + } + + // If we start doing more things, we can remove the delay + System::Delay(1); + } + + return 0; +} \ No newline at end of file diff --git a/src/daisy.h b/src/daisy.h index 599648053..934c58bdc 100644 --- a/src/daisy.h +++ b/src/daisy.h @@ -26,6 +26,7 @@ #include "hid/switch.h" #include "hid/switch3.h" #include "hid/ctrl.h" +#include "hid/digital_control.h" #include "hid/gatein.h" #include "hid/parameter.h" #include "hid/usb.h" diff --git a/src/desktop_devkit.cpp b/src/desktop_devkit.cpp new file mode 100644 index 000000000..951499332 --- /dev/null +++ b/src/desktop_devkit.cpp @@ -0,0 +1,156 @@ +#include "desktop_devkit.h" + +namespace daisy::desktop_devkit +{ +static constexpr I2CHandle::Config led_i2c_config + = {I2CHandle::Config::Peripheral::I2C_1, + {seed::D11, seed::D12}, + I2CHandle::Config::Speed::I2C_1MHZ}; + +static Hardware::LedDriver::DmaBuffer DMA_BUFFER_MEM_SECTION led_dma_buffer_a, + led_dma_buffer_b; + +void Hardware::Init() +{ + // Initialize stuff + seed.Init(true); + + /** Switches */ + tog_sw17.Init(daisy::seed::D19); + tog_sw18.Init(daisy::seed::D28, daisy::seed::D27); + + sd_detect.Init(daisy::seed::D7); + + // Initialize jack input detection with opposite polarity since the + // switching connection of the jack is held to GND while it is unplugged. + left_detect.Init(daisy::seed::D16, + 0.f, + Switch::Type::TYPE_MOMENTARY, + Switch::Polarity::POLARITY_NORMAL); + right_detect.Init(daisy::seed::D17, + 0.f, + Switch::Type::TYPE_MOMENTARY, + Switch::Polarity::POLARITY_NORMAL); + + ButtonSr::Config sr_cfg; + sr_cfg.clk = daisy::seed::D8; + sr_cfg.latch = daisy::seed::D9; + sr_cfg.data[0] = daisy::seed::D10; + button_sr.Init(sr_cfg); + for(auto &b : button) + b.Init(true); + + /** UART MIDI I/O */ + daisy::MidiUartHandler::Config midi_config; + midi_config.transport_config.periph + = daisy::UartHandler::Config::Peripheral::USART_1; + midi_config.transport_config.rx = daisy::seed::D14; + midi_config.transport_config.tx = daisy::seed::D13; + midi.Init(midi_config); + + /** ADC Inputs */ + daisy::AdcChannelConfig adc_cfg[3]; + auto conversion_speed + = daisy::AdcChannelConfig::ConversionSpeed::SPEED_810CYCLES_5; + adc_cfg[0].InitMux(daisy::seed::D15, + kNumPots, + daisy::seed::D24, + daisy::seed::D25, + daisy::seed::D26, + conversion_speed); + adc_cfg[1].InitSingle(daisy::seed::D20, conversion_speed); + adc_cfg[2].InitSingle(daisy::seed::D21, conversion_speed); + seed.adc.Init(adc_cfg, 3); + + for(size_t i = 0; i < kNumPots; i++) + pot[i].Init(seed.adc.GetMuxPtr(0, i), 1000.f); + for(size_t i = 0; i < kNumCVs; i++) + cv[i].InitBipolarCv(seed.adc.GetPtr(i + 1), 1000.f); + + /** Leds */ + daisy::I2CHandle i2c; + i2c.Init(led_i2c_config); + led_driver.Init(i2c, {0x00}, led_dma_buffer_a, led_dma_buffer_b); + ClearLeds(); + UpdateLeds(); + + /** Gate Ins */ + gate_in[0].Init(daisy::seed::D0); + gate_in[1].Init(daisy::seed::D18); + + /** DAC/Gate Output */ + daisy::DacHandle::Config dac_cfg; + dac_cfg.bitdepth = daisy::DacHandle::BitDepth::BITS_12; + dac_cfg.buff_state = daisy::DacHandle::BufferState::ENABLED; + dac_cfg.chn = daisy::DacHandle::Channel::BOTH; + dac_cfg.mode = daisy::DacHandle::Mode::POLLING; + seed.dac.Init(dac_cfg); + // Alternatively: DMA mode + // dac_cfg.mode = daisy::DacHandle::Mode::DMA; + // dac_cfg.target_samplerate = 12000; + + seed.adc.Start(); +} + +void Hardware::UpdateAllControls() +{ + button_sr.Update(); + for(size_t i = 0; i < kNumButtons; i++) + { + int sr_idx = 15 - i; + button[i].Debounce(button_sr.State(sr_idx)); + }; + for(auto &p : pot) + p.Process(); + for(auto &c : cv) + c.Process(); + tog_sw17.Debounce(); + sd_detect.Debounce(); + left_detect.Debounce(); + right_detect.Debounce(); +} + +void Hardware::SetDacOut(DacHandle::Channel channel, uint16_t code) +{ + seed.dac.WriteValue(channel, code); +} + +void Hardware::ClearLeds() +{ + led_driver.SetAllTo(1.f); +} + +void Hardware::UpdateLeds() +{ + led_driver.SwapBuffersAndTransmit(); +} + +void Hardware::SetLed(int idx, float brightness) +{ + led_driver.SetLed(idx, 1.f - brightness); +} + +void Hardware::StartLowPriorityCallback(TimerHandle::PeriodElapsedCallback cb, + uint32_t target_freq, + void * data) +{ + TimerHandle::Config timcfg; + timcfg.periph = daisy::TimerHandle::Config::Peripheral::TIM_5; + timcfg.dir = daisy::TimerHandle::Config::CounterDir::UP; + auto tim_base_freq = daisy::System::GetPClk2Freq(); + auto tim_target_freq = target_freq; + auto tim_period = tim_base_freq / tim_target_freq; + timcfg.period = tim_period; + timcfg.enable_irq = true; + tim5_handle.Init(timcfg); + tim5_handle.SetCallback(cb, data); + tim5_handle.Start(); +} + +/** Stops an ongoing low-priority callback */ +void Hardware::StopLowPriorityCallback() +{ + tim5_handle.Stop(); +} + +} // namespace daisy::desktop_devkit \ No newline at end of file diff --git a/src/desktop_devkit.h b/src/desktop_devkit.h new file mode 100644 index 000000000..12950a9ea --- /dev/null +++ b/src/desktop_devkit.h @@ -0,0 +1,84 @@ +#pragma once +#include "daisy_seed.h" + +namespace daisy::desktop_devkit +{ +/** Number of Pots on the DevKit */ +constexpr size_t kNumPots = 8; +/** Number of CV inputs on the DevKit */ +constexpr size_t kNumCVs = 2; +/** Number of Gate inputs on the DevKit */ +constexpr size_t kNumGateIns = 2; +/** Number of push buttons on the DevKit */ +constexpr size_t kNumButtons = 16; +/** Number of push buttons on the DevKit */ +constexpr size_t kNumLeds = 16; + +class Hardware +{ + public: + Hardware() {} + + using LedDriver = LedDriverPca9685<1, true>; + using ButtonSr = ShiftRegister4021<2, 1>; + + DaisySeed seed; + Switch tog_sw17; + Switch3 tog_sw18; + ButtonSr button_sr; + GateIn gate_in[kNumGateIns]; + AnalogControl pot[kNumPots], cv[kNumCVs]; + DigitalControl button[kNumButtons]; + MidiUartHandler midi; + LedDriver led_driver; + TimerHandle tim5_handle; + // Plug Detection + Switch sd_detect, left_detect, right_detect; + + /** Initialize the hardware */ + void Init(); + + /** Process all analog inputs, debounce digital inputs, and update their + * internally stored states. + */ + void UpdateAllControls(); + + /** Write the code to the DAC output + * @param channel DAC channel to write to + * @param code the 0-4096 code to write corresponding to the output voltage + */ + void SetDacOut(DacHandle::Channel channel, uint16_t code); + + /** Reset all LEDs to their off state */ + void ClearLeds(); + + /** Flush the current LED values to the hardware. + * Changes to the LED brightnesses will only take effect + * when this method is called. + */ + void UpdateLeds(); + + /** Set the brightness for a specific LED + * @param idx index of the LED from 0-15 corresponding to D1 to D16 + * @param brightness brightness level from 0-1 + */ + void SetLed(int idx, float brightness); + + /** This starts a recurring callback at a specified interval. The callback is + * run on the lowest priority interrupt level. This provides a hook for + * non-background tasks that should interrupt low level activity like disk + * i/o, etc. + * + * @param cb callback function to take place at the specified frequency + * @param target_freq target frequency in hertz for the callback to run + * @param data optional pointer to context data accessible from the callback + */ + void StartLowPriorityCallback(TimerHandle::PeriodElapsedCallback cb, + uint32_t target_freq, + void* data = nullptr); + + /** Stops an ongoing low-priority callback */ + void StopLowPriorityCallback(); +}; + +} // namespace daisy::desktop_devkit \ No newline at end of file diff --git a/src/eurorack_devkit.cpp b/src/eurorack_devkit.cpp new file mode 100644 index 000000000..9ce7ca34d --- /dev/null +++ b/src/eurorack_devkit.cpp @@ -0,0 +1,117 @@ +#include "eurorack_devkit.h" + +namespace daisy::eurorack_devkit +{ +void Hardware::Init() +{ + seed.Init(true); + sw1.Init(seed::D0); + sw2.Init(seed::D20); + sw3.Init(seed::D7, seed::D27); //< wired opposite from expected.. + sw4.Init(seed::D12); + + AdcChannelConfig adc_config[5]; + auto conversion_speed + = AdcChannelConfig::ConversionSpeed::SPEED_810CYCLES_5; + adc_config[0].InitMux( + seed::D15, 8, seed::D24, seed::D25, seed::D26, conversion_speed); + adc_config[1].InitSingle(seed::D18, conversion_speed); + adc_config[2].InitSingle(seed::D17, conversion_speed); + adc_config[3].InitSingle(seed::D16, conversion_speed); + adc_config[4].InitSingle(seed::D19, conversion_speed); + seed.adc.Init(adc_config, 5); + for(size_t i = 0; i < kNumPots; i++) + { + pot[i].Init(seed.adc.GetMuxPtr(0, i), 1000.f); + } + for(size_t i = 0; i < kNumCVs; i++) + { + cv[i].InitBipolarCv(seed.adc.GetPtr(i + 1), 1000.f); + } + + led1.Init(seed::D9, seed::D28, seed::D8, false); + led2.Init(seed::D11, false); + + gate_in1.Init(seed::D10); + gate_in2.Init(seed::D21); + + // DAC init + // Maybe this becomes separate and/or optional to make it + // easier to set up these outputs as GPIO if the user wants? + DacHandle::Config dac_config; + dac_config.bitdepth = DacHandle::BitDepth::BITS_12; + dac_config.buff_state = DacHandle::BufferState::DISABLED; + dac_config.mode = DacHandle::Mode::POLLING; + dac_config.chn = DacHandle::Channel::BOTH; + seed.dac.Init(dac_config); + + MidiUartHandler::Config midi_config; + midi_config.transport_config.periph + = UartHandler::Config::Peripheral::USART_1; + midi_config.transport_config.rx = seed::D14; + midi_config.transport_config.tx = seed::D13; + midi.Init(midi_config); + + seed.adc.Start(); +} + +void Hardware::UpdateAllControls() +{ + for(auto &p : pot) + p.Process(); + for(auto &c : cv) + c.Process(); + sw1.Debounce(); + sw2.Debounce(); + sw4.Debounce(); +} + +void Hardware::SetDacOut(DacHandle::Channel channel, uint16_t code) +{ + seed.dac.WriteValue(channel, code); +} + +void Hardware::PrintAllControls() +{ + // TODO: consider updating this to write everything to one buffer, and then + // transmit to avoid the screen flicker of everything writing in separate + // transactions. + using Log = Logger; + const char *sw3_str = sw3.Read() == Switch3::POS_LEFT + ? "L" + : sw3.Read() == Switch3::POS_RIGHT ? "R" : "C"; + + Log::PrintLine("\033[2J\033[H"); //< Clear the terminal + Log::PrintLine("Pedal DevKit Controls:"); + Log::PrintLine("----Switches----"); + Log::PrintLine("\tSW1: %s\tSW2: %s\tSW3: %s", + sw1.Pressed() ? "X" : "O", + sw2.Pressed() ? "X" : "O", + sw3_str, + sw4.Pressed() ? "R" : "L"); + Log::PrintLine("----Gate Ins----"); + Log::PrintLine("\tGate In 1: %s\tGate In 2: %s", + gate_in1.State() ? "X" : "O", + gate_in2.State() ? "X" : "O"); + Log::PrintLine("------ADCs------"); + Log::PrintLine("\tVR1: " FLT_FMT3 "\tVR2: " FLT_FMT3 "\tVR3: " FLT_FMT3 + "\tVR4: " FLT_FMT3, + FLT_VAR3(pot[0].Value()), + FLT_VAR3(pot[1].Value()), + FLT_VAR3(pot[2].Value()), + FLT_VAR3(pot[3].Value())); + Log::PrintLine("\tVR5: " FLT_FMT3 "\tVR6: " FLT_FMT3 "\tVR7: " FLT_FMT3 + "\tVR8: " FLT_FMT3, + FLT_VAR3(pot[4].Value()), + FLT_VAR3(pot[5].Value()), + FLT_VAR3(pot[6].Value()), + FLT_VAR3(pot[7].Value())); + Log::PrintLine("\tCV 1: " FLT_FMT3 "\tCV 2: " FLT_FMT3 "\tCV 3: " FLT_FMT3 + "\tCV 4: " FLT_FMT3, + FLT_VAR3(cv[0].Value()), + FLT_VAR3(cv[1].Value()), + FLT_VAR3(cv[2].Value()), + FLT_VAR3(cv[3].Value())); +} + +} // namespace daisy::eurorack_devkit \ No newline at end of file diff --git a/src/eurorack_devkit.h b/src/eurorack_devkit.h new file mode 100644 index 000000000..d50eb2ecf --- /dev/null +++ b/src/eurorack_devkit.h @@ -0,0 +1,59 @@ +#pragma once + +#include "daisy_seed.h" + +namespace daisy::eurorack_devkit +{ +/** Number of Pots on the DevKit */ +constexpr size_t kNumPots = 8; +/** Number of CV inputs on the DevKit */ +constexpr size_t kNumCVs = 4; + +class Hardware +{ + public: + Hardware() {} + + // SOM + DaisySeed seed; + // Tactile Switches + Switch sw1, sw2; + // Toggle Switches + Switch3 sw3; + Switch sw4; + // Pots + AnalogControl pot[kNumPots]; + // Bipolar CV Inputs + AnalogControl cv[kNumCVs]; + // Gate Inputs + GateIn gate_in1, gate_in2; + // MIDI + MidiUartHandler midi; + + // LEDs + RgbLed led1; + Led led2; + + /** Initialize the hardware */ + void Init(); + + /** Process all analog inputs, debounce digital inputs, and update their + * internally stored states. + */ + void UpdateAllControls(); + + /** Write the code to the DAC output + * @param channel DAC channel to write to + * @param code the 0-4096 code to write corresponding to the output voltage + */ + void SetDacOut(DacHandle::Channel channel, uint16_t code); + + /** Print the state of all hardware inputs in a human readable block over + * serial using the Seed's built-in USB port. + * + * Start the Seed's logger with `seed.StartLog()` prior to using this method. + */ + void PrintAllControls(); +}; + +} // namespace daisy::eurorack_devkit diff --git a/src/hid/digital_control.h b/src/hid/digital_control.h new file mode 100644 index 000000000..5eb55cf49 --- /dev/null +++ b/src/hid/digital_control.h @@ -0,0 +1,54 @@ +#pragma once +#include "sys/system.h" + +namespace daisy +{ +/** Debouncer for non-GPIO switch inputs independent of their source. + * For getting `Switch` like behavior from inputs connected to shiftregisters, + * etc. + * + * Maintains the state and allows checks for transitions, timing, etc. + * + * Unlike `AnalogControl`, this does not take a pointer to an externally + * updated value. This is because the input to this may often be a callable + * (e.g. shiftreg.State(x), gpio.Read()) that may not be stateful. + */ +struct DigitalControl +{ + uint8_t state_; + bool flip_; + uint32_t rising_edge_time_; + + DigitalControl() {} + + inline void Init(bool flip) + { + flip_ = flip; + state_ = 0x00; + } + + inline void Debounce(bool input) + { + uint8_t in_val; + if(flip_) + in_val = input ? 0 : 1; + else + in_val = input ? 1 : 0; + state_ = (state_ << 1) | in_val; + if(RisingEdge()) + { + rising_edge_time_ = System::GetNow(); + } + } + + inline bool RisingEdge() const { return state_ == 0x7f; } + inline bool FallingEdge() const { return state_ == 0x80; } + inline bool Pressed() const { return state_ == 0xff; } + + inline uint32_t TimeHeldMs() const + { + return Pressed() ? System::GetNow() - rising_edge_time_ : 0; + } +}; + +} // namespace daisy \ No newline at end of file diff --git a/src/pedal_devkit.cpp b/src/pedal_devkit.cpp new file mode 100644 index 000000000..d7d1501ba --- /dev/null +++ b/src/pedal_devkit.cpp @@ -0,0 +1,97 @@ +#include "pedal_devkit.h" + +namespace daisy::pedal_devkit +{ +void Hardware::Init() +{ + seed.Init(true); + sw1.Init(seed::D7, seed::D8); //< wired opposite from expected.. + sw2.Init(seed::D9); + sw3.Init(seed::D22); + sw4.Init(seed::D0); + sw5.Init(seed::D28); + fsw_1.Init(seed::D10); + fsw_2.Init(seed::D23); + sd_detect.Init(seed::D11); + + AdcChannelConfig adc_cfg[kNumPots + 1]; + auto conversion_speed + = AdcChannelConfig::ConversionSpeed::SPEED_810CYCLES_5; + // Pots + adc_cfg[0].InitSingle(seed::D15, conversion_speed); + adc_cfg[1].InitSingle(seed::D16, conversion_speed); + adc_cfg[2].InitSingle(seed::D17, conversion_speed); + adc_cfg[3].InitSingle(seed::D18, conversion_speed); + adc_cfg[4].InitSingle(seed::D19, conversion_speed); + adc_cfg[5].InitSingle(seed::D20, conversion_speed); + // Expression + adc_cfg[6].InitSingle(seed::D21, conversion_speed); + seed.adc.Init(adc_cfg, kNumPots + 1); + + for(size_t i = 0; i < kNumPots; i++) + pot[i].Init(seed.adc.GetPtr(i), 1000.f); + expression.Init(seed.adc.GetPtr(kNumPots), 1000.f); + + led1.Init(seed::D24, false); + led2.Init(seed::D25, seed::D27, seed::D26, false); + + MidiUartHandler::Config midi_config; + midi_config.transport_config.periph + = UartHandler::Config::Peripheral::USART_1; + midi_config.transport_config.rx = seed::D14; + midi_config.transport_config.tx = seed::D13; + midi.Init(midi_config); + + seed.adc.Start(); +} + +void Hardware::UpdateAllControls() +{ + for(auto &p : pot) + p.Process(); + expression.Process(); + + sw2.Debounce(); + sw3.Debounce(); + sw4.Debounce(); + sw5.Debounce(); + fsw_1.Debounce(); + fsw_2.Debounce(); + sd_detect.Debounce(); +} + +void Hardware::PrintAllControls() +{ + using Log = Logger; + const char *sw1_str = sw1.Read() == Switch3::POS_LEFT + ? "L" + : sw1.Read() == Switch3::POS_RIGHT ? "R" : "C"; + + Log::PrintLine("\033[2J\033[H]"); //< Clear the terminal + Log::PrintLine("Pedal DevKit Controls:"); + Log::PrintLine("----Switches----"); + Log::PrintLine("\tSW1: %s\tSW2: %s\tSW3: %s", + sw1_str, + sw2.Pressed() ? "R" : "L", + sw3.Pressed() ? "R" : "L"); + Log::PrintLine("\tSW4: %s\tSW5: %s", + sw4.Pressed() ? "X" : "O", + sw5.Pressed() ? "X" : "O"); + Log::PrintLine("\tFSW1: %s\tFSW2: %s", + fsw_1.Pressed() ? "X" : "O", + fsw_2.Pressed() ? "X" : "O"); + Log::PrintLine("\tSD Detect: %s", + sd_detect.Pressed() ? "SD Card Detected" : "No SD Card"); + Log::PrintLine("------ADCs------"); + Log::PrintLine("\tVR1: " FLT_FMT3 "\tVR2: " FLT_FMT3 "\tVR3: " FLT_FMT3, + FLT_VAR3(pot[0].Value()), + FLT_VAR3(pot[1].Value()), + FLT_VAR3(pot[2].Value())); + Log::PrintLine("\tVR4: " FLT_FMT3 "\tVR5: " FLT_FMT3 "\tVR6: " FLT_FMT3, + FLT_VAR3(pot[3].Value()), + FLT_VAR3(pot[4].Value()), + FLT_VAR3(pot[5].Value())); + Log::PrintLine("\tExpression: " FLT_FMT3, FLT_VAR3(expression.Value())); +} + +} // namespace daisy::pedal_devkit \ No newline at end of file diff --git a/src/pedal_devkit.h b/src/pedal_devkit.h new file mode 100644 index 000000000..6488f927d --- /dev/null +++ b/src/pedal_devkit.h @@ -0,0 +1,63 @@ +#pragma once + +#include "daisy_seed.h" + +namespace daisy::pedal_devkit +{ +/** Number of Pots on the DevKit */ +constexpr size_t kNumPots = 6; + +/** Board support for the Pedal DevKit + * + * Provides access to all HW on the DevKit with convenience methods for common + * behaviors like initialization, and updating values. + */ +class Hardware +{ + public: + Hardware() {} + + // SOM + DaisySeed seed; + + + // Analog Inputs + /** Pot inputs. Labeled VR1 through VR6 on the PCB. */ + AnalogControl pot[kNumPots]; + AnalogControl expression; + + // Toggles + Switch3 sw1; + Switch sw2, sw3; + + // Tactile Switches + Switch sw4, sw5; + + // Footswitches + Switch fsw_1, fsw_2; + + // SD Card detection + Switch sd_detect; + + // LEDs + Led led1; + RgbLed led2; + + // 5-Pin DIN MIDI via UART + daisy::MidiUartHandler midi; + + /** Initialize all of the hardware, and start the ADC */ + void Init(); + + /** Process all analog inputs, debounce switch inputs, and update internally + * stored states */ + void UpdateAllControls(); + + /** Print the state of all hardware inputs in a human readable block over + * serial using the Seed's built-in USB port. + * + * Start the Seed's logger with `seed.StartLog()` prior to using this method. + */ + void PrintAllControls(); +}; +} // namespace daisy::pedal_devkit