Cleaned up Schematics

This commit is contained in:
2026-03-18 11:01:39 +01:00
parent f0c2168e2b
commit a24b15c27b
493 changed files with 3025728 additions and 3024156 deletions

View File

@@ -1,8 +1,8 @@
.pio/libdeps
.pio/build/project*
.pio/build/esp32-s3-devkitm-1/*
!.pio/build/esp32-s3-devkitm-1/firmware.bin
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.pio/libdeps
.pio/build/project*
.pio/build/esp32-s3-devkitm-1/*
!.pio/build/esp32-s3-devkitm-1/firmware.bin
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@@ -1,10 +1,10 @@
{
// 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"
]
}
{
// 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"
]
}

View File

@@ -1,456 +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 (kein 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')
```
---
# 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 (kein 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

View File

@@ -1,161 +1,161 @@
/*
@file: FIRMARE.h
@author: Erik Tóth
@contact: etoth@tsn.at
@date: 2025-10-26
@updated: 2025-12-06
@brief: Header for FIRMWARE.cpp (FIXED VERSION)
*/
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MCP4728.h>
#ifndef FIRMWARE_H
#define FIRMWARE_H
#define N_MAX_QUEUE 10
#define N_MAX_ROWS 8
#define N_MAX_COLS 8
#define MS_DEBOUNCE 20
#define N_MAX_DAC_CH 4
struct Key
{
int row;
int col;
};
struct DualVoltageDurationPair
{
uint16_t voltage_ch1;
uint16_t voltage_ch2;
uint16_t duration;
bool active; // NEU: true wenn Step aktive Noten hat, false für Pausen
};
const Key NOT_A_KEY = {-1, -1};
bool isNotKey(Key k);
bool isEqualKey(Key k1, Key k2);
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);
private:
uint8_t _nRows;
uint8_t _nCols;
uint8_t *_pinsRow;
uint8_t *_pinsCol;
bool _keyState[N_MAX_COLS][N_MAX_ROWS];
bool _keyStateLatest[N_MAX_COLS][N_MAX_ROWS];
unsigned long _lastChangeTime[N_MAX_COLS][N_MAX_ROWS];
Key _activeKeys[N_MAX_QUEUE];
uint8_t _nActiveKeys;
uint8_t _nSticky;
void _addActiveKey(uint8_t row, uint8_t col);
void _removeActiveKey(uint8_t row, uint8_t col);
bool _inQueue(uint8_t row, uint8_t col);
bool _inQueue(Key k);
bool _isNotKey(Key k);
bool _isEqualKey(Key k1, Key k2);
};
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();
private:
Adafruit_MCP4728 *_dac;
TwoWire *_wire;
uint8_t _nCV;
uint8_t _row;
uint8_t _col;
MCP4728_channel_t _cvChannelMap[N_MAX_DAC_CH];
uint16_t *_keyToVoltage;
uint8_t _getKeyToVoltageIndex(uint8_t row, uint8_t col);
uint8_t _getKeyToVoltageIndex(Key k);
};
class SequencerBlock
{
public:
SequencerBlock(uint16_t maxDurationMS, uint16_t maxStepCount);
// Aufnahme-Funktionen
void startRecord();
void stopRecord();
void addStep(uint16_t voltage_ch1, uint16_t voltage_ch2);
bool isRecording();
// Wiedergabe-Funktionen
void startPlay();
void stopPlay();
void update();
bool isPlaying();
// Sequenz-Verwaltung
void clear();
void setLoop(bool loop);
// Status-Abfragen
bool timeLimitReached();
bool stepLimitReached();
uint16_t getStepCount();
uint16_t getCurrentVoltageCh1();
uint16_t getCurrentVoltageCh2();
bool isCurrentStepActive(); // NEU: Prüft ob aktueller Step aktive Noten hat
uint16_t getTotalDuration();
private:
/*!
* @brief Memory limiting
* @return (uint16_t) 1024
* @attention Increasing the value might lead to an overflow
* @note sizeOf(DualVoltageDurationPair) = 8 Byte ==> 8 Byte * 1024 = 8192 Byte
*/
const static uint16_t _MAX_SEQUENCE_STEPS = 1024;
// Sequenz memory
DualVoltageDurationPair _sequence[_MAX_SEQUENCE_STEPS];
uint16_t _stepCount;
uint16_t _currentStep;
// Time management
uint16_t _maxDurationMS;
uint16_t _maxStepCount;
unsigned long _recordStartTime;
unsigned long _lastStepTime;
unsigned long _playStartTime;
unsigned long _stepStartTime;
unsigned long _lastAddStepTime; // NEU: Rate-Limiting
// Status flags
bool _isRecording;
bool _isPlaying;
bool _loop;
// Last recorded Voltage: at n-th step minus one
uint16_t _lastVoltageCh1;
uint16_t _lastVoltageCh2;
// helper functions
void _finishCurrentStep();
bool _canAddStep();
};
/*
@file: FIRMARE.h
@author: Erik Tóth
@contact: etoth@tsn.at
@date: 2025-10-26
@updated: 2025-12-06
@brief: Header for FIRMWARE.cpp (FIXED VERSION)
*/
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_MCP4728.h>
#ifndef FIRMWARE_H
#define FIRMWARE_H
#define N_MAX_QUEUE 10
#define N_MAX_ROWS 8
#define N_MAX_COLS 8
#define MS_DEBOUNCE 20
#define N_MAX_DAC_CH 4
struct Key
{
int row;
int col;
};
struct DualVoltageDurationPair
{
uint16_t voltage_ch1;
uint16_t voltage_ch2;
uint16_t duration;
bool active; // NEU: true wenn Step aktive Noten hat, false für Pausen
};
const Key NOT_A_KEY = {-1, -1};
bool isNotKey(Key k);
bool isEqualKey(Key k1, Key k2);
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);
private:
uint8_t _nRows;
uint8_t _nCols;
uint8_t *_pinsRow;
uint8_t *_pinsCol;
bool _keyState[N_MAX_COLS][N_MAX_ROWS];
bool _keyStateLatest[N_MAX_COLS][N_MAX_ROWS];
unsigned long _lastChangeTime[N_MAX_COLS][N_MAX_ROWS];
Key _activeKeys[N_MAX_QUEUE];
uint8_t _nActiveKeys;
uint8_t _nSticky;
void _addActiveKey(uint8_t row, uint8_t col);
void _removeActiveKey(uint8_t row, uint8_t col);
bool _inQueue(uint8_t row, uint8_t col);
bool _inQueue(Key k);
bool _isNotKey(Key k);
bool _isEqualKey(Key k1, Key k2);
};
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();
private:
Adafruit_MCP4728 *_dac;
TwoWire *_wire;
uint8_t _nCV;
uint8_t _row;
uint8_t _col;
MCP4728_channel_t _cvChannelMap[N_MAX_DAC_CH];
uint16_t *_keyToVoltage;
uint8_t _getKeyToVoltageIndex(uint8_t row, uint8_t col);
uint8_t _getKeyToVoltageIndex(Key k);
};
class SequencerBlock
{
public:
SequencerBlock(uint16_t maxDurationMS, uint16_t maxStepCount);
// Aufnahme-Funktionen
void startRecord();
void stopRecord();
void addStep(uint16_t voltage_ch1, uint16_t voltage_ch2);
bool isRecording();
// Wiedergabe-Funktionen
void startPlay();
void stopPlay();
void update();
bool isPlaying();
// Sequenz-Verwaltung
void clear();
void setLoop(bool loop);
// Status-Abfragen
bool timeLimitReached();
bool stepLimitReached();
uint16_t getStepCount();
uint16_t getCurrentVoltageCh1();
uint16_t getCurrentVoltageCh2();
bool isCurrentStepActive(); // NEU: Prüft ob aktueller Step aktive Noten hat
uint16_t getTotalDuration();
private:
/*!
* @brief Memory limiting
* @return (uint16_t) 1024
* @attention Increasing the value might lead to an overflow
* @note sizeOf(DualVoltageDurationPair) = 8 Byte ==> 8 Byte * 1024 = 8192 Byte
*/
const static uint16_t _MAX_SEQUENCE_STEPS = 1024;
// Sequenz memory
DualVoltageDurationPair _sequence[_MAX_SEQUENCE_STEPS];
uint16_t _stepCount;
uint16_t _currentStep;
// Time management
uint16_t _maxDurationMS;
uint16_t _maxStepCount;
unsigned long _recordStartTime;
unsigned long _lastStepTime;
unsigned long _playStartTime;
unsigned long _stepStartTime;
unsigned long _lastAddStepTime; // NEU: Rate-Limiting
// Status flags
bool _isRecording;
bool _isPlaying;
bool _loop;
// Last recorded Voltage: at n-th step minus one
uint16_t _lastVoltageCh1;
uint16_t _lastVoltageCh2;
// helper functions
void _finishCurrentStep();
bool _canAddStep();
};
#endif

