diff --git a/dev/digital/Firmware_TEST/README.md b/dev/digital/Firmware_TEST/README.md new file mode 100644 index 0000000..3b5ac47 --- /dev/null +++ b/dev/digital/Firmware_TEST/README.md @@ -0,0 +1,456 @@ +# Firmware + +## Inhaltsverzeichnis + +1. [Projektübersicht](#projektübersicht) +2. [Systemarchitektur](#systemarchitektur) +3. [Komponenten](#komponenten) +4. [Speicheranalyse](#speicheranalyse) +5. [Installation](#installation) +6. [Verwendung](#verwendung) +7. [Hauptablauf](#hauptablauf) + +--- + +## Projektübersicht + +### Features + +- ✅ **Dual-Channel CV-Sequencer** - 2 unabhängige Control Voltage Ausgänge +- ✅ **4×3 Tastaturmatrix** - Echtzeit-Tasten-Eingabe mit Entprellung +- ✅ **Recording & Playback** - Speichere Sequenzen bis 30 Sekunden +- ✅ **Loop-Funktion** - Endlose Wiederholung oder Einmaledition +- ✅ **Live-Modus** - Direkte Tastatur-zu-CV Ausgabe +- ✅ **Multi-Key Support** - Bis zu 10 gleichzeitig aktive Tasten + +### Hardware + +| Komponente | Modell | Funktion | +|-----------|--------|---------| +| Microcontroller | ESP32 | Hauptprozessor | +| DAC | MCP4728 | 4-Kanal 12-Bit DAC | +| I2C Bus | - | Kommunikation MCU ↔ DAC | +| Tastatur | 4×3 Matrix | Benutzereingabe | +| Buttons | 4× Push-Buttons | Record/Play Steuerung | + +--- + +## Systemarchitektur + +### 3-Schicht-Modell + +``` +┌─────────────────────────────────────────────────────┐ +│ INPUT LAYER (📥) │ +├──────────────────────┬──────────────────────────────┤ +│ Keyboard Matrix │ Sequencer Buttons │ +│ (4×3 Tasten) │ (Record/Play × 2) │ +└──────────────┬───────┴──────────────┬───────────────┘ + │ │ +┌──────────────▼─────────────────────▼───────────────┐ +│ PROCESSING LAYER (⚙️) │ +├──────────────────────┬──────────────────────────────┤ +│ Keyboard Klasse │ SequencerBlock (2×) │ +│ (Queue-Management) │ (Recording & Playback) │ +└──────────────┬───────┴──────────────┬───────────────┘ + │ │ +┌──────────────▼─────────────────────▼───────────────┐ +│ OUTPUT LAYER (📤) │ +├──────────────────────┬──────────────────────────────┤ +│ CV Klasse (DAC) │ CV Ausgänge │ +│ MCP4728 I2C │ (A=Ch1, B=Ch2) │ +└──────────────┬───────┴──────────────┬───────────────┘ + │ │ + └──────────────┬───────┘ + ▼ + Externe Synthesizer / Module +``` + +### Datenfluss + +``` +TASTATUR → KEYBOARD → SEQUENCER/LIVE → DAC → CV AUSGÄNGE + ↑ + BUTTONS +``` + +--- + +## Komponenten + +### 1. Keyboard Klasse + +**Funktion:** Verwaltet die 4×3 Matrix-Tastatur mit Entprellung + +```cpp +class Keyboard { + public: + Keyboard(uint8_t nRows, uint8_t nCols, uint8_t *pinsRow, uint8_t *pinsCol); + void begin(); + void update(); + int getQueueLength(); + Key getQueue(uint8_t index); + // ... +}; +``` + +**Merkmale:** +- Debounce-Zeit: 20ms +- Max. 10 gleichzeitig aktive Tasten +- FIFO-Queue für Tastenreihenfolge +- Rückmeldung als `Key(row, col)` Struktur + +### 2. CV Klasse + +**Funktion:** Verwaltet DAC-Ausgänge über I2C (MCP4728) + +```cpp +class CV { + public: + CV(Adafruit_MCP4728 *dac, TwoWire *wire, uint8_t nCV, + MCP4728_channel_t *cvChannelMap, uint16_t *keyToVoltage, + uint8_t row, uint8_t col); + bool begin(uint8_t pinSDA, uint8_t pinSCL); + void setVoltage(uint8_t cvIndex, Key k); + void setVoltage(uint8_t cvIndex, uint16_t mV); + void clearAll(); + // ... +}; +``` + +**Merkmale:** +- 2 CV-Kanäle (A, B) +- I2C-Kommunikation (Pins 15, 16) +- Voltage-Mapping von Tasten +- Range: 0-4096mV (12-Bit) + +### 3. SequencerBlock Klasse + +**Funktion:** Speichert und spielt Sequenzen auf 2 Kanälen + +```cpp +class SequencerBlock { + public: + SequencerBlock(uint16_t maxDurationMS, uint16_t minStepDurationMS); + + // Recording + void startRecord(); + void stopRecord(); + void addStep(uint16_t voltage_ch1, uint16_t voltage_ch2); + + // Playback + void startPlay(); + void stopPlay(); + void update(); // Must be called regularly + void setLoop(bool loop); + + // Status + uint8_t getStepCount(); + uint16_t getCurrentVoltageCh1(); + uint16_t getCurrentVoltageCh2(); + // ... +}; +``` + +**Merkmale:** +- Max. 128 Steps pro Sequenz +- Max. 30 Sekunden Aufnahmetime +- Dual-Channel Recording +- Loop-Funktion +- Automatisches Time-Limit + +### 4. Button-Verarbeitung + +**3-Mode Play-Button-System:** + +| Click | Aktion | Zustand | +|-------|--------|---------| +| 1x | Play (keine Loop) | Spielt einmalig ab | +| 2x | Loop aktivieren | Endlosschleife | +| 3x | Stop | Stoppt Wiedergabe | + +**Record-Button:** Toggle zwischen Recording starten/stoppen + +--- + +## Speicheranalyse +```bash +RAM: [= ] 6.5% (used 21176 bytes from 327680 bytes) +Flash: [= ] 8.5% (used 283165 bytes from 3342336 bytes) +``` +### RAM-Verbrauch + +| Komponente | Größe | Menge | Gesamt | Notizen | +|-----------|-------|-------|--------|---------| +| SequencerBlock #1 | ~550 B | 1x | 550 B | 128 Steps × 6 Bytes + Variablen | +| SequencerBlock #2 | ~550 B | 1x | 550 B | 128 Steps × 6 Bytes + Variablen | +| Keyboard Objekt | ~130 B | 1x | 130 B | 8×8 Bool Arrays + Pointer | +| CV Objekt | ~50 B | 1x | 50 B | DAC-Pointer + Config | +| keyToVoltage Array | 24 B | 1x | 24 B | 12 Keys × uint16_t | +| Button States | ~50 B | 1x | 50 B | 4 Buttons × ~12 Bytes | +| Lokale Variablen | ~100 B | 1x | 100 B | Loop-Variablen | +| **GESAMT (Schätzung)** | - | - | **~1.5 KB** | - | + +### Flash-Verbrauch + +| Komponente | Größe | +|-----------|-------| +| Arduino/Wire Libraries | ~150 KB | +| Adafruit_MCP4728 | ~20 KB | +| Firmware Code | ~80 KB | +| Bootloader | ~60 KB | +| **GESAMT (Schätzung)** | **~310 KB** | + + +### Sequenzen-Speicher Detail + +```cpp +struct DualVoltageDurationPair { + uint16_t voltage_ch1; // 2 Bytes + uint16_t voltage_ch2; // 2 Bytes + uint16_t duration; // 2 Bytes +}; // = 6 Bytes pro Step + +// Berechnung: +// - N_MAX_SEQUENCE_STEPS = 128 +// - 128 Steps × 6 Bytes = 768 Bytes pro Sequenz +// - 2 Sequenzer = 1536 Bytes (1.5 KB) +``` + +### Optimierungspotential + +| Feature | Größenänderung | Status | +|---------|-----------------|--------| +| 256 Steps statt 128 | +768 B | ✅ Problemlos möglich | +| 60s statt 30s Limit | 0 B | ✅ Kostenloses Upgrade | +| 4 Sequenzer statt 2 | +3.3 KB | ✅ Problemlos möglich | +| 8×8 Tastatur statt 4×3 | ~ +30 B | ✅ Kaum Mehraufwand | + +--- + +## Verwendung + +### Grundlegende Konfiguration + +`include/FIRMWARE_DEF.h`: + +```cpp +#define N_KEYBOARD_ROW 4 // Keyboard Reihen +#define N_KEYBOARD_COL 3 // Keyboard Spalten +#define N_CV_GATES 2 // CV-Ausgänge +#define N_SB 2 // Sequencer + +// I2C Pins +#define PIN_SDA 15 +#define PIN_SCL 16 + +// Keyboard Pins (Reihen) +#define PIN_K_R0 7 +#define PIN_K_R1 8 +#define PIN_K_R2 9 +#define PIN_K_R3 10 + +// Keyboard Pins (Spalten) +#define PIN_K_C0 1 +#define PIN_K_C1 2 +#define PIN_K_C2 4 +``` + +### Spannung-Mapping + +`src/main.cpp`: + +```cpp +// Voltage für jede Tastaturposition (in 1/12V = 83mV Schritten = 1 Halbtonschritt) +uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { + 1*83, 5*83, 9*83, // Row 0: C, E, G + 2*83, 6*83, 10*83, // Row 1: D, F, A + 3*83, 7*83, 11*83, // Row 2: E, G, B + 4*83, 8*83, 12*83 // Row 3: F, A, C (Oktave) +}; +``` + +--- + +## Hauptablauf + +### Main Loop Flowchart + +``` +START + ↓ +SETUP (Initialisierung) + ↓ +┌─── MAIN LOOP ───────────────────────┐ +│ │ +├─ Keyboard Update │ +│ (Tasten auslesen) │ +│ │ +├─ Button Handler │ +│ (Record/Play Buttons) │ +│ │ +├─ Sequencer Update │ +│ (sb1 & sb2 Playback) │ +│ │ +├─ Spannungen bestimmen │ +│ voltage_ch1 = Queue[0] │ +│ voltage_ch2 = Queue[1] │ +│ │ +├─ Recording Check │ +│ IF Recording: addStep() │ +│ │ +├─ Output Priority │ +│ IF sb1.playing() → Output SEQ1 │ +│ ELSE IF sb2.playing() → Output SEQ2│ +│ ELSE → Output Live │ +│ │ +├─ Time-Limit Check │ +│ IF Limit reached: Stop Record │ +│ │ +├─ Delay 10ms │ +│ │ +└─────────────────────────────────────┘ + ↓ + [Loop zurück] +``` + +### State Machine + +| State | Beschreibung | Aktion | +|-------|-------------|--------| +| **IDLE** | Leerlauf | Liest Tasten, gibt Live-Spannungen aus | +| **REC** | Recording aktiv | Speichert Sequenzen, überwacht Zeit-Limits | +| **PLAY** | Playback aktiv | Gibt Sequenzen aus, verwaltet Step-Übergänge | +| **LOOP** | Endlosschleife | Wiederholt Sequenz nahtlos | + +### Ausgabe-Prioritätssystem + +``` +1. Sequencer 1 Playing? + ├─ JA → Gebe SEQ1 Voltages aus (höchste Priorität) + └─ NEIN ↓ + +2. Sequencer 2 Playing? + ├─ JA → Gebe SEQ2 Voltages aus (zweite Priorität) + └─ NEIN ↓ + +3. Live Input + └─ Gebe Tasten-Voltages aus (Standard) +``` + +Dies ermöglicht nahtlose Übergänge und verhindert Konflikte. + +--- + +## Code-Beispiele + +### Beispiel 1: Live-Modus (main.cpp.1) + +Direkte Tastatur-zu-CV Verbindung ohne Sequencer: + +```cpp +void loop() { + keyboard.update(); + + int n = keyboard.getQueueLength(); + + if(n > 0) { + for(int i = 0; (i < N_CV_GATES) && (i < n); i++) { + Key k = keyboard.getQueue(i); + cv.setVoltage(i, k); // Taste direkt auf CV ausgeben + } + } else { + cv.clearAll(); // Keine Taste → 0V + } + + delay(50); +} +``` + +### Beispiel 2: Dual-Channel Sequencer (main.cpp) + +Vollständiger Sequencer mit 2 unabhängigen Kanälen: + +```cpp +void loop() { + keyboard.update(); + handleSequencerButtons(); + + sb1.update(); + sb2.update(); + + // ... Voltage determination ... + + // Recording + if(sb1.isRecording()) { + sb1.addStep(voltage_ch1, voltage_ch2); + } + if(sb2.isRecording()) { + sb2.addStep(voltage_ch1, voltage_ch2); + } + + // Output mit Priorität + if(sb1.isPlaying()) { + cv.setVoltage(0, sb1.getCurrentVoltageCh1()); + cv.setVoltage(1, sb1.getCurrentVoltageCh2()); + } + else if(sb2.isPlaying()) { + cv.setVoltage(0, sb2.getCurrentVoltageCh1()); + cv.setVoltage(1, sb2.getCurrentVoltageCh2()); + } + else { + cv.setVoltage(0, voltage_ch1); + cv.setVoltage(1, voltage_ch2); + } + + delay(10); +} +``` + +--- + +## Dateistruktur + +``` +project/ +├── include/ +│ ├── FIRMWARE.h # Klassen-Definitionen +│ └── FIRMWARE_DEF.h # Konstanten & Pin-Definitionen +│ +└── src/ + ├── main.cpp # Dual-Channel Sequencer Beispiel + ├── main.cpp.1 # Live-Modus Beispiel + ├── main.cpp.2 # Dual-Channel ohne Sequencer + └── FIRMWARE.cpp # Implementierungen + +``` + +--- + +## Technische Spezifikationen + +### Timings + +| Parameter | Wert | Funktion | +|-----------|------|----------| +| Keyboard Debounce | 20ms | Anti-Prellen Verzögerung | +| Button Debounce | 50ms | Button Anti-Prellen | +| Main Loop Delay | 10ms | Update-Rate | +| Max Recording | 30s | Zeit-Limit pro Sequenz | +| Min Step Duration | 50ms | Minimale Step-Länge | + +### Voltage-Mapping + +Verwendet gleichmäßige 83mV Schritte (1/12 Oktave): + +``` +Key (1,0) = 1 × 83mV = 83mV (C) +Key (1,1) = 5 × 83mV = 415mV (E) +Key (1,2) = 9 × 83mV = 747mV (G) +... etc +Key (4,2) = 12 × 83mV = 996mV (C') +``` + +--- + +**Zuletzt aktualisiert:** 2025-11-30 \ No newline at end of file