diff --git a/dev/digital/DAC_MCP4728_Test/.gitignore b/dev/digital/DAC_MCP4728_Test/.gitignore deleted file mode 100644 index 89cc49c..0000000 --- a/dev/digital/DAC_MCP4728_Test/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch diff --git a/dev/digital/DAC_MCP4728_Test/.vscode/extensions.json b/dev/digital/DAC_MCP4728_Test/.vscode/extensions.json deleted file mode 100644 index 080e70d..0000000 --- a/dev/digital/DAC_MCP4728_Test/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "platformio.platformio-ide" - ], - "unwantedRecommendations": [ - "ms-vscode.cpptools-extension-pack" - ] -} diff --git a/dev/digital/DAC_MCP4728_Test/include/README b/dev/digital/DAC_MCP4728_Test/include/README deleted file mode 100644 index 49819c0..0000000 --- a/dev/digital/DAC_MCP4728_Test/include/README +++ /dev/null @@ -1,37 +0,0 @@ - -This directory is intended for project header files. - -A header file is a file containing C declarations and macro definitions -to be shared between several project source files. You request the use of a -header file in your project source file (C, C++, etc) located in `src` folder -by including it, with the C preprocessing directive `#include'. - -```src/main.c - -#include "header.h" - -int main (void) -{ - ... -} -``` - -Including a header file produces the same results as copying the header file -into each source file that needs it. Such copying would be time-consuming -and error-prone. With a header file, the related declarations appear -in only one place. If they need to be changed, they can be changed in one -place, and programs that include the header file will automatically use the -new version when next recompiled. The header file eliminates the labor of -finding and changing all the copies as well as the risk that a failure to -find one copy will result in inconsistencies within a program. - -In C, the convention is to give header files names that end with `.h'. - -Read more about using header files in official GCC documentation: - -* Include Syntax -* Include Operation -* Once-Only Headers -* Computed Includes - -https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/dev/digital/DAC_MCP4728_Test/lib/README b/dev/digital/DAC_MCP4728_Test/lib/README deleted file mode 100644 index 9379397..0000000 --- a/dev/digital/DAC_MCP4728_Test/lib/README +++ /dev/null @@ -1,46 +0,0 @@ - -This directory is intended for project specific (private) libraries. -PlatformIO will compile them to static libraries and link into the executable file. - -The source code of each library should be placed in a separate directory -("lib/your_library_name/[Code]"). - -For example, see the structure of the following example libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -Example contents of `src/main.c` using Foo and Bar: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -The PlatformIO Library Dependency Finder will find automatically dependent -libraries by scanning project source files. - -More information about PlatformIO Library Dependency Finder -- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/dev/digital/DAC_MCP4728_Test/platformio.ini b/dev/digital/DAC_MCP4728_Test/platformio.ini deleted file mode 100644 index 53d7357..0000000 --- a/dev/digital/DAC_MCP4728_Test/platformio.ini +++ /dev/null @@ -1,16 +0,0 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - -[env:esp32-s3-devkitm-1] -platform = espressif32 -board = esp32-s3-devkitm-1 -framework = arduino -build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_JTAG_ON_BOOT=1 -lib_deps = adafruit/Adafruit MCP4728@^1.0.10 diff --git a/dev/digital/DAC_MCP4728_Test/src/main.cpp b/dev/digital/DAC_MCP4728_Test/src/main.cpp deleted file mode 100644 index 1653194..0000000 --- a/dev/digital/DAC_MCP4728_Test/src/main.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include - -#define BAUDRATE 115200 - -#define I2C_SDA 15 -#define I2C_SCL 16 -#define I2C_FREQ 400000 - -#define DAC_MAX 4095 -#define DAC_MIN 0 -#define VREF_mV 2048 - -#define VREF_CONF_IN_USE MCP4728_VREF_INTERNAL - -#define PIN_BTN 40 - -Adafruit_MCP4728 mcp; - -static uint16_t value_mV = VREF_mV; - -bool button_state_old = false; -bool button_state_now = false; - -uint16_t voltage(uint16_t mV) -{ - return map(mV, 0, VREF_mV, DAC_MIN, DAC_MAX); -} - -void setup() -{ - Serial.begin(BAUDRATE); - delay(1000); - Serial.print("\n\rMCP4728 DAC Test"); - - Wire.begin(I2C_SDA, I2C_SCL); - Wire.setClock(I2C_FREQ); - - pinMode(PIN_BTN, INPUT); - - Serial.print("\n\rInitialisiere MCP4728..."); - uint8_t attempts = 0; - while (!mcp.begin()) - { - if(attempts > 20) - { - Serial.print("\n\rError: Es konnte nach 20 Versuchen kein IC gefunden werden. Überprüfe die Verkabelung und starte den ESP neu!"); - for(;;); - } - Serial.print("."); - delay(100); - attempts++; - } - Serial.print("\n\rErfolgreich verbunden!"); - - mcp.setChannelValue(MCP4728_CHANNEL_A, 4095, VREF_CONF_IN_USE); - Serial.print("\n\rDAC-Kanal A auf 0 gesetzt"); - delay(1000); -} - - -void loop() -{ - button_state_now = digitalRead(PIN_BTN); - if (button_state_now != button_state_old) - { - delay(10); // debounce delay - button_state_now = digitalRead(PIN_BTN); - if (button_state_now != button_state_old) - { - button_state_old = button_state_now; - if (button_state_now == HIGH) - { - if(value_mV < 2000) value_mV += (1000/12); - if(value_mV >= 2000) value_mV = 0; - - if (mcp.setChannelValue(MCP4728_CHANNEL_A, voltage(value_mV), VREF_CONF_IN_USE)) Serial.printf("DAC A = %u -> mV: %u\r\n", voltage(value_mV), value_mV); - else Serial.println("Error: Neuer MCP4728 Wert konnte nicht gesetzt werden!"); - } - } - } -} diff --git a/dev/digital/DAC_MCP4728_Test/test/README b/dev/digital/DAC_MCP4728_Test/test/README deleted file mode 100644 index 9b1e87b..0000000 --- a/dev/digital/DAC_MCP4728_Test/test/README +++ /dev/null @@ -1,11 +0,0 @@ - -This directory is intended for PlatformIO Test Runner and project tests. - -Unit Testing is a software testing method by which individual units of -source code, sets of one or more MCU program modules together with associated -control data, usage procedures, and operating procedures, are tested to -determine whether they are fit for use. Unit testing finds problems early -in the development cycle. - -More information about PlatformIO Unit Testing: -- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html diff --git a/dev/digital/Firmware_TEST/include/FIRMWARE.h b/dev/digital/Firmware_TEST/include/FIRMWARE.h index 0d594ef..4b0b99e 100644 --- a/dev/digital/Firmware_TEST/include/FIRMWARE.h +++ b/dev/digital/Firmware_TEST/include/FIRMWARE.h @@ -18,6 +18,7 @@ #define MS_DEBOUNCE 20 #define N_MAX_DAC_CH 4 +#define N_MAX_SEQUENCE_STEPS 128 struct Key { @@ -25,6 +26,12 @@ struct Key int col; }; +struct voltageDurationPair +{ + uint16_t voltage; + uint16_t duration; +}; + const Key NOT_A_KEY = {-1, -1}; bool isNotKey(Key k); @@ -83,4 +90,58 @@ class CV uint8_t _getKeyToVoltageIndex(Key k); }; +class SequencerBlock +{ + public: + SequencerBlock(uint16_t maxDurationMS, uint16_t timeoutMS); + + // Aufnahme-Funktionen + void startRecord(); + void stopRecord(); + void addStep(uint16_t voltage); + bool isRecording(); + + // Wiedergabe-Funktionen + void startPlay(); + void stopPlay(); + void update(); // Muss regelmäßig aufgerufen werden + bool isPlaying(); + + // Sequenz-Verwaltung + void clear(); + void setLoop(bool loop); + + // Status-Abfragen + bool timeLimitReached(); + uint8_t getStepCount(); + uint16_t getCurrentVoltage(); + uint16_t getTotalDuration(); + + private: + // Sequenz-Speicher + voltageDurationPair _sequence[N_MAX_SEQUENCE_STEPS]; + uint8_t _stepCount; + uint8_t _currentStep; + + // Zeitverwaltung + uint16_t _maxDurationMS; + uint16_t _timeoutMS; + unsigned long _recordStartTime; + unsigned long _lastStepTime; + unsigned long _playStartTime; + unsigned long _stepStartTime; + + // Status-Flags + bool _isRecording; + bool _isPlaying; + bool _loop; + + // Letzte aufgenommene Spannung + uint16_t _lastVoltage; + + // Hilfsfunktionen + void _finishCurrentStep(); + bool _canAddStep(); +}; + #endif \ No newline at end of file diff --git a/dev/digital/Firmware_TEST/include/FIRMWARE_DEF.h b/dev/digital/Firmware_TEST/include/FIRMWARE_DEF.h index 7448927..43d5b43 100644 --- a/dev/digital/Firmware_TEST/include/FIRMWARE_DEF.h +++ b/dev/digital/Firmware_TEST/include/FIRMWARE_DEF.h @@ -14,6 +14,7 @@ #define N_KEYBOARD_ROW 4 #define N_KEYBOARD_COL 3 #define N_CV_GATES 2 +#define N_SB 2 #define BAUDRATE 115200 // PIN DEFENTITIONS // I2C PINS @@ -24,7 +25,15 @@ #define PIN_K_R1 8 #define PIN_K_R2 9 #define PIN_K_R3 10 +#define PIN_K_R4 // NOT IN USE #define PIN_K_C0 1 #define PIN_K_C1 2 #define PIN_K_C2 4 +#define PIN_K_C3 // NOT IN USE +#define PIN_K_C4 // NOT IN USE +// SEQUENCER BUTTON PINS +#define PIN_SB_1_REC 0 +#define PIN_SB_1_PLAY 0 +#define PIN_SB_2_REC 0 +#define PIN_SB_2_PLAY 0 #endif \ No newline at end of file diff --git a/dev/digital/Firmware_TEST/src/FIRMWARE.cpp b/dev/digital/Firmware_TEST/src/FIRMWARE.cpp index 9015260..c8b859d 100644 --- a/dev/digital/Firmware_TEST/src/FIRMWARE.cpp +++ b/dev/digital/Firmware_TEST/src/FIRMWARE.cpp @@ -8,6 +8,8 @@ #include "FIRMWARE.h" +// ==================== Helper-Functions ==================== + bool isNotKey(Key k) { if((k.row == NOT_A_KEY.row) && (k.col == NOT_A_KEY.col)) return true; @@ -20,6 +22,8 @@ bool isEqualKey(Key k1, Key k2) else return false; } +// ==================== Keyboard ==================== + Keyboard::Keyboard(uint8_t nRows, uint8_t nCols, uint8_t *pinsRow, uint8_t *pinsCol) { _nRows = nRows; @@ -171,6 +175,8 @@ void Keyboard::_removeActiveKey(uint8_t row, uint8_t col) } } +// ==================== CV ==================== + /*! * @param dac Adafruit_MCP4728 object * @param wire TwoWire object @@ -232,4 +238,264 @@ uint8_t CV::_getKeyToVoltageIndex(uint8_t row, uint8_t col) uint8_t CV::_getKeyToVoltageIndex(Key k) { return (k.row*_col + k.col); +} + +// ==================== SequencerBlock ==================== + + +/*! +* @param maxDurationMS maximum loop duration of recording in milliseconds +* @param timeoutMS stops recording after timeout in milliseconds +* @brief TODO +*/ +SequencerBlock::SequencerBlock(uint16_t maxDurationMS, uint16_t timeoutMS) +{ + _maxDurationMS = maxDurationMS; + _timeoutMS = timeoutMS; + _stepCount = 0; + _currentStep = 0; + _isRecording = false; + _isPlaying = false; + _loop = false; + _lastVoltage = 0; + _recordStartTime = 0; + _lastStepTime = 0; + _playStartTime = 0; + _stepStartTime = 0; +} + +/*! +* @brief starts sequence block recording +*/ +void SequencerBlock::startRecord() +{ + if(_isPlaying) stopPlay(); + + clear(); + _isRecording = true; + _recordStartTime = millis(); + _lastStepTime = _recordStartTime; + _lastVoltage = 0; +} + +/*! +* @brief stops sequence block recording and saves it +*/ +void SequencerBlock::stopRecord() +{ + if(!_isRecording) return; + + _finishCurrentStep(); + _isRecording = false; +} + +/*! +* @brief adds step to sequencer block +* @param voltage voltage step for CV-Gate in millivolts +*/ +void SequencerBlock::addStep(uint16_t voltage) +{ + if(!_isRecording) return; + if(!_canAddStep()) return; + + unsigned long now = millis(); + + // Wenn sich die Spannung geändert hat, vorherigen Schritt abschließen + if(voltage != _lastVoltage && _stepCount > 0) + { + _finishCurrentStep(); + } + + // Neuen Schritt beginnen oder vorhandenen aktualisieren + if(voltage != _lastVoltage || _stepCount == 0) + { + if(_canAddStep()) + { + _sequence[_stepCount].voltage = voltage; + _sequence[_stepCount].duration = 0; + _lastStepTime = now; + _lastVoltage = voltage; + } + } + else + { + if(_stepCount > 0) + { + _sequence[_stepCount - 1].duration = now - _lastStepTime; + } + } +} + +/*! +* @brief checks if sequencer block is recording +* @return true or false +*/ +bool SequencerBlock::isRecording() +{ + return _isRecording; +} + +/*! +* @brief starts playing sequencer block +*/ +void SequencerBlock::startPlay() +{ + if(_stepCount == 0) return; + if(_isRecording) stopRecord(); + + _isPlaying = true; + _currentStep = 0; + _playStartTime = millis(); + _stepStartTime = _playStartTime; +} + +/*! +* @brief stops playing sequencer block +*/ +void SequencerBlock::stopPlay() +{ + _isPlaying = false; + _currentStep = 0; +} + +/*! +* @brief updates sequencer block +* @attention Has to be called every cycle! +*/ +void SequencerBlock::update() +{ + if(!_isPlaying || _stepCount == 0) return; + + unsigned long now = millis(); + unsigned long elapsed = now - _stepStartTime; + + // Prüfen ob aktueller Schritt abgelaufen ist + if(elapsed >= _sequence[_currentStep].duration) + { + _currentStep++; + + // Sequenz-Ende erreicht? + if(_currentStep >= _stepCount) + { + if(_loop) + { + _currentStep = 0; + _stepStartTime = now; + } + else + { + stopPlay(); + return; + } + } + else + { + _stepStartTime = now; + } + } +} + +/*! +* @brief checks if sequencer block is playing +* @return true or false +*/ +bool SequencerBlock::isPlaying() +{ + return _isPlaying; +} + +/*! +* @brief clears recording of sequencer block +*/ +void SequencerBlock::clear() +{ + _stepCount = 0; + _currentStep = 0; + _lastVoltage = 0; + + for(uint8_t i = 0; i < N_MAX_SEQUENCE_STEPS; i++) + { + _sequence[i].voltage = 0; + _sequence[i].duration = 0; + } +} + +/*! +* @brief sets configuation for looping over the recording +* @param loop if set to true, saved recording gets played in a loop +*/ +void SequencerBlock::setLoop(bool loop) +{ + _loop = loop; +} + +/*! +* @brief checks if the recording time limit has been reached +* @return true of false +*/ +bool SequencerBlock::timeLimitReached() +{ + if(!_isRecording) return false; + + unsigned long now = millis(); + unsigned long elapsed = now - _recordStartTime; + + return (elapsed >= _maxDurationMS); +} + +/*! +* @brief returns the currently recoreded steps +* @return uint8_t between 0 and 128 +*/ +uint8_t SequencerBlock::getStepCount() +{ + return _stepCount; +} + +/*! +* @brief if sequencer is playing, returns the current voltage level +* @return uint16_t voltage range for CV +*/ +uint16_t SequencerBlock::getCurrentVoltage() +{ + if(!_isPlaying || _stepCount == 0) return 0; + if(_currentStep >= _stepCount) return 0; + + return _sequence[_currentStep].voltage; +} + +/*! +* @brief gets the length of the recording in the block +* @return uint16_t time in milliseconds +*/ +uint16_t SequencerBlock::getTotalDuration() +{ + uint16_t total = 0; + for(uint8_t i = 0; i < _stepCount; i++) + { + total += _sequence[i].duration; + } + return total; +} + +void SequencerBlock::_finishCurrentStep() +{ + if(_stepCount == 0) return; + + unsigned long now = millis(); + _sequence[_stepCount - 1].duration = now - _lastStepTime; + + // Timeout prüfen - wenn zu lange keine Änderung, Schritt nicht hinzufügen + if(_sequence[_stepCount - 1].duration < _timeoutMS) + { + _stepCount++; + } +} + +bool SequencerBlock::_canAddStep() +{ + if(_stepCount >= N_MAX_SEQUENCE_STEPS) return false; + if(timeLimitReached()) return false; + + return true; } \ No newline at end of file diff --git a/dev/digital/Firmware_TEST/src/main.cpp b/dev/digital/Firmware_TEST/src/main.cpp index 5a77097..1e5990e 100644 --- a/dev/digital/Firmware_TEST/src/main.cpp +++ b/dev/digital/Firmware_TEST/src/main.cpp @@ -1,5 +1,13 @@ /* -* Example Code Two +* Example Code Three +* with Sequencer +* +* Bedienung: +* - Keyboard-Tasten: CV-Ausgabe direkt oder Recording +* - PIN_SB_1_REC: Sequencer 1 Record Start/Stop +* - PIN_SB_1_PLAY: Sequencer 1 Play/Stop +* - PIN_SB_2_REC: Sequencer 2 Record Start/Stop +* - PIN_SB_2_PLAY: Sequencer 2 Play/Stop */ #include "FIRMWARE_DEF.h" #include "FIRMWARE.h" @@ -11,7 +19,7 @@ Keyboard keyboard(N_KEYBOARD_ROW, N_KEYBOARD_COL, pins_keyboard_row, pins_keyboa Adafruit_MCP4728 MCP4728; MCP4728_channel_t cvMap[N_CV_GATES] = {MCP4728_CHANNEL_A, MCP4728_CHANNEL_B}; -uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { +uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { /* 83mV = 1/12V */ 1*83, 5*83, 9*83, 2*83, 6*83, 10*83, 3*83, 7*83, 11*83, @@ -20,41 +28,251 @@ uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { CV cv(&MCP4728, &Wire, N_CV_GATES, cvMap, keyToVoltage, N_KEYBOARD_ROW, N_KEYBOARD_COL); +SequencerBlock sb1(30000, 250); // 30 Sekunden max, 250ms Timeout +SequencerBlock sb2(30000, 250); + +// Button States +struct ButtonState { + bool current; + bool last; + unsigned long lastDebounceTime; +}; + +ButtonState btn_sb1_rec; +ButtonState btn_sb1_play; +ButtonState btn_sb2_rec; +ButtonState btn_sb2_play; + +const unsigned long DEBOUNCE_DELAY = 50; + +// Hilfsfunktion zum Lesen eines Buttons mit Debouncing +bool readButton(byte pin, ButtonState &state) +{ + bool reading = digitalRead(pin) == LOW; // LOW = gedrückt (mit Pull-Up) + bool buttonPressed = false; + + if(reading != state.last) + { + state.lastDebounceTime = millis(); + } + + if((millis() - state.lastDebounceTime) > DEBOUNCE_DELAY) + { + if(reading != state.current) + { + state.current = reading; + if(state.current == true) // Button wurde gerade gedrückt + { + buttonPressed = true; + } + } + } + + state.last = reading; + return buttonPressed; +} + +void initButtons() +{ + pinMode(PIN_SB_1_REC, INPUT_PULLUP); + pinMode(PIN_SB_1_PLAY, INPUT_PULLUP); + pinMode(PIN_SB_2_REC, INPUT_PULLUP); + pinMode(PIN_SB_2_PLAY, INPUT_PULLUP); + + btn_sb1_rec.current = false; + btn_sb1_rec.last = false; + btn_sb1_rec.lastDebounceTime = 0; + + btn_sb1_play.current = false; + btn_sb1_play.last = false; + btn_sb1_play.lastDebounceTime = 0; + + btn_sb2_rec.current = false; + btn_sb2_rec.last = false; + btn_sb2_rec.lastDebounceTime = 0; + + btn_sb2_play.current = false; + btn_sb2_play.last = false; + btn_sb2_play.lastDebounceTime = 0; +} + +void handleSequencerButtons() +{ + // Sequencer 1 Record Button + if(readButton(PIN_SB_1_REC, btn_sb1_rec)) + { + if(sb1.isRecording()) + { + sb1.stopRecord(); + Serial.printf("\n\r[SEQ1] Recording stopped. Steps: %i, Duration: %ims", + sb1.getStepCount(), sb1.getTotalDuration()); + } + else + { + if(sb1.isPlaying()) sb1.stopPlay(); + sb1.startRecord(); + Serial.printf("\n\r[SEQ1] Recording started..."); + } + } + + // Sequencer 1 Play Button + if(readButton(PIN_SB_1_PLAY, btn_sb1_play)) + { + if(sb1.isPlaying()) + { + sb1.stopPlay(); + Serial.printf("\n\r[SEQ1] Playback stopped"); + } + else + { + if(sb1.isRecording()) sb1.stopRecord(); + sb1.startPlay(); + Serial.printf("\n\r[SEQ1] Playback started"); + } + } + + // Sequencer 2 Record Button + if(readButton(PIN_SB_2_REC, btn_sb2_rec)) + { + if(sb2.isRecording()) + { + sb2.stopRecord(); + Serial.printf("\n\r[SEQ2] Recording stopped. Steps: %i, Duration: %ims", + sb2.getStepCount(), sb2.getTotalDuration()); + } + else + { + if(sb2.isPlaying()) sb2.stopPlay(); + sb2.startRecord(); + Serial.printf("\n\r[SEQ2] Recording started..."); + } + } + + // Sequencer 2 Play Button + if(readButton(PIN_SB_2_PLAY, btn_sb2_play)) + { + if(sb2.isPlaying()) + { + sb2.stopPlay(); + Serial.printf("\n\r[SEQ2] Playback stopped"); + } + else + { + if(sb2.isRecording()) sb2.stopRecord(); + sb2.startPlay(); + Serial.printf("\n\r[SEQ2] Playback started"); + } + } +} + void setup() { - Serial.begin(BAUDRATE); - keyboard.begin(); - cv.begin(PIN_SDA, PIN_SCL); + Serial.begin(BAUDRATE); + keyboard.begin(); + cv.begin(PIN_SDA, PIN_SCL); + initButtons(); + + sb1.setLoop(false); + sb2.setLoop(false); + + Serial.printf("\n\r=== Sequencer System Started ==="); + Serial.printf("\n\rControls:"); + Serial.printf("\n\r PIN_SB_1_REC: SEQ1 Record Start/Stop"); + Serial.printf("\n\r PIN_SB_1_PLAY: SEQ1 Play/Stop"); + Serial.printf("\n\r PIN_SB_2_REC: SEQ2 Record Start/Stop"); + Serial.printf("\n\r PIN_SB_2_PLAY: SEQ2 Play/Stop"); + Serial.printf("\n\r================================\n\r"); } void loop() { - keyboard.update(); - - int n = keyboard.getQueueLength(); - - if(n > 0) - { - Serial.printf("\n\rCurrent queue length: %i", n); - if(n == 1) + keyboard.update(); + handleSequencerButtons(); + + // Sequencer Update (für Wiedergabe) + sb1.update(); + sb2.update(); + + int n = keyboard.getQueueLength(); + + // Keyboard-Tasten verarbeiten + if(n > 0) { - cv.setVoltage(0, keyboard.getQueue(0)); - cv.setVoltage(1, NOT_A_KEY); + // Alle Keyboard-Tasten für CV-Ausgabe verwenden + int cvIndex = 0; + for(int i = 0; i < n && cvIndex < N_CV_GATES; i++) + { + Key k = keyboard.getQueue(i); + if(!isNotKey(k)) + { + uint16_t voltage = keyToVoltage[k.row * N_KEYBOARD_COL + k.col]; + + // Bei Recording: Spannung aufnehmen + if(sb1.isRecording()) + { + sb1.addStep(voltage); + } + if(sb2.isRecording()) + { + sb2.addStep(voltage); + } + + // Live-Ausgabe nur wenn nicht gerade wiedergegeben wird + if(!sb1.isPlaying() && !sb2.isPlaying()) + { + cv.setVoltage(cvIndex++, k); + } + } + } + + // Restliche CV-Ausgänge auf 0 setzen wenn live gespielt wird + if(!sb1.isPlaying() && !sb2.isPlaying()) + { + for(int i = cvIndex; i < N_CV_GATES; i++) + { + cv.setVoltage(i, 0); + } + } } - else if(n >= 2) + else { - cv.setVoltage(0, keyboard.getQueue(0)); - cv.setVoltage(1, keyboard.getQueue(1)); + // Keine Tasten gedrückt + if(sb1.isRecording()) + { + sb1.addStep(0); + } + if(sb2.isRecording()) + { + sb2.addStep(0); + } + + if(!sb1.isPlaying() && !sb2.isPlaying()) + { + cv.clearAll(); + } } - - for(int i = 0; (i < N_CV_GATES) && (i < n); i++) + + // Sequencer-Wiedergabe auf CV-Ausgänge + if(sb1.isPlaying()) { - Key k = keyboard.getQueue(i); - if(isNotKey(k)) Serial.printf("\n\rQueue position %i: NOT A KEY", i); - else Serial.printf("\n\rQueue position %i: R%iC%i", i, k.row, k.col); + cv.setVoltage(0, sb1.getCurrentVoltage()); } - } - else cv.clearAll(); - - delay(50); + if(sb2.isPlaying()) + { + cv.setVoltage(1, sb2.getCurrentVoltage()); + } + + // Time-Limit Warnung + if(sb1.isRecording() && sb1.timeLimitReached()) + { + sb1.stopRecord(); + Serial.printf("\n\r[SEQ1] Time limit reached! Recording stopped."); + } + if(sb2.isRecording() && sb2.timeLimitReached()) + { + sb2.stopRecord(); + Serial.printf("\n\r[SEQ2] Time limit reached! Recording stopped."); + } + + delay(10); // Kürzeres Delay für bessere Sequencer-Auflösung } \ No newline at end of file diff --git a/dev/digital/Firmware_TEST/src/main.cpp.2 b/dev/digital/Firmware_TEST/src/main.cpp.2 new file mode 100644 index 0000000..5a77097 --- /dev/null +++ b/dev/digital/Firmware_TEST/src/main.cpp.2 @@ -0,0 +1,60 @@ +/* +* Example Code Two +*/ +#include "FIRMWARE_DEF.h" +#include "FIRMWARE.h" + +static byte pins_keyboard_row[N_KEYBOARD_ROW] = {PIN_K_R0, PIN_K_R1, PIN_K_R2, PIN_K_R3}; +static byte pins_keyboard_col[N_KEYBOARD_COL] = {PIN_K_C0, PIN_K_C1, PIN_K_C2}; + +Keyboard keyboard(N_KEYBOARD_ROW, N_KEYBOARD_COL, pins_keyboard_row, pins_keyboard_col); + +Adafruit_MCP4728 MCP4728; +MCP4728_channel_t cvMap[N_CV_GATES] = {MCP4728_CHANNEL_A, MCP4728_CHANNEL_B}; +uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { + 1*83, 5*83, 9*83, + 2*83, 6*83, 10*83, + 3*83, 7*83, 11*83, + 4*83, 8*83, 12*83 +}; + +CV cv(&MCP4728, &Wire, N_CV_GATES, cvMap, keyToVoltage, N_KEYBOARD_ROW, N_KEYBOARD_COL); + +void setup() +{ + Serial.begin(BAUDRATE); + keyboard.begin(); + cv.begin(PIN_SDA, PIN_SCL); +} + +void loop() +{ + keyboard.update(); + + int n = keyboard.getQueueLength(); + + if(n > 0) + { + Serial.printf("\n\rCurrent queue length: %i", n); + if(n == 1) + { + cv.setVoltage(0, keyboard.getQueue(0)); + cv.setVoltage(1, NOT_A_KEY); + } + else if(n >= 2) + { + cv.setVoltage(0, keyboard.getQueue(0)); + cv.setVoltage(1, keyboard.getQueue(1)); + } + + for(int i = 0; (i < N_CV_GATES) && (i < n); i++) + { + Key k = keyboard.getQueue(i); + if(isNotKey(k)) Serial.printf("\n\rQueue position %i: NOT A KEY", i); + else Serial.printf("\n\rQueue position %i: R%iC%i", i, k.row, k.col); + } + } + else cv.clearAll(); + + delay(50); +} \ No newline at end of file