View File

@@ -1,49 +1,49 @@
/*
@file: FIRMARE_DEF.h
@author: Erik Tóth
@contact: etoth@tsn.at
@date: 2025-10-26
@updated: 2026-03-08
@brief: Header for constant definitions
*/
#ifndef FIRMWARE_DEF_H
#define FIRMWARE_DEF_H
#include <Arduino.h>
#include <Wire.h>
// CONSTANTS DEFINITONS
#define N_KEYBOARD_ROW 5
#define N_KEYBOARD_COL 5
#define N_CV_GATES 2
#define N_SB 2
#define BAUDRATE 115200
#define N_MAX_SEQ_STEPS 512
// PIN DEFENTITIONS
// I2C PINS
#define PIN_SDA 15
#define PIN_SCL 16
// KEYBOARD PINS
#define PIN_K_R0 7
#define PIN_K_R1 8
#define PIN_K_R2 9
#define PIN_K_R3 10
#define PIN_K_R4 11
#define PIN_K_C0 1
#define PIN_K_C1 2
#define PIN_K_C2 4
#define PIN_K_C3 5
#define PIN_K_C4 6
// SEQUENCER BUTTON PINS
#define PIN_SB_1_REC 33
#define PIN_SB_1_PLAY 34
#define PIN_SB_2_REC 35
#define PIN_SB_2_PLAY 36
// MISC/INFO PINS
#define PIN_VCO1_EN 38
#define PIN_VCO2_EN 39
#define PIN_REC 37
#define PIN_BPM 12
#define PIN_B_METRONOME 14
#define PIN_L_METRONOME 13
/*
@file: FIRMARE_DEF.h
@author: Erik Tóth
@contact: etoth@tsn.at
@date: 2025-10-26
@updated: 2026-03-08
@brief: Header for constant definitions
*/
#ifndef FIRMWARE_DEF_H
#define FIRMWARE_DEF_H
#include <Arduino.h>
#include <Wire.h>
// CONSTANTS DEFINITONS
#define N_KEYBOARD_ROW 5
#define N_KEYBOARD_COL 5
#define N_CV_GATES 2
#define N_SB 2
#define BAUDRATE 115200
#define N_MAX_SEQ_STEPS 512
// PIN DEFENTITIONS
// I2C PINS
#define PIN_SDA 15
#define PIN_SCL 16
// KEYBOARD PINS
#define PIN_K_R0 7
#define PIN_K_R1 8
#define PIN_K_R2 9
#define PIN_K_R3 10
#define PIN_K_R4 11
#define PIN_K_C0 1
#define PIN_K_C1 2
#define PIN_K_C2 4
#define PIN_K_C3 5
#define PIN_K_C4 6
// SEQUENCER BUTTON PINS
#define PIN_SB_1_REC 33
#define PIN_SB_1_PLAY 34
#define PIN_SB_2_REC 35
#define PIN_SB_2_PLAY 36
// MISC/INFO PINS
#define PIN_VCO1_EN 38
#define PIN_VCO2_EN 39
#define PIN_REC 37
#define PIN_BPM 12
#define PIN_B_METRONOME 14
#define PIN_L_METRONOME 13
#endif

View File

@@ -1,37 +1,37 @@
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
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

View File

@@ -1,46 +1,46 @@
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 <Foo.h>
#include <Bar.h>
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
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 <Foo.h>
#include <Bar.h>
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

View File

@@ -1,16 +1,16 @@
; 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
; 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,50 +1,50 @@
/*
* Example Code One
*/
#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);
for(int i = 0; (i < N_CV_GATES) && (i < n); i++)
{
Key k = keyboard.getQueue(i);
cv.setVoltage(i, k);
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);
/*
* Example Code One
*/
#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);
for(int i = 0; (i < N_CV_GATES) && (i < n); i++)
{
Key k = keyboard.getQueue(i);
cv.setVoltage(i, k);
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);
}

View File

@@ -1,60 +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);
/*
* 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);
}

View File

@@ -1,11 +1,11 @@
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
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