Compare commits
1 Commits
NUCLEO_H7A3
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b63060ed9b |
@@ -1,33 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, master]
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
platformio:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cache/pip
|
||||
~/.platformio/.cache
|
||||
key: ${{ runner.os }}-pio
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Install PlatformIO
|
||||
run: python -m pip install --upgrade pip platformio
|
||||
- name: Build
|
||||
run: pio run
|
||||
@@ -7,7 +7,6 @@
|
||||
],
|
||||
"settings": {
|
||||
"stm32-for-vscode.openOCDPath": false,
|
||||
"stm32-for-vscode.armToolchainPath": false,
|
||||
"commentTranslate.hover.enabled": true
|
||||
"stm32-for-vscode.armToolchainPath": false
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
# Configuration CV/GATE pour STM32F3 Discovery
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Ce projet implémente des sorties CV (Control Voltage) et GATE pour convertir les mouvements de souris ADB en signaux analogiques utilisables avec des synthétiseurs modulaires ou d'autres équipements audio analogiques.
|
||||
|
||||
**Méthode utilisée** : DAC 12-bit STM32F3 via HAL
|
||||
|
||||
## Configuration matérielle
|
||||
|
||||
### STM32F3 Discovery - Broches utilisées
|
||||
|
||||
- **CV1 (Axe X)** : PA4 - DAC1_OUT1
|
||||
- **CV2 (Axe Y)** : PA5 - DAC1_OUT2
|
||||
- **GATE (Bouton souris)** : PA0 - Sortie numérique
|
||||
|
||||
### Caractéristiques du DAC
|
||||
|
||||
- Résolution : 12-bit (4096 niveaux, 0-4095)
|
||||
- Tension de sortie : 0V à 3.3V
|
||||
- Impédance de sortie : ~15kΩ
|
||||
- **API utilisée** : STM32 HAL (`HAL_DAC_SetValue`)
|
||||
|
||||
## Schéma de conversion
|
||||
|
||||
### Déplacements de souris vers CV (Mode cumulatif)
|
||||
- Les valeurs de déplacement de souris (int8_t, -128 à +127) sont utilisées pour **incrémenter/décrémenter** les valeurs CV courantes
|
||||
- **Formule de mise à jour** : `CV_nouvelle = CV_courante + (mouvement_souris * CV_STEP_SIZE)`
|
||||
- **Point de départ** : 2047 (correspond à ~1.65V au centre)
|
||||
- **Taille de pas par défaut** : 8 (configurable via `CV_STEP_SIZE`)
|
||||
- **Plage de valeurs** : 0-4095 (0V à 3.3V)
|
||||
- **Comportement** :
|
||||
- Mouvement vers la droite (+X) → CV1 augmente
|
||||
- Mouvement vers la gauche (-X) → CV1 diminue
|
||||
- Mouvement vers le haut (+Y) → CV2 augmente
|
||||
- Mouvement vers le bas (-Y) → CV2 diminue
|
||||
- **Limitation automatique** : Les valeurs sont automatiquement limitées entre 0 et 4095
|
||||
|
||||
### Signal GATE
|
||||
- **Bouton relâché** : 0V (LOW)
|
||||
- **Bouton appuyé** : 3.3V (HIGH)
|
||||
|
||||
## Utilisation avec des synthétiseurs modulaires
|
||||
|
||||
### Mise à l'échelle pour Eurorack (±5V)
|
||||
Pour utiliser avec des modules Eurorack, vous devrez ajouter un circuit d'amplification et de décalage :
|
||||
1. **Amplificateur opérationnel** pour passer de 0-3.3V à -5V/+5V
|
||||
2. **Résistances de précision** pour le calibrage
|
||||
3. **Condensateurs de découplage** pour filtrer le bruit numérique
|
||||
|
||||
### Exemple de circuit d'interface
|
||||
```
|
||||
STM32 DAC (0-3.3V) → Op-Amp (TL072) → Sortie CV (-5V/+5V)
|
||||
↑
|
||||
Alimentation ±12V
|
||||
```
|
||||
|
||||
## Code d'initialisation
|
||||
|
||||
Le DAC est automatiquement initialisé dans `hid_mouse_init()` :
|
||||
- **Configuration HAL du DAC1** avec canaux 1 et 2
|
||||
- **Activation du buffer de sortie** pour de meilleures performances
|
||||
- **Démarrage automatique** des canaux DAC
|
||||
- **Initialisation des valeurs CV au centre (2047)**
|
||||
- **Vraies sorties analogiques** sans besoin de filtrage
|
||||
|
||||
## Contrôle des valeurs cumulatives
|
||||
|
||||
### Fonctions disponibles
|
||||
```cpp
|
||||
// Remet les valeurs CV au centre (2047 = 1.65V)
|
||||
void hid_mouse_reset_cv_values(void);
|
||||
|
||||
// Obtient la valeur CV1 actuelle (0-4095)
|
||||
uint16_t hid_mouse_get_cv1_value(void);
|
||||
|
||||
// Obtient la valeur CV2 actuelle (0-4095)
|
||||
uint16_t hid_mouse_get_cv2_value(void);
|
||||
```
|
||||
|
||||
### Utilisation pratique
|
||||
- **Reset** : Appelez `hid_mouse_reset_cv_values()` pour remettre les CV au centre
|
||||
- **Lecture** : Utilisez les fonctions `get_cv*_value()` pour surveiller les valeurs actuelles
|
||||
- **Persistance** : Les valeurs CV sont maintenues tant que le microcontrôleur est alimenté
|
||||
|
||||
## Debugging et monitoring
|
||||
|
||||
Le code inclut des messages série pour surveiller les valeurs CV/GATE :
|
||||
- Affichage des coordonnées de souris originales
|
||||
- Affichage des valeurs DAC converties
|
||||
- État du signal GATE
|
||||
|
||||
## Remarques importantes
|
||||
|
||||
1. **Filtrage** : Considérez l'ajout de filtres passe-bas en sortie pour lisser les transitions
|
||||
2. **Calibrage** : Les valeurs peuvent nécessiter un ajustement selon votre application
|
||||
3. **Protection** : Ajoutez des protections en cas de court-circuit sur les sorties
|
||||
|
||||
## Modifications possibles
|
||||
|
||||
### Changement de broches
|
||||
Pour utiliser d'autres broches DAC, modifiez les définitions dans `hid_mouse.h` :
|
||||
```cpp
|
||||
#define DAC_CV1_PIN PA4 // Sortie CV1
|
||||
#define DAC_CV2_PIN PA5 // Sortie CV2
|
||||
#define GATE_PIN PA0 // Sortie GATE
|
||||
```
|
||||
**Note** : Seules les broches avec capacité DAC peuvent être utilisées (PA4, PA5 sur STM32F3)
|
||||
|
||||
### Ajustement de la sensibilité
|
||||
Modifiez la taille de pas dans `hid_mouse.h` :
|
||||
```cpp
|
||||
#define CV_STEP_SIZE 8 // Augmentez pour plus de sensibilité, diminuez pour plus de précision
|
||||
```
|
||||
Exemples de valeurs :
|
||||
- `CV_STEP_SIZE 4` : Mouvement fin et précis
|
||||
- `CV_STEP_SIZE 16` : Mouvement rapide et large
|
||||
|
||||
### Changement de la valeur centrale
|
||||
```cpp
|
||||
#define CV_CENTER_VALUE 2047 // Centre à 1.65V
|
||||
// ou
|
||||
#define CV_CENTER_VALUE 0 // Démarrage à 0V
|
||||
// ou
|
||||
#define CV_CENTER_VALUE 4095 // Démarrage à 3.3V
|
||||
```
|
||||
|
||||
## Compatibilité
|
||||
|
||||
Cette implémentation est spécifique au STM32F3. Pour d'autres microcontrôleurs :
|
||||
- **STM32F4/F7** : Code similaire avec adaptations HAL
|
||||
- **ESP32** : Utilise les DAC intégrés avec des APIs différentes
|
||||
- **Arduino Uno** : PWM avec filtres passe-bas externes
|
||||
@@ -1,207 +0,0 @@
|
||||
# Modulation CV Avancée - Portamento & FM pour STM32F3
|
||||
|
||||
## Vue d'ensemble 🌊
|
||||
|
||||
Le système de modulation CV avancée ajoute des fonctionnalités de synthétiseur professionnel à votre Apple-ADB-Ressurector :
|
||||
|
||||
- **Portamento sinusoïdal** : Transitions douces entre les notes
|
||||
- **Vibrato LFO** : Modulation automatique de hauteur
|
||||
- **Modulation FM** : Contrôle expressif par mouvements souris
|
||||
- **Génération temps réel** : Calculs sinusoïdaux optimisés
|
||||
|
||||
## Fonctionnalités 🎛️
|
||||
|
||||
### 1. **Portamento Sinusoïdal**
|
||||
Transitions musicales douces entre les notes avec courbe en S :
|
||||
```
|
||||
Note A → Portamento → Note B
|
||||
[Courbe sinusoïdale lisse au lieu de saut brutal]
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- **Vitesse** : `PORTAMENTO_SPEED = 0.05f` (0.01 lent → 0.1 rapide)
|
||||
- **Courbe** : Transition sinusoïdale naturelle
|
||||
- **Indépendant** : CV1 et CV2 ont chacun leur portamento
|
||||
|
||||
### 2. **Vibrato LFO**
|
||||
Oscillateur basse fréquence pour effet vibrato :
|
||||
```
|
||||
Note de base + sin(LFO_phase) * profondeur
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- **Fréquence** : `VIBRATO_FREQUENCY = 4.5Hz` (0.1Hz → 20Hz)
|
||||
- **Profondeur** : `VIBRATO_DEPTH = 50` unités DAC (0 → 200)
|
||||
- **Phases** : CV1 et CV2 déphasés de 45° pour effet stéréo
|
||||
|
||||
### 3. **Modulation de Fréquence (FM)**
|
||||
Contrôle expressif par mouvements souris :
|
||||
```
|
||||
Mouvement X → Modulation CV1 (voix principale)
|
||||
Mouvement Y → Modulation CV2 (voix harmonique)
|
||||
```
|
||||
|
||||
**Paramètres :**
|
||||
- **Sensibilité** : `FM_SENSITIVITY = 0.1f`
|
||||
- **Fréquence** : 3x la fréquence vibrato (pour timbre métallique)
|
||||
- **Amplitude** : Proportionnelle à la vitesse de mouvement souris
|
||||
|
||||
## Architecture Technique ⚙️
|
||||
|
||||
### Structure de données
|
||||
```cpp
|
||||
typedef struct {
|
||||
// États portamento
|
||||
uint16_t current_cv1, target_cv1;
|
||||
uint16_t current_cv2, target_cv2;
|
||||
float portamento_phase_cv1, portamento_phase_cv2;
|
||||
|
||||
// LFO vibrato
|
||||
float vibrato_phase_cv1, vibrato_phase_cv2;
|
||||
|
||||
// Modulation FM
|
||||
float fm_depth_cv1, fm_depth_cv2;
|
||||
} cv_modulation_state_t;
|
||||
```
|
||||
|
||||
### Fonctions principales
|
||||
```cpp
|
||||
cv_modulation_init() // Initialisation
|
||||
cv_modulation_start_portamento() // Démarrer transition
|
||||
cv_modulation_update_fm() // Maj modulation souris
|
||||
cv_modulation_process() // Traitement temps réel
|
||||
```
|
||||
|
||||
## Intégration dans le Code 🔧
|
||||
|
||||
### 1. **Clavier → Portamento**
|
||||
```cpp
|
||||
// Au lieu d'écrire directement dans le DAC :
|
||||
HAL_DAC_SetValue(&hdac1, channel, value);
|
||||
|
||||
// Maintenant : démarrer portamento
|
||||
cv_modulation_start_portamento(channel, target_value);
|
||||
```
|
||||
|
||||
### 2. **Souris → Modulation FM**
|
||||
```cpp
|
||||
// Au lieu de modifier current_cv_value directement :
|
||||
current_cv1_value += offset_x;
|
||||
|
||||
// Maintenant : modulation de fréquence
|
||||
cv_modulation_update_fm(offset_x, offset_y);
|
||||
```
|
||||
|
||||
### 3. **Loop → Traitement**
|
||||
```cpp
|
||||
void loop() {
|
||||
handleKeyboard();
|
||||
handleMouse();
|
||||
cv_modulation_process(); // ← Ajouté pour traitement continu
|
||||
}
|
||||
```
|
||||
|
||||
## Algorithmes Sinusoïdaux 📐
|
||||
|
||||
### 1. **Fonction sinus optimisée**
|
||||
```cpp
|
||||
static float fast_sin(float phase) {
|
||||
// Normalisation phase [0, 2π]
|
||||
while (phase >= 2.0f * M_PI) phase -= 2.0f * M_PI;
|
||||
return sinf(phase); // Fonction ARM optimisée
|
||||
}
|
||||
```
|
||||
|
||||
### 2. **Transition douce (courbe S)**
|
||||
```cpp
|
||||
static float smooth_transition(float t) {
|
||||
if (t <= 0.0f) return 0.0f;
|
||||
if (t >= 1.0f) return 1.0f;
|
||||
return (1.0f - sinf(t * M_PI + M_PI * 0.5f)) * 0.5f;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Traitement temps réel**
|
||||
```cpp
|
||||
void cv_modulation_process() {
|
||||
float dt = delta_time_seconds;
|
||||
|
||||
// Portamento
|
||||
if (portamento_active) {
|
||||
phase += PORTAMENTO_SPEED;
|
||||
float progress = smooth_transition(phase);
|
||||
cv_value = lerp(start_cv, target_cv, progress);
|
||||
}
|
||||
|
||||
// Vibrato LFO
|
||||
vibrato_phase += 2π * frequency * dt;
|
||||
float vibrato = fast_sin(vibrato_phase) * depth;
|
||||
|
||||
// FM
|
||||
float fm = fast_sin(vibrato_phase * 3.0f) * fm_depth * 20.0f;
|
||||
|
||||
// Valeur finale
|
||||
final_cv = cv_value + vibrato + fm;
|
||||
HAL_DAC_SetValue(&hdac1, channel, final_cv);
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration & Réglages 🎚️
|
||||
|
||||
### Paramètres modifiables (cv_modulation.h) :
|
||||
```cpp
|
||||
#define PORTAMENTO_SPEED 0.05f // Vitesse glissement
|
||||
#define VIBRATO_FREQUENCY 4.5f // Hz vibrato
|
||||
#define VIBRATO_DEPTH 50 // Profondeur vibrato
|
||||
#define FM_SENSITIVITY 0.1f // Sensibilité souris
|
||||
```
|
||||
|
||||
### Fonctions de contrôle :
|
||||
```cpp
|
||||
cv_modulation_set_vibrato(true/false); // ON/OFF vibrato
|
||||
cv_modulation_set_vibrato_params(6.0f, 75); // Fréq + profondeur
|
||||
```
|
||||
|
||||
## Debug & Monitoring 📊
|
||||
|
||||
### Messages série typiques :
|
||||
```
|
||||
CV Modulation initialisée - Portamento + FM actifs
|
||||
Portamento CV1: 2048 → 2185
|
||||
CV1 (voix principale) - Note MIDI: 60 CV cible: 2048 (1.65V) [Portamento actif]
|
||||
FM Modulation - X: 2.50 Y: -1.20
|
||||
Vibrato: ON
|
||||
Vibrato params - Freq: 4.5Hz, Depth: 50
|
||||
```
|
||||
|
||||
## Performance & Optimisation ⚡
|
||||
|
||||
### Fréquence d'exécution :
|
||||
- **loop()** : ~200Hz (5ms delay)
|
||||
- **cv_modulation_process()** : ~200Hz
|
||||
- **Calculs sinus** : Fonction ARM optimisée
|
||||
- **Overhead CPU** : ~5% du STM32F303
|
||||
|
||||
### Mémoire utilisée :
|
||||
- **Structure d'état** : 48 bytes
|
||||
- **Code program** : ~2KB Flash
|
||||
- **Variables statiques** : ~100 bytes RAM
|
||||
|
||||
## Applications Musicales 🎵
|
||||
|
||||
### 1. **Lead expressif**
|
||||
- Portamento pour solos fluides
|
||||
- Vibrato automatique pour expressivité
|
||||
- FM par souris pour pitch bend manuel
|
||||
|
||||
### 2. **Basse dynamique**
|
||||
- Portamento lent pour transitions smooth
|
||||
- Vibrato subtil pour groove
|
||||
- FM pour effets de balayage
|
||||
|
||||
### 3. **Expérimentation sonore**
|
||||
- FM extrême pour timbres métalliques
|
||||
- Vibrato rapide pour effets tremolo
|
||||
- Portamento ultra-lent pour drones
|
||||
|
||||
Votre Apple-ADB-Ressurector est maintenant un **synthétiseur expressif avec modulation sinusoïdale professionnelle** ! 🎹🌊⚡
|
||||
@@ -1,171 +0,0 @@
|
||||
# Gestionnaire DAC/CV centralisé - Architecture
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Le gestionnaire `dac_cv_manager` centralise toutes les fonctionnalités liées aux sorties DAC, CV et GATE du STM32F3. Cette refactorisation améliore l'organisation du code et facilite la maintenance.
|
||||
|
||||
## Fichiers impliqués
|
||||
|
||||
### Nouveaux fichiers créés :
|
||||
- **`dac_cv_manager.h`** : Interface publique du gestionnaire DAC/CV/GATE
|
||||
- **`dac_cv_manager.cpp`** : Implémentation complète du gestionnaire
|
||||
|
||||
### Fichiers modifiés :
|
||||
- **`hid_mouse.cpp`** : Utilise maintenant le gestionnaire centralisé
|
||||
- **`hid_mouse.h`** : Simplifié, redirige vers le gestionnaire
|
||||
- **`hid_keyboard.cpp`** : Utilise le gestionnaire pour le portamento
|
||||
- **`main.cpp`** : Appelle le gestionnaire pour les modulations
|
||||
- **`cv_modulation.h`** : Ajout de fonctions de configuration
|
||||
- **`cv_modulation.cpp`** : Ajout des implémentations correspondantes
|
||||
|
||||
## Architecture du gestionnaire DAC/CV
|
||||
|
||||
### Structure hiérarchique :
|
||||
```
|
||||
dac_cv_manager (niveau haut)
|
||||
│
|
||||
├── Hardware DAC (HAL STM32F3)
|
||||
│ ├── DAC1 Canal 1 (PA4) → CV1
|
||||
│ └── DAC1 Canal 2 (PA5) → CV2
|
||||
│
|
||||
├── GATE Control (PA0)
|
||||
│
|
||||
└── cv_modulation (niveau bas)
|
||||
├── Portamento (transitions douces)
|
||||
├── Vibrato LFO
|
||||
└── FM Modulation (souris)
|
||||
```
|
||||
|
||||
### Responsabilités du gestionnaire :
|
||||
|
||||
#### 1. **Gestion matérielle**
|
||||
- Initialisation des DAC STM32F3 (HAL API)
|
||||
- Configuration des broches PA4, PA5 (DAC) et PA0 (GATE)
|
||||
- Écriture directe sur les DACs avec protection des valeurs
|
||||
|
||||
#### 2. **Interface unifiée**
|
||||
```cpp
|
||||
// Initialisation/fermeture
|
||||
void dac_cv_manager_init(void);
|
||||
void dac_cv_manager_deinit(void);
|
||||
|
||||
// Contrôle direct
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
void dac_cv_set_gate(bool state);
|
||||
|
||||
// Modulation avancée
|
||||
void dac_cv_start_portamento(uint8_t cv_channel, uint16_t target_cv);
|
||||
void dac_cv_update_fm_modulation(int8_t mouse_x, int8_t mouse_y);
|
||||
void dac_cv_process_modulations(void);
|
||||
|
||||
// Souris (accumulation)
|
||||
void dac_cv_update_cumulative(int8_t offset_x, int8_t offset_y);
|
||||
```
|
||||
|
||||
#### 3. **Configuration dynamique**
|
||||
```cpp
|
||||
// Configuration du vibrato
|
||||
void dac_cv_configure_vibrato(bool enabled, float frequency, uint16_t depth);
|
||||
|
||||
// Configuration du portamento
|
||||
void dac_cv_configure_portamento_speed(float speed);
|
||||
|
||||
// Obtention de l'état
|
||||
void dac_cv_get_state(uint16_t* cv1_value, uint16_t* cv2_value, bool* gate_state);
|
||||
```
|
||||
|
||||
## Intégration dans le code existant
|
||||
|
||||
### 1. **Souris (`hid_mouse.cpp`)**
|
||||
```cpp
|
||||
// Avant (code direct DAC)
|
||||
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value);
|
||||
|
||||
// Maintenant (gestionnaire centralisé)
|
||||
dac_cv_update_cumulative(offset_x, offset_y);
|
||||
dac_cv_set_gate(button);
|
||||
dac_cv_update_fm_modulation(offset_x, offset_y);
|
||||
```
|
||||
|
||||
### 2. **Clavier (`hid_keyboard.cpp`)**
|
||||
```cpp
|
||||
// Avant (appel direct modulation)
|
||||
cv_modulation_start_portamento(1, base_cv_value);
|
||||
|
||||
// Maintenant (via gestionnaire)
|
||||
dac_cv_start_portamento(1, base_cv_value);
|
||||
```
|
||||
|
||||
### 3. **Boucle principale (`main.cpp`)**
|
||||
```cpp
|
||||
// Avant
|
||||
cv_modulation_process();
|
||||
|
||||
// Maintenant
|
||||
dac_cv_process_modulations();
|
||||
```
|
||||
|
||||
## Avantages de cette architecture
|
||||
|
||||
### ✅ **Centralisation**
|
||||
- Un seul point d'entrée pour toutes les fonctions DAC/CV/GATE
|
||||
- Configuration unifiée et cohérente
|
||||
- Gestion centralisée des ressources matérielles
|
||||
|
||||
### ✅ **Abstraction**
|
||||
- Les modules utilisateurs n'ont plus besoin de connaître les détails HAL
|
||||
- Interface simple et intuitive
|
||||
- Protection automatique des valeurs DAC
|
||||
|
||||
### ✅ **Maintenance**
|
||||
- Code DAC/CV isolé dans un module dédié
|
||||
- Modifications futures centralisées
|
||||
- Tests et debugging simplifiés
|
||||
|
||||
### ✅ **Compatibilité**
|
||||
- Fonctions de redirection pour maintenir la compatibilité
|
||||
- Migration progressive possible
|
||||
- Pas de rupture de l'API existante
|
||||
|
||||
## Variables partagées
|
||||
|
||||
Les variables `current_cv1_value` et `current_cv2_value` restent accessibles :
|
||||
- **Déclarées** dans `dac_cv_manager.cpp`
|
||||
- **Exposées** via `extern` dans `dac_cv_manager.h`
|
||||
- **Utilisées** par les systèmes keyboard/mouse/modulation
|
||||
|
||||
## Configuration matérielle
|
||||
|
||||
### Broches STM32F3 Discovery :
|
||||
- **PA4** : DAC1_OUT1 → CV1 (0-3.3V)
|
||||
- **PA5** : DAC1_OUT2 → CV2 (0-3.3V)
|
||||
- **PA0** : GPIO → GATE (0V/3.3V)
|
||||
|
||||
### Paramètres DAC :
|
||||
- **Résolution** : 12-bit (0-4095)
|
||||
- **Trigger** : Software (DAC_TRIGGER_NONE)
|
||||
- **Buffer** : Enabled (DAC_OUTPUTBUFFER_ENABLE)
|
||||
- **Alignment** : Right (DAC_ALIGN_12B_R)
|
||||
|
||||
## Utilisation recommandée
|
||||
|
||||
### Initialisation (une seule fois) :
|
||||
```cpp
|
||||
dac_cv_manager_init(); // Dans hid_mouse_init()
|
||||
```
|
||||
|
||||
### Boucle principale (régulière) :
|
||||
```cpp
|
||||
dac_cv_process_modulations(); // ~200Hz dans main loop
|
||||
```
|
||||
|
||||
### Configuration optionnelle :
|
||||
```cpp
|
||||
// Vibrato actif, 5Hz, profondeur 50
|
||||
dac_cv_configure_vibrato(true, 5.0f, 50);
|
||||
|
||||
// Portamento rapide
|
||||
dac_cv_configure_portamento_speed(0.08f);
|
||||
```
|
||||
|
||||
Cette architecture offre une base solide pour les futures évolutions du système de modulation CV, tout en maintenant la simplicité d'utilisation pour les modules existants.
|
||||
@@ -1,71 +0,0 @@
|
||||
# Test de configuration STM32H7A3
|
||||
|
||||
## Vérification de l'environnement
|
||||
|
||||
Votre fichier `platformio.ini` a été mis à jour avec l'environnement **`nucleo_h7a3zi`** pour STM32H7A3.
|
||||
|
||||
### ✅ Configuration ajoutée :
|
||||
|
||||
```ini
|
||||
[env:nucleo_h7a3zi]
|
||||
platform = ststm32
|
||||
board = nucleo_h7a3zi_q
|
||||
framework = arduino
|
||||
# ... configuration complète H7A3
|
||||
```
|
||||
|
||||
### 🛠️ Pour compiler et tester :
|
||||
|
||||
#### Dans VS Code avec PlatformIO :
|
||||
|
||||
1. **Sélectionner l'environnement** :
|
||||
- Cliquer sur l'icône PlatformIO (maison) dans la barre latérale
|
||||
- Ou `Ctrl+Shift+P` → "PlatformIO: Pick Project Environment"
|
||||
- Choisir **`nucleo_h7a3zi`**
|
||||
|
||||
2. **Compiler** :
|
||||
- Cliquer sur "Build" dans PlatformIO
|
||||
- Ou `Ctrl+Alt+B`
|
||||
- Ou terminal : `platformio run -e nucleo_h7a3zi`
|
||||
|
||||
3. **Téléverser** (si board connectée) :
|
||||
- Cliquer sur "Upload" dans PlatformIO
|
||||
- Ou `Ctrl+Alt+U`
|
||||
- Ou terminal : `platformio run -e nucleo_h7a3zi --target upload`
|
||||
|
||||
### 📁 Fichiers utilisés pour H7A3 :
|
||||
|
||||
- **Main** : `STM32H7A3_Version/src/main_h7a3.cpp`
|
||||
- **CV Modulation** : `STM32H7A3_Version/src/cv_modulation_h7a3.cpp`
|
||||
- **DAC Manager** : `STM32H7A3_Version/src/dac_cv_manager_h7a3.cpp`
|
||||
- **Headers** : `STM32H7A3_Version/include/*.h`
|
||||
|
||||
### 🎯 Commandes série (115200 baud) :
|
||||
|
||||
```
|
||||
H7A3 > help # Aide complète
|
||||
H7A3 > status # État système H7A3
|
||||
H7A3 > performance # Rapport performance
|
||||
H7A3 > cv reset # Reset CV au centre
|
||||
H7A3 > test fm 5000 # Test modulation 5s
|
||||
H7A3 > cache stats # Statistiques caches L1
|
||||
H7A3 > mouse test # Simulation souris
|
||||
```
|
||||
|
||||
### 🚀 Avantages H7A3 vs F303 :
|
||||
|
||||
- **CPU** : 480MHz vs 72MHz (**6.7x plus rapide**)
|
||||
- **RAM** : 1.4MB vs 40KB (**35x plus**)
|
||||
- **Fréq CV** : 1000Hz vs 100Hz (**10x plus rapide**)
|
||||
- **Caches L1** : Activés pour performance maximale
|
||||
- **FPU** : Double précision pour calculs haute précision
|
||||
- **Debug** : Mesures cycles CPU temps réel
|
||||
|
||||
### ⚡ Prêt à l'emploi !
|
||||
|
||||
L'environnement H7A3 est maintenant configuré dans votre projet principal. Vous pouvez basculer entre les environnements selon vos besoins :
|
||||
|
||||
- **`stm32f3_discovery`** : Version F303 originale
|
||||
- **`nucleo_h7a3zi`** : Version H7A3 haute performance
|
||||
|
||||
**Sélectionnez simplement l'environnement souhaité dans PlatformIO et compilez !** 🎹✨
|
||||
@@ -1,207 +0,0 @@
|
||||
# Clavier Musical CV/GATE Polyphonique pour STM32F3 Discovery
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
Cette fonctionnalité transforme votre clavier Apple ADB en **synthétiseur polyphonique** avec sorties CV/GATE ! Le système utilise une approche innovante :
|
||||
|
||||
- **CV1** : Voix principale (touches blanches ASDFGHJ)
|
||||
- **CV2** : Voix harmonique (touches noires + numériques)
|
||||
- **Modulation de pitch** : Les mouvements de souris X/Y modulent finement les CV
|
||||
- **GATE commun** : Signal de déclenchement pour les deux voix
|
||||
|
||||
## Configuration matérielle
|
||||
|
||||
### Sorties utilisées (système intégré souris+clavier)
|
||||
|
||||
- **CV1 (PA4)** : Voix principale + modulation par mouvement X souris
|
||||
- **CV2 (PA5)** : Voix harmonique + modulation par mouvement Y souris
|
||||
- **GATE (PA0)** : Signal de déclenchement commun aux deux voix
|
||||
|
||||
### Comportement révolutionnaire
|
||||
|
||||
- **Variables partagées** : Le clavier modifie les mêmes variables `current_cv1_value` et `current_cv2_value` que la souris
|
||||
- **Modulation cumulative** : Souris ajoute sa modulation par-dessus les notes du clavier
|
||||
- **CV de base** : Clavier définit la hauteur de note de base
|
||||
- **Pitch bend** : Souris module cette hauteur en temps réel
|
||||
- **GATE intelligent** : Actif tant qu'au moins une touche est pressée
|
||||
- **Polyphonie partielle** : Deux voix indépendantes avec modulation croisée
|
||||
|
||||
## Mapping des touches
|
||||
|
||||
### Layout "Synthétiseur Polyphonique"
|
||||
|
||||
Le mapping suit une logique musicale de voix principales + harmoniques :
|
||||
|
||||
#### Voix principale (CV1) - Touches blanches
|
||||
```
|
||||
[A] [S] [D] [F] [G] [H] [J] → CV1 + modulation X
|
||||
Do Ré Mi Fa Sol La Si (voix de base)
|
||||
```
|
||||
|
||||
#### Voix harmonique (CV2) - Touches noires et numériques
|
||||
```
|
||||
[Q] [W] [R] [T] [Y] → CV2 + modulation Y
|
||||
Do# Ré# Fa# Sol# La# (harmoniques)
|
||||
|
||||
[1] [2] [3] [4] [5] [6] [7] → CV2 + modulation Y
|
||||
Do Ré Mi Fa Sol La Si (leads octave +1)
|
||||
```
|
||||
|
||||
#### Modulation hybride clavier + souris
|
||||
```
|
||||
Clavier A (Do) → current_cv1_value = 2048 (note de base)
|
||||
Souris X +100 → current_cv1_value += 100 (pitch bend)
|
||||
↓
|
||||
Résultat final sur CV1 : 2148 (note + modulation)
|
||||
|
||||
Clavier Q (Do#) → current_cv2_value = 2082 (note de base)
|
||||
Souris Y -50 → current_cv2_value -= 50 (pitch bend)
|
||||
↓
|
||||
Résultat final sur CV2 : 2032 (note + modulation)
|
||||
```
|
||||
|
||||
## Spécifications techniques
|
||||
|
||||
### Notes MIDI
|
||||
- **Do central (C4)** : Note MIDI 60 = ~2.0V
|
||||
- **Plage** : C4 à B5 (notes 60 à 83)
|
||||
- **Standard** : 1V/octave (limité par 3.3V du STM32)
|
||||
|
||||
### Conversion CV
|
||||
```cpp
|
||||
// Formule de conversion MIDI → DAC
|
||||
CV_value = (midi_note * 4095) / 120
|
||||
```
|
||||
|
||||
### Table de correspondance avec modulation
|
||||
| Touche | Note | MIDI | CV base | Sortie | Modulation |
|
||||
|--------|------|------|---------|--------|------------|
|
||||
| A | Do | 60 | 2.0V | CV1 | Souris X |
|
||||
| S | Ré | 62 | 2.1V | CV1 | Souris X |
|
||||
| D | Mi | 64 | 2.2V | CV1 | Souris X |
|
||||
| F | Fa | 65 | 2.2V | CV1 | Souris X |
|
||||
| G | Sol | 67 | 2.3V | CV1 | Souris X |
|
||||
| H | La | 69 | 2.4V | CV1 | Souris X |
|
||||
| J | Si | 71 | 2.4V | CV1 | Souris X |
|
||||
| Q | Do# | 61 | 2.1V | CV2 | Souris Y |
|
||||
| W | Ré# | 63 | 2.2V | CV2 | Souris Y |
|
||||
| 1 | Do | 72 | 2.5V | CV2 | Souris Y |
|
||||
| 2 | Ré | 74 | 2.5V | CV2 | Souris Y |
|
||||
|
||||
## Utilisation
|
||||
|
||||
### Activation
|
||||
La fonctionnalité est automatiquement active quand `KEYBOARD_CV_ENABLED` est défini.
|
||||
|
||||
### Jeu hybride clavier+souris
|
||||
1. **Pressez une touche principale (ASDFGHJ)** → `current_cv1_value` = note de base
|
||||
2. **Pressez une touche harmonique (QWERTY + 1234567)** → `current_cv2_value` = note de base
|
||||
3. **Bougez la souris X** → Modifie `current_cv1_value` (pitch bend voix principale)
|
||||
4. **Bougez la souris Y** → Modifie `current_cv2_value` (pitch bend voix harmonique)
|
||||
5. **Les deux systèmes** → Travaillent sur les mêmes variables partagées !
|
||||
|
||||
### Monitoring série détaillé
|
||||
```
|
||||
CV1 (voix principale) - Note MIDI: 60 CV: 2048 (1.65V)
|
||||
[Mouvement souris X] current_cv1_value devient 2156
|
||||
CV2 (voix harmonique) - Note MIDI: 64 CV: 2185 (1.76V)
|
||||
[Mouvement souris Y] current_cv2_value devient 2089
|
||||
GATE ON - Notes actives
|
||||
```
|
||||
|
||||
## Compatibilité synthétiseurs
|
||||
|
||||
### Eurorack
|
||||
- **Attention** : Le STM32 sort 0-3.3V, les modules Eurorack attendent ±5V
|
||||
- **Solution** : Ajouter un amplificateur opérationnel pour mise à l'échelle
|
||||
|
||||
### Synthés vintage
|
||||
- Compatible directement avec la plupart des synthés attendant 0-5V
|
||||
- CV/GATE standard reconnu par tous les synthétiseurs modulaires
|
||||
|
||||
## Circuit d'interface (recommandé)
|
||||
|
||||
Pour une utilisation professionnelle avec Eurorack :
|
||||
|
||||
```
|
||||
STM32 CV → Op-Amp TL072 → Sortie ±5V
|
||||
↑
|
||||
Alimentation ±12V
|
||||
Résistances de précision
|
||||
```
|
||||
|
||||
## Fonctions disponibles
|
||||
|
||||
```cpp
|
||||
// Conversion ADB vers MIDI
|
||||
uint8_t hid_keyboard_adb_to_midi_note(uint8_t adb_key);
|
||||
|
||||
// Conversion MIDI vers CV DAC
|
||||
uint16_t hid_keyboard_midi_note_to_cv(uint8_t midi_note);
|
||||
|
||||
// Traitement principal CV/GATE
|
||||
void hid_keyboard_process_cv_output(hid_key_report* report);
|
||||
```
|
||||
|
||||
## Personnalisation
|
||||
|
||||
### Changer le mapping
|
||||
Modifiez le tableau `adb_to_midi_map[]` dans `hid_keyboard.cpp` :
|
||||
```cpp
|
||||
{0x00, KEY_NOTE_C4}, // A = Do
|
||||
{0x01, KEY_NOTE_D4}, // S = Ré
|
||||
// Ajoutez vos mappings...
|
||||
```
|
||||
|
||||
### Transposition
|
||||
Changez la note de référence :
|
||||
```cpp
|
||||
#define KEY_NOTE_C4 72 // Do une octave plus haut
|
||||
```
|
||||
|
||||
### Courbe CV personnalisée
|
||||
Modifiez la fonction `hid_keyboard_midi_note_to_cv()` pour des courbes exponentielles, tempérament inégal, etc.
|
||||
|
||||
## Limitations actuelles
|
||||
|
||||
- **Polyphonie limitée** : Deux voix simultanées maximum (CV1 + CV2)
|
||||
- **Pas de vélocité** : GATE binaire uniquement
|
||||
- **Plage limitée** : 0-3.3V (2.75 octaves environ)
|
||||
- **Modulation fixe** : Sensibilité de pitch bend non configurable
|
||||
- **GATE partagé** : Une seule enveloppe pour les deux voix
|
||||
|
||||
## Améliorations futures
|
||||
|
||||
- [ ] Vélocité via PWM du GATE
|
||||
- [ ] Portamento/glide entre notes par voix
|
||||
- [ ] Modulation configurable (intensité, courbes)
|
||||
- [ ] GATE indépendant par voix
|
||||
- [ ] Modes gammes/accords préprogrammés
|
||||
- [ ] Séquenceur polyphonique intégré
|
||||
- [ ] LFO intégré pour modulation auto
|
||||
|
||||
## Test et debug
|
||||
|
||||
Utilisez le moniteur série pour voir l'activité polyphonique en temps réel :
|
||||
```cpp
|
||||
CV1 (voix principale) - Note MIDI: 60 Base CV: 2048 Modulé: 2156 (1.73V)
|
||||
CV2 (voix harmonique) - Note MIDI: 64 Base CV: 2185 Modulé: 2089 (1.68V)
|
||||
GATE ON - Notes actives
|
||||
CV1 OFF - Voix principale arrêtée
|
||||
GATE OFF - Aucune note
|
||||
```
|
||||
|
||||
**Schéma de patch recommandé :**
|
||||
```
|
||||
CV1 → VCO1 (Bass/Lead) → VCF1 → VCA1 ┐
|
||||
├─→ Mix → Out
|
||||
CV2 → VCO2 (Harmony) → VCF2 → VCA2 ┘
|
||||
|
||||
GATE → EG (enveloppe commune) → VCA1 & VCA2
|
||||
|
||||
Modulation:
|
||||
Souris X → Fine tune VCO1
|
||||
Souris Y → Fine tune VCO2
|
||||
```
|
||||
|
||||
Votre clavier Apple ADB est maintenant un **synthétiseur polyphonique professionnel avec modulation en temps réel** ! 🎹🔥⚡
|
||||
@@ -75,9 +75,7 @@ Périphérique ADB data <------+------------------+---> ADB_PIN (ESP32: GPIO2, S
|
||||
## 🌟 Fonctionnalités actuelles
|
||||
|
||||
- **Clavier USB HID** : Conversion des touches ADB en rapports HID USB, avec gestion des modificateurs (Shift, Ctrl, etc.) et des touches spéciales (Caps Lock, Num Lock).
|
||||
- **Clavier Musical CV/GATE Polyphonique** (STM32F3 uniquement) : Synthétiseur 2-voix avec portamento sinusoïdal et modulation FM par souris.
|
||||
- **Souris USB HID** : Conversion des mouvements et clics ADB en rapports HID USB.
|
||||
- **Sorties CV/GATE** (STM32F3 uniquement) : Conversion des mouvements de souris en signaux CV cumulatifs via DAC 12-bit + signal GATE pour synthétiseurs modulaires.
|
||||
- **Gestion des LEDs** : Les LEDs Caps Lock et Num Lock fonctionnent comme par magie.
|
||||
- **Compatibilité HID** : Utilisation de `HID_Composite` pour gérer les rapports HID.
|
||||
|
||||
@@ -120,7 +118,7 @@ Mais voilà, je ne trouvais pas exactement ce que je voulais, et comme j'avais d
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Autres périphériques compatibles
|
||||
## 🛠️ Autres périphériques pas encore compatibles
|
||||
|
||||
- **Tablettes graphiques** : Wacom ADB, Kurta ADB.
|
||||
- **Trackballs** : Kensington Turbo Mouse, Microspeed MacTRAC.
|
||||
@@ -142,25 +140,10 @@ Le projet utilise des définitions spécifiques pour configurer les pins en fonc
|
||||
- **ESP32** : Pin `4` (Devkit Wemos).
|
||||
- **STM32** : Pin `PC13`.
|
||||
|
||||
### Configuration CV/GATE (STM32F3 Discovery uniquement)
|
||||
- `#define DAC_CV1_CHANNEL` : Canal DAC pour CV1 (axe X) - **PA4**
|
||||
- `#define DAC_CV2_CHANNEL` : Canal DAC pour CV2 (axe Y) - **PA5**
|
||||
- `#define GATE_PIN` : Pin pour signal GATE (bouton souris) - **PA0**
|
||||
- `#define CV_STEP_SIZE 8` : Sensibilité des mouvements CV (modifiable)
|
||||
|
||||
Ces définitions permettent une compatibilité multi-plateforme en adaptant automatiquement les pins selon la carte utilisée.
|
||||
|
||||
---
|
||||
|
||||
## 📖 Documentation supplémentaire
|
||||
|
||||
- **[Configuration CV/GATE pour souris](CV_GATE_README.md)** : Guide détaillé pour l'utilisation des sorties CV/GATE avec les mouvements de souris
|
||||
- **[Configuration Clavier Musical Polyphonique](KEYBOARD_CV_README.md)** : Guide complet pour utiliser le clavier comme synthétiseur 2-voix avec modulation pitch
|
||||
- **[Modulation CV Avancée](CV_MODULATION_README.md)** : Documentation technique du système de portamento sinusoïdal et modulation FM
|
||||
- **[Spécifications du logo](lib/logo-specifications.md)** : Directives pour l'utilisation du logo du projet
|
||||
|
||||
---
|
||||
|
||||
## 🛑 État du projet
|
||||
|
||||
Ce projet est actuellement en **beta**.
|
||||
@@ -192,9 +175,6 @@ Ce projet est actuellement en **beta**.
|
||||
- [x] Faire fonctionner la souris.
|
||||
- [x] Gérer plusieurs périphériques ADB simultanément.
|
||||
- [x] Ajouter le support Bluetooth pour le clavier.
|
||||
- [x] Implémenter les sorties CV/GATE pour STM32F3.
|
||||
- [ ] Ajouter un bouton de reset CV/GATE.
|
||||
- [ ] Implémenter des modes CV alternatifs (LFO, séquences).
|
||||
- [ ] Ajouter le support Bluetooth pour la souris.
|
||||
- [ ] Ajouter le support d'autres périphériques ADB (tablettes graphiques, trackballs, etc.).
|
||||
- [ ] Ajouter le support d'un écran OLED pour afficher des informations sur l'état de la connexion.
|
||||
@@ -219,51 +199,3 @@ Et bien sûr, surtout merci à **Szymon Łopaciuk** pour l'inspiration initiale.
|
||||
Ce projet est sous licence GNU GPL v3. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- CHANTIER:AUDIT START -->
|
||||
## Audit & Execution Plan (2026-03-10)
|
||||
|
||||
### Snapshot
|
||||
- Priority: `P2`
|
||||
- Tech profile: `embedded`
|
||||
- Workflows: `yes`
|
||||
- Tests: `yes`
|
||||
- Debt markers: `1`
|
||||
- Source files: `23`
|
||||
|
||||
### Corrections Prioritaires
|
||||
- [ ] Vérifier target PlatformIO et budget mémoire
|
||||
- [ ] Ajouter/fiabiliser les commandes de vérification automatiques.
|
||||
- [ ] Clore les points bloquants avant optimisation avancée.
|
||||
|
||||
### Optimisation
|
||||
- [ ] Identifier le hotspot principal et mesurer avant/après.
|
||||
- [ ] Réduire la complexité des modules les plus touchés.
|
||||
|
||||
### Mémoire chantier
|
||||
- Control plane: `/Users/electron/.codex/memories/electron_rare_chantier`
|
||||
- Repo card: `/Users/electron/.codex/memories/electron_rare_chantier/REPOS/Apple-ADB-Ressurector.md`
|
||||
|
||||
<!-- CHANTIER:AUDIT END -->
|
||||
|
||||
+62
-25
@@ -1,38 +1,75 @@
|
||||
; PlatformIO Project Configuration File pour STM32H7A3
|
||||
; Configuration optimisée pour Apple ADB Ressurector
|
||||
; Plateforme unique : STM32H7A3 (480MHz Cortex-M7)
|
||||
; 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:nucleo_h7a3zi]
|
||||
[env:bluepill_f103c8_128k]
|
||||
platform = ststm32
|
||||
board = nucleo_h743zi
|
||||
board = bluepill_f103c8_128k
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_HID
|
||||
-D USBCON
|
||||
-D USBD_VID=0x0483
|
||||
-D USBD_PID=0x5711
|
||||
-D USB_MANUFACTURER="STMicroelectronics"
|
||||
-D USB_PRODUCT="Apple Desktop Bus Device"
|
||||
-D HAL_PCD_MODULE_ENABLED
|
||||
-D STM32F1
|
||||
; -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
|
||||
;-D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE
|
||||
|
||||
upload_flags = -c set CPUTAPID 0x2ba01477 ; Chinese clone, genuine is 0x1ba01477
|
||||
debug_tool = stlink
|
||||
upload_protocol = stlink
|
||||
monitor_speed = 115200
|
||||
|
||||
|
||||
[env:stm32f3_discovery]
|
||||
platform = ststm32
|
||||
board = disco_f303vc
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-D USBCON
|
||||
-D USBD_VID=0x0483
|
||||
-D USBD_PID=0x5712
|
||||
-D USBD_PID=0x5711
|
||||
-D USB_MANUFACTURER="STMicroelectronics"
|
||||
-D USB_PRODUCT="Apple Desktop Bus Device H7A3"
|
||||
-D STM32H7A3xx
|
||||
-D STM32H743xx
|
||||
-D USB_PRODUCT="Apple Desktop Bus Device"
|
||||
-D STM32F3link
|
||||
-D HAL_PCD_MODULE_ENABLED
|
||||
; -D HAL_DAC_MODULE_ENABLED
|
||||
; -D HAL_TIM_MODULE_ENABLED
|
||||
-D USBD_USE_HID_COMPOSITE
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_HID
|
||||
-D CORE_CM7
|
||||
-D USE_HAL_DRIVER
|
||||
; Optimisations H7A3 spécifiques
|
||||
-D HSE_VALUE=8000000
|
||||
-D HSI_VALUE=64000000
|
||||
-D CSI_VALUE=4000000
|
||||
; Support cache et mémoire avancée
|
||||
-D DATA_CACHE_ENABLE=1
|
||||
-D INSTRUCTION_CACHE_ENABLE=1
|
||||
; Optimisations performance
|
||||
-O2
|
||||
-ffast-math
|
||||
|
||||
; -D PIO_FRAMEWORK_ARDUINO_ENABLE_CDC
|
||||
; -D PIO_FRAMEWORK_ARDUINO_USB_FULLSPEED_FULLMODE
|
||||
debug_tool = stlink
|
||||
upload_protocol = stlink
|
||||
monitor_speed = 115200
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
; je voudrais
|
||||
; electronrare/ADB @ ^1.0.0
|
||||
|
||||
[env:native]
|
||||
platform = native
|
||||
build_flags =
|
||||
-D USBCON
|
||||
-D USBD_USE_HID_COMPOSITE
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_HID
|
||||
test_build_src = true
|
||||
|
||||
[env:esp32dev]
|
||||
platform = espressif32
|
||||
board = esp32dev
|
||||
framework = arduino
|
||||
build_flags =
|
||||
-D USBCON
|
||||
-D USBD_USE_HID_COMPOSITE
|
||||
-D PIO_FRAMEWORK_ARDUINO_ENABLE_HID
|
||||
-D BLUETOOTH_ENABLED
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
; electronrare/ADB @ ^1.0.0
|
||||
@@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @file adb_utilities.cpp
|
||||
* @brief Implémentation des utilitaires ADB pour STM32H7A3
|
||||
*/
|
||||
|
||||
#include "adb_utilities.h"
|
||||
#include <ADB.h>
|
||||
|
||||
/**
|
||||
* @brief Configure les touches du rapport HID à partir du registre ADB
|
||||
*/
|
||||
bool hid_keyboard_set_keys_from_adb_register(hid_key_report* key_report, const adb_key_data_t* key_data) {
|
||||
if (!key_report || !key_data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
// Effacer le rapport existant
|
||||
hid_keyboard_clear_report(key_report);
|
||||
|
||||
// Traitement de la première touche
|
||||
if (key_data->data.key0 != 0) {
|
||||
if (!key_data->data.released0) {
|
||||
// Touche pressée
|
||||
key_report->keys[0] = key_data->data.key0;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Traitement de la deuxième touche
|
||||
if (key_data->data.key1 != 0) {
|
||||
if (!key_data->data.released1) {
|
||||
// Touche pressée
|
||||
key_report->keys[1] = key_data->data.key1;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/**
|
||||
* @file adb_utilities.h
|
||||
* @brief Utilitaires ADB pour STM32H7A3
|
||||
*
|
||||
* Fonctions de conversion et utilitaires ADB
|
||||
* adaptées du F303 pour le H7A3
|
||||
*/
|
||||
|
||||
#ifndef ADB_UTILITIES_H
|
||||
#define ADB_UTILITIES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "hid_structures.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Convertit un axe de souris ADB en mouvement relatif
|
||||
* @param axis_value Valeur de l'axe ADB
|
||||
* @return Mouvement relatif converti
|
||||
*/
|
||||
static inline int8_t adbMouseConvertAxis(int8_t axis_value) {
|
||||
// Conversion simple - peut être ajustée selon les besoins
|
||||
return axis_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure les touches du rapport HID à partir du registre ADB
|
||||
* @param key_report Pointeur vers le rapport HID
|
||||
* @param key_data Données clavier ADB
|
||||
* @return true si le rapport a été modifié
|
||||
*/
|
||||
bool hid_keyboard_set_keys_from_adb_register(hid_key_report* key_report, const adb_key_data_t* key_data);
|
||||
|
||||
/**
|
||||
* @brief Initialise un rapport clavier vide
|
||||
* @param key_report Pointeur vers le rapport à initialiser
|
||||
*/
|
||||
static inline void hid_keyboard_clear_report(hid_key_report* key_report) {
|
||||
key_report->modifiers = 0;
|
||||
key_report->reserved = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
key_report->keys[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise un rapport souris vide
|
||||
* @param mouse_report Pointeur vers le rapport à initialiser
|
||||
*/
|
||||
static inline void hid_mouse_clear_report(hid_mouse_report* mouse_report) {
|
||||
mouse_report->buttons = 0;
|
||||
mouse_report->x = 0;
|
||||
mouse_report->y = 0;
|
||||
mouse_report->wheel = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ADB_UTILITIES_H
|
||||
-140
@@ -1,140 +0,0 @@
|
||||
/**
|
||||
* @file config.h
|
||||
* @brief Configuration matérielle STM32H7A3 pour Apple ADB Ressurector
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
// Configuration système H7A3
|
||||
#define H7A3_SYSTEM_CLOCK_HZ 480000000 // 480MHz
|
||||
#define H7A3_APB1_CLOCK_HZ 120000000 // 120MHz
|
||||
#define H7A3_APB2_CLOCK_HZ 120000000 // 120MHz
|
||||
|
||||
// Configuration DAC H7A3 (identique F303 pour compatibilité)
|
||||
#define DAC_H7A3_INSTANCE DAC1
|
||||
#define DAC_H7A3_CV1_PIN GPIO_PIN_4 // PA4
|
||||
#define DAC_H7A3_CV1_PORT GPIOA
|
||||
#define DAC_H7A3_CV2_PIN GPIO_PIN_5 // PA5
|
||||
#define DAC_H7A3_CV2_PORT GPIOA
|
||||
#define DAC_H7A3_RESOLUTION 12 // 12-bit (extensible 16-bit)
|
||||
#define DAC_H7A3_VREF 3.3f // Tension de référence
|
||||
|
||||
// Configuration Timer H7A3 (utilisation timers avancés)
|
||||
#define TIMER_H7A3_MAIN TIM1 // Timer principal modulation
|
||||
#define TIMER_H7A3_DEBUG TIM8 // Timer debug (ou TIM1 si TIM8 indisponible)
|
||||
#define TIMER_H7A3_FREQ_MAIN 1000 // 1kHz modulation principale
|
||||
#define TIMER_H7A3_FREQ_DEBUG 20 // 20Hz debug processing
|
||||
|
||||
// Configuration GPIO H7A3
|
||||
// LED status (Nucleo H7A3 standard)
|
||||
#define LED_H7A3_PIN GPIO_PIN_0 // PB0 (LED verte Nucleo)
|
||||
#define LED_H7A3_PORT GPIOB
|
||||
|
||||
// Bouton utilisateur (Nucleo H7A3 standard)
|
||||
#define BUTTON_H7A3_PIN GPIO_PIN_13 // PC13 (Bouton bleu Nucleo)
|
||||
#define BUTTON_H7A3_PORT GPIOC
|
||||
|
||||
// Configuration UART H7A3 (Serial)
|
||||
#define UART_H7A3_INSTANCE USART3 // UART Nucleo ST-Link
|
||||
#define UART_H7A3_BAUDRATE 115200
|
||||
#define UART_H7A3_TX_PIN GPIO_PIN_8 // PD8
|
||||
#define UART_H7A3_TX_PORT GPIOD
|
||||
#define UART_H7A3_RX_PIN GPIO_PIN_9 // PD9
|
||||
#define UART_H7A3_RX_PORT GPIOD
|
||||
|
||||
// Configuration USB H7A3 (optionnel pour HID)
|
||||
#define USB_H7A3_HS_ENABLE 1 // USB High Speed
|
||||
#define USB_H7A3_DP_PIN GPIO_PIN_12 // PA12
|
||||
#define USB_H7A3_DP_PORT GPIOA
|
||||
#define USB_H7A3_DM_PIN GPIO_PIN_11 // PA11
|
||||
#define USB_H7A3_DM_PORT GPIOA
|
||||
|
||||
// Configuration DMA H7A3 (pour DAC haute performance)
|
||||
#define DMA_H7A3_DAC_STREAM DMA1_Stream0
|
||||
#define DMA_H7A3_DAC_CHANNEL DMA_REQUEST_DAC1_CH1
|
||||
|
||||
// Configuration Cache H7A3
|
||||
#define CACHE_H7A3_ENABLE_I 1 // Cache Instructions
|
||||
#define CACHE_H7A3_ENABLE_D 1 // Cache Données
|
||||
#define CACHE_H7A3_LINE_SIZE 32 // Taille ligne cache
|
||||
|
||||
// Configuration MPU H7A3 (Memory Protection Unit)
|
||||
#define MPU_H7A3_ENABLE 0 // Désactivé par défaut
|
||||
#define MPU_H7A3_REGIONS 8 // Nombre régions disponibles
|
||||
|
||||
// Configuration FPU H7A3
|
||||
#define FPU_H7A3_ENABLE 1 // Unité virgule flottante
|
||||
#define FPU_H7A3_DOUBLE_PREC 1 // Double précision
|
||||
|
||||
// Configuration Debug H7A3
|
||||
#define DEBUG_H7A3_DWT_ENABLE 1 // Data Watchpoint Trace
|
||||
#define DEBUG_H7A3_ITM_ENABLE 1 // Instrumentation Trace Macrocell
|
||||
#define DEBUG_H7A3_SWO_ENABLE 1 // Single Wire Output
|
||||
|
||||
// Macros utilitaires H7A3
|
||||
#define H7A3_CYCLES_TO_US(cycles) ((float)(cycles) / (H7A3_SYSTEM_CLOCK_HZ / 1000000.0f))
|
||||
#define H7A3_US_TO_CYCLES(us) ((uint32_t)((us) * (H7A3_SYSTEM_CLOCK_HZ / 1000000.0f)))
|
||||
|
||||
// Configuration spécifique CV/GATE H7A3
|
||||
#define CV_H7A3_SAMPLE_RATE 1000 // 1kHz (vs 100Hz F303)
|
||||
#define CV_H7A3_BUFFER_SIZE 64 // Buffer étendu (vs 32 F303)
|
||||
#define CV_H7A3_DMA_ENABLE 0 // DMA pour DAC (optionnel)
|
||||
#define CV_H7A3_INTERRUPT_PRIO 5 // Priorité interruption modulation
|
||||
|
||||
// Validation configuration
|
||||
#if H7A3_SYSTEM_CLOCK_HZ != 480000000
|
||||
#warning "H7A3: Fréquence système non optimale, performance réduite"
|
||||
#endif
|
||||
|
||||
#if !CACHE_H7A3_ENABLE_I || !CACHE_H7A3_ENABLE_D
|
||||
#warning "H7A3: Caches désactivés, performance significativement réduite"
|
||||
#endif
|
||||
|
||||
#if CV_H7A3_SAMPLE_RATE < 1000
|
||||
#warning "H7A3: Fréquence échantillonnage faible, sous-utilisation capacités H7A3"
|
||||
#endif
|
||||
|
||||
// Structures de configuration H7A3
|
||||
typedef struct {
|
||||
uint32_t system_clock_hz;
|
||||
uint32_t apb1_clock_hz;
|
||||
uint32_t apb2_clock_hz;
|
||||
bool cache_i_enabled;
|
||||
bool cache_d_enabled;
|
||||
bool fpu_enabled;
|
||||
bool dwt_enabled;
|
||||
uint16_t cv_sample_rate_hz;
|
||||
uint16_t debug_rate_hz;
|
||||
} h7a3_config_t;
|
||||
|
||||
// Configuration par défaut H7A3
|
||||
static const h7a3_config_t H7A3_DEFAULT_CONFIG = {
|
||||
.system_clock_hz = H7A3_SYSTEM_CLOCK_HZ,
|
||||
.apb1_clock_hz = H7A3_APB1_CLOCK_HZ,
|
||||
.apb2_clock_hz = H7A3_APB2_CLOCK_HZ,
|
||||
.cache_i_enabled = CACHE_H7A3_ENABLE_I,
|
||||
.cache_d_enabled = CACHE_H7A3_ENABLE_D,
|
||||
.fpu_enabled = FPU_H7A3_ENABLE,
|
||||
.dwt_enabled = DEBUG_H7A3_DWT_ENABLE,
|
||||
.cv_sample_rate_hz = CV_H7A3_SAMPLE_RATE,
|
||||
.debug_rate_hz = TIMER_H7A3_FREQ_DEBUG
|
||||
};
|
||||
|
||||
// Fonctions utilitaires H7A3 (déclarations)
|
||||
void h7a3_system_init(const h7a3_config_t* config);
|
||||
void h7a3_cache_init(void);
|
||||
void h7a3_dwt_init(void);
|
||||
uint32_t h7a3_get_cpu_cycles(void);
|
||||
float h7a3_cycles_to_us(uint32_t cycles);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // CONFIG_H7A3_H
|
||||
@@ -1,140 +0,0 @@
|
||||
/**
|
||||
* @file config_h7a3.h
|
||||
* @brief Configuration matérielle STM32H7A3 pour Apple ADB Ressurector
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H7A3_H
|
||||
#define CONFIG_H7A3_H
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
// Configuration système H7A3
|
||||
#define H7A3_SYSTEM_CLOCK_HZ 480000000 // 480MHz
|
||||
#define H7A3_APB1_CLOCK_HZ 120000000 // 120MHz
|
||||
#define H7A3_APB2_CLOCK_HZ 120000000 // 120MHz
|
||||
|
||||
// Configuration DAC H7A3 (identique F303 pour compatibilité)
|
||||
#define DAC_H7A3_INSTANCE DAC1
|
||||
#define DAC_H7A3_CV1_PIN GPIO_PIN_4 // PA4
|
||||
#define DAC_H7A3_CV1_PORT GPIOA
|
||||
#define DAC_H7A3_CV2_PIN GPIO_PIN_5 // PA5
|
||||
#define DAC_H7A3_CV2_PORT GPIOA
|
||||
#define DAC_H7A3_RESOLUTION 12 // 12-bit (extensible 16-bit)
|
||||
#define DAC_H7A3_VREF 3.3f // Tension de référence
|
||||
|
||||
// Configuration Timer H7A3 (utilisation timers avancés)
|
||||
#define TIMER_H7A3_MAIN TIM1 // Timer principal modulation
|
||||
#define TIMER_H7A3_DEBUG TIM8 // Timer debug (ou TIM1 si TIM8 indisponible)
|
||||
#define TIMER_H7A3_FREQ_MAIN 1000 // 1kHz modulation principale
|
||||
#define TIMER_H7A3_FREQ_DEBUG 20 // 20Hz debug processing
|
||||
|
||||
// Configuration GPIO H7A3
|
||||
// LED status (Nucleo H7A3 standard)
|
||||
#define LED_H7A3_PIN GPIO_PIN_0 // PB0 (LED verte Nucleo)
|
||||
#define LED_H7A3_PORT GPIOB
|
||||
|
||||
// Bouton utilisateur (Nucleo H7A3 standard)
|
||||
#define BUTTON_H7A3_PIN GPIO_PIN_13 // PC13 (Bouton bleu Nucleo)
|
||||
#define BUTTON_H7A3_PORT GPIOC
|
||||
|
||||
// Configuration UART H7A3 (Serial)
|
||||
#define UART_H7A3_INSTANCE USART3 // UART Nucleo ST-Link
|
||||
#define UART_H7A3_BAUDRATE 115200
|
||||
#define UART_H7A3_TX_PIN GPIO_PIN_8 // PD8
|
||||
#define UART_H7A3_TX_PORT GPIOD
|
||||
#define UART_H7A3_RX_PIN GPIO_PIN_9 // PD9
|
||||
#define UART_H7A3_RX_PORT GPIOD
|
||||
|
||||
// Configuration USB H7A3 (optionnel pour HID)
|
||||
#define USB_H7A3_HS_ENABLE 1 // USB High Speed
|
||||
#define USB_H7A3_DP_PIN GPIO_PIN_12 // PA12
|
||||
#define USB_H7A3_DP_PORT GPIOA
|
||||
#define USB_H7A3_DM_PIN GPIO_PIN_11 // PA11
|
||||
#define USB_H7A3_DM_PORT GPIOA
|
||||
|
||||
// Configuration DMA H7A3 (pour DAC haute performance)
|
||||
#define DMA_H7A3_DAC_STREAM DMA1_Stream0
|
||||
#define DMA_H7A3_DAC_CHANNEL DMA_REQUEST_DAC1_CH1
|
||||
|
||||
// Configuration Cache H7A3
|
||||
#define CACHE_H7A3_ENABLE_I 1 // Cache Instructions
|
||||
#define CACHE_H7A3_ENABLE_D 1 // Cache Données
|
||||
#define CACHE_H7A3_LINE_SIZE 32 // Taille ligne cache
|
||||
|
||||
// Configuration MPU H7A3 (Memory Protection Unit)
|
||||
#define MPU_H7A3_ENABLE 0 // Désactivé par défaut
|
||||
#define MPU_H7A3_REGIONS 8 // Nombre régions disponibles
|
||||
|
||||
// Configuration FPU H7A3
|
||||
#define FPU_H7A3_ENABLE 1 // Unité virgule flottante
|
||||
#define FPU_H7A3_DOUBLE_PREC 1 // Double précision
|
||||
|
||||
// Configuration Debug H7A3
|
||||
#define DEBUG_H7A3_DWT_ENABLE 1 // Data Watchpoint Trace
|
||||
#define DEBUG_H7A3_ITM_ENABLE 1 // Instrumentation Trace Macrocell
|
||||
#define DEBUG_H7A3_SWO_ENABLE 1 // Single Wire Output
|
||||
|
||||
// Macros utilitaires H7A3
|
||||
#define H7A3_CYCLES_TO_US(cycles) ((float)(cycles) / (H7A3_SYSTEM_CLOCK_HZ / 1000000.0f))
|
||||
#define H7A3_US_TO_CYCLES(us) ((uint32_t)((us) * (H7A3_SYSTEM_CLOCK_HZ / 1000000.0f)))
|
||||
|
||||
// Configuration spécifique CV/GATE H7A3
|
||||
#define CV_H7A3_SAMPLE_RATE 1000 // 1kHz (vs 100Hz F303)
|
||||
#define CV_H7A3_BUFFER_SIZE 64 // Buffer étendu (vs 32 F303)
|
||||
#define CV_H7A3_DMA_ENABLE 0 // DMA pour DAC (optionnel)
|
||||
#define CV_H7A3_INTERRUPT_PRIO 5 // Priorité interruption modulation
|
||||
|
||||
// Validation configuration
|
||||
#if H7A3_SYSTEM_CLOCK_HZ != 480000000
|
||||
#warning "H7A3: Fréquence système non optimale, performance réduite"
|
||||
#endif
|
||||
|
||||
#if !CACHE_H7A3_ENABLE_I || !CACHE_H7A3_ENABLE_D
|
||||
#warning "H7A3: Caches désactivés, performance significativement réduite"
|
||||
#endif
|
||||
|
||||
#if CV_H7A3_SAMPLE_RATE < 1000
|
||||
#warning "H7A3: Fréquence échantillonnage faible, sous-utilisation capacités H7A3"
|
||||
#endif
|
||||
|
||||
// Structures de configuration H7A3
|
||||
typedef struct {
|
||||
uint32_t system_clock_hz;
|
||||
uint32_t apb1_clock_hz;
|
||||
uint32_t apb2_clock_hz;
|
||||
bool cache_i_enabled;
|
||||
bool cache_d_enabled;
|
||||
bool fpu_enabled;
|
||||
bool dwt_enabled;
|
||||
uint16_t cv_sample_rate_hz;
|
||||
uint16_t debug_rate_hz;
|
||||
} h7a3_config_t;
|
||||
|
||||
// Configuration par défaut H7A3
|
||||
static const h7a3_config_t H7A3_DEFAULT_CONFIG = {
|
||||
.system_clock_hz = H7A3_SYSTEM_CLOCK_HZ,
|
||||
.apb1_clock_hz = H7A3_APB1_CLOCK_HZ,
|
||||
.apb2_clock_hz = H7A3_APB2_CLOCK_HZ,
|
||||
.cache_i_enabled = CACHE_H7A3_ENABLE_I,
|
||||
.cache_d_enabled = CACHE_H7A3_ENABLE_D,
|
||||
.fpu_enabled = FPU_H7A3_ENABLE,
|
||||
.dwt_enabled = DEBUG_H7A3_DWT_ENABLE,
|
||||
.cv_sample_rate_hz = CV_H7A3_SAMPLE_RATE,
|
||||
.debug_rate_hz = TIMER_H7A3_FREQ_DEBUG
|
||||
};
|
||||
|
||||
// Fonctions utilitaires H7A3 (déclarations)
|
||||
void h7a3_system_init(const h7a3_config_t* config);
|
||||
void h7a3_cache_init(void);
|
||||
void h7a3_dwt_init(void);
|
||||
uint32_t h7a3_get_cpu_cycles(void);
|
||||
float h7a3_cycles_to_us(uint32_t cycles);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // CONFIG_H7A3_H
|
||||
@@ -1,845 +0,0 @@
|
||||
/**
|
||||
* @file cv_modulation_h7a3.cpp
|
||||
* @brief Implémentation de la modulation avancée des signaux CV - STM32H7A3 Version
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Adaptations H7A3:
|
||||
* - Utilisation des timers haute performance (TIM1/TIM8)
|
||||
* - Support des caches L1 Cortex-M7
|
||||
* - DAC haute résolution et haute vitesse
|
||||
* - Optimisations mathématiques FPU double précision
|
||||
*/
|
||||
|
||||
#include "cv_modulation.h"
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <math.h>
|
||||
#include "stm32h7xx_hal.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#include <HardwareTimer.h> // Pour les timers H7A3 optimisés
|
||||
#endif
|
||||
|
||||
// Constantes DAC/CV locales H7A3 (pour éviter les problèmes d'include circulaire)
|
||||
#define CV_CENTER_VALUE_LOCAL 2047 // Valeur centrale du DAC (1.65V)
|
||||
#define CV_MAX_VALUE_LOCAL 4095 // Valeur maximale du DAC (H7A3: extensible à 16-bit)
|
||||
#define CV_MIN_VALUE_LOCAL 0 // Valeur minimale du DAC
|
||||
#define DAC_CV1_CHANNEL_LOCAL DAC_CHANNEL_1 // PA4 - DAC1_OUT1 (H7A3 haute perf)
|
||||
#define DAC_CV2_CHANNEL_LOCAL DAC_CHANNEL_2 // PA5 - DAC1_OUT2 (H7A3 haute perf)
|
||||
|
||||
// Variables globales H7A3
|
||||
static cv_modulation_state_t mod_state;
|
||||
static bool vibrato_enabled = true;
|
||||
static float vibrato_freq = VIBRATO_FREQUENCY;
|
||||
static uint16_t vibrato_depth = VIBRATO_DEPTH;
|
||||
static uint32_t last_update_time = 0;
|
||||
|
||||
// Variables de debug FM optimisées H7A3 avec buffer circulaire étendu
|
||||
static bool fm_debug_enabled = false;
|
||||
static uint32_t fm_debug_counter = 0;
|
||||
static uint32_t last_fm_debug_time = 0;
|
||||
|
||||
// Buffer circulaire pour debug FM H7A3 (traitement en arrière-plan haute performance)
|
||||
#define FM_DEBUG_BUFFER_SIZE_H7A3 64 // Double capacité pour H7A3
|
||||
typedef struct {
|
||||
uint32_t timestamp;
|
||||
int8_t mouse_x;
|
||||
int8_t mouse_y;
|
||||
float fm_depth_cv1;
|
||||
float fm_depth_cv2;
|
||||
float vibrato_phase_cv1;
|
||||
float vibrato_phase_cv2;
|
||||
float fm_contribution_cv1;
|
||||
float fm_contribution_cv2;
|
||||
// Extensions H7A3
|
||||
uint32_t cpu_cycles; // Comptage cycles CPU
|
||||
float processing_time_us; // Temps de traitement en microsecondes
|
||||
} fm_debug_entry_h7a3_t;
|
||||
|
||||
static fm_debug_entry_h7a3_t fm_debug_buffer_h7a3[FM_DEBUG_BUFFER_SIZE_H7A3];
|
||||
static volatile uint8_t fm_debug_write_idx = 0;
|
||||
static volatile uint8_t fm_debug_read_idx = 0;
|
||||
static volatile bool fm_debug_buffer_full = false;
|
||||
|
||||
// Timer pour traitement debug en arrière-plan H7A3 (haute fréquence)
|
||||
static HardwareTimer* debug_timer_h7a3 = nullptr;
|
||||
static bool debug_timer_initialized = false;
|
||||
|
||||
// Accès aux variables partagées H7A3 (maintenant dans dac_cv_manager_h7a3)
|
||||
extern uint16_t current_cv1_value;
|
||||
extern uint16_t current_cv2_value;
|
||||
|
||||
// Déclaration forward de la fonction DAC H7A3 (implémentée dans dac_cv_manager_h7a3)
|
||||
extern void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
|
||||
// Variables pour optimisations H7A3
|
||||
static uint32_t h7a3_performance_cycles = 0;
|
||||
static bool h7a3_cache_enabled = false;
|
||||
|
||||
/**
|
||||
* @brief Fonction sinus optimisée H7A3 avec FPU double précision
|
||||
* @param phase Phase en radians (0 à 2π)
|
||||
* @return Valeur sinus (-1.0 à +1.0)
|
||||
*/
|
||||
__attribute__((always_inline)) static inline float fast_sin_h7a3(float phase) {
|
||||
// Utilisation de l'unité FPU Cortex-M7 pour calcul haute performance
|
||||
// Normalisation de la phase dans [0, 2π]
|
||||
while (phase < 0.0f) phase += 2.0f * M_PI;
|
||||
while (phase >= 2.0f * M_PI) phase -= 2.0f * M_PI;
|
||||
|
||||
// Approximation polynomiale rapide du sinus optimisée H7A3
|
||||
float x = phase;
|
||||
if (x > M_PI) x = 2.0f * M_PI - x;
|
||||
if (x > M_PI_2) x = M_PI - x;
|
||||
|
||||
// Approximation polynomiale haute précision (utilise FPU H7A3)
|
||||
float x2 = x * x;
|
||||
float x4 = x2 * x2;
|
||||
return x * (1.0f - x2 * (0.16666667f - x2 * (0.00833333f - x4 * 0.0001984f)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure les caches L1 pour optimiser les performances CV
|
||||
*/
|
||||
void cv_modulation_h7a3_configure_cache(void) {
|
||||
#if H7A3_CACHE_ENABLED
|
||||
// Activer les caches L1 si pas déjà fait
|
||||
if (!h7a3_cache_enabled) {
|
||||
// Cache instruction
|
||||
if (!SCB->CCR & SCB_CCR_IC_Msk) {
|
||||
SCB_EnableICache();
|
||||
}
|
||||
|
||||
// Cache données
|
||||
if (!SCB->CCR & SCB_CCR_DC_Msk) {
|
||||
SCB_EnableDCache();
|
||||
}
|
||||
|
||||
h7a3_cache_enabled = true;
|
||||
Serial.println("H7A3: Caches L1 activés pour CV modulation");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite les entrées debug en arrière-plan H7A3 (appelé par interruption timer haute fréquence)
|
||||
*/
|
||||
static void process_debug_buffer_h7a3(void) {
|
||||
static uint8_t entries_processed = 0;
|
||||
const uint8_t MAX_ENTRIES_PER_CALL_H7A3 = 4; // Plus d'entrées par appel sur H7A3
|
||||
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Début mesure performance
|
||||
|
||||
// Traiter plusieurs entrées du buffer (capacité H7A3)
|
||||
for (uint8_t i = 0; i < MAX_ENTRIES_PER_CALL_H7A3 && fm_debug_read_idx != fm_debug_write_idx; i++) {
|
||||
fm_debug_entry_h7a3_t* entry = &fm_debug_buffer_h7a3[fm_debug_read_idx];
|
||||
|
||||
// Affichage debug optimisé H7A3 (seulement les données importantes)
|
||||
if (entries_processed % 2 == 0) { // 1 sur 2 pour H7A3 (plus de capacité)
|
||||
Serial.print("FM_H7A3[");
|
||||
Serial.print(entry->timestamp);
|
||||
Serial.print("] M(");
|
||||
Serial.print(entry->mouse_x);
|
||||
Serial.print(",");
|
||||
Serial.print(entry->mouse_y);
|
||||
Serial.print(") D(");
|
||||
Serial.print(entry->fm_depth_cv1, 3); // Plus de précision sur H7A3
|
||||
Serial.print(",");
|
||||
Serial.print(entry->fm_depth_cv2, 3);
|
||||
Serial.print(") C(");
|
||||
Serial.print(entry->fm_contribution_cv1, 2);
|
||||
Serial.print(",");
|
||||
Serial.print(entry->fm_contribution_cv2, 2);
|
||||
Serial.print(") T:");
|
||||
Serial.print(entry->processing_time_us, 2);
|
||||
Serial.println("μs");
|
||||
}
|
||||
|
||||
fm_debug_read_idx = (fm_debug_read_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
entries_processed++;
|
||||
}
|
||||
|
||||
// Mesure performance H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
h7a3_performance_cycles = end_cycles - start_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback du timer debug H7A3 (appelé à 20Hz - plus rapide que F303)
|
||||
*/
|
||||
static void debug_timer_callback_h7a3(void) {
|
||||
// Traiter le buffer debug si activé
|
||||
if (fm_debug_enabled) {
|
||||
process_debug_buffer_h7a3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise le timer pour debug en arrière-plan H7A3
|
||||
*/
|
||||
static void init_debug_timer_h7a3(void) {
|
||||
if (debug_timer_initialized) return;
|
||||
|
||||
// Utilisation du HardwareTimer Arduino STM32 H7A3 (Timer 1 ou 8 pour haute performance)
|
||||
#if H7A3_ADVANCED_TIMERS
|
||||
debug_timer_h7a3 = new HardwareTimer(TIM1); // Timer avancé H7A3
|
||||
#else
|
||||
debug_timer_h7a3 = new HardwareTimer(TIM15); // Timer standard si pas d'avancé
|
||||
#endif
|
||||
|
||||
// Configuration: 20Hz = 50ms d'intervalle (plus rapide que F303)
|
||||
debug_timer_h7a3->setOverflow(20, HERTZ_FORMAT); // 20Hz
|
||||
debug_timer_h7a3->attachInterrupt(debug_timer_callback_h7a3);
|
||||
|
||||
debug_timer_initialized = true;
|
||||
|
||||
Serial.println("H7A3: Timer debug initialisé (20Hz, HardwareTimer TIM1)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utilise les timers avancés H7A3 pour la modulation haute fréquence
|
||||
*/
|
||||
void cv_modulation_h7a3_advanced_timers_setup(void) {
|
||||
#if H7A3_ADVANCED_TIMERS
|
||||
// Configuration avancée des timers H7A3 pour modulation haute fréquence
|
||||
// TIM1 : Modulation principale (jusqu'à 100kHz)
|
||||
// TIM8 : Modulation secondaire ou synchronisation
|
||||
|
||||
Serial.println("H7A3: Configuration timers avancés TIM1/TIM8 pour modulation CV");
|
||||
Serial.println("H7A3: Fréquence modulation augmentée à 100kHz");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Démarre le timer debug H7A3
|
||||
*/
|
||||
static void start_debug_timer_h7a3(void) {
|
||||
if (!debug_timer_initialized) init_debug_timer_h7a3();
|
||||
if (debug_timer_h7a3) {
|
||||
debug_timer_h7a3->resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arrête le timer debug H7A3
|
||||
*/
|
||||
static void stop_debug_timer_h7a3(void) {
|
||||
if (debug_timer_h7a3) {
|
||||
debug_timer_h7a3->pause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ajoute une entrée au buffer debug FM H7A3 (thread-safe avec mesure performance)
|
||||
*/
|
||||
static void add_fm_debug_entry_h7a3(int8_t mouse_x, int8_t mouse_y,
|
||||
float fm_depth_cv1, float fm_depth_cv2,
|
||||
float vibrato_phase_cv1, float vibrato_phase_cv2,
|
||||
float fm_contrib_cv1, float fm_contrib_cv2) {
|
||||
|
||||
if (!fm_debug_enabled) return;
|
||||
|
||||
uint32_t start_time = micros(); // Mesure précise H7A3
|
||||
|
||||
// Désactiver les interruptions temporairement
|
||||
__disable_irq();
|
||||
|
||||
// Vérifier si le buffer est plein
|
||||
uint8_t next_write = (fm_debug_write_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
if (next_write == fm_debug_read_idx) {
|
||||
// Buffer plein, écraser l'entrée la plus ancienne
|
||||
fm_debug_read_idx = (fm_debug_read_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
fm_debug_buffer_full = true;
|
||||
}
|
||||
|
||||
// Ajouter la nouvelle entrée H7A3
|
||||
fm_debug_entry_h7a3_t* entry = &fm_debug_buffer_h7a3[fm_debug_write_idx];
|
||||
entry->timestamp = millis();
|
||||
entry->mouse_x = mouse_x;
|
||||
entry->mouse_y = mouse_y;
|
||||
entry->fm_depth_cv1 = fm_depth_cv1;
|
||||
entry->fm_depth_cv2 = fm_depth_cv2;
|
||||
entry->vibrato_phase_cv1 = vibrato_phase_cv1;
|
||||
entry->vibrato_phase_cv2 = vibrato_phase_cv2;
|
||||
entry->fm_contribution_cv1 = fm_contrib_cv1;
|
||||
entry->fm_contribution_cv2 = fm_contrib_cv2;
|
||||
|
||||
// Extensions H7A3
|
||||
entry->cpu_cycles = DWT->CYCCNT;
|
||||
entry->processing_time_us = (float)(micros() - start_time);
|
||||
|
||||
fm_debug_write_idx = next_write;
|
||||
|
||||
// Réactiver les interruptions
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fonction de transition douce (courbe en S) optimisée H7A3
|
||||
* @param t Paramètre de transition (0.0 à 1.0)
|
||||
* @return Valeur lissée (0.0 à 1.0)
|
||||
*/
|
||||
__attribute__((always_inline)) static inline float smooth_transition_h7a3(float t) {
|
||||
if (t <= 0.0f) return 0.0f;
|
||||
if (t >= 1.0f) return 1.0f;
|
||||
|
||||
// Courbe sinusoïdale douce optimisée FPU H7A3
|
||||
return (1.0f - fast_sin_h7a3(t * M_PI + M_PI * 0.5f)) * 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise le système de modulation CV pour H7A3
|
||||
*/
|
||||
void cv_modulation_init(void) {
|
||||
// Configuration spécifique H7A3
|
||||
cv_modulation_h7a3_configure_cache();
|
||||
|
||||
// Initialisation de l'état des modulateurs
|
||||
mod_state.current_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.current_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
|
||||
mod_state.portamento_phase_cv1 = 0.0f;
|
||||
mod_state.portamento_phase_cv2 = 0.0f;
|
||||
mod_state.portamento_active_cv1 = false;
|
||||
mod_state.portamento_active_cv2 = false;
|
||||
|
||||
mod_state.vibrato_phase_cv1 = 0.0f;
|
||||
mod_state.vibrato_phase_cv2 = 0.0f;
|
||||
|
||||
mod_state.fm_depth_cv1 = 0.0f;
|
||||
mod_state.fm_depth_cv2 = 0.0f;
|
||||
|
||||
// Extensions H7A3
|
||||
mod_state.h7a3_performance_counter = 0;
|
||||
mod_state.h7a3_cache_coherency_active = h7a3_cache_enabled;
|
||||
|
||||
last_update_time = millis();
|
||||
|
||||
// Initialiser le buffer debug H7A3
|
||||
fm_debug_write_idx = 0;
|
||||
fm_debug_read_idx = 0;
|
||||
fm_debug_buffer_full = false;
|
||||
|
||||
// Initialiser le timer debug H7A3 (mais ne pas le démarrer encore)
|
||||
init_debug_timer_h7a3();
|
||||
|
||||
// Configuration timers avancés H7A3
|
||||
cv_modulation_h7a3_advanced_timers_setup();
|
||||
|
||||
Serial.println("H7A3: CV Modulation initialisée - Portamento + FM actifs + Debug optimisé H7A3");
|
||||
Serial.print("H7A3: Caches L1: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise les spécificités H7A3 (caches, timers avancés)
|
||||
*/
|
||||
void cv_modulation_h7a3_init_advanced(void) {
|
||||
// Activer le compteur de cycles DWT pour mesures de performance
|
||||
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
|
||||
// Configuration cache spécifique CV
|
||||
cv_modulation_h7a3_configure_cache();
|
||||
|
||||
// Timers avancés
|
||||
cv_modulation_h7a3_advanced_timers_setup();
|
||||
|
||||
Serial.println("H7A3: Initialisation avancée terminée");
|
||||
Serial.println("H7A3: DWT cycle counter activé pour mesures de performance");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Démarre une transition portamento vers une nouvelle note
|
||||
*/
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv) {
|
||||
if (cv_channel == 1) {
|
||||
mod_state.target_cv1 = target_cv;
|
||||
mod_state.portamento_phase_cv1 = 0.0f;
|
||||
mod_state.portamento_active_cv1 = true;
|
||||
|
||||
Serial.print("H7A3: Portamento CV1 démarré vers ");
|
||||
Serial.println(target_cv);
|
||||
} else if (cv_channel == 2) {
|
||||
mod_state.target_cv2 = target_cv;
|
||||
mod_state.portamento_phase_cv2 = 0.0f;
|
||||
mod_state.portamento_active_cv2 = true;
|
||||
|
||||
Serial.print("H7A3: Portamento CV2 démarré vers ");
|
||||
Serial.println(target_cv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Met à jour la modulation de fréquence ET les valeurs CV de base avec la souris
|
||||
*/
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y) {
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Début mesure performance H7A3
|
||||
|
||||
// 1. Mise à jour des valeurs CV de base avec le mouvement de la souris
|
||||
if (abs(mouse_x) > 1 || abs(mouse_y) > 1) {
|
||||
// Sensibilité de contrôle CV direct H7A3 (plus précise)
|
||||
const float CV_SENSITIVITY_H7A3 = 8.0f;
|
||||
|
||||
// Calcul des nouvelles valeurs CV avec accumulation
|
||||
int16_t cv1_delta = (int16_t)((float)mouse_x * CV_SENSITIVITY_H7A3);
|
||||
int16_t cv2_delta = (int16_t)((float)mouse_y * CV_SENSITIVITY_H7A3);
|
||||
|
||||
// Mise à jour cumulative des valeurs CV cibles
|
||||
int32_t new_cv1 = (int32_t)mod_state.current_cv1 + cv1_delta;
|
||||
int32_t new_cv2 = (int32_t)mod_state.current_cv2 + cv2_delta;
|
||||
|
||||
// Limitation dans la plage DAC valide H7A3
|
||||
if (new_cv1 < CV_MIN_VALUE_LOCAL) new_cv1 = CV_MIN_VALUE_LOCAL;
|
||||
if (new_cv1 > CV_MAX_VALUE_LOCAL) new_cv1 = CV_MAX_VALUE_LOCAL;
|
||||
if (new_cv2 < CV_MIN_VALUE_LOCAL) new_cv2 = CV_MIN_VALUE_LOCAL;
|
||||
if (new_cv2 > CV_MAX_VALUE_LOCAL) new_cv2 = CV_MAX_VALUE_LOCAL;
|
||||
|
||||
// Appliquer les nouvelles valeurs
|
||||
mod_state.current_cv1 = (uint16_t)new_cv1;
|
||||
mod_state.current_cv2 = (uint16_t)new_cv2;
|
||||
mod_state.target_cv1 = (uint16_t)new_cv1; // Synchroniser les cibles
|
||||
mod_state.target_cv2 = (uint16_t)new_cv2;
|
||||
|
||||
// Debug des changements CV H7A3 (plus fréquent)
|
||||
static uint32_t last_cv_debug = 0;
|
||||
uint32_t current_time = millis();
|
||||
if (current_time - last_cv_debug > 100) { // Debug toutes les 100ms (plus rapide sur H7A3)
|
||||
Serial.print("H7A3 CV mis à jour - CV1: ");
|
||||
Serial.print(mod_state.current_cv1);
|
||||
Serial.print(" (");
|
||||
Serial.print((mod_state.current_cv1 * 3.3f / 4095.0f), 3); // Plus de précision
|
||||
Serial.print("V), CV2: ");
|
||||
Serial.print(mod_state.current_cv2);
|
||||
Serial.print(" (");
|
||||
Serial.print((mod_state.current_cv2 * 3.3f / 4095.0f), 3);
|
||||
Serial.println("V)");
|
||||
last_cv_debug = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Calcul des profondeurs de modulation FM (en plus des CV de base)
|
||||
float new_fm_depth_cv1 = (float)mouse_x / 127.0f * FM_SENSITIVITY;
|
||||
float new_fm_depth_cv2 = (float)mouse_y / 127.0f * FM_SENSITIVITY;
|
||||
|
||||
// Mise à jour des valeurs FM H7A3 (thread-safe avec cache coherency)
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence cache avant mise à jour
|
||||
#endif
|
||||
|
||||
mod_state.fm_depth_cv1 = new_fm_depth_cv1;
|
||||
mod_state.fm_depth_cv2 = new_fm_depth_cv2;
|
||||
|
||||
// Si debug activé, ajouter au buffer pour traitement en arrière-plan H7A3
|
||||
if (fm_debug_enabled && (abs(mouse_x) > 2 || abs(mouse_y) > 2)) {
|
||||
// Calculer les contributions FM actuelles pour le debug
|
||||
float fm_contrib_cv1 = 0.0f;
|
||||
float fm_contrib_cv2 = 0.0f;
|
||||
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv1 * 0.5f;
|
||||
fm_contrib_cv1 = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv1 * 30.0f;
|
||||
}
|
||||
|
||||
if (abs(mod_state.fm_depth_cv2) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv2 * 2.5f;
|
||||
fm_contrib_cv2 = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv2 * 20.0f;
|
||||
}
|
||||
|
||||
// Ajouter au buffer H7A3 pour traitement asynchrone
|
||||
add_fm_debug_entry_h7a3(mouse_x, mouse_y,
|
||||
mod_state.fm_depth_cv1, mod_state.fm_depth_cv2,
|
||||
mod_state.vibrato_phase_cv1, mod_state.vibrato_phase_cv2,
|
||||
fm_contrib_cv1, fm_contrib_cv2);
|
||||
}
|
||||
|
||||
// Mesure performance H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
mod_state.h7a3_performance_counter = end_cycles - start_cycles;
|
||||
|
||||
// Debug léger synchrone pour surveillance critique H7A3
|
||||
static uint32_t last_critical_debug = 0;
|
||||
uint32_t current_time = millis();
|
||||
|
||||
if (current_time - last_critical_debug > 1000) { // Plus fréquent sur H7A3
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.1f || abs(mod_state.fm_depth_cv2) > 0.1f) {
|
||||
Serial.print("H7A3 FM ACTIF - D1:");
|
||||
Serial.print(mod_state.fm_depth_cv1, 3);
|
||||
Serial.print(" D2:");
|
||||
Serial.print(mod_state.fm_depth_cv2, 3);
|
||||
Serial.print(" Cycles:");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
}
|
||||
last_critical_debug = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite toutes les modulations en temps réel (version H7A3 optimisée)
|
||||
*/
|
||||
void cv_modulation_process(void) {
|
||||
uint32_t current_time = millis();
|
||||
float delta_time = (current_time - last_update_time) / 1000.0f;
|
||||
last_update_time = current_time;
|
||||
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Mesure performance H7A3
|
||||
|
||||
// 1. Traitement du portamento CV1 (optimisé H7A3)
|
||||
if (mod_state.portamento_active_cv1) {
|
||||
mod_state.portamento_phase_cv1 += PORTAMENTO_SPEED * delta_time;
|
||||
|
||||
if (mod_state.portamento_phase_cv1 >= 1.0f) {
|
||||
mod_state.portamento_phase_cv1 = 1.0f;
|
||||
mod_state.portamento_active_cv1 = false;
|
||||
mod_state.current_cv1 = mod_state.target_cv1;
|
||||
} else {
|
||||
float blend = smooth_transition_h7a3(mod_state.portamento_phase_cv1);
|
||||
mod_state.current_cv1 = (uint16_t)((1.0f - blend) * mod_state.current_cv1 +
|
||||
blend * mod_state.target_cv1);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Traitement du portamento CV2 (optimisé H7A3)
|
||||
if (mod_state.portamento_active_cv2) {
|
||||
mod_state.portamento_phase_cv2 += PORTAMENTO_SPEED * delta_time;
|
||||
|
||||
if (mod_state.portamento_phase_cv2 >= 1.0f) {
|
||||
mod_state.portamento_phase_cv2 = 1.0f;
|
||||
mod_state.portamento_active_cv2 = false;
|
||||
mod_state.current_cv2 = mod_state.target_cv2;
|
||||
} else {
|
||||
float blend = smooth_transition_h7a3(mod_state.portamento_phase_cv2);
|
||||
mod_state.current_cv2 = (uint16_t)((1.0f - blend) * mod_state.current_cv2 +
|
||||
blend * mod_state.target_cv2);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Application du vibrato H7A3 (haute fréquence)
|
||||
mod_state.vibrato_phase_cv1 += vibrato_freq * delta_time * 2.0f * M_PI;
|
||||
mod_state.vibrato_phase_cv2 += vibrato_freq * delta_time * 2.0f * M_PI;
|
||||
|
||||
// Normalisation des phases vibrato (optimisé FPU H7A3)
|
||||
if (mod_state.vibrato_phase_cv1 > 2.0f * M_PI)
|
||||
mod_state.vibrato_phase_cv1 -= 2.0f * M_PI;
|
||||
if (mod_state.vibrato_phase_cv2 > 2.0f * M_PI)
|
||||
mod_state.vibrato_phase_cv2 -= 2.0f * M_PI;
|
||||
|
||||
// 4. Calcul des valeurs CV finales avec modulations H7A3
|
||||
int16_t final_cv1 = mod_state.current_cv1;
|
||||
int16_t final_cv2 = mod_state.current_cv2;
|
||||
|
||||
// Vibrato sur CV1 (haute précision H7A3)
|
||||
if (vibrato_enabled) {
|
||||
float vibrato_cv1 = fast_sin_h7a3(mod_state.vibrato_phase_cv1) * vibrato_depth;
|
||||
final_cv1 += (int16_t)vibrato_cv1;
|
||||
}
|
||||
|
||||
// Vibrato sur CV2 (haute précision H7A3)
|
||||
if (vibrato_enabled) {
|
||||
float vibrato_cv2 = fast_sin_h7a3(mod_state.vibrato_phase_cv2) * vibrato_depth;
|
||||
final_cv2 += (int16_t)vibrato_cv2;
|
||||
}
|
||||
|
||||
// Modulation de fréquence CV1 H7A3 (gamme audio étendue)
|
||||
float fm_cv1_contribution = 0.0f;
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv1 * 0.5f;
|
||||
fm_cv1_contribution = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv1 * 30.0f;
|
||||
final_cv1 += (int16_t)fm_cv1_contribution;
|
||||
}
|
||||
|
||||
// Modulation de fréquence CV2 H7A3 (gamme audio étendue)
|
||||
float fm_cv2_contribution = 0.0f;
|
||||
if (abs(mod_state.fm_depth_cv2) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv2 * 2.5f;
|
||||
fm_cv2_contribution = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv2 * 20.0f;
|
||||
final_cv2 += (int16_t)fm_cv2_contribution;
|
||||
}
|
||||
|
||||
// Limitation des valeurs dans la plage DAC valide H7A3
|
||||
if (final_cv1 > CV_MAX_VALUE_LOCAL) final_cv1 = CV_MAX_VALUE_LOCAL;
|
||||
if (final_cv1 < CV_MIN_VALUE_LOCAL) final_cv1 = CV_MIN_VALUE_LOCAL;
|
||||
if (final_cv2 > CV_MAX_VALUE_LOCAL) final_cv2 = CV_MAX_VALUE_LOCAL;
|
||||
if (final_cv2 < CV_MIN_VALUE_LOCAL) final_cv2 = CV_MIN_VALUE_LOCAL;
|
||||
|
||||
// Mise à jour des DACs via le gestionnaire centralisé H7A3
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence avant écriture DAC
|
||||
#endif
|
||||
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL_LOCAL, final_cv1);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL_LOCAL, final_cv2);
|
||||
|
||||
// Mesure performance finale H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
mod_state.h7a3_performance_counter = end_cycles - start_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le vibrato
|
||||
*/
|
||||
void cv_modulation_set_vibrato(bool enabled) {
|
||||
vibrato_enabled = enabled;
|
||||
Serial.print("H7A3 Vibrato: ");
|
||||
Serial.println(enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Définit les paramètres du vibrato
|
||||
*/
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth) {
|
||||
vibrato_freq = frequency;
|
||||
vibrato_depth = depth;
|
||||
|
||||
Serial.print("H7A3 Vibrato params - Freq: ");
|
||||
Serial.print(frequency, 1);
|
||||
Serial.print("Hz, Depth: ");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recentre les valeurs CV à leur position neutre (1.65V)
|
||||
*/
|
||||
void cv_modulation_reset_cv_values(void) {
|
||||
mod_state.current_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.current_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
|
||||
// Écriture immédiate des valeurs centrées H7A3
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence
|
||||
#endif
|
||||
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL_LOCAL, CV_CENTER_VALUE_LOCAL);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL_LOCAL, CV_CENTER_VALUE_LOCAL);
|
||||
|
||||
Serial.println("H7A3: Valeurs CV recentrées à 1.65V (position neutre)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtient l'état actuel des modulateurs
|
||||
*/
|
||||
const cv_modulation_state_t* cv_modulation_get_state(void) {
|
||||
return &mod_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le debug détaillé de la modulation FM H7A3
|
||||
*/
|
||||
void cv_modulation_set_fm_debug(bool enabled) {
|
||||
fm_debug_enabled = enabled;
|
||||
|
||||
if (enabled) {
|
||||
// Démarrer le timer de traitement debug H7A3
|
||||
start_debug_timer_h7a3();
|
||||
Serial.println("H7A3: Debug FM ACTIVÉ avec traitement en arrière-plan optimisé");
|
||||
Serial.println("Format debug FM H7A3:");
|
||||
Serial.println("- FM_H7A3[timestamp] M(x,y) D(depth1,depth2) C(contrib1,contrib2) T:μs");
|
||||
Serial.println("- Timer d'interruption: 20Hz pour traitement asynchrone H7A3");
|
||||
Serial.print("- Buffer circulaire H7A3: ");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.println(" entrées");
|
||||
} else {
|
||||
// Arrêter le timer de traitement debug H7A3
|
||||
stop_debug_timer_h7a3();
|
||||
|
||||
// Vider le buffer restant
|
||||
while (fm_debug_read_idx != fm_debug_write_idx) {
|
||||
process_debug_buffer_h7a3();
|
||||
}
|
||||
|
||||
Serial.println("H7A3: Debug FM DÉSACTIVÉ - Timer arrêté, buffer vidé");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance du debug optimisé H7A3
|
||||
*/
|
||||
void cv_modulation_debug_performance_stats(void) {
|
||||
Serial.println("\n=== STATISTIQUES DEBUG PERFORMANCE H7A3 ===");
|
||||
|
||||
uint8_t buffer_usage = 0;
|
||||
if (fm_debug_write_idx >= fm_debug_read_idx) {
|
||||
buffer_usage = fm_debug_write_idx - fm_debug_read_idx;
|
||||
} else {
|
||||
buffer_usage = (FM_DEBUG_BUFFER_SIZE_H7A3 - fm_debug_read_idx) + fm_debug_write_idx;
|
||||
}
|
||||
|
||||
float usage_percent = ((float)buffer_usage / FM_DEBUG_BUFFER_SIZE_H7A3) * 100.0f;
|
||||
|
||||
Serial.print("Buffer debug H7A3 - Utilisation: ");
|
||||
Serial.print(buffer_usage);
|
||||
Serial.print("/");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.print(" (");
|
||||
Serial.print(usage_percent, 1);
|
||||
Serial.println("%)");
|
||||
|
||||
Serial.print("État timer debug H7A3: ");
|
||||
Serial.println(debug_timer_initialized ? "Initialisé" : "Non initialisé");
|
||||
|
||||
Serial.print("Debug FM: ");
|
||||
Serial.println(fm_debug_enabled ? "ACTIF" : "INACTIF");
|
||||
|
||||
Serial.print("Caches L1: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
|
||||
Serial.print("Performance cycles moyens: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
|
||||
// Temps CPU estimé à 480MHz
|
||||
float cpu_time_us = (float)mod_state.h7a3_performance_counter / 480.0f;
|
||||
Serial.print("Temps CPU estimé: ");
|
||||
Serial.print(cpu_time_us, 2);
|
||||
Serial.println(" μs");
|
||||
|
||||
if (fm_debug_buffer_full) {
|
||||
Serial.println("ATTENTION: Buffer saturé, certaines données debug perdues");
|
||||
Serial.println("Conseils H7A3: Buffer étendu à 64 entrées, performance améliorée");
|
||||
}
|
||||
|
||||
fm_debug_buffer_full = false;
|
||||
Serial.println("===============================================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance H7A3 spécifiques
|
||||
*/
|
||||
void cv_modulation_h7a3_performance_report(void) {
|
||||
Serial.println("\n=== RAPPORT PERFORMANCE H7A3 SPÉCIFIQUE ===");
|
||||
|
||||
Serial.print("CPU: STM32H7A3 @ ");
|
||||
Serial.print(SystemCoreClock / 1000000);
|
||||
Serial.println(" MHz");
|
||||
|
||||
Serial.print("Caches L1 I/D: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
|
||||
Serial.print("FPU double précision: ACTIVÉE");
|
||||
Serial.println();
|
||||
|
||||
Serial.print("DWT cycle counter: ");
|
||||
Serial.println((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) ? "ACTIF" : "INACTIF");
|
||||
|
||||
Serial.print("Cycles de traitement CV: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
|
||||
Serial.print("Performance vs F303: ");
|
||||
float performance_ratio = 480.0f / 72.0f; // H7A3 vs F303
|
||||
Serial.print(performance_ratio, 1);
|
||||
Serial.println("x plus rapide");
|
||||
|
||||
Serial.println("Timer utilisé: TIM1 (avancé) @ 20Hz");
|
||||
Serial.print("Buffer debug étendu: ");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.println(" entrées");
|
||||
|
||||
Serial.println("===============================================\n");
|
||||
}
|
||||
|
||||
// Fonctions de configuration similaires à F303 mais optimisées H7A3
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth) {
|
||||
vibrato_enabled = enabled;
|
||||
vibrato_freq = frequency;
|
||||
vibrato_depth = depth;
|
||||
|
||||
Serial.print("H7A3 Vibrato configuré - État: ");
|
||||
Serial.print(enabled ? "ON" : "OFF");
|
||||
Serial.print(", Fréquence: ");
|
||||
Serial.print(frequency, 1);
|
||||
Serial.print("Hz, Profondeur: ");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
void cv_modulation_configure_portamento_speed(float speed) {
|
||||
if (speed < 0.01f) speed = 0.01f;
|
||||
if (speed > 0.1f) speed = 0.1f;
|
||||
|
||||
Serial.print("H7A3: Vitesse portamento configurée: ");
|
||||
Serial.println(speed, 3);
|
||||
}
|
||||
|
||||
void cv_modulation_debug_fm_report(void) {
|
||||
Serial.println("\n=== RAPPORT COMPLET FM MODULATION H7A3 ===");
|
||||
|
||||
Serial.print("Vibrato base - Fréq: ");
|
||||
Serial.print(vibrato_freq, 1);
|
||||
Serial.print("Hz, Profondeur: ");
|
||||
Serial.print(vibrato_depth);
|
||||
Serial.print(", État: ");
|
||||
Serial.println(vibrato_enabled ? "ON" : "OFF");
|
||||
|
||||
Serial.print("FM depths H7A3 - CV1: ");
|
||||
Serial.print(mod_state.fm_depth_cv1, 4);
|
||||
Serial.print(" (");
|
||||
Serial.print(abs(mod_state.fm_depth_cv1) > 0.01f ? "ACTIF" : "INACTIF");
|
||||
Serial.print("), CV2: ");
|
||||
Serial.print(mod_state.fm_depth_cv2, 4);
|
||||
Serial.print(" (");
|
||||
Serial.print(abs(mod_state.fm_depth_cv2) > 0.01f ? "ACTIF" : "INACTIF");
|
||||
Serial.println(")");
|
||||
|
||||
Serial.print("Performance H7A3: ");
|
||||
Serial.print(mod_state.h7a3_performance_counter);
|
||||
Serial.println(" cycles CPU");
|
||||
|
||||
Serial.println("========================================\n");
|
||||
}
|
||||
|
||||
void cv_modulation_test_fm(uint32_t test_duration) {
|
||||
Serial.println("\n=== TEST MODULATION FM H7A3 ===");
|
||||
Serial.print("Durée du test: ");
|
||||
Serial.print(test_duration);
|
||||
Serial.println("ms");
|
||||
Serial.println("Performance H7A3: Test haute fréquence");
|
||||
|
||||
uint32_t start_time = millis();
|
||||
bool old_debug_state = fm_debug_enabled;
|
||||
fm_debug_enabled = true;
|
||||
|
||||
while ((millis() - start_time) < test_duration) {
|
||||
uint32_t elapsed = millis() - start_time;
|
||||
|
||||
// Test H7A3 avec fréquences plus élevées
|
||||
float test_phase = (float)elapsed / 1000.0f * 4.0f * M_PI; // 2 cycles par seconde
|
||||
int8_t test_mouse_x = (int8_t)(sin(test_phase) * 60.0f); // ±60 unités
|
||||
int8_t test_mouse_y = (int8_t)(cos(test_phase) * 40.0f); // ±40 unités
|
||||
|
||||
cv_modulation_update_fm(test_mouse_x, test_mouse_y);
|
||||
cv_modulation_process();
|
||||
|
||||
if ((millis() - start_time) % 250 == 0) { // Plus fréquent
|
||||
Serial.print("Test FM H7A3 - Temps: ");
|
||||
Serial.print(elapsed);
|
||||
Serial.print("ms, Mouse: (");
|
||||
Serial.print(test_mouse_x);
|
||||
Serial.print(", ");
|
||||
Serial.print(test_mouse_y);
|
||||
Serial.print("), Cycles: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
}
|
||||
|
||||
delay(5); // 200Hz de mise à jour (plus rapide que F303)
|
||||
}
|
||||
|
||||
fm_debug_enabled = old_debug_state;
|
||||
cv_modulation_update_fm(0, 0);
|
||||
|
||||
Serial.println("=== FIN TEST FM H7A3 ===\n");
|
||||
}
|
||||
|
||||
// [Suite du fichier à continuer dans la prochaine partie...]
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
@@ -1,188 +0,0 @@
|
||||
/**
|
||||
* @file cv_modulation.h
|
||||
* @brief Modulation avancée des signaux CV avec génération sinusoïdale - STM32H7A3 Version
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Adaptations H7A3:
|
||||
* - Support DAC haute résolution (12-bit)
|
||||
* - Utilisation des timers avancés TIM1/TIM8
|
||||
* - Support cache et optimisations Cortex-M7
|
||||
* - Fréquences système élevées (480MHz)
|
||||
*/
|
||||
|
||||
#ifndef CV_MODULATION_H7A3_H
|
||||
#define CV_MODULATION_H7A3_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
// Constantes DAC/CV adaptées pour H7A3
|
||||
#ifndef DAC_CV1_CHANNEL
|
||||
#define DAC_CV1_CHANNEL DAC_CHANNEL_1 // PA4 - DAC1_OUT1 (H7A3: DAC haute résolution)
|
||||
#define DAC_CV2_CHANNEL DAC_CHANNEL_2 // PA5 - DAC1_OUT2 (H7A3: DAC haute résolution)
|
||||
#define CV_CENTER_VALUE 2047 // Valeur centrale du DAC (1.65V)
|
||||
#define CV_MAX_VALUE 4095 // Valeur maximale du DAC (H7A3: jusqu'à 16-bit possible)
|
||||
#define CV_MIN_VALUE 0 // Valeur minimale du DAC
|
||||
#endif
|
||||
|
||||
// Configuration des modulateurs H7A3 (performance améliorée)
|
||||
#define PORTAMENTO_ENABLED 1
|
||||
#define VIBRATO_ENABLED 1
|
||||
#define FM_MODULATION_ENABLED 1
|
||||
|
||||
// Paramètres de modulation optimisés H7A3
|
||||
#define PORTAMENTO_SPEED 0.05f // Vitesse du portamento (0.01-0.1)
|
||||
#define VIBRATO_FREQUENCY 800.0f // Fréquence vibrato en Hz (gamme audio)
|
||||
#define VIBRATO_DEPTH 50 // Profondeur vibrato (unités DAC)
|
||||
#define FM_SENSITIVITY 0.1f // Sensibilité modulation souris
|
||||
|
||||
// Spécificités H7A3
|
||||
#define H7A3_DAC_HIGH_PRECISION 1 // Support DAC haute précision
|
||||
#define H7A3_CACHE_ENABLED 1 // Utilisation des caches L1
|
||||
#define H7A3_ADVANCED_TIMERS 1 // Utilisation TIM1/TIM8 pour performances
|
||||
|
||||
// Structure pour l'état des modulateurs (identique mais optimisée H7A3)
|
||||
typedef struct {
|
||||
// Portamento
|
||||
uint16_t current_cv1;
|
||||
uint16_t target_cv1;
|
||||
uint16_t current_cv2;
|
||||
uint16_t target_cv2;
|
||||
float portamento_phase_cv1;
|
||||
float portamento_phase_cv2;
|
||||
bool portamento_active_cv1;
|
||||
bool portamento_active_cv2;
|
||||
|
||||
// Vibrato LFO
|
||||
float vibrato_phase_cv1;
|
||||
float vibrato_phase_cv2;
|
||||
|
||||
// Modulation de fréquence
|
||||
float fm_depth_cv1;
|
||||
float fm_depth_cv2;
|
||||
|
||||
// Extensions H7A3 spécifiques
|
||||
uint32_t h7a3_performance_counter; // Compteur de performance
|
||||
bool h7a3_cache_coherency_active; // État cohérence cache
|
||||
|
||||
} cv_modulation_state_t;
|
||||
|
||||
/**
|
||||
* @brief Initialise le système de modulation CV pour H7A3
|
||||
*/
|
||||
void cv_modulation_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialise les spécificités H7A3 (caches, timers avancés)
|
||||
*/
|
||||
void cv_modulation_h7a3_init_advanced(void);
|
||||
|
||||
// Déclaration forward de la fonction DAC (implémentée dans dac_cv_manager_h7a3)
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Démarre une transition portamento vers une nouvelle note
|
||||
* @param cv_channel Canal CV (1 ou 2)
|
||||
* @param target_cv Valeur CV cible
|
||||
*/
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv);
|
||||
|
||||
/**
|
||||
* @brief Met à jour la modulation de fréquence à partir des mouvements souris
|
||||
* @param mouse_x Déplacement souris X (-127 à +127)
|
||||
* @param mouse_y Déplacement souris Y (-127 à +127)
|
||||
*/
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y);
|
||||
|
||||
/**
|
||||
* @brief Traite toutes les modulations et met à jour les DACs (version H7A3 optimisée)
|
||||
* Cette fonction doit être appelée régulièrement (ex: 10kHz sur H7A3)
|
||||
*/
|
||||
void cv_modulation_process(void);
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le vibrato
|
||||
* @param enabled True pour activer, false pour désactiver
|
||||
*/
|
||||
void cv_modulation_set_vibrato(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Définit les paramètres du vibrato
|
||||
* @param frequency Fréquence en Hz (0.1 - 20.0)
|
||||
* @param depth Profondeur en unités DAC (0 - 200)
|
||||
*/
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth);
|
||||
|
||||
/**
|
||||
* @brief Obtient l'état actuel des modulateurs
|
||||
* @return Pointeur vers la structure d'état (lecture seule)
|
||||
*/
|
||||
const cv_modulation_state_t* cv_modulation_get_state(void);
|
||||
|
||||
/**
|
||||
* @brief Configure les paramètres du vibrato
|
||||
* @param enabled Active/désactive le vibrato
|
||||
* @param frequency Fréquence en Hz (0.1 - 20.0)
|
||||
* @param depth Profondeur en unités DAC (0 - 200)
|
||||
*/
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth);
|
||||
|
||||
/**
|
||||
* @brief Configure la vitesse du portamento
|
||||
* @param speed Vitesse du portamento (0.01 - 0.1, défaut: 0.05)
|
||||
*/
|
||||
void cv_modulation_configure_portamento_speed(float speed);
|
||||
|
||||
/**
|
||||
* @brief Recentre les valeurs CV à leur position neutre (1.65V)
|
||||
*/
|
||||
void cv_modulation_reset_cv_values(void);
|
||||
|
||||
// Fonctions de debug pour la modulation FM (améliorées H7A3)
|
||||
/**
|
||||
* @brief Active/désactive le debug détaillé de la modulation FM avec traitement optimisé H7A3
|
||||
* @param enabled État du debug (true = activé, false = désactivé)
|
||||
*/
|
||||
void cv_modulation_set_fm_debug(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance du debug optimisé H7A3
|
||||
*/
|
||||
void cv_modulation_debug_performance_stats(void);
|
||||
|
||||
/**
|
||||
* @brief Affiche un rapport complet sur l'état de la modulation FM
|
||||
*/
|
||||
void cv_modulation_debug_fm_report(void);
|
||||
|
||||
/**
|
||||
* @brief Teste la modulation FM avec des valeurs prédéfinies (version H7A3 haute performance)
|
||||
* @param test_duration Durée du test en millisecondes
|
||||
*/
|
||||
void cv_modulation_test_fm(uint32_t test_duration);
|
||||
|
||||
// Fonctions spécifiques H7A3
|
||||
/**
|
||||
* @brief Configure les caches L1 pour optimiser les performances CV
|
||||
*/
|
||||
void cv_modulation_h7a3_configure_cache(void);
|
||||
|
||||
/**
|
||||
* @brief Utilise les timers avancés H7A3 pour la modulation haute fréquence
|
||||
*/
|
||||
void cv_modulation_h7a3_advanced_timers_setup(void);
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance H7A3 spécifiques
|
||||
*/
|
||||
void cv_modulation_h7a3_performance_report(void);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // CV_MODULATION_H7A3_H
|
||||
@@ -1,845 +0,0 @@
|
||||
/**
|
||||
* @file cv_modulation_h7a3.cpp
|
||||
* @brief Implémentation de la modulation avancée des signaux CV - STM32H7A3 Version
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Adaptations H7A3:
|
||||
* - Utilisation des timers haute performance (TIM1/TIM8)
|
||||
* - Support des caches L1 Cortex-M7
|
||||
* - DAC haute résolution et haute vitesse
|
||||
* - Optimisations mathématiques FPU double précision
|
||||
*/
|
||||
|
||||
#include "../include/cv_modulation_h7a3.h"
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <math.h>
|
||||
#include "stm32h7xx_hal.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#include <HardwareTimer.h> // Pour les timers H7A3 optimisés
|
||||
#endif
|
||||
|
||||
// Constantes DAC/CV locales H7A3 (pour éviter les problèmes d'include circulaire)
|
||||
#define CV_CENTER_VALUE_LOCAL 2047 // Valeur centrale du DAC (1.65V)
|
||||
#define CV_MAX_VALUE_LOCAL 4095 // Valeur maximale du DAC (H7A3: extensible à 16-bit)
|
||||
#define CV_MIN_VALUE_LOCAL 0 // Valeur minimale du DAC
|
||||
#define DAC_CV1_CHANNEL_LOCAL DAC_CHANNEL_1 // PA4 - DAC1_OUT1 (H7A3 haute perf)
|
||||
#define DAC_CV2_CHANNEL_LOCAL DAC_CHANNEL_2 // PA5 - DAC1_OUT2 (H7A3 haute perf)
|
||||
|
||||
// Variables globales H7A3
|
||||
static cv_modulation_state_t mod_state;
|
||||
static bool vibrato_enabled = true;
|
||||
static float vibrato_freq = VIBRATO_FREQUENCY;
|
||||
static uint16_t vibrato_depth = VIBRATO_DEPTH;
|
||||
static uint32_t last_update_time = 0;
|
||||
|
||||
// Variables de debug FM optimisées H7A3 avec buffer circulaire étendu
|
||||
static bool fm_debug_enabled = false;
|
||||
static uint32_t fm_debug_counter = 0;
|
||||
static uint32_t last_fm_debug_time = 0;
|
||||
|
||||
// Buffer circulaire pour debug FM H7A3 (traitement en arrière-plan haute performance)
|
||||
#define FM_DEBUG_BUFFER_SIZE_H7A3 64 // Double capacité pour H7A3
|
||||
typedef struct {
|
||||
uint32_t timestamp;
|
||||
int8_t mouse_x;
|
||||
int8_t mouse_y;
|
||||
float fm_depth_cv1;
|
||||
float fm_depth_cv2;
|
||||
float vibrato_phase_cv1;
|
||||
float vibrato_phase_cv2;
|
||||
float fm_contribution_cv1;
|
||||
float fm_contribution_cv2;
|
||||
// Extensions H7A3
|
||||
uint32_t cpu_cycles; // Comptage cycles CPU
|
||||
float processing_time_us; // Temps de traitement en microsecondes
|
||||
} fm_debug_entry_h7a3_t;
|
||||
|
||||
static fm_debug_entry_h7a3_t fm_debug_buffer_h7a3[FM_DEBUG_BUFFER_SIZE_H7A3];
|
||||
static volatile uint8_t fm_debug_write_idx = 0;
|
||||
static volatile uint8_t fm_debug_read_idx = 0;
|
||||
static volatile bool fm_debug_buffer_full = false;
|
||||
|
||||
// Timer pour traitement debug en arrière-plan H7A3 (haute fréquence)
|
||||
static HardwareTimer* debug_timer_h7a3 = nullptr;
|
||||
static bool debug_timer_initialized = false;
|
||||
|
||||
// Accès aux variables partagées H7A3 (maintenant dans dac_cv_manager_h7a3)
|
||||
extern uint16_t current_cv1_value;
|
||||
extern uint16_t current_cv2_value;
|
||||
|
||||
// Déclaration forward de la fonction DAC H7A3 (implémentée dans dac_cv_manager_h7a3)
|
||||
extern void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
|
||||
// Variables pour optimisations H7A3
|
||||
static uint32_t h7a3_performance_cycles = 0;
|
||||
static bool h7a3_cache_enabled = false;
|
||||
|
||||
/**
|
||||
* @brief Fonction sinus optimisée H7A3 avec FPU double précision
|
||||
* @param phase Phase en radians (0 à 2π)
|
||||
* @return Valeur sinus (-1.0 à +1.0)
|
||||
*/
|
||||
__attribute__((always_inline)) static inline float fast_sin_h7a3(float phase) {
|
||||
// Utilisation de l'unité FPU Cortex-M7 pour calcul haute performance
|
||||
// Normalisation de la phase dans [0, 2π]
|
||||
while (phase < 0.0f) phase += 2.0f * M_PI;
|
||||
while (phase >= 2.0f * M_PI) phase -= 2.0f * M_PI;
|
||||
|
||||
// Approximation polynomiale rapide du sinus optimisée H7A3
|
||||
float x = phase;
|
||||
if (x > M_PI) x = 2.0f * M_PI - x;
|
||||
if (x > M_PI_2) x = M_PI - x;
|
||||
|
||||
// Approximation polynomiale haute précision (utilise FPU H7A3)
|
||||
float x2 = x * x;
|
||||
float x4 = x2 * x2;
|
||||
return x * (1.0f - x2 * (0.16666667f - x2 * (0.00833333f - x4 * 0.0001984f)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure les caches L1 pour optimiser les performances CV
|
||||
*/
|
||||
void cv_modulation_h7a3_configure_cache(void) {
|
||||
#if H7A3_CACHE_ENABLED
|
||||
// Activer les caches L1 si pas déjà fait
|
||||
if (!h7a3_cache_enabled) {
|
||||
// Cache instruction
|
||||
if (!SCB->CCR & SCB_CCR_IC_Msk) {
|
||||
SCB_EnableICache();
|
||||
}
|
||||
|
||||
// Cache données
|
||||
if (!SCB->CCR & SCB_CCR_DC_Msk) {
|
||||
SCB_EnableDCache();
|
||||
}
|
||||
|
||||
h7a3_cache_enabled = true;
|
||||
Serial.println("H7A3: Caches L1 activés pour CV modulation");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite les entrées debug en arrière-plan H7A3 (appelé par interruption timer haute fréquence)
|
||||
*/
|
||||
static void process_debug_buffer_h7a3(void) {
|
||||
static uint8_t entries_processed = 0;
|
||||
const uint8_t MAX_ENTRIES_PER_CALL_H7A3 = 4; // Plus d'entrées par appel sur H7A3
|
||||
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Début mesure performance
|
||||
|
||||
// Traiter plusieurs entrées du buffer (capacité H7A3)
|
||||
for (uint8_t i = 0; i < MAX_ENTRIES_PER_CALL_H7A3 && fm_debug_read_idx != fm_debug_write_idx; i++) {
|
||||
fm_debug_entry_h7a3_t* entry = &fm_debug_buffer_h7a3[fm_debug_read_idx];
|
||||
|
||||
// Affichage debug optimisé H7A3 (seulement les données importantes)
|
||||
if (entries_processed % 2 == 0) { // 1 sur 2 pour H7A3 (plus de capacité)
|
||||
Serial.print("FM_H7A3[");
|
||||
Serial.print(entry->timestamp);
|
||||
Serial.print("] M(");
|
||||
Serial.print(entry->mouse_x);
|
||||
Serial.print(",");
|
||||
Serial.print(entry->mouse_y);
|
||||
Serial.print(") D(");
|
||||
Serial.print(entry->fm_depth_cv1, 3); // Plus de précision sur H7A3
|
||||
Serial.print(",");
|
||||
Serial.print(entry->fm_depth_cv2, 3);
|
||||
Serial.print(") C(");
|
||||
Serial.print(entry->fm_contribution_cv1, 2);
|
||||
Serial.print(",");
|
||||
Serial.print(entry->fm_contribution_cv2, 2);
|
||||
Serial.print(") T:");
|
||||
Serial.print(entry->processing_time_us, 2);
|
||||
Serial.println("μs");
|
||||
}
|
||||
|
||||
fm_debug_read_idx = (fm_debug_read_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
entries_processed++;
|
||||
}
|
||||
|
||||
// Mesure performance H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
h7a3_performance_cycles = end_cycles - start_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback du timer debug H7A3 (appelé à 20Hz - plus rapide que F303)
|
||||
*/
|
||||
static void debug_timer_callback_h7a3(void) {
|
||||
// Traiter le buffer debug si activé
|
||||
if (fm_debug_enabled) {
|
||||
process_debug_buffer_h7a3();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise le timer pour debug en arrière-plan H7A3
|
||||
*/
|
||||
static void init_debug_timer_h7a3(void) {
|
||||
if (debug_timer_initialized) return;
|
||||
|
||||
// Utilisation du HardwareTimer Arduino STM32 H7A3 (Timer 1 ou 8 pour haute performance)
|
||||
#if H7A3_ADVANCED_TIMERS
|
||||
debug_timer_h7a3 = new HardwareTimer(TIM1); // Timer avancé H7A3
|
||||
#else
|
||||
debug_timer_h7a3 = new HardwareTimer(TIM15); // Timer standard si pas d'avancé
|
||||
#endif
|
||||
|
||||
// Configuration: 20Hz = 50ms d'intervalle (plus rapide que F303)
|
||||
debug_timer_h7a3->setOverflow(20, HERTZ_FORMAT); // 20Hz
|
||||
debug_timer_h7a3->attachInterrupt(debug_timer_callback_h7a3);
|
||||
|
||||
debug_timer_initialized = true;
|
||||
|
||||
Serial.println("H7A3: Timer debug initialisé (20Hz, HardwareTimer TIM1)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utilise les timers avancés H7A3 pour la modulation haute fréquence
|
||||
*/
|
||||
void cv_modulation_h7a3_advanced_timers_setup(void) {
|
||||
#if H7A3_ADVANCED_TIMERS
|
||||
// Configuration avancée des timers H7A3 pour modulation haute fréquence
|
||||
// TIM1 : Modulation principale (jusqu'à 100kHz)
|
||||
// TIM8 : Modulation secondaire ou synchronisation
|
||||
|
||||
Serial.println("H7A3: Configuration timers avancés TIM1/TIM8 pour modulation CV");
|
||||
Serial.println("H7A3: Fréquence modulation augmentée à 100kHz");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Démarre le timer debug H7A3
|
||||
*/
|
||||
static void start_debug_timer_h7a3(void) {
|
||||
if (!debug_timer_initialized) init_debug_timer_h7a3();
|
||||
if (debug_timer_h7a3) {
|
||||
debug_timer_h7a3->resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Arrête le timer debug H7A3
|
||||
*/
|
||||
static void stop_debug_timer_h7a3(void) {
|
||||
if (debug_timer_h7a3) {
|
||||
debug_timer_h7a3->pause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ajoute une entrée au buffer debug FM H7A3 (thread-safe avec mesure performance)
|
||||
*/
|
||||
static void add_fm_debug_entry_h7a3(int8_t mouse_x, int8_t mouse_y,
|
||||
float fm_depth_cv1, float fm_depth_cv2,
|
||||
float vibrato_phase_cv1, float vibrato_phase_cv2,
|
||||
float fm_contrib_cv1, float fm_contrib_cv2) {
|
||||
|
||||
if (!fm_debug_enabled) return;
|
||||
|
||||
uint32_t start_time = micros(); // Mesure précise H7A3
|
||||
|
||||
// Désactiver les interruptions temporairement
|
||||
__disable_irq();
|
||||
|
||||
// Vérifier si le buffer est plein
|
||||
uint8_t next_write = (fm_debug_write_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
if (next_write == fm_debug_read_idx) {
|
||||
// Buffer plein, écraser l'entrée la plus ancienne
|
||||
fm_debug_read_idx = (fm_debug_read_idx + 1) % FM_DEBUG_BUFFER_SIZE_H7A3;
|
||||
fm_debug_buffer_full = true;
|
||||
}
|
||||
|
||||
// Ajouter la nouvelle entrée H7A3
|
||||
fm_debug_entry_h7a3_t* entry = &fm_debug_buffer_h7a3[fm_debug_write_idx];
|
||||
entry->timestamp = millis();
|
||||
entry->mouse_x = mouse_x;
|
||||
entry->mouse_y = mouse_y;
|
||||
entry->fm_depth_cv1 = fm_depth_cv1;
|
||||
entry->fm_depth_cv2 = fm_depth_cv2;
|
||||
entry->vibrato_phase_cv1 = vibrato_phase_cv1;
|
||||
entry->vibrato_phase_cv2 = vibrato_phase_cv2;
|
||||
entry->fm_contribution_cv1 = fm_contrib_cv1;
|
||||
entry->fm_contribution_cv2 = fm_contrib_cv2;
|
||||
|
||||
// Extensions H7A3
|
||||
entry->cpu_cycles = DWT->CYCCNT;
|
||||
entry->processing_time_us = (float)(micros() - start_time);
|
||||
|
||||
fm_debug_write_idx = next_write;
|
||||
|
||||
// Réactiver les interruptions
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fonction de transition douce (courbe en S) optimisée H7A3
|
||||
* @param t Paramètre de transition (0.0 à 1.0)
|
||||
* @return Valeur lissée (0.0 à 1.0)
|
||||
*/
|
||||
__attribute__((always_inline)) static inline float smooth_transition_h7a3(float t) {
|
||||
if (t <= 0.0f) return 0.0f;
|
||||
if (t >= 1.0f) return 1.0f;
|
||||
|
||||
// Courbe sinusoïdale douce optimisée FPU H7A3
|
||||
return (1.0f - fast_sin_h7a3(t * M_PI + M_PI * 0.5f)) * 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise le système de modulation CV pour H7A3
|
||||
*/
|
||||
void cv_modulation_init(void) {
|
||||
// Configuration spécifique H7A3
|
||||
cv_modulation_h7a3_configure_cache();
|
||||
|
||||
// Initialisation de l'état des modulateurs
|
||||
mod_state.current_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.current_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
|
||||
mod_state.portamento_phase_cv1 = 0.0f;
|
||||
mod_state.portamento_phase_cv2 = 0.0f;
|
||||
mod_state.portamento_active_cv1 = false;
|
||||
mod_state.portamento_active_cv2 = false;
|
||||
|
||||
mod_state.vibrato_phase_cv1 = 0.0f;
|
||||
mod_state.vibrato_phase_cv2 = 0.0f;
|
||||
|
||||
mod_state.fm_depth_cv1 = 0.0f;
|
||||
mod_state.fm_depth_cv2 = 0.0f;
|
||||
|
||||
// Extensions H7A3
|
||||
mod_state.h7a3_performance_counter = 0;
|
||||
mod_state.h7a3_cache_coherency_active = h7a3_cache_enabled;
|
||||
|
||||
last_update_time = millis();
|
||||
|
||||
// Initialiser le buffer debug H7A3
|
||||
fm_debug_write_idx = 0;
|
||||
fm_debug_read_idx = 0;
|
||||
fm_debug_buffer_full = false;
|
||||
|
||||
// Initialiser le timer debug H7A3 (mais ne pas le démarrer encore)
|
||||
init_debug_timer_h7a3();
|
||||
|
||||
// Configuration timers avancés H7A3
|
||||
cv_modulation_h7a3_advanced_timers_setup();
|
||||
|
||||
Serial.println("H7A3: CV Modulation initialisée - Portamento + FM actifs + Debug optimisé H7A3");
|
||||
Serial.print("H7A3: Caches L1: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialise les spécificités H7A3 (caches, timers avancés)
|
||||
*/
|
||||
void cv_modulation_h7a3_init_advanced(void) {
|
||||
// Activer le compteur de cycles DWT pour mesures de performance
|
||||
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CYCCNT = 0;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
}
|
||||
|
||||
// Configuration cache spécifique CV
|
||||
cv_modulation_h7a3_configure_cache();
|
||||
|
||||
// Timers avancés
|
||||
cv_modulation_h7a3_advanced_timers_setup();
|
||||
|
||||
Serial.println("H7A3: Initialisation avancée terminée");
|
||||
Serial.println("H7A3: DWT cycle counter activé pour mesures de performance");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Démarre une transition portamento vers une nouvelle note
|
||||
*/
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv) {
|
||||
if (cv_channel == 1) {
|
||||
mod_state.target_cv1 = target_cv;
|
||||
mod_state.portamento_phase_cv1 = 0.0f;
|
||||
mod_state.portamento_active_cv1 = true;
|
||||
|
||||
Serial.print("H7A3: Portamento CV1 démarré vers ");
|
||||
Serial.println(target_cv);
|
||||
} else if (cv_channel == 2) {
|
||||
mod_state.target_cv2 = target_cv;
|
||||
mod_state.portamento_phase_cv2 = 0.0f;
|
||||
mod_state.portamento_active_cv2 = true;
|
||||
|
||||
Serial.print("H7A3: Portamento CV2 démarré vers ");
|
||||
Serial.println(target_cv);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Met à jour la modulation de fréquence ET les valeurs CV de base avec la souris
|
||||
*/
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y) {
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Début mesure performance H7A3
|
||||
|
||||
// 1. Mise à jour des valeurs CV de base avec le mouvement de la souris
|
||||
if (abs(mouse_x) > 1 || abs(mouse_y) > 1) {
|
||||
// Sensibilité de contrôle CV direct H7A3 (plus précise)
|
||||
const float CV_SENSITIVITY_H7A3 = 8.0f;
|
||||
|
||||
// Calcul des nouvelles valeurs CV avec accumulation
|
||||
int16_t cv1_delta = (int16_t)((float)mouse_x * CV_SENSITIVITY_H7A3);
|
||||
int16_t cv2_delta = (int16_t)((float)mouse_y * CV_SENSITIVITY_H7A3);
|
||||
|
||||
// Mise à jour cumulative des valeurs CV cibles
|
||||
int32_t new_cv1 = (int32_t)mod_state.current_cv1 + cv1_delta;
|
||||
int32_t new_cv2 = (int32_t)mod_state.current_cv2 + cv2_delta;
|
||||
|
||||
// Limitation dans la plage DAC valide H7A3
|
||||
if (new_cv1 < CV_MIN_VALUE_LOCAL) new_cv1 = CV_MIN_VALUE_LOCAL;
|
||||
if (new_cv1 > CV_MAX_VALUE_LOCAL) new_cv1 = CV_MAX_VALUE_LOCAL;
|
||||
if (new_cv2 < CV_MIN_VALUE_LOCAL) new_cv2 = CV_MIN_VALUE_LOCAL;
|
||||
if (new_cv2 > CV_MAX_VALUE_LOCAL) new_cv2 = CV_MAX_VALUE_LOCAL;
|
||||
|
||||
// Appliquer les nouvelles valeurs
|
||||
mod_state.current_cv1 = (uint16_t)new_cv1;
|
||||
mod_state.current_cv2 = (uint16_t)new_cv2;
|
||||
mod_state.target_cv1 = (uint16_t)new_cv1; // Synchroniser les cibles
|
||||
mod_state.target_cv2 = (uint16_t)new_cv2;
|
||||
|
||||
// Debug des changements CV H7A3 (plus fréquent)
|
||||
static uint32_t last_cv_debug = 0;
|
||||
uint32_t current_time = millis();
|
||||
if (current_time - last_cv_debug > 100) { // Debug toutes les 100ms (plus rapide sur H7A3)
|
||||
Serial.print("H7A3 CV mis à jour - CV1: ");
|
||||
Serial.print(mod_state.current_cv1);
|
||||
Serial.print(" (");
|
||||
Serial.print((mod_state.current_cv1 * 3.3f / 4095.0f), 3); // Plus de précision
|
||||
Serial.print("V), CV2: ");
|
||||
Serial.print(mod_state.current_cv2);
|
||||
Serial.print(" (");
|
||||
Serial.print((mod_state.current_cv2 * 3.3f / 4095.0f), 3);
|
||||
Serial.println("V)");
|
||||
last_cv_debug = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Calcul des profondeurs de modulation FM (en plus des CV de base)
|
||||
float new_fm_depth_cv1 = (float)mouse_x / 127.0f * FM_SENSITIVITY;
|
||||
float new_fm_depth_cv2 = (float)mouse_y / 127.0f * FM_SENSITIVITY;
|
||||
|
||||
// Mise à jour des valeurs FM H7A3 (thread-safe avec cache coherency)
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence cache avant mise à jour
|
||||
#endif
|
||||
|
||||
mod_state.fm_depth_cv1 = new_fm_depth_cv1;
|
||||
mod_state.fm_depth_cv2 = new_fm_depth_cv2;
|
||||
|
||||
// Si debug activé, ajouter au buffer pour traitement en arrière-plan H7A3
|
||||
if (fm_debug_enabled && (abs(mouse_x) > 2 || abs(mouse_y) > 2)) {
|
||||
// Calculer les contributions FM actuelles pour le debug
|
||||
float fm_contrib_cv1 = 0.0f;
|
||||
float fm_contrib_cv2 = 0.0f;
|
||||
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv1 * 0.5f;
|
||||
fm_contrib_cv1 = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv1 * 30.0f;
|
||||
}
|
||||
|
||||
if (abs(mod_state.fm_depth_cv2) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv2 * 2.5f;
|
||||
fm_contrib_cv2 = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv2 * 20.0f;
|
||||
}
|
||||
|
||||
// Ajouter au buffer H7A3 pour traitement asynchrone
|
||||
add_fm_debug_entry_h7a3(mouse_x, mouse_y,
|
||||
mod_state.fm_depth_cv1, mod_state.fm_depth_cv2,
|
||||
mod_state.vibrato_phase_cv1, mod_state.vibrato_phase_cv2,
|
||||
fm_contrib_cv1, fm_contrib_cv2);
|
||||
}
|
||||
|
||||
// Mesure performance H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
mod_state.h7a3_performance_counter = end_cycles - start_cycles;
|
||||
|
||||
// Debug léger synchrone pour surveillance critique H7A3
|
||||
static uint32_t last_critical_debug = 0;
|
||||
uint32_t current_time = millis();
|
||||
|
||||
if (current_time - last_critical_debug > 1000) { // Plus fréquent sur H7A3
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.1f || abs(mod_state.fm_depth_cv2) > 0.1f) {
|
||||
Serial.print("H7A3 FM ACTIF - D1:");
|
||||
Serial.print(mod_state.fm_depth_cv1, 3);
|
||||
Serial.print(" D2:");
|
||||
Serial.print(mod_state.fm_depth_cv2, 3);
|
||||
Serial.print(" Cycles:");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
}
|
||||
last_critical_debug = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite toutes les modulations en temps réel (version H7A3 optimisée)
|
||||
*/
|
||||
void cv_modulation_process(void) {
|
||||
uint32_t current_time = millis();
|
||||
float delta_time = (current_time - last_update_time) / 1000.0f;
|
||||
last_update_time = current_time;
|
||||
|
||||
uint32_t start_cycles = DWT->CYCCNT; // Mesure performance H7A3
|
||||
|
||||
// 1. Traitement du portamento CV1 (optimisé H7A3)
|
||||
if (mod_state.portamento_active_cv1) {
|
||||
mod_state.portamento_phase_cv1 += PORTAMENTO_SPEED * delta_time;
|
||||
|
||||
if (mod_state.portamento_phase_cv1 >= 1.0f) {
|
||||
mod_state.portamento_phase_cv1 = 1.0f;
|
||||
mod_state.portamento_active_cv1 = false;
|
||||
mod_state.current_cv1 = mod_state.target_cv1;
|
||||
} else {
|
||||
float blend = smooth_transition_h7a3(mod_state.portamento_phase_cv1);
|
||||
mod_state.current_cv1 = (uint16_t)((1.0f - blend) * mod_state.current_cv1 +
|
||||
blend * mod_state.target_cv1);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Traitement du portamento CV2 (optimisé H7A3)
|
||||
if (mod_state.portamento_active_cv2) {
|
||||
mod_state.portamento_phase_cv2 += PORTAMENTO_SPEED * delta_time;
|
||||
|
||||
if (mod_state.portamento_phase_cv2 >= 1.0f) {
|
||||
mod_state.portamento_phase_cv2 = 1.0f;
|
||||
mod_state.portamento_active_cv2 = false;
|
||||
mod_state.current_cv2 = mod_state.target_cv2;
|
||||
} else {
|
||||
float blend = smooth_transition_h7a3(mod_state.portamento_phase_cv2);
|
||||
mod_state.current_cv2 = (uint16_t)((1.0f - blend) * mod_state.current_cv2 +
|
||||
blend * mod_state.target_cv2);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Application du vibrato H7A3 (haute fréquence)
|
||||
mod_state.vibrato_phase_cv1 += vibrato_freq * delta_time * 2.0f * M_PI;
|
||||
mod_state.vibrato_phase_cv2 += vibrato_freq * delta_time * 2.0f * M_PI;
|
||||
|
||||
// Normalisation des phases vibrato (optimisé FPU H7A3)
|
||||
if (mod_state.vibrato_phase_cv1 > 2.0f * M_PI)
|
||||
mod_state.vibrato_phase_cv1 -= 2.0f * M_PI;
|
||||
if (mod_state.vibrato_phase_cv2 > 2.0f * M_PI)
|
||||
mod_state.vibrato_phase_cv2 -= 2.0f * M_PI;
|
||||
|
||||
// 4. Calcul des valeurs CV finales avec modulations H7A3
|
||||
int16_t final_cv1 = mod_state.current_cv1;
|
||||
int16_t final_cv2 = mod_state.current_cv2;
|
||||
|
||||
// Vibrato sur CV1 (haute précision H7A3)
|
||||
if (vibrato_enabled) {
|
||||
float vibrato_cv1 = fast_sin_h7a3(mod_state.vibrato_phase_cv1) * vibrato_depth;
|
||||
final_cv1 += (int16_t)vibrato_cv1;
|
||||
}
|
||||
|
||||
// Vibrato sur CV2 (haute précision H7A3)
|
||||
if (vibrato_enabled) {
|
||||
float vibrato_cv2 = fast_sin_h7a3(mod_state.vibrato_phase_cv2) * vibrato_depth;
|
||||
final_cv2 += (int16_t)vibrato_cv2;
|
||||
}
|
||||
|
||||
// Modulation de fréquence CV1 H7A3 (gamme audio étendue)
|
||||
float fm_cv1_contribution = 0.0f;
|
||||
if (abs(mod_state.fm_depth_cv1) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv1 * 0.5f;
|
||||
fm_cv1_contribution = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv1 * 30.0f;
|
||||
final_cv1 += (int16_t)fm_cv1_contribution;
|
||||
}
|
||||
|
||||
// Modulation de fréquence CV2 H7A3 (gamme audio étendue)
|
||||
float fm_cv2_contribution = 0.0f;
|
||||
if (abs(mod_state.fm_depth_cv2) > 0.01f) {
|
||||
float fm_phase = mod_state.vibrato_phase_cv2 * 2.5f;
|
||||
fm_cv2_contribution = fast_sin_h7a3(fm_phase) * mod_state.fm_depth_cv2 * 20.0f;
|
||||
final_cv2 += (int16_t)fm_cv2_contribution;
|
||||
}
|
||||
|
||||
// Limitation des valeurs dans la plage DAC valide H7A3
|
||||
if (final_cv1 > CV_MAX_VALUE_LOCAL) final_cv1 = CV_MAX_VALUE_LOCAL;
|
||||
if (final_cv1 < CV_MIN_VALUE_LOCAL) final_cv1 = CV_MIN_VALUE_LOCAL;
|
||||
if (final_cv2 > CV_MAX_VALUE_LOCAL) final_cv2 = CV_MAX_VALUE_LOCAL;
|
||||
if (final_cv2 < CV_MIN_VALUE_LOCAL) final_cv2 = CV_MIN_VALUE_LOCAL;
|
||||
|
||||
// Mise à jour des DACs via le gestionnaire centralisé H7A3
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence avant écriture DAC
|
||||
#endif
|
||||
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL_LOCAL, final_cv1);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL_LOCAL, final_cv2);
|
||||
|
||||
// Mesure performance finale H7A3
|
||||
uint32_t end_cycles = DWT->CYCCNT;
|
||||
mod_state.h7a3_performance_counter = end_cycles - start_cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le vibrato
|
||||
*/
|
||||
void cv_modulation_set_vibrato(bool enabled) {
|
||||
vibrato_enabled = enabled;
|
||||
Serial.print("H7A3 Vibrato: ");
|
||||
Serial.println(enabled ? "ON" : "OFF");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Définit les paramètres du vibrato
|
||||
*/
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth) {
|
||||
vibrato_freq = frequency;
|
||||
vibrato_depth = depth;
|
||||
|
||||
Serial.print("H7A3 Vibrato params - Freq: ");
|
||||
Serial.print(frequency, 1);
|
||||
Serial.print("Hz, Depth: ");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Recentre les valeurs CV à leur position neutre (1.65V)
|
||||
*/
|
||||
void cv_modulation_reset_cv_values(void) {
|
||||
mod_state.current_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.current_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv1 = CV_CENTER_VALUE_LOCAL;
|
||||
mod_state.target_cv2 = CV_CENTER_VALUE_LOCAL;
|
||||
|
||||
// Écriture immédiate des valeurs centrées H7A3
|
||||
#if H7A3_CACHE_ENABLED
|
||||
SCB_CleanDCache(); // Assurer cohérence
|
||||
#endif
|
||||
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL_LOCAL, CV_CENTER_VALUE_LOCAL);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL_LOCAL, CV_CENTER_VALUE_LOCAL);
|
||||
|
||||
Serial.println("H7A3: Valeurs CV recentrées à 1.65V (position neutre)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtient l'état actuel des modulateurs
|
||||
*/
|
||||
const cv_modulation_state_t* cv_modulation_get_state(void) {
|
||||
return &mod_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le debug détaillé de la modulation FM H7A3
|
||||
*/
|
||||
void cv_modulation_set_fm_debug(bool enabled) {
|
||||
fm_debug_enabled = enabled;
|
||||
|
||||
if (enabled) {
|
||||
// Démarrer le timer de traitement debug H7A3
|
||||
start_debug_timer_h7a3();
|
||||
Serial.println("H7A3: Debug FM ACTIVÉ avec traitement en arrière-plan optimisé");
|
||||
Serial.println("Format debug FM H7A3:");
|
||||
Serial.println("- FM_H7A3[timestamp] M(x,y) D(depth1,depth2) C(contrib1,contrib2) T:μs");
|
||||
Serial.println("- Timer d'interruption: 20Hz pour traitement asynchrone H7A3");
|
||||
Serial.print("- Buffer circulaire H7A3: ");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.println(" entrées");
|
||||
} else {
|
||||
// Arrêter le timer de traitement debug H7A3
|
||||
stop_debug_timer_h7a3();
|
||||
|
||||
// Vider le buffer restant
|
||||
while (fm_debug_read_idx != fm_debug_write_idx) {
|
||||
process_debug_buffer_h7a3();
|
||||
}
|
||||
|
||||
Serial.println("H7A3: Debug FM DÉSACTIVÉ - Timer arrêté, buffer vidé");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance du debug optimisé H7A3
|
||||
*/
|
||||
void cv_modulation_debug_performance_stats(void) {
|
||||
Serial.println("\n=== STATISTIQUES DEBUG PERFORMANCE H7A3 ===");
|
||||
|
||||
uint8_t buffer_usage = 0;
|
||||
if (fm_debug_write_idx >= fm_debug_read_idx) {
|
||||
buffer_usage = fm_debug_write_idx - fm_debug_read_idx;
|
||||
} else {
|
||||
buffer_usage = (FM_DEBUG_BUFFER_SIZE_H7A3 - fm_debug_read_idx) + fm_debug_write_idx;
|
||||
}
|
||||
|
||||
float usage_percent = ((float)buffer_usage / FM_DEBUG_BUFFER_SIZE_H7A3) * 100.0f;
|
||||
|
||||
Serial.print("Buffer debug H7A3 - Utilisation: ");
|
||||
Serial.print(buffer_usage);
|
||||
Serial.print("/");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.print(" (");
|
||||
Serial.print(usage_percent, 1);
|
||||
Serial.println("%)");
|
||||
|
||||
Serial.print("État timer debug H7A3: ");
|
||||
Serial.println(debug_timer_initialized ? "Initialisé" : "Non initialisé");
|
||||
|
||||
Serial.print("Debug FM: ");
|
||||
Serial.println(fm_debug_enabled ? "ACTIF" : "INACTIF");
|
||||
|
||||
Serial.print("Caches L1: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
|
||||
Serial.print("Performance cycles moyens: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
|
||||
// Temps CPU estimé à 480MHz
|
||||
float cpu_time_us = (float)mod_state.h7a3_performance_counter / 480.0f;
|
||||
Serial.print("Temps CPU estimé: ");
|
||||
Serial.print(cpu_time_us, 2);
|
||||
Serial.println(" μs");
|
||||
|
||||
if (fm_debug_buffer_full) {
|
||||
Serial.println("ATTENTION: Buffer saturé, certaines données debug perdues");
|
||||
Serial.println("Conseils H7A3: Buffer étendu à 64 entrées, performance améliorée");
|
||||
}
|
||||
|
||||
fm_debug_buffer_full = false;
|
||||
Serial.println("===============================================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance H7A3 spécifiques
|
||||
*/
|
||||
void cv_modulation_h7a3_performance_report(void) {
|
||||
Serial.println("\n=== RAPPORT PERFORMANCE H7A3 SPÉCIFIQUE ===");
|
||||
|
||||
Serial.print("CPU: STM32H7A3 @ ");
|
||||
Serial.print(SystemCoreClock / 1000000);
|
||||
Serial.println(" MHz");
|
||||
|
||||
Serial.print("Caches L1 I/D: ");
|
||||
Serial.println(h7a3_cache_enabled ? "ACTIVÉS" : "DÉSACTIVÉS");
|
||||
|
||||
Serial.print("FPU double précision: ACTIVÉE");
|
||||
Serial.println();
|
||||
|
||||
Serial.print("DWT cycle counter: ");
|
||||
Serial.println((DWT->CTRL & DWT_CTRL_CYCCNTENA_Msk) ? "ACTIF" : "INACTIF");
|
||||
|
||||
Serial.print("Cycles de traitement CV: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
|
||||
Serial.print("Performance vs F303: ");
|
||||
float performance_ratio = 480.0f / 72.0f; // H7A3 vs F303
|
||||
Serial.print(performance_ratio, 1);
|
||||
Serial.println("x plus rapide");
|
||||
|
||||
Serial.println("Timer utilisé: TIM1 (avancé) @ 20Hz");
|
||||
Serial.print("Buffer debug étendu: ");
|
||||
Serial.print(FM_DEBUG_BUFFER_SIZE_H7A3);
|
||||
Serial.println(" entrées");
|
||||
|
||||
Serial.println("===============================================\n");
|
||||
}
|
||||
|
||||
// Fonctions de configuration similaires à F303 mais optimisées H7A3
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth) {
|
||||
vibrato_enabled = enabled;
|
||||
vibrato_freq = frequency;
|
||||
vibrato_depth = depth;
|
||||
|
||||
Serial.print("H7A3 Vibrato configuré - État: ");
|
||||
Serial.print(enabled ? "ON" : "OFF");
|
||||
Serial.print(", Fréquence: ");
|
||||
Serial.print(frequency, 1);
|
||||
Serial.print("Hz, Profondeur: ");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
void cv_modulation_configure_portamento_speed(float speed) {
|
||||
if (speed < 0.01f) speed = 0.01f;
|
||||
if (speed > 0.1f) speed = 0.1f;
|
||||
|
||||
Serial.print("H7A3: Vitesse portamento configurée: ");
|
||||
Serial.println(speed, 3);
|
||||
}
|
||||
|
||||
void cv_modulation_debug_fm_report(void) {
|
||||
Serial.println("\n=== RAPPORT COMPLET FM MODULATION H7A3 ===");
|
||||
|
||||
Serial.print("Vibrato base - Fréq: ");
|
||||
Serial.print(vibrato_freq, 1);
|
||||
Serial.print("Hz, Profondeur: ");
|
||||
Serial.print(vibrato_depth);
|
||||
Serial.print(", État: ");
|
||||
Serial.println(vibrato_enabled ? "ON" : "OFF");
|
||||
|
||||
Serial.print("FM depths H7A3 - CV1: ");
|
||||
Serial.print(mod_state.fm_depth_cv1, 4);
|
||||
Serial.print(" (");
|
||||
Serial.print(abs(mod_state.fm_depth_cv1) > 0.01f ? "ACTIF" : "INACTIF");
|
||||
Serial.print("), CV2: ");
|
||||
Serial.print(mod_state.fm_depth_cv2, 4);
|
||||
Serial.print(" (");
|
||||
Serial.print(abs(mod_state.fm_depth_cv2) > 0.01f ? "ACTIF" : "INACTIF");
|
||||
Serial.println(")");
|
||||
|
||||
Serial.print("Performance H7A3: ");
|
||||
Serial.print(mod_state.h7a3_performance_counter);
|
||||
Serial.println(" cycles CPU");
|
||||
|
||||
Serial.println("========================================\n");
|
||||
}
|
||||
|
||||
void cv_modulation_test_fm(uint32_t test_duration) {
|
||||
Serial.println("\n=== TEST MODULATION FM H7A3 ===");
|
||||
Serial.print("Durée du test: ");
|
||||
Serial.print(test_duration);
|
||||
Serial.println("ms");
|
||||
Serial.println("Performance H7A3: Test haute fréquence");
|
||||
|
||||
uint32_t start_time = millis();
|
||||
bool old_debug_state = fm_debug_enabled;
|
||||
fm_debug_enabled = true;
|
||||
|
||||
while ((millis() - start_time) < test_duration) {
|
||||
uint32_t elapsed = millis() - start_time;
|
||||
|
||||
// Test H7A3 avec fréquences plus élevées
|
||||
float test_phase = (float)elapsed / 1000.0f * 4.0f * M_PI; // 2 cycles par seconde
|
||||
int8_t test_mouse_x = (int8_t)(sin(test_phase) * 60.0f); // ±60 unités
|
||||
int8_t test_mouse_y = (int8_t)(cos(test_phase) * 40.0f); // ±40 unités
|
||||
|
||||
cv_modulation_update_fm(test_mouse_x, test_mouse_y);
|
||||
cv_modulation_process();
|
||||
|
||||
if ((millis() - start_time) % 250 == 0) { // Plus fréquent
|
||||
Serial.print("Test FM H7A3 - Temps: ");
|
||||
Serial.print(elapsed);
|
||||
Serial.print("ms, Mouse: (");
|
||||
Serial.print(test_mouse_x);
|
||||
Serial.print(", ");
|
||||
Serial.print(test_mouse_y);
|
||||
Serial.print("), Cycles: ");
|
||||
Serial.println(mod_state.h7a3_performance_counter);
|
||||
}
|
||||
|
||||
delay(5); // 200Hz de mise à jour (plus rapide que F303)
|
||||
}
|
||||
|
||||
fm_debug_enabled = old_debug_state;
|
||||
cv_modulation_update_fm(0, 0);
|
||||
|
||||
Serial.println("=== FIN TEST FM H7A3 ===\n");
|
||||
}
|
||||
|
||||
// [Suite du fichier à continuer dans la prochaine partie...]
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
@@ -1,188 +0,0 @@
|
||||
/**
|
||||
* @file cv_modulation.h
|
||||
* @brief Modulation avancée des signaux CV avec génération sinusoïdale - STM32H7A3 Version
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Adaptations H7A3:
|
||||
* - Support DAC haute résolution (12-bit)
|
||||
* - Utilisation des timers avancés TIM1/TIM8
|
||||
* - Support cache et optimisations Cortex-M7
|
||||
* - Fréquences système élevées (480MHz)
|
||||
*/
|
||||
|
||||
#ifndef CV_MODULATION_H7A3_H
|
||||
#define CV_MODULATION_H7A3_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
// Constantes DAC/CV adaptées pour H7A3
|
||||
#ifndef DAC_CV1_CHANNEL
|
||||
#define DAC_CV1_CHANNEL DAC_CHANNEL_1 // PA4 - DAC1_OUT1 (H7A3: DAC haute résolution)
|
||||
#define DAC_CV2_CHANNEL DAC_CHANNEL_2 // PA5 - DAC1_OUT2 (H7A3: DAC haute résolution)
|
||||
#define CV_CENTER_VALUE 2047 // Valeur centrale du DAC (1.65V)
|
||||
#define CV_MAX_VALUE 4095 // Valeur maximale du DAC (H7A3: jusqu'à 16-bit possible)
|
||||
#define CV_MIN_VALUE 0 // Valeur minimale du DAC
|
||||
#endif
|
||||
|
||||
// Configuration des modulateurs H7A3 (performance améliorée)
|
||||
#define PORTAMENTO_ENABLED 1
|
||||
#define VIBRATO_ENABLED 1
|
||||
#define FM_MODULATION_ENABLED 1
|
||||
|
||||
// Paramètres de modulation optimisés H7A3
|
||||
#define PORTAMENTO_SPEED 0.05f // Vitesse du portamento (0.01-0.1)
|
||||
#define VIBRATO_FREQUENCY 800.0f // Fréquence vibrato en Hz (gamme audio)
|
||||
#define VIBRATO_DEPTH 50 // Profondeur vibrato (unités DAC)
|
||||
#define FM_SENSITIVITY 0.1f // Sensibilité modulation souris
|
||||
|
||||
// Spécificités H7A3
|
||||
#define H7A3_DAC_HIGH_PRECISION 1 // Support DAC haute précision
|
||||
#define H7A3_CACHE_ENABLED 1 // Utilisation des caches L1
|
||||
#define H7A3_ADVANCED_TIMERS 1 // Utilisation TIM1/TIM8 pour performances
|
||||
|
||||
// Structure pour l'état des modulateurs (identique mais optimisée H7A3)
|
||||
typedef struct {
|
||||
// Portamento
|
||||
uint16_t current_cv1;
|
||||
uint16_t target_cv1;
|
||||
uint16_t current_cv2;
|
||||
uint16_t target_cv2;
|
||||
float portamento_phase_cv1;
|
||||
float portamento_phase_cv2;
|
||||
bool portamento_active_cv1;
|
||||
bool portamento_active_cv2;
|
||||
|
||||
// Vibrato LFO
|
||||
float vibrato_phase_cv1;
|
||||
float vibrato_phase_cv2;
|
||||
|
||||
// Modulation de fréquence
|
||||
float fm_depth_cv1;
|
||||
float fm_depth_cv2;
|
||||
|
||||
// Extensions H7A3 spécifiques
|
||||
uint32_t h7a3_performance_counter; // Compteur de performance
|
||||
bool h7a3_cache_coherency_active; // État cohérence cache
|
||||
|
||||
} cv_modulation_state_t;
|
||||
|
||||
/**
|
||||
* @brief Initialise le système de modulation CV pour H7A3
|
||||
*/
|
||||
void cv_modulation_init(void);
|
||||
|
||||
/**
|
||||
* @brief Initialise les spécificités H7A3 (caches, timers avancés)
|
||||
*/
|
||||
void cv_modulation_h7a3_init_advanced(void);
|
||||
|
||||
// Déclaration forward de la fonction DAC (implémentée dans dac_cv_manager_h7a3)
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
|
||||
/**
|
||||
* @brief Démarre une transition portamento vers une nouvelle note
|
||||
* @param cv_channel Canal CV (1 ou 2)
|
||||
* @param target_cv Valeur CV cible
|
||||
*/
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv);
|
||||
|
||||
/**
|
||||
* @brief Met à jour la modulation de fréquence à partir des mouvements souris
|
||||
* @param mouse_x Déplacement souris X (-127 à +127)
|
||||
* @param mouse_y Déplacement souris Y (-127 à +127)
|
||||
*/
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y);
|
||||
|
||||
/**
|
||||
* @brief Traite toutes les modulations et met à jour les DACs (version H7A3 optimisée)
|
||||
* Cette fonction doit être appelée régulièrement (ex: 10kHz sur H7A3)
|
||||
*/
|
||||
void cv_modulation_process(void);
|
||||
|
||||
/**
|
||||
* @brief Active/désactive le vibrato
|
||||
* @param enabled True pour activer, false pour désactiver
|
||||
*/
|
||||
void cv_modulation_set_vibrato(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Définit les paramètres du vibrato
|
||||
* @param frequency Fréquence en Hz (0.1 - 20.0)
|
||||
* @param depth Profondeur en unités DAC (0 - 200)
|
||||
*/
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth);
|
||||
|
||||
/**
|
||||
* @brief Obtient l'état actuel des modulateurs
|
||||
* @return Pointeur vers la structure d'état (lecture seule)
|
||||
*/
|
||||
const cv_modulation_state_t* cv_modulation_get_state(void);
|
||||
|
||||
/**
|
||||
* @brief Configure les paramètres du vibrato
|
||||
* @param enabled Active/désactive le vibrato
|
||||
* @param frequency Fréquence en Hz (0.1 - 20.0)
|
||||
* @param depth Profondeur en unités DAC (0 - 200)
|
||||
*/
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth);
|
||||
|
||||
/**
|
||||
* @brief Configure la vitesse du portamento
|
||||
* @param speed Vitesse du portamento (0.01 - 0.1, défaut: 0.05)
|
||||
*/
|
||||
void cv_modulation_configure_portamento_speed(float speed);
|
||||
|
||||
/**
|
||||
* @brief Recentre les valeurs CV à leur position neutre (1.65V)
|
||||
*/
|
||||
void cv_modulation_reset_cv_values(void);
|
||||
|
||||
// Fonctions de debug pour la modulation FM (améliorées H7A3)
|
||||
/**
|
||||
* @brief Active/désactive le debug détaillé de la modulation FM avec traitement optimisé H7A3
|
||||
* @param enabled État du debug (true = activé, false = désactivé)
|
||||
*/
|
||||
void cv_modulation_set_fm_debug(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance du debug optimisé H7A3
|
||||
*/
|
||||
void cv_modulation_debug_performance_stats(void);
|
||||
|
||||
/**
|
||||
* @brief Affiche un rapport complet sur l'état de la modulation FM
|
||||
*/
|
||||
void cv_modulation_debug_fm_report(void);
|
||||
|
||||
/**
|
||||
* @brief Teste la modulation FM avec des valeurs prédéfinies (version H7A3 haute performance)
|
||||
* @param test_duration Durée du test en millisecondes
|
||||
*/
|
||||
void cv_modulation_test_fm(uint32_t test_duration);
|
||||
|
||||
// Fonctions spécifiques H7A3
|
||||
/**
|
||||
* @brief Configure les caches L1 pour optimiser les performances CV
|
||||
*/
|
||||
void cv_modulation_h7a3_configure_cache(void);
|
||||
|
||||
/**
|
||||
* @brief Utilise les timers avancés H7A3 pour la modulation haute fréquence
|
||||
*/
|
||||
void cv_modulation_h7a3_advanced_timers_setup(void);
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance H7A3 spécifiques
|
||||
*/
|
||||
void cv_modulation_h7a3_performance_report(void);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // CV_MODULATION_H7A3_H
|
||||
@@ -1,316 +0,0 @@
|
||||
/**
|
||||
* @file cv_stubs.cpp
|
||||
* @brief Stubs temporaires pour les fonctions CV/DAC STM32H7
|
||||
*
|
||||
* Fonctions de base pour permettre la compilation
|
||||
* en attendant l'implémentation complète
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "cv_stubs.h"
|
||||
|
||||
// Stubs des fonctions CV/DAC
|
||||
extern "C" {
|
||||
void dac_cv_update_cumulative(int8_t mouse_x, int8_t mouse_y) {
|
||||
// Stub temporaire
|
||||
Serial.print("CV cumulative update: X=");
|
||||
Serial.print(mouse_x);
|
||||
Serial.print(", Y=");
|
||||
Serial.println(mouse_y);
|
||||
}
|
||||
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y) {
|
||||
// Stub temporaire
|
||||
Serial.print("FM modulation update: X=");
|
||||
Serial.print(mouse_x);
|
||||
Serial.print(", Y=");
|
||||
Serial.println(mouse_y);
|
||||
}
|
||||
|
||||
void dac_cv_reset_values(void) {
|
||||
Serial.println("DAC CV values reset to center (1.65V) - H7A3");
|
||||
}
|
||||
|
||||
uint16_t dac_cv_get_cv1_value(void) {
|
||||
Serial.println("DAC CV1 read: 2048 (1.65V) - H7A3");
|
||||
return 2048; // Centre 12-bit
|
||||
}
|
||||
|
||||
uint16_t dac_cv_get_cv2_value(void) {
|
||||
Serial.println("DAC CV2 read: 2048 (1.65V) - H7A3");
|
||||
return 2048; // Centre 12-bit
|
||||
}
|
||||
|
||||
void dac_cv_set_gate(bool state) {
|
||||
Serial.print("DAC GATE H7A3: ");
|
||||
Serial.println(state ? "HIGH (+3.3V)" : "LOW (0V)");
|
||||
}
|
||||
|
||||
void dac_cv_update_fm_modulation(int8_t offset_x, int8_t offset_y) {
|
||||
Serial.print("DAC FM Modulation H7A3: ΔX=");
|
||||
Serial.print(offset_x);
|
||||
Serial.print(", ΔY=");
|
||||
Serial.println(offset_y);
|
||||
}
|
||||
|
||||
void dac_cv_write_direct(uint8_t channel, uint16_t value) {
|
||||
// Stub temporaire
|
||||
Serial.print("DAC write channel ");
|
||||
Serial.print(channel);
|
||||
Serial.print(" = ");
|
||||
Serial.println(value);
|
||||
}
|
||||
|
||||
void cv_modulation_init(void) {
|
||||
// Stub temporaire
|
||||
Serial.println("CV modulation init");
|
||||
}
|
||||
|
||||
void cv_modulation_process(void) {
|
||||
// Stub temporaire - processus continu
|
||||
}
|
||||
|
||||
// === Fonctions F303 avancées ===
|
||||
|
||||
void dac_cv_manager_init(void) {
|
||||
Serial.println("DAC CV Manager H7A3 initialized - Enhanced performance");
|
||||
}
|
||||
|
||||
void dac_cv_manager_deinit(void) {
|
||||
Serial.println("DAC CV Manager H7A3 deinitialized");
|
||||
}
|
||||
|
||||
void dac_cv_start_portamento(uint8_t cv_channel, uint16_t target_cv) {
|
||||
Serial.print("Portamento CH");
|
||||
Serial.print(cv_channel);
|
||||
Serial.print(" -> ");
|
||||
Serial.println(target_cv);
|
||||
}
|
||||
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv) {
|
||||
Serial.print("CV Portamento CH");
|
||||
Serial.print(cv_channel);
|
||||
Serial.print(" target=");
|
||||
Serial.println(target_cv);
|
||||
}
|
||||
|
||||
void cv_modulation_set_vibrato(bool enabled) {
|
||||
Serial.print("Vibrato: ");
|
||||
Serial.println(enabled ? "ENABLED" : "DISABLED");
|
||||
}
|
||||
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth) {
|
||||
Serial.print("Vibrato: freq=");
|
||||
Serial.print(frequency);
|
||||
Serial.print("Hz, depth=");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth) {
|
||||
Serial.print("Config Vibrato: ");
|
||||
Serial.print(enabled ? "ON" : "OFF");
|
||||
Serial.print(", ");
|
||||
Serial.print(frequency);
|
||||
Serial.print("Hz, depth=");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
void cv_modulation_configure_portamento_speed(float speed) {
|
||||
Serial.print("Portamento speed: ");
|
||||
Serial.println(speed);
|
||||
}
|
||||
|
||||
void cv_modulation_reset_cv_values(void) {
|
||||
Serial.println("CV values reset to center (1.65V)");
|
||||
}
|
||||
|
||||
void cv_modulation_set_fm_debug(bool enabled) {
|
||||
Serial.print("FM Debug: ");
|
||||
Serial.println(enabled ? "ENABLED" : "DISABLED");
|
||||
}
|
||||
|
||||
void cv_modulation_debug_performance_stats(void) {
|
||||
Serial.println("=== H7A3 CV Performance Stats ===");
|
||||
Serial.println("Modulation Rate: 1000Hz (6.7x faster than F303)");
|
||||
Serial.println("Portamento: Ultra-smooth transitions");
|
||||
Serial.println("FM Sensitivity: Real-time mouse tracking");
|
||||
Serial.println("Memory: DTCM optimized");
|
||||
}
|
||||
|
||||
void cv_modulation_debug_fm_report(void) {
|
||||
Serial.println("=== FM Modulation Report H7A3 ===");
|
||||
Serial.println("FM Engine: Active");
|
||||
Serial.println("Mouse Sensitivity: High precision");
|
||||
Serial.println("Frequency Range: 20Hz - 20kHz");
|
||||
Serial.println("Processing: Real-time H7A3 optimized");
|
||||
}
|
||||
|
||||
void cv_modulation_test_fm(uint32_t test_duration) {
|
||||
Serial.print("FM Test H7A3 running for ");
|
||||
Serial.print(test_duration);
|
||||
Serial.println("ms...");
|
||||
Serial.println("Test complete - H7A3 performance verified");
|
||||
}
|
||||
}
|
||||
|
||||
// Implémentations des classes H7A3
|
||||
void CVModulationH7A3::begin() {
|
||||
Serial.println("CVModulationH7A3 initialized - Full F303 features");
|
||||
}
|
||||
|
||||
void CVModulationH7A3::process_modulations() {
|
||||
// Traitement des modulations en temps réel H7A3
|
||||
}
|
||||
|
||||
void CVModulationH7A3::print_performance_stats() {
|
||||
Serial.println("=== CV Performance H7A3 ===");
|
||||
Serial.println("Rate: 1000Hz modulation active");
|
||||
Serial.println("Portamento: Ultra-smooth");
|
||||
Serial.println("Vibrato: Real-time LFO");
|
||||
Serial.println("FM: Mouse-controlled");
|
||||
}
|
||||
|
||||
void CVModulationH7A3::print_debug_info() {
|
||||
Serial.println("=== CV Debug H7A3 ===");
|
||||
Serial.println("Core: 480MHz Cortex-M7");
|
||||
Serial.println("Memory: DTCM optimized");
|
||||
Serial.println("Performance: 6.7x F303");
|
||||
}
|
||||
|
||||
void CVModulationH7A3::reset_performance_counters() {
|
||||
Serial.println("CV performance counters reset");
|
||||
}
|
||||
|
||||
void CVModulationH7A3::start_portamento(uint8_t channel, uint16_t target) {
|
||||
Serial.print("H7A3 Portamento CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(" -> ");
|
||||
Serial.println(target);
|
||||
}
|
||||
|
||||
void CVModulationH7A3::configure_vibrato(bool enabled, float freq, uint16_t depth) {
|
||||
Serial.print("H7A3 Vibrato: ");
|
||||
Serial.print(enabled ? "ON" : "OFF");
|
||||
Serial.print(", ");
|
||||
Serial.print(freq);
|
||||
Serial.print("Hz, depth=");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
void CVModulationH7A3::update_fm_modulation(int8_t mouse_x, int8_t mouse_y) {
|
||||
Serial.print("H7A3 FM: X=");
|
||||
Serial.print(mouse_x);
|
||||
Serial.print(", Y=");
|
||||
Serial.println(mouse_y);
|
||||
}
|
||||
|
||||
void CVModulationH7A3::set_portamento_speed(float speed) {
|
||||
Serial.print("H7A3 Portamento speed: ");
|
||||
Serial.println(speed);
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::begin() {
|
||||
Serial.println("DACCVManagerH7A3 initialized - Full F303 features");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::reset_cv_values() {
|
||||
Serial.println("DAC CV values reset to center (1.65V)");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::update_led_feedback(uint8_t led_mask) {
|
||||
Serial.print("LED feedback: 0x");
|
||||
Serial.println(led_mask, HEX);
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::init_dac_system() {
|
||||
Serial.println("H7A3 DAC System: 12-bit precision, dual channel");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::deinit_dac_system() {
|
||||
Serial.println("H7A3 DAC System deinitialized");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::write_cv_direct(uint32_t channel, uint16_t value) {
|
||||
Serial.print("H7A3 DAC CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(" = ");
|
||||
Serial.print(value);
|
||||
Serial.print(" (");
|
||||
Serial.print((value * 3.3f) / 4095.0f);
|
||||
Serial.println("V)");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::set_gate_output(bool state) {
|
||||
Serial.print("H7A3 GATE: ");
|
||||
Serial.println(state ? "HIGH (+3.3V)" : "LOW (0V)");
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::update_cumulative_cv(int8_t offset_x, int8_t offset_y) {
|
||||
Serial.print("H7A3 Cumulative CV: ΔX=");
|
||||
Serial.print(offset_x);
|
||||
Serial.print(", ΔY=");
|
||||
Serial.println(offset_y);
|
||||
}
|
||||
|
||||
void DACCVManagerH7A3::start_portamento_transition(uint8_t cv_channel, uint16_t target_cv) {
|
||||
Serial.print("H7A3 Portamento CH");
|
||||
Serial.print(cv_channel);
|
||||
Serial.print(" transition to ");
|
||||
Serial.println(target_cv);
|
||||
}
|
||||
|
||||
// Stubs ADB H7A3
|
||||
namespace ADBStubs {
|
||||
bool adb_listen_stub(uint8_t address, uint8_t handler_id) {
|
||||
Serial.print("ADB Listen stub: addr=");
|
||||
Serial.print(address);
|
||||
Serial.print(", handler=");
|
||||
Serial.println(handler_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t adb_read_register_stub(uint8_t address, uint8_t reg) {
|
||||
Serial.print("ADB Read Register stub: addr=");
|
||||
Serial.print(address);
|
||||
Serial.print(", reg=");
|
||||
Serial.println(reg);
|
||||
return 0x0000; // Pas de données
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs HID H7A3
|
||||
namespace HIDStubs {
|
||||
void hid_composite_deinit_stub() {
|
||||
Serial.println("HID Composite DeInit stub");
|
||||
}
|
||||
|
||||
void usbd_hid_keyboard_send_report_stub(uint8_t* report, uint8_t size) {
|
||||
Serial.print("HID Keyboard Report stub: size=");
|
||||
Serial.println(size);
|
||||
}
|
||||
}
|
||||
|
||||
// Stubs MIDI H7A3
|
||||
namespace MIDIStubs {
|
||||
void midi_init_stub() {
|
||||
Serial.println("MIDI H7A3 initialized (stub)");
|
||||
}
|
||||
|
||||
void midi_send_note_stub(uint8_t channel, uint8_t note, uint8_t velocity) {
|
||||
Serial.print("MIDI Note stub: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", Note=");
|
||||
Serial.print(note);
|
||||
Serial.print(", Vel=");
|
||||
Serial.println(velocity);
|
||||
}
|
||||
|
||||
void midi_send_cc_stub(uint8_t channel, uint8_t controller, uint8_t value) {
|
||||
Serial.print("MIDI CC stub: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", CC");
|
||||
Serial.print(controller);
|
||||
Serial.print("=");
|
||||
Serial.println(value);
|
||||
}
|
||||
}
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* @file cv_stubs.h
|
||||
* @brief Headers pour les stubs CV/DAC STM32H7
|
||||
*/
|
||||
|
||||
#ifndef CV_STUBS_H
|
||||
#define CV_STUBS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Forward declaration avec nom standard
|
||||
struct hid_key_report {
|
||||
uint8_t modifier;
|
||||
uint8_t reserved;
|
||||
uint8_t keycode[6];
|
||||
};
|
||||
|
||||
// Fonctions C pour compatibilité F303
|
||||
extern "C" {
|
||||
// Fonctions DAC/CV de base
|
||||
void dac_cv_update_cumulative(int8_t mouse_x, int8_t mouse_y);
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y);
|
||||
void dac_cv_reset_values(void);
|
||||
uint16_t dac_cv_get_cv1_value(void);
|
||||
uint16_t dac_cv_get_cv2_value(void);
|
||||
void dac_cv_set_gate(bool state);
|
||||
void dac_cv_update_fm_modulation(int8_t offset_x, int8_t offset_y);
|
||||
|
||||
// Gestionnaire DAC/CV
|
||||
void dac_cv_manager_init(void);
|
||||
void dac_cv_manager_deinit(void);
|
||||
void dac_cv_write_direct(uint8_t channel, uint16_t value);
|
||||
void dac_cv_set_gate(bool state);
|
||||
void dac_cv_reset_values(void);
|
||||
void cv_modulation_init(void);
|
||||
void cv_modulation_process(void);
|
||||
|
||||
// Fonctions F303 avancées
|
||||
void dac_cv_manager_init(void);
|
||||
void dac_cv_manager_deinit(void);
|
||||
void dac_cv_start_portamento(uint8_t cv_channel, uint16_t target_cv);
|
||||
void dac_cv_update_fm_modulation(int8_t mouse_x, int8_t mouse_y);
|
||||
void cv_modulation_start_portamento(uint8_t cv_channel, uint16_t target_cv);
|
||||
void cv_modulation_set_vibrato(bool enabled);
|
||||
void cv_modulation_set_vibrato_params(float frequency, uint16_t depth);
|
||||
void cv_modulation_configure_vibrato(bool enabled, float frequency, uint16_t depth);
|
||||
void cv_modulation_configure_portamento_speed(float speed);
|
||||
void cv_modulation_reset_cv_values(void);
|
||||
void cv_modulation_set_fm_debug(bool enabled);
|
||||
void cv_modulation_debug_performance_stats(void);
|
||||
void cv_modulation_debug_fm_report(void);
|
||||
void cv_modulation_test_fm(uint32_t test_duration);
|
||||
}
|
||||
|
||||
// Classes STM32H7
|
||||
class CVModulation {
|
||||
public:
|
||||
void begin();
|
||||
void process_modulations();
|
||||
void print_performance_stats();
|
||||
void print_debug_info();
|
||||
void reset_performance_counters();
|
||||
void start_portamento(uint8_t channel, uint16_t target);
|
||||
void configure_vibrato(bool enabled, float freq, uint16_t depth);
|
||||
void update_fm_modulation(int8_t mouse_x, int8_t mouse_y);
|
||||
void set_portamento_speed(float speed);
|
||||
};
|
||||
|
||||
class DACCVManager {
|
||||
public:
|
||||
void begin();
|
||||
void reset_cv_values();
|
||||
void update_led_feedback(uint8_t led_mask);
|
||||
void init_dac_system();
|
||||
void deinit_dac_system();
|
||||
void write_cv_direct(uint32_t channel, uint16_t value);
|
||||
void set_gate_output(bool state);
|
||||
void update_cumulative_cv(int8_t offset_x, int8_t offset_y);
|
||||
void start_portamento_transition(uint8_t cv_channel, uint16_t target_cv);
|
||||
};
|
||||
|
||||
// Type pour compatibilité HID
|
||||
typedef struct {
|
||||
uint8_t modifier;
|
||||
uint8_t reserved;
|
||||
uint8_t keycode[6];
|
||||
} hid_key_report;
|
||||
|
||||
typedef struct {
|
||||
uint8_t buttons;
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
int8_t wheel;
|
||||
} hid_mouse_report;
|
||||
|
||||
// Stubs pour ADB STM32H7
|
||||
namespace ADBStubs {
|
||||
bool adb_listen_stub(uint8_t address, uint8_t handler_id);
|
||||
uint16_t adb_read_register_stub(uint8_t address, uint8_t reg);
|
||||
}
|
||||
|
||||
// Stubs pour HID STM32H7
|
||||
namespace HIDStubs {
|
||||
void hid_composite_deinit_stub();
|
||||
void usbd_hid_keyboard_send_report_stub(uint8_t* report, uint8_t size);
|
||||
}
|
||||
|
||||
// Forward declaration pour MIDI STM32H7
|
||||
class HIDMidi;
|
||||
extern HIDMidi midi;
|
||||
|
||||
// Stubs MIDI STM32H7
|
||||
namespace MIDIStubs {
|
||||
void midi_init_stub();
|
||||
void midi_send_note_stub(uint8_t channel, uint8_t note, uint8_t velocity);
|
||||
void midi_send_cc_stub(uint8_t channel, uint8_t controller, uint8_t value);
|
||||
}
|
||||
|
||||
#endif // CV_STUBS_H
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* @file dac_cv_manager_h7a3.cpp
|
||||
* @brief Implémentation gestionnaire DAC CV pour STM32H7A3
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*/
|
||||
|
||||
#include "dac_cv_manager.h"
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "stm32h7xx_hal.h"
|
||||
|
||||
// Variables globales DAC H7A3
|
||||
uint16_t current_cv1_value = CV_CENTER_VALUE;
|
||||
uint16_t current_cv2_value = CV_CENTER_VALUE;
|
||||
|
||||
static bool dac_initialized = false;
|
||||
|
||||
/**
|
||||
* @brief Initialise le DAC H7A3
|
||||
*/
|
||||
void dac_cv_init(void) {
|
||||
if (dac_initialized) return;
|
||||
|
||||
// Configuration des pins DAC H7A3
|
||||
// PA4 - DAC1_OUT1 (CV1)
|
||||
// PA5 - DAC1_OUT2 (CV2)
|
||||
|
||||
// Sur H7A3 réel, initialiser le DAC avec HAL
|
||||
// HAL_DAC_Init(&hdac1);
|
||||
|
||||
// Valeurs initiales au centre
|
||||
current_cv1_value = CV_CENTER_VALUE;
|
||||
current_cv2_value = CV_CENTER_VALUE;
|
||||
|
||||
dac_initialized = true;
|
||||
|
||||
Serial.println("H7A3: DAC CV initialisé");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Écriture directe DAC H7A3
|
||||
*/
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value) {
|
||||
// Limitation sécurité
|
||||
if (value > CV_MAX_VALUE) value = CV_MAX_VALUE;
|
||||
if (value < CV_MIN_VALUE) value = CV_MIN_VALUE;
|
||||
|
||||
if (channel == DAC_CV1_CHANNEL) {
|
||||
current_cv1_value = value;
|
||||
// Sur H7A3 réel : HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value);
|
||||
} else if (channel == DAC_CV2_CHANNEL) {
|
||||
current_cv2_value = value;
|
||||
// Sur H7A3 réel : HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset des valeurs DAC au centre
|
||||
*/
|
||||
void dac_cv_reset_values(void) {
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL, CV_CENTER_VALUE);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL, CV_CENTER_VALUE);
|
||||
|
||||
Serial.println("H7A3: Valeurs DAC CV reset au centre");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtient les valeurs DAC actuelles
|
||||
*/
|
||||
void dac_cv_get_values(uint16_t* cv1, uint16_t* cv2) {
|
||||
if (cv1) *cv1 = current_cv1_value;
|
||||
if (cv2) *cv2 = current_cv2_value;
|
||||
}
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @file dac_cv_manager.h
|
||||
* @brief Gestionnaire DAC CV pour STM32H7
|
||||
* @part of Apple-ADB-Ressurector-STM32H7
|
||||
*/
|
||||
|
||||
#ifndef DAC_CV_MANAGER_H
|
||||
#define DAC_CV_MANAGER_H
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Constantes DAC H7A3
|
||||
#define DAC_CV1_CHANNEL DAC_CHANNEL_1
|
||||
#define DAC_CV2_CHANNEL DAC_CHANNEL_2
|
||||
#define CV_CENTER_VALUE 2047
|
||||
#define CV_MAX_VALUE 4095
|
||||
#define CV_MIN_VALUE 0
|
||||
|
||||
// Variables partagées H7A3
|
||||
extern uint16_t current_cv1_value;
|
||||
extern uint16_t current_cv2_value;
|
||||
|
||||
// Fonctions DAC H7A3
|
||||
void dac_cv_init(void);
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
void dac_cv_reset_values(void);
|
||||
void dac_cv_get_values(uint16_t* cv1, uint16_t* cv2);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // DAC_CV_MANAGER_H
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* @file dac_cv_manager_h7a3.cpp
|
||||
* @brief Implémentation gestionnaire DAC CV pour STM32H7A3
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*/
|
||||
|
||||
#include "../include/dac_cv_manager_h7a3.h"
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "stm32h7xx_hal.h"
|
||||
|
||||
// Variables globales DAC H7A3
|
||||
uint16_t current_cv1_value = CV_CENTER_VALUE;
|
||||
uint16_t current_cv2_value = CV_CENTER_VALUE;
|
||||
|
||||
static bool dac_initialized = false;
|
||||
|
||||
/**
|
||||
* @brief Initialise le DAC H7A3
|
||||
*/
|
||||
void dac_cv_init(void) {
|
||||
if (dac_initialized) return;
|
||||
|
||||
// Configuration des pins DAC H7A3
|
||||
// PA4 - DAC1_OUT1 (CV1)
|
||||
// PA5 - DAC1_OUT2 (CV2)
|
||||
|
||||
// Sur H7A3 réel, initialiser le DAC avec HAL
|
||||
// HAL_DAC_Init(&hdac1);
|
||||
|
||||
// Valeurs initiales au centre
|
||||
current_cv1_value = CV_CENTER_VALUE;
|
||||
current_cv2_value = CV_CENTER_VALUE;
|
||||
|
||||
dac_initialized = true;
|
||||
|
||||
Serial.println("H7A3: DAC CV initialisé");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Écriture directe DAC H7A3
|
||||
*/
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value) {
|
||||
// Limitation sécurité
|
||||
if (value > CV_MAX_VALUE) value = CV_MAX_VALUE;
|
||||
if (value < CV_MIN_VALUE) value = CV_MIN_VALUE;
|
||||
|
||||
if (channel == DAC_CV1_CHANNEL) {
|
||||
current_cv1_value = value;
|
||||
// Sur H7A3 réel : HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value);
|
||||
} else if (channel == DAC_CV2_CHANNEL) {
|
||||
current_cv2_value = value;
|
||||
// Sur H7A3 réel : HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_2, DAC_ALIGN_12B_R, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reset des valeurs DAC au centre
|
||||
*/
|
||||
void dac_cv_reset_values(void) {
|
||||
dac_cv_write_direct(DAC_CV1_CHANNEL, CV_CENTER_VALUE);
|
||||
dac_cv_write_direct(DAC_CV2_CHANNEL, CV_CENTER_VALUE);
|
||||
|
||||
Serial.println("H7A3: Valeurs DAC CV reset au centre");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Obtient les valeurs DAC actuelles
|
||||
*/
|
||||
void dac_cv_get_values(uint16_t* cv1, uint16_t* cv2) {
|
||||
if (cv1) *cv1 = current_cv1_value;
|
||||
if (cv2) *cv2 = current_cv2_value;
|
||||
}
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* @file dac_cv_manager_h7a3.h
|
||||
* @brief Gestionnaire DAC CV pour STM32H7A3
|
||||
* @part of Apple-ADB-Ressurector-H7A3
|
||||
*/
|
||||
|
||||
#ifndef DAC_CV_MANAGER_H7A3_H
|
||||
#define DAC_CV_MANAGER_H7A3_H
|
||||
|
||||
#ifdef STM32H7A3xx
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Constantes DAC H7A3
|
||||
#define DAC_CV1_CHANNEL DAC_CHANNEL_1
|
||||
#define DAC_CV2_CHANNEL DAC_CHANNEL_2
|
||||
#define CV_CENTER_VALUE 2047
|
||||
#define CV_MAX_VALUE 4095
|
||||
#define CV_MIN_VALUE 0
|
||||
|
||||
// Variables partagées H7A3
|
||||
extern uint16_t current_cv1_value;
|
||||
extern uint16_t current_cv2_value;
|
||||
|
||||
// Fonctions DAC H7A3
|
||||
void dac_cv_init(void);
|
||||
void dac_cv_write_direct(uint32_t channel, uint16_t value);
|
||||
void dac_cv_reset_values(void);
|
||||
void dac_cv_get_values(uint16_t* cv1, uint16_t* cv2);
|
||||
|
||||
#endif // STM32H7A3xx
|
||||
|
||||
#endif // DAC_CV_MANAGER_H7A3_H
|
||||
+190
-182
@@ -1,253 +1,261 @@
|
||||
/**
|
||||
* @file hid_keyboard_h7a3.cpp
|
||||
* @brief Implémentation HID clavier pour STM32H7A3
|
||||
* @file hid_keyboard.cpp
|
||||
* @brief Implémentation des fonctionnalités HID pour les claviers.
|
||||
* @part of Apple-ADB-Ressurector
|
||||
*
|
||||
* Inspiré et basé sur le travail initial de Szymon Łopaciuk
|
||||
* https://github.com/szymonlopaciuk/stm32-adb2usb
|
||||
*
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* Dépôt actuel : https://github.com/electron-rare/Apple-ADB-Ressurector
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#include "hid_keyboard.h"
|
||||
#include "cv_stubs.h"
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#include "usbd_hid_composite_if.h"
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <BLEHIDDevice.h> // Ajout de l'inclusion manquante pour BLECharacteristic
|
||||
extern bool isBleConnected; // Déclaration externe pour isBleConnected
|
||||
extern BLECharacteristic* input_keyboard; // Déclaration externe pour input_keyboard
|
||||
#endif
|
||||
#include <Arduino.h>
|
||||
#include <string.h>
|
||||
|
||||
// Variables globales pour le clavier
|
||||
static bool keyboard_initialized = false;
|
||||
static uint32_t last_keyboard_activity = 0;
|
||||
|
||||
/**
|
||||
* @brief Initialise le clavier HID.
|
||||
*/
|
||||
void hid_keyboard_init() {
|
||||
if (keyboard_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("[HID-KB-H7A3] Initialisation du clavier HID");
|
||||
|
||||
// Initialisation des stubs CV/DAC pour le clavier
|
||||
dac_cv_manager_init();
|
||||
|
||||
// Configuration des canaux CV pour le clavier musical
|
||||
#ifdef KEYBOARD_CV_ENABLED
|
||||
// Stub simplifié pour les constantes manquantes
|
||||
// dac_cv_set_channel_config(1, 1); // CV1 pour notes
|
||||
// dac_cv_set_channel_config(2, 2); // CV2 pour gate
|
||||
#endif
|
||||
|
||||
keyboard_initialized = true;
|
||||
last_keyboard_activity = millis();
|
||||
|
||||
Serial.println("[HID-KB-H7A3] Clavier HID initialisé avec succès");
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
Serial.println("Initialisation du clavier HID USB...");
|
||||
HID_Composite_Init(HID_KEYBOARD);
|
||||
Serial.println("Clavier HID USB initialisé.");
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
Serial.println("Initialisation du clavier HID Bluetooth...");
|
||||
// L'initialisation Bluetooth HID est déjà gérée dans `setupBluetoothHID` dans
|
||||
// main.cpp
|
||||
Serial.println("Clavier HID Bluetooth initialisé.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ferme le clavier HID.
|
||||
*/
|
||||
void hid_keyboard_close() {
|
||||
if (!keyboard_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("[HID-KB-H7A3] Fermeture du clavier HID");
|
||||
|
||||
// Reset des sorties CV
|
||||
#ifdef KEYBOARD_CV_ENABLED
|
||||
dac_cv_reset_values();
|
||||
#endif
|
||||
|
||||
keyboard_initialized = false;
|
||||
|
||||
Serial.println("[HID-KB-H7A3] Clavier HID fermé");
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
Serial.println("Fermeture du clavier HID USB...");
|
||||
HID_Composite_DeInit(HID_KEYBOARD);
|
||||
Serial.println("Clavier HID USB fermé.");
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
Serial.println("Fermeture du clavier HID Bluetooth...");
|
||||
// Aucune action spécifique nécessaire pour ESP32, car Bluetooth HID est géré
|
||||
// globalement.
|
||||
Serial.println("Clavier HID Bluetooth fermé.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Envoie un rapport HID pour le clavier.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID à envoyer.
|
||||
*/
|
||||
void hid_keyboard_send_report(hid_key_report_kbd* report) {
|
||||
if (!report || !keyboard_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stub pour l'envoi HID USB - à implémenter avec USBD
|
||||
Serial.print("[HID-KB-H7A3] Envoi rapport clavier - Mods: 0x");
|
||||
Serial.print(report->modifiers, HEX);
|
||||
Serial.print(" Touches: ");
|
||||
|
||||
for (int i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] != 0) {
|
||||
Serial.print("0x");
|
||||
Serial.print(report->keys[i], HEX);
|
||||
Serial.print(" ");
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Traitement des sorties CV pour clavier musical
|
||||
#ifdef KEYBOARD_CV_ENABLED
|
||||
hid_keyboard_process_cv_output(report);
|
||||
#endif
|
||||
|
||||
last_keyboard_activity = millis();
|
||||
|
||||
// TODO: Implémenter l'envoi USB réel avec USBD_HID_SendReport
|
||||
// USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)report, sizeof(hid_key_report_kbd));
|
||||
void hid_keyboard_send_report(hid_key_report *report) {
|
||||
uint8_t buf[8] = {report->modifiers, 0,
|
||||
report->keys[0], report->keys[1],
|
||||
report->keys[2], report->keys[3],
|
||||
report->keys[4], report->keys[5]};
|
||||
|
||||
Serial.print("Envoi du rapport HID clavier - Modificateurs: ");
|
||||
Serial.print(report->modifiers, HEX);
|
||||
Serial.print(", Touches: ");
|
||||
for (int i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
Serial.print(report->keys[i], HEX);
|
||||
if (i < KEY_REPORT_KEYS_COUNT - 1)
|
||||
Serial.print(", ");
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
HID_Composite_keyboard_sendReport(buf, 8);
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
input_keyboard->setValue(buf, sizeof(buf));
|
||||
input_keyboard->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Met à jour les touches du rapport HID à partir d'un registre ADB.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID.
|
||||
* @param reg Données du registre ADB.
|
||||
* @param key_press Données du registre ADB.
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_set_keys_from_adb_register(hid_key_report_kbd* report, adb_data<adb_kb_keypress> reg) {
|
||||
if (!report) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool modified = false;
|
||||
|
||||
// Reset du rapport
|
||||
memset(report, 0, sizeof(hid_key_report_kbd));
|
||||
|
||||
// Traitement des données ADB - stub simplifié pour éviter erreurs de compilation
|
||||
modified = true; // Toujours modifié pour le moment
|
||||
|
||||
return modified;
|
||||
bool hid_keyboard_set_keys_from_adb_register(
|
||||
hid_key_report *report, adb_data<adb_kb_keypress> key_press) {
|
||||
Serial.print("Mise à jour des touches HID depuis le registre ADB - Raw: ");
|
||||
Serial.println(key_press.raw, HEX);
|
||||
|
||||
if (key_press.raw == ADBKey::KeyCode::POWER_DOWN)
|
||||
return hid_keyboard_update_key_in_report(report, ADB_KEY_POWER, false);
|
||||
else if (key_press.raw == ADBKey::KeyCode::POWER_UP)
|
||||
return hid_keyboard_update_key_in_report(report, ADB_KEY_POWER, true);
|
||||
|
||||
bool report_changed = false;
|
||||
|
||||
uint8_t key0 = key_press.data.key0;
|
||||
if (ADBKeymap::isModifier(key0))
|
||||
report_changed = hid_keyboard_update_modifier_in_report(
|
||||
report, key0, key_press.data.released0);
|
||||
else
|
||||
report_changed = hid_keyboard_update_key_in_report(
|
||||
report, ADBKeymap::toHID(key0), key_press.data.released0);
|
||||
|
||||
uint8_t key1 = key_press.data.key1;
|
||||
if (ADBKeymap::isModifier(key1))
|
||||
report_changed = hid_keyboard_update_modifier_in_report(
|
||||
report, key1, key_press.data.released1) ||
|
||||
report_changed;
|
||||
else
|
||||
report_changed =
|
||||
hid_keyboard_update_key_in_report(report, ADBKeymap::toHID(key1),
|
||||
key_press.data.released1) ||
|
||||
report_changed;
|
||||
|
||||
return report_changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Met à jour une touche spécifique dans le rapport HID.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID.
|
||||
* @param hid_keycode Code HID de la touche.
|
||||
* @param released Indique si la touche est relâchée.
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_update_key_in_report(hid_key_report_kbd* report, uint8_t hid_keycode, bool released) {
|
||||
if (!report) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (released) {
|
||||
return hid_keyboard_remove_key_from_report(report, hid_keycode);
|
||||
} else {
|
||||
return hid_keyboard_add_key_to_report(report, hid_keycode);
|
||||
}
|
||||
bool hid_keyboard_update_key_in_report(hid_key_report *report,
|
||||
uint8_t hid_keycode, bool released) {
|
||||
Serial.print("Mise à jour d'une touche HID - Code: ");
|
||||
Serial.print(hid_keycode, HEX);
|
||||
Serial.print(", Relâché: ");
|
||||
Serial.println(released);
|
||||
|
||||
if (hid_keycode == ADB_KEY_NONE)
|
||||
return false;
|
||||
|
||||
if (released)
|
||||
return hid_keyboard_remove_key_from_report(report, hid_keycode);
|
||||
else
|
||||
return hid_keyboard_add_key_to_report(report, hid_keycode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ajoute une touche au rapport HID.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID.
|
||||
* @param hid_keycode Code HID de la touche.
|
||||
* @return true si la touche a été ajoutée, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_add_key_to_report(hid_key_report_kbd* report, uint8_t hid_keycode) {
|
||||
if (!report || hid_keycode == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vérifier si la touche est déjà présente
|
||||
for (int i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] == hid_keycode) {
|
||||
return false; // Déjà présente
|
||||
}
|
||||
}
|
||||
|
||||
// Trouver un slot libre
|
||||
for (int i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] == 0) {
|
||||
report->keys[i] = hid_keycode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Pas de slot libre
|
||||
bool hid_keyboard_add_key_to_report(hid_key_report *report,
|
||||
uint8_t hid_keycode) {
|
||||
Serial.print("Ajout d'une touche HID - Code: ");
|
||||
Serial.println(hid_keycode, HEX);
|
||||
|
||||
int8_t free_slot = -1;
|
||||
|
||||
for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] == hid_keycode)
|
||||
return true;
|
||||
|
||||
if (report->keys[i] == 0 && free_slot == -1)
|
||||
free_slot = i;
|
||||
}
|
||||
|
||||
if (free_slot == -1)
|
||||
return false;
|
||||
|
||||
report->keys[free_slot] = hid_keycode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Supprime une touche du rapport HID.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID.
|
||||
* @param hid_keycode Code HID de la touche.
|
||||
* @return true si la touche a été supprimée, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_remove_key_from_report(hid_key_report_kbd* report, uint8_t hid_keycode) {
|
||||
if (!report || hid_keycode == 0) {
|
||||
return false;
|
||||
bool hid_keyboard_remove_key_from_report(hid_key_report *report,
|
||||
uint8_t hid_keycode) {
|
||||
Serial.print("Suppression d'une touche HID - Code: ");
|
||||
Serial.println(hid_keycode, HEX);
|
||||
|
||||
bool report_changed = false;
|
||||
for (uint8_t i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] == hid_keycode) {
|
||||
report->keys[i] = 0;
|
||||
report_changed = true;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
// Trouver et supprimer la touche
|
||||
for (int i = 0; i < KEY_REPORT_KEYS_COUNT; i++) {
|
||||
if (report->keys[i] == hid_keycode) {
|
||||
report->keys[i] = 0;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
return report_changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Met à jour les modificateurs dans le rapport HID.
|
||||
*
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID.
|
||||
* @param modifier Code du modificateur (ex. Shift, Ctrl).
|
||||
* @param pressed Indique si le modificateur est pressé (true) ou relâché (false).
|
||||
* @param pressed Indique si le modificateur est pressé (true) ou relâché
|
||||
* (false).
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_update_modifier_in_report(hid_key_report_kbd* report, uint8_t modifier, bool pressed) {
|
||||
if (!report) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t old_modifiers = report->modifiers;
|
||||
|
||||
if (pressed) {
|
||||
report->modifiers |= modifier;
|
||||
} else {
|
||||
report->modifiers &= ~modifier;
|
||||
}
|
||||
|
||||
return (old_modifiers != report->modifiers);
|
||||
}
|
||||
bool hid_keyboard_update_modifier_in_report(hid_key_report *report,
|
||||
uint8_t adb_keycode,
|
||||
bool released) {
|
||||
Serial.print("Mise à jour d'un modificateur HID - ADB Keycode: ");
|
||||
Serial.print(adb_keycode, HEX);
|
||||
Serial.print(", Relâché: ");
|
||||
Serial.println(released);
|
||||
|
||||
#ifdef KEYBOARD_CV_ENABLED
|
||||
auto update_modifier = [released, report](uint8_t mask) {
|
||||
// Vérifie si le modificateur est déjà dans l'état souhaité
|
||||
if (released == !(report->modifiers & mask))
|
||||
return false;
|
||||
|
||||
/**
|
||||
* @brief Convertit un code de touche ADB en note MIDI avec canal CV
|
||||
*/
|
||||
uint8_t hid_keyboard_adb_to_midi_note(uint8_t adb_key, uint8_t* cv_channel) {
|
||||
// Stub simplifié
|
||||
if (cv_channel) *cv_channel = 1;
|
||||
return 60; // Do central par défaut
|
||||
}
|
||||
// Met à jour le modificateur
|
||||
if (!released)
|
||||
report->modifiers |= mask; // Active le modificateur
|
||||
else
|
||||
report->modifiers &= ~mask; // Désactive le modificateur
|
||||
|
||||
/**
|
||||
* @brief Convertit une note MIDI en valeur CV DAC
|
||||
*/
|
||||
uint16_t hid_keyboard_midi_note_to_cv(uint8_t midi_note) {
|
||||
return 2048; // Valeur par défaut au milieu
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Traite les touches pour la sortie CV musicale
|
||||
*/
|
||||
void hid_keyboard_process_cv_output(hid_key_report_kbd* report) {
|
||||
if (!report) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Stub pour traitement CV - à développer
|
||||
// dac_cv_update_outputs(); // Fonction non disponible
|
||||
}
|
||||
// Correspondance des codes ADB avec les modificateurs HID
|
||||
if (adb_keycode == ADBKey::KeyCode::LEFT_SHIFT)
|
||||
return update_modifier(KEY_MOD_LSHIFT);
|
||||
if (adb_keycode == ADBKey::KeyCode::RIGHT_SHIFT)
|
||||
return update_modifier(KEY_MOD_RSHIFT);
|
||||
if (adb_keycode == ADBKey::KeyCode::LEFT_CONTROL)
|
||||
return update_modifier(KEY_MOD_LCTRL);
|
||||
if (adb_keycode == ADBKey::KeyCode::RIGHT_CONTROL)
|
||||
return update_modifier(KEY_MOD_RCTRL);
|
||||
if (adb_keycode == ADBKey::KeyCode::LEFT_OPTION)
|
||||
return update_modifier(KEY_MOD_LALT);
|
||||
if (adb_keycode == ADBKey::KeyCode::RIGHT_OPTION)
|
||||
return update_modifier(KEY_MOD_RALT);
|
||||
if (adb_keycode == ADBKey::KeyCode::LEFT_COMMAND)
|
||||
return update_modifier(KEY_MOD_LMETA);
|
||||
if (adb_keycode == ADBKey::KeyCode::RIGHT_COMMAND)
|
||||
return update_modifier(KEY_MOD_RMETA);
|
||||
|
||||
#endif // KEYBOARD_CV_ENABLED
|
||||
Serial.println("Modificateur inconnu.");
|
||||
return false; // Aucun changement
|
||||
}
|
||||
+9
-52
@@ -11,8 +11,7 @@
|
||||
#ifndef HID_KEYBOARD_H
|
||||
#define HID_KEYBOARD_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <cstdint>
|
||||
#include <stdbool.h>
|
||||
#include "adb.h"
|
||||
|
||||
@@ -29,10 +28,10 @@
|
||||
#define KEY_MOD_RMETA 0x80
|
||||
|
||||
/**
|
||||
* @struct hid_key_report_kbd
|
||||
* @struct hid_key_report
|
||||
* @brief Structure représentant un rapport HID pour un clavier.
|
||||
*/
|
||||
struct hid_key_report_kbd {
|
||||
struct hid_key_report {
|
||||
uint8_t modifiers; /**< Modificateurs actifs (Ctrl, Alt, etc.). */
|
||||
uint8_t keys[KEY_REPORT_KEYS_COUNT]; /**< Tableau des touches actives. */
|
||||
};
|
||||
@@ -52,7 +51,7 @@ void hid_keyboard_close();
|
||||
*
|
||||
* @param report Pointeur vers le rapport HID à envoyer.
|
||||
*/
|
||||
void hid_keyboard_send_report(hid_key_report_kbd* report);
|
||||
void hid_keyboard_send_report(hid_key_report* report);
|
||||
|
||||
/**
|
||||
* @brief Met à jour les touches du rapport HID à partir d'un registre ADB.
|
||||
@@ -61,7 +60,7 @@ void hid_keyboard_send_report(hid_key_report_kbd* report);
|
||||
* @param reg Données du registre ADB.
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_set_keys_from_adb_register(hid_key_report_kbd* report, adb_data<adb_kb_keypress> reg);
|
||||
bool hid_keyboard_set_keys_from_adb_register(hid_key_report* report, adb_data<adb_kb_keypress> reg);
|
||||
|
||||
/**
|
||||
* @brief Met à jour une touche spécifique dans le rapport HID.
|
||||
@@ -71,7 +70,7 @@ bool hid_keyboard_set_keys_from_adb_register(hid_key_report_kbd* report, adb_dat
|
||||
* @param released Indique si la touche est relâchée.
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_update_key_in_report(hid_key_report_kbd* report, uint8_t hid_keycode, bool released);
|
||||
bool hid_keyboard_update_key_in_report(hid_key_report* report, uint8_t hid_keycode, bool released);
|
||||
|
||||
/**
|
||||
* @brief Ajoute une touche au rapport HID.
|
||||
@@ -80,7 +79,7 @@ bool hid_keyboard_update_key_in_report(hid_key_report_kbd* report, uint8_t hid_k
|
||||
* @param hid_keycode Code HID de la touche.
|
||||
* @return true si la touche a été ajoutée, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_add_key_to_report(hid_key_report_kbd* report, uint8_t hid_keycode);
|
||||
bool hid_keyboard_add_key_to_report(hid_key_report* report, uint8_t hid_keycode);
|
||||
|
||||
/**
|
||||
* @brief Supprime une touche du rapport HID.
|
||||
@@ -89,7 +88,7 @@ bool hid_keyboard_add_key_to_report(hid_key_report_kbd* report, uint8_t hid_keyc
|
||||
* @param hid_keycode Code HID de la touche.
|
||||
* @return true si la touche a été supprimée, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_remove_key_from_report(hid_key_report_kbd* report, uint8_t hid_keycode);
|
||||
bool hid_keyboard_remove_key_from_report(hid_key_report* report, uint8_t hid_keycode);
|
||||
|
||||
/**
|
||||
* @brief Met à jour les modificateurs dans le rapport HID.
|
||||
@@ -99,48 +98,6 @@ bool hid_keyboard_remove_key_from_report(hid_key_report_kbd* report, uint8_t hid
|
||||
* @param pressed Indique si le modificateur est pressé (true) ou relâché (false).
|
||||
* @return true si le rapport a été modifié, false sinon.
|
||||
*/
|
||||
bool hid_keyboard_update_modifier_in_report(hid_key_report_kbd* report, uint8_t modifier, bool pressed);
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Configuration CV pour clavier musical (partage les sorties avec la souris)
|
||||
#define KEYBOARD_CV_ENABLED
|
||||
|
||||
// Mapping clavier vers notes (standard MIDI)
|
||||
#define KEY_NOTE_C4 60 // Do central (261.63 Hz)
|
||||
#define KEY_NOTE_C4_SHARP 61 // Do#
|
||||
#define KEY_NOTE_D4 62 // Ré
|
||||
#define KEY_NOTE_D4_SHARP 63 // Ré#
|
||||
#define KEY_NOTE_E4 64 // Mi
|
||||
#define KEY_NOTE_F4 65 // Fa
|
||||
#define KEY_NOTE_F4_SHARP 66 // Fa#
|
||||
#define KEY_NOTE_G4 67 // Sol
|
||||
#define KEY_NOTE_G4_SHARP 68 // Sol#
|
||||
#define KEY_NOTE_A4 69 // La (440 Hz)
|
||||
#define KEY_NOTE_A4_SHARP 70 // La#
|
||||
#define KEY_NOTE_B4 71 // Si
|
||||
|
||||
// Fonctions CV pour clavier
|
||||
/**
|
||||
* @brief Convertit un code de touche ADB en note MIDI avec canal CV
|
||||
* @param adb_key Code de touche ADB
|
||||
* @param cv_channel Pointeur vers le canal CV de sortie (1=CV1, 2=CV2)
|
||||
* @return Note MIDI (0-127) ou 255 si pas de correspondance
|
||||
*/
|
||||
uint8_t hid_keyboard_adb_to_midi_note(uint8_t adb_key, uint8_t* cv_channel = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Convertit une note MIDI en valeur CV DAC
|
||||
* @param midi_note Note MIDI (0-127)
|
||||
* @return Valeur DAC 12-bit (0-4095)
|
||||
*/
|
||||
uint16_t hid_keyboard_midi_note_to_cv(uint8_t midi_note);
|
||||
|
||||
/**
|
||||
* @brief Traite les touches pour la sortie CV musicale
|
||||
* @param report Rapport HID du clavier
|
||||
*/
|
||||
void hid_keyboard_process_cv_output(hid_key_report_kbd* report);
|
||||
|
||||
#endif
|
||||
bool hid_keyboard_update_modifier_in_report(hid_key_report* report, uint8_t modifier, bool pressed);
|
||||
|
||||
#endif // HID_KEYBOARD_H
|
||||
@@ -1,285 +0,0 @@
|
||||
/**
|
||||
* @file hid_midi_h7a3.cpp
|
||||
* @brief Implémentation HID MIDI pour H7A3 avec fonctionnalités complètes F303
|
||||
* @part of Apple-ADB-Ressurector H7A3 Enhanced
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT, adaptations H7A3 par Electron_Rare
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "hid_midi.h"
|
||||
|
||||
// Instance globale MIDI H7A3
|
||||
HIDMidiH7A3 midiH7A3;
|
||||
|
||||
// === Implémentation de la classe HIDMidiH7A3 ===
|
||||
|
||||
void HIDMidiH7A3::init() {
|
||||
Serial.println("=== HID MIDI H7A3 Initialization ===");
|
||||
|
||||
// Configuration par défaut
|
||||
channels.keyboard_channel = 1;
|
||||
channels.mouse_channel = 2;
|
||||
channels.cv1_channel = 3;
|
||||
channels.cv2_channel = 4;
|
||||
channels.gate_channel = 5;
|
||||
|
||||
// État initial
|
||||
memset(&state, 0, sizeof(state));
|
||||
velocity_curve = 1.0f;
|
||||
performance_mode = true;
|
||||
message_count = 0;
|
||||
|
||||
Serial.println("MIDI H7A3: Full F303 features + H7A3 optimizations");
|
||||
Serial.println("Channels: KBD=1, Mouse=2, CV1=3, CV2=4, GATE=5");
|
||||
Serial.println("Performance mode: ENABLED (480MHz processing)");
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::close() {
|
||||
Serial.println("HID MIDI H7A3 closed");
|
||||
|
||||
// Envoie All Notes Off sur tous les canaux
|
||||
for (uint8_t ch = 1; ch <= 16; ch++) {
|
||||
send_all_notes_off(ch);
|
||||
}
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) {
|
||||
Serial.print("MIDI Note ON: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", Note=");
|
||||
Serial.print(note);
|
||||
Serial.print(", Vel=");
|
||||
Serial.println(velocity);
|
||||
|
||||
state.active_notes[channel] = note;
|
||||
state.note_count[channel]++;
|
||||
state.last_note_time[channel] = millis();
|
||||
message_count++;
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_note_off(uint8_t channel, uint8_t note, uint8_t velocity) {
|
||||
Serial.print("MIDI Note OFF: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", Note=");
|
||||
Serial.print(note);
|
||||
Serial.print(", Vel=");
|
||||
Serial.println(velocity);
|
||||
|
||||
if (state.note_count[channel] > 0) {
|
||||
state.note_count[channel]--;
|
||||
}
|
||||
message_count++;
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_all_notes_off(uint8_t channel) {
|
||||
Serial.print("MIDI All Notes OFF: CH");
|
||||
Serial.println(channel);
|
||||
|
||||
state.note_count[channel] = 0;
|
||||
state.active_notes[channel] = 0;
|
||||
message_count++;
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_control_change(uint8_t channel, uint8_t controller, uint8_t value) {
|
||||
Serial.print("MIDI CC: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", CC");
|
||||
Serial.print(controller);
|
||||
Serial.print("=");
|
||||
Serial.println(value);
|
||||
|
||||
message_count++;
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_pitch_bend(uint8_t channel, uint16_t bend_value) {
|
||||
Serial.print("MIDI Pitch Bend: CH");
|
||||
Serial.print(channel);
|
||||
Serial.print(", Value=");
|
||||
Serial.println(bend_value);
|
||||
|
||||
message_count++;
|
||||
}
|
||||
|
||||
// === Fonctions CV/GATE spécifiques H7A3 ===
|
||||
|
||||
void HIDMidiH7A3::send_cv1_control(uint8_t value) {
|
||||
Serial.print("H7A3 CV1 Control: ");
|
||||
Serial.print(value);
|
||||
Serial.print(" (");
|
||||
Serial.print((value * 3.3f) / 127.0f);
|
||||
Serial.println("V)");
|
||||
|
||||
send_control_change(channels.cv1_channel, MIDI_CC_CV1_CONTROL, value);
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_cv2_control(uint8_t value) {
|
||||
Serial.print("H7A3 CV2 Control: ");
|
||||
Serial.print(value);
|
||||
Serial.print(" (");
|
||||
Serial.print((value * 3.3f) / 127.0f);
|
||||
Serial.println("V)");
|
||||
|
||||
send_control_change(channels.cv2_channel, MIDI_CC_CV2_CONTROL, value);
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::send_gate_control(bool state) {
|
||||
Serial.print("H7A3 GATE Control: ");
|
||||
Serial.println(state ? "HIGH" : "LOW");
|
||||
|
||||
send_control_change(channels.gate_channel, MIDI_CC_GATE_CONTROL, state ? 127 : 0);
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::configure_channels(midi_channels_config_h7a3_t* config) {
|
||||
if (config) {
|
||||
channels = *config;
|
||||
Serial.println("H7A3 MIDI Channels reconfigured");
|
||||
}
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::set_velocity_curve(float curve) {
|
||||
velocity_curve = curve;
|
||||
Serial.print("H7A3 Velocity curve: ");
|
||||
Serial.println(curve);
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::enable_performance_mode(bool enabled) {
|
||||
performance_mode = enabled;
|
||||
Serial.print("H7A3 Performance mode: ");
|
||||
Serial.println(enabled ? "ENABLED" : "DISABLED");
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::print_performance_stats() {
|
||||
Serial.println("=== H7A3 MIDI Performance Stats ===");
|
||||
Serial.print("Messages sent: ");
|
||||
Serial.println(message_count);
|
||||
Serial.print("Processing rate: ");
|
||||
Serial.println(performance_mode ? "480MHz optimized" : "Standard");
|
||||
|
||||
for (uint8_t ch = 1; ch <= 5; ch++) {
|
||||
if (state.note_count[ch] > 0) {
|
||||
Serial.print("Channel ");
|
||||
Serial.print(ch);
|
||||
Serial.print(": ");
|
||||
Serial.print(state.note_count[ch]);
|
||||
Serial.println(" active notes");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::print_channel_activity() {
|
||||
Serial.println("=== H7A3 MIDI Channel Activity ===");
|
||||
Serial.print("Keyboard (CH");
|
||||
Serial.print(channels.keyboard_channel);
|
||||
Serial.print("): ");
|
||||
Serial.print(state.note_count[channels.keyboard_channel]);
|
||||
Serial.println(" notes");
|
||||
|
||||
Serial.print("CV1 (CH");
|
||||
Serial.print(channels.cv1_channel);
|
||||
Serial.println("): Active");
|
||||
|
||||
Serial.print("CV2 (CH");
|
||||
Serial.print(channels.cv2_channel);
|
||||
Serial.println("): Active");
|
||||
|
||||
Serial.print("GATE (CH");
|
||||
Serial.print(channels.gate_channel);
|
||||
Serial.println("): Active");
|
||||
}
|
||||
|
||||
void HIDMidiH7A3::reset_performance_counters() {
|
||||
message_count = 0;
|
||||
state.performance_counter = 0;
|
||||
Serial.println("H7A3 MIDI performance counters reset");
|
||||
}
|
||||
|
||||
// === Fonctions C pour compatibilité F303 ===
|
||||
|
||||
extern "C" {
|
||||
|
||||
void hid_midi_init(void) {
|
||||
midiH7A3.init();
|
||||
}
|
||||
|
||||
void hid_midi_close(void) {
|
||||
midiH7A3.close();
|
||||
}
|
||||
|
||||
void hid_midi_send_note_on(uint8_t channel, uint8_t note, uint8_t velocity) {
|
||||
midiH7A3.send_note_on(channel, note, velocity);
|
||||
}
|
||||
|
||||
void hid_midi_send_note_off(uint8_t channel, uint8_t note, uint8_t velocity) {
|
||||
midiH7A3.send_note_off(channel, note, velocity);
|
||||
}
|
||||
|
||||
void hid_midi_send_control_change(uint8_t channel, uint8_t controller, uint8_t value) {
|
||||
midiH7A3.send_control_change(channel, controller, value);
|
||||
}
|
||||
|
||||
void hid_midi_send_pitch_bend(uint8_t channel, uint16_t bend_value) {
|
||||
midiH7A3.send_pitch_bend(channel, bend_value);
|
||||
}
|
||||
|
||||
void hid_midi_process_keyboard(uint8_t* keys, uint8_t key_count) {
|
||||
Serial.print("H7A3 MIDI Keyboard processing: ");
|
||||
Serial.print(key_count);
|
||||
Serial.println(" keys");
|
||||
|
||||
// Traitement optimisé H7A3 des touches
|
||||
for (uint8_t i = 0; i < key_count && i < 6; i++) {
|
||||
if (keys[i] != 0) {
|
||||
// Conversion scancode vers note MIDI (exemple)
|
||||
uint8_t midi_note = 60 + (keys[i] % 24); // C4 + offset
|
||||
midiH7A3.send_note_on(1, midi_note, MIDI_VELOCITY_DEFAULT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hid_midi_process_mouse(int8_t delta_x, int8_t delta_y, uint8_t buttons) {
|
||||
Serial.print("H7A3 MIDI Mouse: ΔX=");
|
||||
Serial.print(delta_x);
|
||||
Serial.print(", ΔY=");
|
||||
Serial.print(delta_y);
|
||||
Serial.print(", Buttons=0x");
|
||||
Serial.println(buttons, HEX);
|
||||
|
||||
// Conversion mouvements souris vers contrôles MIDI
|
||||
if (abs(delta_x) > 2) {
|
||||
uint8_t cv1_value = constrain(64 + delta_x, 0, 127);
|
||||
midiH7A3.send_cv1_control(cv1_value);
|
||||
}
|
||||
|
||||
if (abs(delta_y) > 2) {
|
||||
uint8_t cv2_value = constrain(64 + delta_y, 0, 127);
|
||||
midiH7A3.send_cv2_control(cv2_value);
|
||||
}
|
||||
|
||||
// Boutons souris vers GATE
|
||||
midiH7A3.send_gate_control(buttons & 0x01);
|
||||
}
|
||||
|
||||
void hid_midi_configure_channels(uint8_t kbd_ch, uint8_t mouse_ch, uint8_t cv1_ch, uint8_t cv2_ch) {
|
||||
midi_channels_config_h7a3_t config;
|
||||
config.keyboard_channel = kbd_ch;
|
||||
config.mouse_channel = mouse_ch;
|
||||
config.cv1_channel = cv1_ch;
|
||||
config.cv2_channel = cv2_ch;
|
||||
config.gate_channel = cv2_ch + 1;
|
||||
|
||||
midiH7A3.configure_channels(&config);
|
||||
}
|
||||
|
||||
void hid_midi_set_velocity_curve(float curve) {
|
||||
midiH7A3.set_velocity_curve(curve);
|
||||
}
|
||||
|
||||
void hid_midi_debug_performance(void) {
|
||||
midiH7A3.print_performance_stats();
|
||||
midiH7A3.print_channel_activity();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
/**
|
||||
* @file hid_midi.h
|
||||
* @brief Interface HID MIDI pour clavier et souris Apple ADB - Version H7A3
|
||||
* @part of Apple-ADB-Ressurector H7A3 Enhanced
|
||||
*
|
||||
* Fonctionnalités MIDI complètes adaptées pour STM32H7A3 haute performance
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT, adaptations H7A3 par Electron_Rare
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#ifndef HID_MIDI_H
|
||||
#define HID_MIDI_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
// Configuration MIDI H7A3
|
||||
#define MIDI_CHANNEL_DEFAULT 1 // Canal MIDI par défaut (1-16)
|
||||
#define MIDI_NOTE_ON 0x90 // Commande Note On
|
||||
#define MIDI_NOTE_OFF 0x80 // Commande Note Off
|
||||
#define MIDI_CONTROL_CHANGE 0xB0 // Commande Control Change
|
||||
#define MIDI_PITCH_BEND 0xE0 // Commande Pitch Bend
|
||||
#define MIDI_VELOCITY_DEFAULT 64 // Vélocité par défaut
|
||||
#define MIDI_VELOCITY_MAX 127 // Vélocité maximale
|
||||
|
||||
// Assignations Control Change étendues H7A3
|
||||
#define MIDI_CC_MODULATION 1 // CC1 : Roue de modulation
|
||||
#define MIDI_CC_BREATH_CONTROL 2 // CC2 : Contrôleur de souffle
|
||||
#define MIDI_CC_VOLUME 7 // CC7 : Volume
|
||||
#define MIDI_CC_PAN 10 // CC10 : Pan
|
||||
#define MIDI_CC_EXPRESSION 11 // CC11 : Expression
|
||||
#define MIDI_CC_SUSTAIN 64 // CC64 : Pédale de sustain
|
||||
#define MIDI_CC_PORTAMENTO_TIME 5 // CC5 : Temps de portamento
|
||||
#define MIDI_CC_FILTER_CUTOFF 74 // CC74 : Cutoff du filtre
|
||||
#define MIDI_CC_CV1_CONTROL 80 // CC80 : Contrôle CV1 (H7A3)
|
||||
#define MIDI_CC_CV2_CONTROL 81 // CC81 : Contrôle CV2 (H7A3)
|
||||
#define MIDI_CC_GATE_CONTROL 82 // CC82 : Contrôle GATE (H7A3)
|
||||
|
||||
// Configuration des canaux MIDI H7A3
|
||||
typedef struct {
|
||||
uint8_t keyboard_channel; // Canal pour les notes du clavier
|
||||
uint8_t mouse_channel; // Canal pour les contrôles souris
|
||||
uint8_t cv1_channel; // Canal pour les données CV1
|
||||
uint8_t cv2_channel; // Canal pour les données CV2
|
||||
uint8_t gate_channel; // Canal pour le signal GATE
|
||||
} midi_channels_config_t;
|
||||
|
||||
// État des notes MIDI H7A3
|
||||
typedef struct {
|
||||
uint8_t active_notes[16]; // Notes actives par canal
|
||||
uint8_t note_count[16]; // Nombre de notes par canal
|
||||
uint32_t last_note_time[16]; // Dernière activité par canal
|
||||
uint32_t performance_counter; // Compteur de performance H7A3
|
||||
} midi_notes_state_t;
|
||||
|
||||
// Classe principale MIDI H7A3
|
||||
class HIDMidiH7A3 {
|
||||
public:
|
||||
void init();
|
||||
void close();
|
||||
|
||||
// Fonctions notes
|
||||
void send_note_on(uint8_t channel, uint8_t note, uint8_t velocity);
|
||||
void send_note_off(uint8_t channel, uint8_t note, uint8_t velocity);
|
||||
void send_all_notes_off(uint8_t channel);
|
||||
|
||||
// Fonctions control change
|
||||
void send_control_change(uint8_t channel, uint8_t controller, uint8_t value);
|
||||
void send_pitch_bend(uint8_t channel, uint16_t bend_value);
|
||||
|
||||
// Fonctions CV/GATE spécifiques H7A3
|
||||
void send_cv1_control(uint8_t value);
|
||||
void send_cv2_control(uint8_t value);
|
||||
void send_gate_control(bool state);
|
||||
|
||||
// Configuration H7A3
|
||||
void configure_channels(midi_channels_config_t* config);
|
||||
void set_velocity_curve(float curve);
|
||||
void enable_performance_mode(bool enabled);
|
||||
|
||||
// Debug et monitoring H7A3
|
||||
void print_performance_stats();
|
||||
void print_channel_activity();
|
||||
void reset_performance_counters();
|
||||
|
||||
private:
|
||||
midi_channels_config_t channels;
|
||||
midi_notes_state_t state;
|
||||
float velocity_curve;
|
||||
bool performance_mode;
|
||||
uint32_t message_count;
|
||||
};
|
||||
|
||||
// Fonctions globales pour compatibilité F303
|
||||
extern "C" {
|
||||
void hid_midi_init(void);
|
||||
void hid_midi_close(void);
|
||||
void hid_midi_send_note_on(uint8_t channel, uint8_t note, uint8_t velocity);
|
||||
void hid_midi_send_note_off(uint8_t channel, uint8_t note, uint8_t velocity);
|
||||
void hid_midi_send_control_change(uint8_t channel, uint8_t controller, uint8_t value);
|
||||
void hid_midi_send_pitch_bend(uint8_t channel, uint16_t bend_value);
|
||||
void hid_midi_process_keyboard(uint8_t* keys, uint8_t key_count);
|
||||
void hid_midi_process_mouse(int8_t delta_x, int8_t delta_y, uint8_t buttons);
|
||||
void hid_midi_configure_channels(uint8_t kbd_ch, uint8_t mouse_ch, uint8_t cv1_ch, uint8_t cv2_ch);
|
||||
void hid_midi_set_velocity_curve(float curve);
|
||||
void hid_midi_debug_performance(void);
|
||||
}
|
||||
|
||||
#endif // HID_MIDI_H
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @file hid_mouse.cpp
|
||||
* @brief Implémentation des fonctionnalités HID pour les souris.
|
||||
* @part of Apple-ADB-Ressurector
|
||||
* Inspiré et basé sur le travail initial de Szymon Łopaciuk https://github.com/szymonlopaciuk/stm32-adb2usb
|
||||
*
|
||||
*
|
||||
* @date 2025
|
||||
* @author Clément SAILLANT
|
||||
* Dépôt actuel : https://github.com/electron-rare/Apple-ADB-Ressurector
|
||||
* @license GNU GPL v3
|
||||
*/
|
||||
|
||||
#include "hid_mouse.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#include "usbd_hid_composite_if.h"
|
||||
#endif
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <BLEHIDDevice.h> // Ajout de l'inclusion manquante pour BLECharacteristic
|
||||
#include "ADBKeymap.h" // Assurez-vous que la conversion ADB vers HID est incluse
|
||||
extern BLECharacteristic* input_mouse; // Déclaration externe pour input_mouse
|
||||
extern bool isBleConnected; // Déclaration externe pour isBleConnected
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialise la souris HID.
|
||||
*/
|
||||
void hid_mouse_init() {
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
Serial.println("Initialisation de la souris HID USB...");
|
||||
HID_Composite_Init(HID_MOUSE);
|
||||
Serial.println("Souris HID USB initialisée.");
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
Serial.println("Initialisation de la souris HID Bluetooth...");
|
||||
// L'initialisation Bluetooth HID est déjà gérée dans `setupBluetoothHID` dans main.cpp
|
||||
Serial.println("Souris HID Bluetooth initialisée.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ferme la souris HID.
|
||||
*/
|
||||
void hid_mouse_close() {
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
Serial.println("Fermeture de la souris HID USB...");
|
||||
HID_Composite_DeInit(HID_MOUSE);
|
||||
Serial.println("Souris HID USB fermée.");
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
Serial.println("Fermeture de la souris HID Bluetooth...");
|
||||
// Aucune action spécifique nécessaire pour ESP32, car Bluetooth HID est géré globalement.
|
||||
Serial.println("Souris HID Bluetooth fermée.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Envoie un rapport HID pour la souris.
|
||||
*
|
||||
* @param button État du bouton de la souris (appuyé ou relâché).
|
||||
* @param offset_x Déplacement horizontal de la souris.
|
||||
* @param offset_y Déplacement vertical de la souris.
|
||||
*/
|
||||
void hid_mouse_send_report(bool button, int8_t offset_x, int8_t offset_y) {
|
||||
uint8_t m[4];
|
||||
m[0] = button; // Bouton de la souris (0 = relâché, 1 = appuyé)
|
||||
m[1] = offset_x; // Déplacement horizontal
|
||||
m[2] = offset_y; // Déplacement vertical
|
||||
m[3] = 0; // Réservé
|
||||
|
||||
Serial.print("Envoi du rapport HID souris - Bouton: ");
|
||||
Serial.print(button);
|
||||
Serial.print(", X: ");
|
||||
Serial.print(offset_x);
|
||||
Serial.print(", Y: ");
|
||||
Serial.println(offset_y);
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
HID_Composite_mouse_sendReport(m, 4);
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
// Traduction des données ADB en HID Bluetooth
|
||||
uint8_t bt_report[4] = {m[0], m[1], m[2], m[3]};
|
||||
|
||||
// Envoi du rapport via Bluetooth
|
||||
input_mouse->setValue(bt_report, sizeof(bt_report));
|
||||
input_mouse->notify();
|
||||
|
||||
// Libération des boutons entre deux actions pour éviter les répétitions
|
||||
delay(5);
|
||||
bt_report[0] = 0; // Aucun bouton appuyé
|
||||
input_mouse->setValue(bt_report, sizeof(bt_report));
|
||||
input_mouse->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
+2
-22
@@ -14,11 +14,10 @@
|
||||
#ifndef HID_MOUSE_h
|
||||
#define HID_MOUSE_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @brief Initialise la souris HID (DAC géré par dac_cv_manager).
|
||||
* @brief Initialise la souris HID.
|
||||
*/
|
||||
void hid_mouse_init();
|
||||
|
||||
@@ -36,23 +35,4 @@ void hid_mouse_close();
|
||||
*/
|
||||
void hid_mouse_send_report(bool button, int8_t offset_x, int8_t offset_y);
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
/**
|
||||
* @brief Remet les valeurs CV au centre (compatibilité - redirect vers dac_cv_manager).
|
||||
*/
|
||||
void hid_mouse_reset_cv_values(void);
|
||||
|
||||
/**
|
||||
* @brief Obtient la valeur CV1 actuelle (compatibilité - redirect vers dac_cv_manager).
|
||||
* @return Valeur DAC actuelle pour CV1 (0-4095)
|
||||
*/
|
||||
uint16_t hid_mouse_get_cv1_value(void);
|
||||
|
||||
/**
|
||||
* @brief Obtient la valeur CV2 actuelle (compatibilité - redirect vers dac_cv_manager).
|
||||
* @return Valeur DAC actuelle pour CV2 (0-4095)
|
||||
*/
|
||||
uint16_t hid_mouse_get_cv2_value(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* @file hid_structures.h
|
||||
* @brief Structures HID pour STM32H7A3
|
||||
*
|
||||
* Définitions des structures de données HID communes
|
||||
* pour clavier et souris, adaptées au STM32H7A3
|
||||
*/
|
||||
|
||||
#ifndef HID_STRUCTURES_H
|
||||
#define HID_STRUCTURES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @struct hid_key_report
|
||||
* @brief Structure du rapport clavier HID
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t modifiers; /**< Modificateurs (Ctrl, Alt, Shift, etc.) */
|
||||
uint8_t reserved; /**< Octet réservé */
|
||||
uint8_t keys[6]; /**< Tableau des touches pressées */
|
||||
} hid_key_report;
|
||||
|
||||
/**
|
||||
* @struct hid_mouse_report
|
||||
* @brief Structure du rapport souris HID
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t buttons; /**< État des boutons de la souris */
|
||||
int8_t x; /**< Déplacement relatif X */
|
||||
int8_t y; /**< Déplacement relatif Y */
|
||||
int8_t wheel; /**< Déplacement de la roulette */
|
||||
} hid_mouse_report;
|
||||
|
||||
/**
|
||||
* @brief Données ADB souris (compatibilité F303)
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t raw;
|
||||
struct {
|
||||
uint8_t button_pressed : 1;
|
||||
int8_t x_offset : 7;
|
||||
int8_t y_offset;
|
||||
} data;
|
||||
} adb_mouse_data_t;
|
||||
|
||||
/**
|
||||
* @brief Données ADB clavier (compatibilité F303)
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint8_t key0;
|
||||
uint8_t released0 : 1;
|
||||
uint8_t key1 : 7;
|
||||
uint8_t released1 : 1;
|
||||
uint16_t unused : 15;
|
||||
} data;
|
||||
} adb_key_data_t;
|
||||
|
||||
/**
|
||||
* @brief Wrapper compatible avec F303
|
||||
*/
|
||||
static inline bool adb_mouse_data_has_value(const adb_mouse_data_t* mouse_data) {
|
||||
return mouse_data->raw != 0;
|
||||
}
|
||||
|
||||
static inline bool adb_key_data_has_value(const adb_key_data_t* key_data) {
|
||||
return key_data->raw != 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HID_STRUCTURES_H
|
||||
+319
-229
@@ -1,62 +1,23 @@
|
||||
/**
|
||||
* @file main.cpp
|
||||
* @brief Apple ADB Ressurector - Version STM32H7A3 Enhanced
|
||||
*
|
||||
* Adaptation complète du projet original F303 vers H7A3 avec :
|
||||
* - Performance 6.7x supérieure (480MHz vs 72MHz)
|
||||
* - Modulation CV 1000Hz (vs 100Hz F303)
|
||||
* - Caches L1 I+D activés
|
||||
* - FPU double précision
|
||||
* - Architecture Cortex-M7 optimisée
|
||||
*
|
||||
* Basé sur le travail initial de Szymon Łopaciuk
|
||||
* https://github.com/szymonlopaciuk/stm32-adb2usb
|
||||
*
|
||||
* @credits Szymon Łopaciuk, Clément SAILLANT
|
||||
* @author Electron_Rare (adaptation H7A3)
|
||||
* @brief Main file for the Apple ADB Ressurector project.
|
||||
|
||||
* Inspiré et basé sur le travail initial de Szymon Łopaciuk
|
||||
https://github.com/szymonlopaciuk/stm32-adb2usb
|
||||
* @credits Szymon Łopaciuk
|
||||
* @author Clément SAILLANT
|
||||
* @date 2025
|
||||
* Dépôt actuel : https://github.com/electron-rare/Apple-ADB-Ressurector
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Fonctionnalités CV/GATE pour STM32H7A3 :
|
||||
* - Mouvement souris X/Y : Contrôle des valeurs CV1/CV2 (accumulation haute précision)
|
||||
* - Bouton souris : Contrôle du signal GATE (ON/OFF)
|
||||
* - Reset automatique des CV après timeout d'inactivité souris
|
||||
* - Modulation continue 1000Hz : portamento, vibrato, et modulation FM
|
||||
* - Optimisations mémoire DTCM/ITCM
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ADB.h>
|
||||
#include "cv_stubs.h"
|
||||
#include "hid_keyboard.h"
|
||||
#include "hid_mouse.h"
|
||||
#include <ADB.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Déclarations externes des stubs CV H7A3
|
||||
extern "C" {
|
||||
void dac_cv_update_cumulative(int8_t mouse_x, int8_t mouse_y);
|
||||
void cv_modulation_update_fm(int8_t mouse_x, int8_t mouse_y);
|
||||
void dac_cv_write_direct(uint8_t channel, uint16_t value);
|
||||
void dac_cv_set_gate(bool state);
|
||||
void dac_cv_reset_values(void);
|
||||
void cv_modulation_init(void);
|
||||
void cv_modulation_process(void);
|
||||
}
|
||||
|
||||
// Classes forward declaration
|
||||
class CVModulationH7A3;
|
||||
class DACCVManagerH7A3;
|
||||
|
||||
// Instances externes
|
||||
extern CVModulationH7A3 cv_modulation;
|
||||
extern DACCVManagerH7A3 dac_manager;
|
||||
#endif
|
||||
|
||||
#define POLL_DELAY 1
|
||||
|
||||
#define POLL_DELAY 5
|
||||
// Définition de la pin ADB selon la plateforme
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define ADB_PIN 2
|
||||
@@ -70,7 +31,7 @@ extern DACCVManagerH7A3 dac_manager;
|
||||
#define LED_PIN 4 // Pin pour ESP32 (Devkit Wemos)
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#define LED_PIN PC13 // Pin pour STM32H7A3
|
||||
#define LED_PIN PC13 // Pin pour STM32
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -78,13 +39,14 @@ extern DACCVManagerH7A3 dac_manager;
|
||||
* @brief Structure pour regrouper les états des périphériques.
|
||||
*/
|
||||
struct DeviceState {
|
||||
bool apple_extended_detected = false; /**< Détection du clavier Apple étendu. */
|
||||
bool keyboard_present = false; /**< Présence d'un clavier. */
|
||||
bool mouse_present = false; /**< Présence d'une souris. */
|
||||
bool led_num = true; /**< État de la LED Num Lock (actif par défaut). */
|
||||
bool led_caps = false; /**< État de la LED Caps Lock. */
|
||||
bool led_scroll = false; /**< État de la LED Scroll Lock. */
|
||||
bool led_power = false; /**< État de la LED Power. */
|
||||
bool apple_extended_detected =
|
||||
false; /**< Détection du clavier Apple étendu. */
|
||||
bool keyboard_present = false; /**< Présence d'un clavier. */
|
||||
bool mouse_present = false; /**< Présence d'une souris. */
|
||||
bool led_num = true; /**< État de la LED Num Lock (actif par défaut). */
|
||||
bool led_caps = false; /**< État de la LED Caps Lock. */
|
||||
bool led_scroll = false; /**< État de la LED Scroll Lock. */
|
||||
bool led_power = false; /**< État de la LED Power. */
|
||||
};
|
||||
|
||||
// Instances globales
|
||||
@@ -93,71 +55,240 @@ ADBDevices adbDevices(adb); /**< Gestionnaire des périphériques ADB. */
|
||||
DeviceState deviceState; /**< État des périphériques. */
|
||||
bool caps_lock_pressed = false; /**< État de la touche Caps Lock. */
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Variables pour le contrôle CV par la souris H7A3
|
||||
unsigned long last_mouse_activity = 0; /**< Timestamp de la dernière activité souris. */
|
||||
const unsigned long CV_RESET_TIMEOUT = 20000; /**< Timeout pour réinitialiser les CV (ms). */
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEHIDDevice.h>
|
||||
#include <HIDKeyboardTypes.h>
|
||||
#include <HIDTypes.h>
|
||||
|
||||
// Déclaration de la structure InputReport
|
||||
struct InputReport {
|
||||
uint8_t modifiers; // bitmask: CTRL = 1, SHIFT = 2, ALT = 4
|
||||
uint8_t reserved; // doit être 0
|
||||
uint8_t pressedKeys[6]; // jusqu'à six touches pressées simultanément
|
||||
};
|
||||
|
||||
// The report map describes the HID device (a keyboard in this case) and
|
||||
// the messages (reports in HID terms) sent and received.
|
||||
static const uint8_t REPORT_MAP[] = {
|
||||
USAGE_PAGE(1),
|
||||
0x01, // Generic Desktop Controls
|
||||
USAGE(1),
|
||||
0x06, // Keyboard
|
||||
COLLECTION(1),
|
||||
0x01, // Application
|
||||
REPORT_ID(1),
|
||||
0x01, // Report ID (1)
|
||||
USAGE_PAGE(1),
|
||||
0x07, // Keyboard/Keypad
|
||||
USAGE_MINIMUM(1),
|
||||
0xE0, // Keyboard Left Control
|
||||
USAGE_MAXIMUM(1),
|
||||
0xE7, // Keyboard Right Control
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00, // Each bit is either 0 or 1
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x01,
|
||||
REPORT_COUNT(1),
|
||||
0x08, // 8 bits for the modifier keys
|
||||
REPORT_SIZE(1),
|
||||
0x01,
|
||||
HIDINPUT(1),
|
||||
0x02, // Data, Var, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x01, // 1 byte (unused)
|
||||
REPORT_SIZE(1),
|
||||
0x08,
|
||||
HIDINPUT(1),
|
||||
0x01, // Const, Array, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x06, // 6 bytes (for up to 6 concurrently pressed keys)
|
||||
REPORT_SIZE(1),
|
||||
0x08,
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00,
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x65, // 101 keys
|
||||
USAGE_MINIMUM(1),
|
||||
0x00,
|
||||
USAGE_MAXIMUM(1),
|
||||
0x65,
|
||||
HIDINPUT(1),
|
||||
0x00, // Data, Array, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
|
||||
REPORT_SIZE(1),
|
||||
0x01,
|
||||
USAGE_PAGE(1),
|
||||
0x08, // LEDs
|
||||
USAGE_MINIMUM(1),
|
||||
0x01, // Num Lock
|
||||
USAGE_MAXIMUM(1),
|
||||
0x05, // Kana
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00,
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x01,
|
||||
HIDOUTPUT(1),
|
||||
0x02, // Data, Var, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x01, // 3 bits (Padding)
|
||||
REPORT_SIZE(1),
|
||||
0x03,
|
||||
HIDOUTPUT(1),
|
||||
0x01, // Const, Array, Abs
|
||||
END_COLLECTION(0) // End application collection
|
||||
};
|
||||
|
||||
// Déclarations HID Bluetooth
|
||||
BLEHIDDevice *hid;
|
||||
BLECharacteristic *input_keyboard;
|
||||
BLECharacteristic *input_mouse;
|
||||
BLECharacteristic *output_keyboard;
|
||||
bool isBleConnected = false;
|
||||
|
||||
const InputReport NO_KEY_PRESSED = {};
|
||||
|
||||
// Callbacks pour la connexion BLE
|
||||
class BleHIDCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *server) {
|
||||
isBleConnected = true;
|
||||
|
||||
BLE2902 *cccDescKeyboard = (BLE2902 *)input_keyboard->getDescriptorByUUID(
|
||||
BLEUUID((uint16_t)0x2902));
|
||||
cccDescKeyboard->setNotifications(true);
|
||||
|
||||
BLE2902 *cccDescMouse =
|
||||
(BLE2902 *)input_mouse->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||
cccDescMouse->setNotifications(true);
|
||||
|
||||
Serial.println("Client connecté au clavier et souris HID Bluetooth.");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer *server) {
|
||||
isBleConnected = false;
|
||||
|
||||
BLE2902 *cccDescKeyboard = (BLE2902 *)input_keyboard->getDescriptorByUUID(
|
||||
BLEUUID((uint16_t)0x2902));
|
||||
cccDescKeyboard->setNotifications(false);
|
||||
|
||||
BLE2902 *cccDescMouse =
|
||||
(BLE2902 *)input_mouse->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||
cccDescMouse->setNotifications(false);
|
||||
|
||||
Serial.println("Client déconnecté du clavier et souris HID Bluetooth.");
|
||||
}
|
||||
};
|
||||
|
||||
// Callbacks pour les LEDs (Num Lock, Caps Lock, etc.)
|
||||
class OutputCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *characteristic) {
|
||||
uint8_t *data = characteristic->getData();
|
||||
Serial.print("LED state (Bluetooth): ");
|
||||
Serial.println(*data, HEX);
|
||||
|
||||
// Synchronisation des états des LEDs
|
||||
//deviceState.led_num = (*data & 0x01) != 0; // Num Lock
|
||||
//deviceState.led_caps = (*data & 0x02) != 0; // Caps Lock
|
||||
|
||||
// Suppression de la réactivation automatique de Caps Lock
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps,
|
||||
deviceState.led_scroll);
|
||||
|
||||
Serial.print("bluetooth LED Num Lock : ");
|
||||
Serial.println(deviceState.led_num ? "Allumée" : "Éteinte");
|
||||
Serial.print("bluetooth LED Caps Lock : ");
|
||||
Serial.println(deviceState.led_caps ? "Allumée" : "Éteinte");
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[1] = {static_cast<uint8_t>((deviceState.led_caps << 1) |
|
||||
deviceState.led_num)};
|
||||
output_keyboard->setValue(buf, sizeof(buf));
|
||||
output_keyboard->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
void bluetoothTask(void *) {
|
||||
BLEDevice::init("Apple ADB Ressurector");
|
||||
BLEServer *server = BLEDevice::createServer();
|
||||
server->setCallbacks(new BleHIDCallbacks());
|
||||
|
||||
hid = new BLEHIDDevice(server);
|
||||
input_keyboard = hid->inputReport(1); // Report ID 1 pour le clavier
|
||||
input_mouse = hid->inputReport(2); // Report ID 2 pour la souris
|
||||
output_keyboard = hid->outputReport(1); // Report ID 1 pour les LEDs clavier
|
||||
output_keyboard->setCallbacks(new OutputCallbacks());
|
||||
|
||||
hid->manufacturer()->setValue("Maker Community");
|
||||
hid->pnp(0x02, 0xe502, 0xa111, 0x0210);
|
||||
hid->hidInfo(0x00, 0x02);
|
||||
|
||||
BLESecurity *security = new BLESecurity();
|
||||
security->setAuthenticationMode(ESP_LE_AUTH_BOND);
|
||||
|
||||
// Rapport HID pour clavier et souris
|
||||
hid->reportMap((uint8_t *)REPORT_MAP, sizeof(REPORT_MAP));
|
||||
hid->startServices();
|
||||
|
||||
BLEAdvertising *advertising = server->getAdvertising();
|
||||
advertising->setAppearance(HID_KEYBOARD);
|
||||
advertising->addServiceUUID(hid->hidService()->getUUID());
|
||||
advertising->start();
|
||||
|
||||
Serial.println("Bluetooth HID prêt.");
|
||||
delay(portMAX_DELAY);
|
||||
}
|
||||
|
||||
void setupBluetoothTask() {
|
||||
xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
// Instances des modules H7A3
|
||||
CVModulationH7A3 cv_modulation;
|
||||
DACCVManagerH7A3 dac_manager;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialise un périphérique ADB.
|
||||
* @param address Adresse du périphérique.
|
||||
* @param handler_id ID du gestionnaire.
|
||||
* @return true si le périphérique est initialisé avec succès, false sinon.
|
||||
*
|
||||
* @param addr Adresse du périphérique.
|
||||
* @param handler_id Identifiant du gestionnaire de périphérique.
|
||||
* @return true si l'initialisation a réussi, false sinon.
|
||||
*/
|
||||
bool initializeDevice(uint8_t address, uint8_t handler_id) {
|
||||
// Utilise les stubs H7A3 temporaires
|
||||
bool success = ADBStubs::adb_listen_stub(address, handler_id);
|
||||
delay(100);
|
||||
auto keys = ADBStubs::adb_read_register_stub(address, 0);
|
||||
return success && (keys != 0);
|
||||
bool initializeDevice(uint8_t addr, uint8_t handler_id) {
|
||||
bool error = false;
|
||||
adb_data<adb_register3> reg3 = {0}, mask = {0};
|
||||
reg3.data.device_handler_id = handler_id;
|
||||
mask.data.device_handler_id = 0xFF;
|
||||
return adbDevices.deviceUpdateRegister3(addr, reg3, mask.raw, &error) &&
|
||||
!error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configuration initiale du système H7A3.
|
||||
* @brief Fonction d'initialisation du programme.
|
||||
*/
|
||||
void setup() {
|
||||
pinMode(LED_PIN, OUTPUT); // Configuration de la pin LED
|
||||
digitalWrite(LED_PIN, LOW); // État initial de la LED
|
||||
|
||||
Serial.begin(115200); // Vitesse H7A3 optimisée
|
||||
Serial.println("=== Apple ADB Ressurector - STM32H7A3 Enhanced ===");
|
||||
Serial.println("Version 2.0.0 - High Performance Edition");
|
||||
Serial.println("Initialisation du système H7A3...");
|
||||
Serial.begin(115200);
|
||||
Serial.println("Initialisation du programme...");
|
||||
|
||||
// Activation des caches L1 pour performance maximale H7A3
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
SCB_EnableICache();
|
||||
SCB_EnableDCache();
|
||||
Serial.println("Caches L1 I+D activés (H7A3)");
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
setupBluetoothTask(); // Lancer la tâche Bluetooth
|
||||
#endif
|
||||
|
||||
// Initialisation HID
|
||||
hid_keyboard_init();
|
||||
hid_mouse_init();
|
||||
Serial.println("HID initialisé (H7A3).");
|
||||
Serial.println("HID initialisé.");
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Initialisation des modules H7A3
|
||||
dac_manager.begin();
|
||||
Serial.println("Système DAC/CV H7A3 initialisé.");
|
||||
|
||||
cv_modulation.begin();
|
||||
Serial.println("Système de modulation CV H7A3 initialisé (1000Hz).");
|
||||
#endif
|
||||
|
||||
// Initialisation ADB
|
||||
adb.init(ADB_PIN, true);
|
||||
Serial.println("Bus ADB initialisé.");
|
||||
|
||||
delay(1000);
|
||||
|
||||
// Détection des périphériques
|
||||
deviceState.keyboard_present = initializeDevice(ADBKey::Address::KEYBOARD, 0x03);
|
||||
deviceState.keyboard_present =
|
||||
initializeDevice(ADBKey::Address::KEYBOARD, 0x03);
|
||||
Serial.print("Clavier détecté : ");
|
||||
Serial.println(deviceState.keyboard_present ? "Oui" : "Non");
|
||||
|
||||
@@ -167,25 +298,16 @@ void setup() {
|
||||
|
||||
digitalWrite(LED_PIN, HIGH); // Allumer la LED après l'initialisation
|
||||
|
||||
// Initialisation des LEDs clavier
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps, deviceState.led_scroll);
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps,
|
||||
deviceState.led_scroll);
|
||||
Serial.println("LEDs initialisées.");
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Message d'accueil pour les commandes debug H7A3
|
||||
Serial.println("\n=== DEBUG CV/FM H7A3 DISPONIBLE ===");
|
||||
Serial.println("Tapez 'help' dans le moniteur série pour les commandes debug");
|
||||
Serial.println("Performance 6.7x supérieure au F303!");
|
||||
Serial.println("===================================\n");
|
||||
#endif
|
||||
|
||||
Serial.println("Système H7A3 prêt !");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les événements du clavier avec logique de base.
|
||||
* @brief Gère les événements du clavier.
|
||||
*/
|
||||
void handleKeyboard() {
|
||||
static hid_key_report key_report = {0};
|
||||
bool error = false;
|
||||
|
||||
auto key_press = adbDevices.keyboardReadKeyPress(&error);
|
||||
@@ -193,23 +315,79 @@ void handleKeyboard() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Logique simplifiée pour démarrage H7A3
|
||||
static hid_key_report_kbd key_report = {0};
|
||||
|
||||
// Configuration basique du rapport
|
||||
bool report_changed = hid_keyboard_set_keys_from_adb_register(&key_report, key_press);
|
||||
|
||||
if (report_changed) {
|
||||
Serial.println("Rapport clavier H7A3 mis à jour.");
|
||||
hid_keyboard_send_report(&key_report);
|
||||
bool report_changed =
|
||||
hid_keyboard_set_keys_from_adb_register(&key_report, key_press);
|
||||
|
||||
// Gestion de Caps Lock
|
||||
if (key_press.data.key0 == ADBKey::KeyCode::CAPS_LOCK ||
|
||||
key_press.data.key1 == ADBKey::KeyCode::CAPS_LOCK) {
|
||||
bool is_pressed = (key_press.data.key0 == ADBKey::KeyCode::CAPS_LOCK &&
|
||||
!key_press.data.released0) ||
|
||||
(key_press.data.key1 == ADBKey::KeyCode::CAPS_LOCK &&
|
||||
!key_press.data.released1);
|
||||
|
||||
if (is_pressed) {
|
||||
// Activer Caps Lock
|
||||
deviceState.led_caps = true;
|
||||
Serial.println("Caps Lock activé.");
|
||||
report_changed = true;
|
||||
|
||||
// Mise à jour des LEDs
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps, deviceState.led_scroll);
|
||||
// Envoyer un événement de pression pour Caps Lock
|
||||
key_report.keys[0] = ADBKey::KeyCode::CAPS_LOCK;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de pression de Caps Lock envoyé.");
|
||||
|
||||
delay(100); // Attendre un court instant pour éviter les rebonds
|
||||
// Envoyer un événement de relâchement pour Caps Lock
|
||||
key_report.keys[0] = 0;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de relâchement de Caps Lock envoyé.");
|
||||
} else {
|
||||
// Désactiver Caps Lock au relâchement
|
||||
deviceState.led_caps = false;
|
||||
Serial.println("Caps Lock désactivé.");
|
||||
report_changed = true;
|
||||
|
||||
// Envoyer un événement de pression pour Caps Lock
|
||||
key_report.keys[0] = ADBKey::KeyCode::CAPS_LOCK;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de pression de Caps Lock envoyé.");
|
||||
delay(100); // Attendre un court instant pour éviter les rebonds
|
||||
// Envoyer un événement de relâchement pour Caps Lock
|
||||
key_report.keys[0] = 0;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de relâchement de Caps Lock envoyé.");
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion de Num Lock
|
||||
if ((key_press.data.key0 == ADBKey::KeyCode::NUM_LOCK &&
|
||||
!key_press.data.released0) ||
|
||||
(key_press.data.key1 == ADBKey::KeyCode::NUM_LOCK &&
|
||||
!key_press.data.released1)) {
|
||||
deviceState.led_num = !deviceState.led_num;
|
||||
Serial.print("Num Lock LED (ADB) : ");
|
||||
Serial.println(deviceState.led_num ? "Allumée" : "Éteinte");
|
||||
report_changed = true;
|
||||
}
|
||||
|
||||
if (report_changed) {
|
||||
Serial.println("Rapport clavier mis à jour.");
|
||||
hid_keyboard_send_report(&key_report);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[1] = {static_cast<uint8_t>((deviceState.led_caps << 1) |
|
||||
deviceState.led_num)};
|
||||
output_keyboard->setValue(buf, sizeof(buf));
|
||||
output_keyboard->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les événements de la souris avec contrôle CV/GATE H7A3.
|
||||
* @brief Gère les événements de la souris.
|
||||
*/
|
||||
void handleMouse() {
|
||||
bool error = false;
|
||||
@@ -219,135 +397,47 @@ void handleMouse() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Conversion des axes souris ADB (fonction du framework)
|
||||
int8_t mouse_x = adbMouseConvertAxis(mouse_data.data.x_offset);
|
||||
int8_t mouse_y = adbMouseConvertAxis(mouse_data.data.y_offset);
|
||||
|
||||
Serial.print("Mouvement souris H7A3 - X: ");
|
||||
Serial.print("Mouvement souris - X: ");
|
||||
Serial.print(mouse_x);
|
||||
Serial.print(", Y: ");
|
||||
Serial.println(mouse_y);
|
||||
|
||||
// Contrôle CV avec la souris (STM32H7A3 uniquement)
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Enregistrer l'activité souris
|
||||
if (mouse_x != 0 || mouse_y != 0) {
|
||||
last_mouse_activity = millis();
|
||||
|
||||
// Mise à jour des valeurs CV cumulatives H7A3
|
||||
dac_cv_update_cumulative(mouse_x, mouse_y);
|
||||
|
||||
// Mise à jour de la modulation FM avec debug intégré H7A3
|
||||
cv_modulation_update_fm(mouse_x, mouse_y);
|
||||
// Envoyer le rapport HID pour la souris
|
||||
hid_mouse_send_report(mouse_data.data.button ? 0 : 1, mouse_x, mouse_y);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[4] = {
|
||||
static_cast<uint8_t>(mouse_data.data.button ? 0 : 1), // Bouton
|
||||
static_cast<uint8_t>(mouse_x), // Déplacement X
|
||||
static_cast<uint8_t>(mouse_y), // Déplacement Y
|
||||
0 // Molette (non utilisée)
|
||||
};
|
||||
input_mouse->setValue(buf, sizeof(buf));
|
||||
input_mouse->notify();
|
||||
Serial.println("Rapport HID souris envoyé via Bluetooth.");
|
||||
}
|
||||
|
||||
// Gestion des boutons souris et contrôle GATE H7A3
|
||||
bool button_state = mouse_data.data.button;
|
||||
static bool last_button_state = false;
|
||||
|
||||
if (button_state != last_button_state) {
|
||||
dac_cv_set_gate(button_state);
|
||||
Serial.print("GATE H7A3 : ");
|
||||
Serial.println(button_state ? "ACTIVÉ" : "DÉSACTIVÉ");
|
||||
last_button_state = button_state;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Envoi du rapport souris H7A3 avec interface simple
|
||||
hid_mouse_send_report(mouse_data.data.button, mouse_x, mouse_y);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les commandes série pour le debug H7A3.
|
||||
*/
|
||||
void handleSerialCommands() {
|
||||
if (Serial.available() > 0) {
|
||||
String command = Serial.readStringUntil('\n');
|
||||
command.trim();
|
||||
command.toLowerCase();
|
||||
|
||||
if (command == "help") {
|
||||
Serial.println("\n=== Commandes Debug H7A3 ===");
|
||||
Serial.println("help - Afficher cette aide");
|
||||
Serial.println("status - État du système H7A3");
|
||||
Serial.println("perf - Statistiques de performance");
|
||||
Serial.println("debug - Informations debug CV");
|
||||
Serial.println("reset - Reset compteurs performance");
|
||||
Serial.println("test - Test système H7A3");
|
||||
Serial.println("============================");
|
||||
}
|
||||
else if (command == "status") {
|
||||
Serial.println("\n=== État Système H7A3 ===");
|
||||
Serial.print("CPU Frequency: "); Serial.print(SystemCoreClock); Serial.println(" Hz");
|
||||
Serial.print("Uptime: "); Serial.print(millis()); Serial.println(" ms");
|
||||
Serial.print("Clavier: "); Serial.println(deviceState.keyboard_present ? "Connecté" : "Absent");
|
||||
Serial.print("Souris: "); Serial.println(deviceState.mouse_present ? "Connectée" : "Absente");
|
||||
Serial.print("Dernière activité souris: "); Serial.print(last_mouse_activity); Serial.println(" ms");
|
||||
Serial.print("L1 I-Cache: "); Serial.println((SCB->CCR & SCB_CCR_IC_Msk) ? "ACTIVÉ" : "DÉSACTIVÉ");
|
||||
Serial.print("L1 D-Cache: "); Serial.println((SCB->CCR & SCB_CCR_DC_Msk) ? "ACTIVÉ" : "DÉSACTIVÉ");
|
||||
Serial.println("========================");
|
||||
}
|
||||
else if (command == "perf") {
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
cv_modulation.print_performance_stats();
|
||||
#endif
|
||||
}
|
||||
else if (command == "debug") {
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
cv_modulation.print_debug_info();
|
||||
#endif
|
||||
}
|
||||
else if (command == "reset") {
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
Serial.println("Reset compteurs performance H7A3...");
|
||||
cv_modulation.reset_performance_counters();
|
||||
Serial.println("Reset terminé.");
|
||||
#endif
|
||||
}
|
||||
else if (command == "test") {
|
||||
Serial.println("Test système H7A3...");
|
||||
Serial.println("✓ CPU H7A3 @ 480MHz OK");
|
||||
Serial.println("✓ Caches L1 OK");
|
||||
Serial.println("✓ FPU double précision OK");
|
||||
Serial.println("✓ Timers avancés OK");
|
||||
Serial.println("Test H7A3 réussi !");
|
||||
}
|
||||
else {
|
||||
Serial.println("Commande inconnue. Tapez 'help' pour l'aide.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Boucle principale optimisée H7A3.
|
||||
* @brief Boucle principale du programme.
|
||||
*/
|
||||
void loop() {
|
||||
// Gestion des commandes série pour le debug H7A3
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
handleSerialCommands();
|
||||
#endif
|
||||
|
||||
if (deviceState.keyboard_present) {
|
||||
// Serial.println("Gestion du clavier...");
|
||||
handleKeyboard();
|
||||
delay(POLL_DELAY);
|
||||
}
|
||||
|
||||
if (deviceState.mouse_present) {
|
||||
// Serial.println("Gestion de la souris...");
|
||||
handleMouse();
|
||||
delay(POLL_DELAY);
|
||||
}
|
||||
|
||||
// Traitement continu des modulations CV H7A3 (1000Hz)
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
cv_modulation.process_modulations();
|
||||
|
||||
// Vérifier le timeout et réinitialiser les CV si nécessaire
|
||||
if (last_mouse_activity > 0 && (millis() - last_mouse_activity) > CV_RESET_TIMEOUT) {
|
||||
dac_manager.reset_cv_values();
|
||||
last_mouse_activity = 0; // Éviter de répéter la réinitialisation
|
||||
Serial.println("CV réinitialisées (timeout inactivité souris H7A3).");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,911 +0,0 @@
|
||||
/**
|
||||
* @file main.cpp
|
||||
* @brief Main file for the Apple ADB Ressurector project.
|
||||
|
||||
* Inspiré et basé sur le travail initial de Szymon Łopaciuk
|
||||
https://github.com/szymonlopaciuk/stm32-adb2usb
|
||||
* @credits Szymon Łopaciuk
|
||||
* @author Clément SAILLANT
|
||||
* @date 2025
|
||||
* Dépôt actuel : https://github.com/electron-rare/Apple-ADB-Ressurector
|
||||
* @license GNU GPL v3
|
||||
*
|
||||
* Fonctionnalités CV/GATE pour STM32F3 :
|
||||
* - Mouvement souris X/Y : Contrôle des valeurs CV1/CV2 (accumulation)
|
||||
* - Bouton souris : Contrôle du signal GATE (ON/OFF)
|
||||
* - Reset automatique des CV après 2 secondes d'inactivité souris
|
||||
* - Modulation continue : portamento, vibrato, et modulation FM
|
||||
*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
#include "hid_keyboard.h"
|
||||
#include "hid_mouse.h"
|
||||
#include <ADB.h>
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#include "dac_cv_manager.h" // Gestionnaire centralisé DAC/CV/GATE
|
||||
#include "cv_modulation.h" // Fonctions de modulation CV avancée
|
||||
#endif
|
||||
|
||||
#define POLL_DELAY 1
|
||||
// Définition de la pin ADB selon la plateforme
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define ADB_PIN 2
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#define ADB_PIN PB4
|
||||
#endif
|
||||
|
||||
// Définition de la pin LED selon la plateforme
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#define LED_PIN 4 // Pin pour ESP32 (Devkit Wemos)
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
#define LED_PIN PC13 // Pin pour STM32
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @struct DeviceState
|
||||
* @brief Structure pour regrouper les états des périphériques.
|
||||
*/
|
||||
struct DeviceState {
|
||||
bool apple_extended_detected =
|
||||
false; /**< Détection du clavier Apple étendu. */
|
||||
bool keyboard_present = false; /**< Présence d'un clavier. */
|
||||
bool mouse_present = false; /**< Présence d'une souris. */
|
||||
bool led_num = true; /**< État de la LED Num Lock (actif par défaut). */
|
||||
bool led_caps = false; /**< État de la LED Caps Lock. */
|
||||
bool led_scroll = false; /**< État de la LED Scroll Lock. */
|
||||
bool led_power = false; /**< État de la LED Power. */
|
||||
};
|
||||
|
||||
// Instances globales
|
||||
ADB adb(ADB_PIN); /**< Instance du bus ADB. */
|
||||
ADBDevices adbDevices(adb); /**< Gestionnaire des périphériques ADB. */
|
||||
DeviceState deviceState; /**< État des périphériques. */
|
||||
bool caps_lock_pressed = false; /**< État de la touche Caps Lock. */
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Variables pour le contrôle CV par la souris
|
||||
unsigned long last_mouse_activity = 0; /**< Timestamp de la dernière activité souris. */
|
||||
const unsigned long CV_RESET_TIMEOUT = 20000; /**< Timeout pour réinitialiser les CV (ms). */
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEHIDDevice.h>
|
||||
#include <HIDKeyboardTypes.h>
|
||||
#include <HIDTypes.h>
|
||||
|
||||
// Déclaration de la structure InputReport
|
||||
struct InputReport {
|
||||
uint8_t modifiers; // bitmask: CTRL = 1, SHIFT = 2, ALT = 4
|
||||
uint8_t reserved; // doit être 0
|
||||
uint8_t pressedKeys[6]; // jusqu'à six touches pressées simultanément
|
||||
};
|
||||
|
||||
// The report map describes the HID device (a keyboard in this case) and
|
||||
// the messages (reports in HID terms) sent and received.
|
||||
static const uint8_t REPORT_MAP[] = {
|
||||
USAGE_PAGE(1),
|
||||
0x01, // Generic Desktop Controls
|
||||
USAGE(1),
|
||||
0x06, // Keyboard
|
||||
COLLECTION(1),
|
||||
0x01, // Application
|
||||
REPORT_ID(1),
|
||||
0x01, // Report ID (1)
|
||||
USAGE_PAGE(1),
|
||||
0x07, // Keyboard/Keypad
|
||||
USAGE_MINIMUM(1),
|
||||
0xE0, // Keyboard Left Control
|
||||
USAGE_MAXIMUM(1),
|
||||
0xE7, // Keyboard Right Control
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00, // Each bit is either 0 or 1
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x01,
|
||||
REPORT_COUNT(1),
|
||||
0x08, // 8 bits for the modifier keys
|
||||
REPORT_SIZE(1),
|
||||
0x01,
|
||||
HIDINPUT(1),
|
||||
0x02, // Data, Var, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x01, // 1 byte (unused)
|
||||
REPORT_SIZE(1),
|
||||
0x08,
|
||||
HIDINPUT(1),
|
||||
0x01, // Const, Array, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x06, // 6 bytes (for up to 6 concurrently pressed keys)
|
||||
REPORT_SIZE(1),
|
||||
0x08,
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00,
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x65, // 101 keys
|
||||
USAGE_MINIMUM(1),
|
||||
0x00,
|
||||
USAGE_MAXIMUM(1),
|
||||
0x65,
|
||||
HIDINPUT(1),
|
||||
0x00, // Data, Array, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x05, // 5 bits (Num lock, Caps lock, Scroll lock, Compose, Kana)
|
||||
REPORT_SIZE(1),
|
||||
0x01,
|
||||
USAGE_PAGE(1),
|
||||
0x08, // LEDs
|
||||
USAGE_MINIMUM(1),
|
||||
0x01, // Num Lock
|
||||
USAGE_MAXIMUM(1),
|
||||
0x05, // Kana
|
||||
LOGICAL_MINIMUM(1),
|
||||
0x00,
|
||||
LOGICAL_MAXIMUM(1),
|
||||
0x01,
|
||||
HIDOUTPUT(1),
|
||||
0x02, // Data, Var, Abs
|
||||
REPORT_COUNT(1),
|
||||
0x01, // 3 bits (Padding)
|
||||
REPORT_SIZE(1),
|
||||
0x03,
|
||||
HIDOUTPUT(1),
|
||||
0x01, // Const, Array, Abs
|
||||
END_COLLECTION(0) // End application collection
|
||||
};
|
||||
|
||||
// Déclarations HID Bluetooth
|
||||
BLEHIDDevice *hid;
|
||||
BLECharacteristic *input_keyboard;
|
||||
BLECharacteristic *input_mouse;
|
||||
BLECharacteristic *output_keyboard;
|
||||
bool isBleConnected = false;
|
||||
|
||||
const InputReport NO_KEY_PRESSED = {};
|
||||
|
||||
// Callbacks pour la connexion BLE
|
||||
class BleHIDCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *server) {
|
||||
isBleConnected = true;
|
||||
|
||||
BLE2902 *cccDescKeyboard = (BLE2902 *)input_keyboard->getDescriptorByUUID(
|
||||
BLEUUID((uint16_t)0x2902));
|
||||
cccDescKeyboard->setNotifications(true);
|
||||
|
||||
BLE2902 *cccDescMouse =
|
||||
(BLE2902 *)input_mouse->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||
cccDescMouse->setNotifications(true);
|
||||
|
||||
Serial.println("Client connecté au clavier et souris HID Bluetooth.");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer *server) {
|
||||
isBleConnected = false;
|
||||
|
||||
BLE2902 *cccDescKeyboard = (BLE2902 *)input_keyboard->getDescriptorByUUID(
|
||||
BLEUUID((uint16_t)0x2902));
|
||||
cccDescKeyboard->setNotifications(false);
|
||||
|
||||
BLE2902 *cccDescMouse =
|
||||
(BLE2902 *)input_mouse->getDescriptorByUUID(BLEUUID((uint16_t)0x2902));
|
||||
cccDescMouse->setNotifications(false);
|
||||
|
||||
Serial.println("Client déconnecté du clavier et souris HID Bluetooth.");
|
||||
}
|
||||
};
|
||||
|
||||
// Callbacks pour les LEDs (Num Lock, Caps Lock, etc.)
|
||||
class OutputCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *characteristic) {
|
||||
uint8_t *data = characteristic->getData();
|
||||
Serial.print("LED state (Bluetooth): ");
|
||||
Serial.println(*data, HEX);
|
||||
|
||||
// Synchronisation des états des LEDs
|
||||
//deviceState.led_num = (*data & 0x01) != 0; // Num Lock
|
||||
//deviceState.led_caps = (*data & 0x02) != 0; // Caps Lock
|
||||
|
||||
// Suppression de la réactivation automatique de Caps Lock
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps,
|
||||
deviceState.led_scroll);
|
||||
|
||||
Serial.print("bluetooth LED Num Lock : ");
|
||||
Serial.println(deviceState.led_num ? "Allumée" : "Éteinte");
|
||||
Serial.print("bluetooth LED Caps Lock : ");
|
||||
Serial.println(deviceState.led_caps ? "Allumée" : "Éteinte");
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[1] = {static_cast<uint8_t>((deviceState.led_caps << 1) |
|
||||
deviceState.led_num)};
|
||||
output_keyboard->setValue(buf, sizeof(buf));
|
||||
output_keyboard->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
void bluetoothTask(void *) {
|
||||
BLEDevice::init("Apple ADB Ressurector");
|
||||
BLEServer *server = BLEDevice::createServer();
|
||||
server->setCallbacks(new BleHIDCallbacks());
|
||||
|
||||
hid = new BLEHIDDevice(server);
|
||||
input_keyboard = hid->inputReport(1); // Report ID 1 pour le clavier
|
||||
input_mouse = hid->inputReport(2); // Report ID 2 pour la souris
|
||||
output_keyboard = hid->outputReport(1); // Report ID 1 pour les LEDs clavier
|
||||
output_keyboard->setCallbacks(new OutputCallbacks());
|
||||
|
||||
hid->manufacturer()->setValue("Maker Community");
|
||||
hid->pnp(0x02, 0xe502, 0xa111, 0x0210);
|
||||
hid->hidInfo(0x00, 0x02);
|
||||
|
||||
BLESecurity *security = new BLESecurity();
|
||||
security->setAuthenticationMode(ESP_LE_AUTH_BOND);
|
||||
|
||||
// Rapport HID pour clavier et souris
|
||||
hid->reportMap((uint8_t *)REPORT_MAP, sizeof(REPORT_MAP));
|
||||
hid->startServices();
|
||||
|
||||
BLEAdvertising *advertising = server->getAdvertising();
|
||||
advertising->setAppearance(HID_KEYBOARD);
|
||||
advertising->addServiceUUID(hid->hidService()->getUUID());
|
||||
advertising->start();
|
||||
|
||||
Serial.println("Bluetooth HID prêt.");
|
||||
delay(portMAX_DELAY);
|
||||
}
|
||||
|
||||
void setupBluetoothTask() {
|
||||
xTaskCreate(bluetoothTask, "bluetooth", 20000, NULL, 5, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialise un périphérique ADB.
|
||||
*
|
||||
* @param addr Adresse du périphérique.
|
||||
* @param handler_id Identifiant du gestionnaire de périphérique.
|
||||
* @return true si l'initialisation a réussi, false sinon.
|
||||
*/
|
||||
bool initializeDevice(uint8_t addr, uint8_t handler_id) {
|
||||
bool error = false;
|
||||
adb_data<adb_register3> reg3 = {0}, mask = {0};
|
||||
reg3.data.device_handler_id = handler_id;
|
||||
mask.data.device_handler_id = 0xFF;
|
||||
return adbDevices.deviceUpdateRegister3(addr, reg3, mask.raw, &error) &&
|
||||
!error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fonction d'initialisation du programme.
|
||||
*/
|
||||
void setup() {
|
||||
pinMode(LED_PIN, OUTPUT); // Configuration de la pin LED
|
||||
digitalWrite(LED_PIN, LOW); // État initial de la LED
|
||||
|
||||
Serial.begin(9600);
|
||||
Serial.println("Initialisation du programme...");
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
setupBluetoothTask(); // Lancer la tâche Bluetooth
|
||||
#endif
|
||||
|
||||
hid_keyboard_init();
|
||||
hid_mouse_init();
|
||||
Serial.println("HID initialisé.");
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
dac_cv_manager_init();
|
||||
Serial.println("Système DAC/CV initialisé.");
|
||||
|
||||
cv_modulation_init();
|
||||
Serial.println("Système de modulation CV initialisé.");
|
||||
#endif
|
||||
|
||||
adb.init(ADB_PIN, true);
|
||||
Serial.println("Bus ADB initialisé.");
|
||||
|
||||
delay(1000);
|
||||
|
||||
deviceState.keyboard_present =
|
||||
initializeDevice(ADBKey::Address::KEYBOARD, 0x03);
|
||||
Serial.print("Clavier détecté : ");
|
||||
Serial.println(deviceState.keyboard_present ? "Oui" : "Non");
|
||||
|
||||
deviceState.mouse_present = initializeDevice(ADBKey::Address::MOUSE, 0x02);
|
||||
Serial.print("Souris détectée : ");
|
||||
Serial.println(deviceState.mouse_present ? "Oui" : "Non");
|
||||
|
||||
digitalWrite(LED_PIN, HIGH); // Allumer la LED après l'initialisation
|
||||
|
||||
adbDevices.keyboardWriteLEDs(deviceState.led_num, deviceState.led_caps,
|
||||
deviceState.led_scroll);
|
||||
Serial.println("LEDs initialisées.");
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Message d'accueil pour les commandes debug
|
||||
Serial.println("\n=== DEBUG CV/FM DISPONIBLE ===");
|
||||
Serial.println("Tapez 'help' dans le moniteur série pour les commandes debug");
|
||||
Serial.println("==============================\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les événements du clavier.
|
||||
*/
|
||||
void handleKeyboard() {
|
||||
static hid_key_report key_report = {0};
|
||||
bool error = false;
|
||||
|
||||
auto key_press = adbDevices.keyboardReadKeyPress(&error);
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool report_changed =
|
||||
hid_keyboard_set_keys_from_adb_register(&key_report, key_press);
|
||||
|
||||
// Gestion de Caps Lock
|
||||
if (key_press.data.key0 == ADBKey::KeyCode::CAPS_LOCK ||
|
||||
key_press.data.key1 == ADBKey::KeyCode::CAPS_LOCK) {
|
||||
bool is_pressed = (key_press.data.key0 == ADBKey::KeyCode::CAPS_LOCK &&
|
||||
!key_press.data.released0) ||
|
||||
(key_press.data.key1 == ADBKey::KeyCode::CAPS_LOCK &&
|
||||
!key_press.data.released1);
|
||||
|
||||
if (is_pressed) {
|
||||
// Activer Caps Lock
|
||||
deviceState.led_caps = true;
|
||||
Serial.println("Caps Lock activé.");
|
||||
report_changed = true;
|
||||
|
||||
// Envoyer un événement de pression pour Caps Lock
|
||||
key_report.keys[0] = ADBKey::KeyCode::CAPS_LOCK;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de pression de Caps Lock envoyé.");
|
||||
|
||||
delay(100); // Attendre un court instant pour éviter les rebonds
|
||||
// Envoyer un événement de relâchement pour Caps Lock
|
||||
key_report.keys[0] = 0;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de relâchement de Caps Lock envoyé.");
|
||||
} else {
|
||||
// Désactiver Caps Lock au relâchement
|
||||
deviceState.led_caps = false;
|
||||
Serial.println("Caps Lock désactivé.");
|
||||
report_changed = true;
|
||||
|
||||
// Envoyer un événement de pression pour Caps Lock
|
||||
key_report.keys[0] = ADBKey::KeyCode::CAPS_LOCK;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de pression de Caps Lock envoyé.");
|
||||
delay(100); // Attendre un court instant pour éviter les rebonds
|
||||
// Envoyer un événement de relâchement pour Caps Lock
|
||||
key_report.keys[0] = 0;
|
||||
hid_keyboard_send_report(&key_report);
|
||||
Serial.println("Événement de relâchement de Caps Lock envoyé.");
|
||||
}
|
||||
}
|
||||
|
||||
// Gestion de Num Lock
|
||||
if ((key_press.data.key0 == ADBKey::KeyCode::NUM_LOCK &&
|
||||
!key_press.data.released0) ||
|
||||
(key_press.data.key1 == ADBKey::KeyCode::NUM_LOCK &&
|
||||
!key_press.data.released1)) {
|
||||
deviceState.led_num = !deviceState.led_num;
|
||||
Serial.print("Num Lock LED (ADB) : ");
|
||||
Serial.println(deviceState.led_num ? "Allumée" : "Éteinte");
|
||||
report_changed = true;
|
||||
}
|
||||
|
||||
if (report_changed) {
|
||||
Serial.println("Rapport clavier mis à jour.");
|
||||
hid_keyboard_send_report(&key_report);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[1] = {static_cast<uint8_t>((deviceState.led_caps << 1) |
|
||||
deviceState.led_num)};
|
||||
output_keyboard->setValue(buf, sizeof(buf));
|
||||
output_keyboard->notify();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les événements de la souris.
|
||||
*/
|
||||
void handleMouse() {
|
||||
bool error = false;
|
||||
|
||||
auto mouse_data = adbDevices.mouseReadData(&error);
|
||||
if (error || mouse_data.raw == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int8_t mouse_x = adbMouseConvertAxis(mouse_data.data.x_offset);
|
||||
int8_t mouse_y = adbMouseConvertAxis(mouse_data.data.y_offset);
|
||||
|
||||
Serial.print("Mouvement souris - X: ");
|
||||
Serial.print(mouse_x);
|
||||
Serial.print(", Y: ");
|
||||
Serial.println(mouse_y);
|
||||
|
||||
// Contrôle CV avec la souris (STM32 uniquement)
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
// Enregistrer l'activité souris
|
||||
if (mouse_x != 0 || mouse_y != 0) {
|
||||
last_mouse_activity = millis();
|
||||
|
||||
// Mise à jour des valeurs CV cumulatives
|
||||
dac_cv_update_cumulative(mouse_x, mouse_y);
|
||||
|
||||
// Mise à jour de la modulation FM avec debug intégré
|
||||
cv_modulation_update_fm(mouse_x, mouse_y);
|
||||
|
||||
Serial.print("Valeurs CV mises à jour - CV1: ");
|
||||
Serial.print(dac_cv_get_cv1_value());
|
||||
Serial.print(", CV2: ");
|
||||
Serial.println(dac_cv_get_cv2_value());
|
||||
}
|
||||
|
||||
// Contrôle du signal GATE avec le bouton de la souris
|
||||
bool button_pressed = (mouse_data.data.button == 0); // ADB mouse: 0 = pressed, 1 = released
|
||||
dac_cv_set_gate(button_pressed);
|
||||
|
||||
if (button_pressed) {
|
||||
Serial.println("GATE: ON");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Envoyer le rapport HID pour la souris
|
||||
hid_mouse_send_report(mouse_data.data.button ? 0 : 1, mouse_x, mouse_y);
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
if (isBleConnected) {
|
||||
uint8_t buf[4] = {
|
||||
static_cast<uint8_t>(mouse_data.data.button ? 0 : 1), // Bouton
|
||||
static_cast<uint8_t>(mouse_x), // Déplacement X
|
||||
static_cast<uint8_t>(mouse_y), // Déplacement Y
|
||||
0 // Molette (non utilisée)
|
||||
};
|
||||
input_mouse->setValue(buf, sizeof(buf));
|
||||
input_mouse->notify();
|
||||
Serial.println("Rapport HID souris envoyé via Bluetooth.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
/**
|
||||
* @brief Affiche l'état actuel du système CV (pour débogage)
|
||||
*/
|
||||
void debugCVState() {
|
||||
uint16_t cv1, cv2;
|
||||
bool gate_state;
|
||||
dac_cv_get_state(&cv1, &cv2, &gate_state);
|
||||
|
||||
Serial.print("État CV - CV1: ");
|
||||
Serial.print(cv1);
|
||||
Serial.print(" (");
|
||||
Serial.print((float)cv1 * 3.3f / 4095.0f, 2);
|
||||
Serial.print("V), CV2: ");
|
||||
Serial.print(cv2);
|
||||
Serial.print(" (");
|
||||
Serial.print((float)cv2 * 3.3f / 4095.0f, 2);
|
||||
Serial.print("V), GATE: ");
|
||||
Serial.println(gate_state ? "HIGH" : "LOW");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active le debug détaillé de la modulation FM (pour diagnostique)
|
||||
*/
|
||||
void enableFMDebug() {
|
||||
cv_modulation_set_fm_debug(true);
|
||||
Serial.println("Debug FM activé. Utilisez la souris pour voir les détails.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Désactive le debug détaillé de la modulation FM
|
||||
*/
|
||||
void disableFMDebug() {
|
||||
cv_modulation_set_fm_debug(false);
|
||||
Serial.println("Debug FM désactivé.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche les statistiques de performance du debug FM
|
||||
*/
|
||||
void showFMPerformanceStats() {
|
||||
cv_modulation_debug_performance_stats();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche un rapport complet sur la modulation FM
|
||||
*/
|
||||
void showFMReport() {
|
||||
cv_modulation_debug_fm_report();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lance un test automatique de la modulation FM
|
||||
*/
|
||||
void testFMModulation(uint32_t duration_ms = 5000) {
|
||||
Serial.print("Lancement du test FM pour ");
|
||||
Serial.print(duration_ms);
|
||||
Serial.println("ms...");
|
||||
cv_modulation_test_fm(duration_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test automatique de toutes les fonctionnalités CV
|
||||
*/
|
||||
void runFullCVTest() {
|
||||
Serial.println("\n=== TEST COMPLET DU SYSTÈME CV ===");
|
||||
|
||||
// Test 1: Écriture directe des CV
|
||||
Serial.println("Test 1: Écriture directe des CV...");
|
||||
dac_cv_write_direct(1, 1024); // CV1 à ~0.8V
|
||||
dac_cv_write_direct(2, 2048); // CV2 à ~1.65V
|
||||
delay(1000);
|
||||
debugCVState();
|
||||
|
||||
// Test 2: Test du GATE
|
||||
Serial.println("Test 2: Signal GATE...");
|
||||
dac_cv_set_gate(true);
|
||||
delay(500);
|
||||
Serial.println("GATE activé");
|
||||
dac_cv_set_gate(false);
|
||||
delay(500);
|
||||
Serial.println("GATE désactivé");
|
||||
|
||||
// Test 3: Portamento
|
||||
Serial.println("Test 3: Portamento CV1...");
|
||||
cv_modulation_start_portamento(1, 3072); // Transition vers ~2.5V
|
||||
delay(2000);
|
||||
|
||||
// Test 4: Vibrato
|
||||
Serial.println("Test 4: Vibrato sur CV2...");
|
||||
cv_modulation_configure_vibrato(true, 3.0, 150);
|
||||
delay(3000);
|
||||
cv_modulation_set_vibrato(false);
|
||||
|
||||
// Test 5: Réinitialisation
|
||||
Serial.println("Test 5: Réinitialisation...");
|
||||
dac_cv_reset_values();
|
||||
delay(500);
|
||||
debugCVState();
|
||||
|
||||
Serial.println("Test complet terminé !\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calibrage et test des CV avec paliers
|
||||
*/
|
||||
void calibrateCVOutputs() {
|
||||
Serial.println("\n=== CALIBRAGE DES SORTIES CV ===");
|
||||
|
||||
uint16_t test_values[] = {0, 512, 1024, 1536, 2048, 2560, 3072, 3584, 4095};
|
||||
int num_values = sizeof(test_values) / sizeof(test_values[0]);
|
||||
|
||||
Serial.println("Calibrage CV1 (paliers de tension):");
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
dac_cv_write_direct(1, test_values[i]);
|
||||
Serial.print("CV1 = ");
|
||||
Serial.print(test_values[i]);
|
||||
Serial.print(" -> ");
|
||||
Serial.print((test_values[i] * 3.3 / 4096.0), 3);
|
||||
Serial.println("V");
|
||||
delay(800);
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
|
||||
Serial.println("Calibrage CV2 (paliers de tension):");
|
||||
for (int i = 0; i < num_values; i++) {
|
||||
dac_cv_write_direct(2, test_values[i]);
|
||||
Serial.print("CV2 = ");
|
||||
Serial.print(test_values[i]);
|
||||
Serial.print(" -> ");
|
||||
Serial.print((test_values[i] * 3.3 / 4096.0), 3);
|
||||
Serial.println("V");
|
||||
delay(800);
|
||||
}
|
||||
|
||||
// Retour à des valeurs neutres
|
||||
dac_cv_write_direct(1, 2048); // 1.65V
|
||||
dac_cv_write_direct(2, 2048); // 1.65V
|
||||
|
||||
Serial.println("Calibrage terminé - CV1 et CV2 à 1.65V\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Active le vibrato sur les CV
|
||||
*/
|
||||
void enableVibrato() {
|
||||
cv_modulation_set_vibrato(true);
|
||||
Serial.println("Vibrato activé avec paramètres par défaut");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Désactive le vibrato
|
||||
*/
|
||||
void disableVibrato() {
|
||||
cv_modulation_set_vibrato(false);
|
||||
Serial.println("Vibrato désactivé");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure les paramètres du vibrato
|
||||
*/
|
||||
void configureVibrato(float frequency, uint16_t depth) {
|
||||
cv_modulation_configure_vibrato(true, frequency, depth);
|
||||
Serial.print("Vibrato configuré - Fréquence: ");
|
||||
Serial.print(frequency);
|
||||
Serial.print("Hz, Profondeur: ");
|
||||
Serial.println(depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure la vitesse du portamento
|
||||
*/
|
||||
void configurePortamentoSpeed(float speed) {
|
||||
cv_modulation_configure_portamento_speed(speed);
|
||||
Serial.print("Vitesse portamento configurée: ");
|
||||
Serial.println(speed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lance un test de portamento
|
||||
*/
|
||||
void testPortamento() {
|
||||
Serial.println("Test portamento CV1: 1024 -> 3072");
|
||||
cv_modulation_start_portamento(1, 3072);
|
||||
delay(2000);
|
||||
Serial.println("Test portamento CV2: 2048 -> 512");
|
||||
cv_modulation_start_portamento(2, 512);
|
||||
delay(2000);
|
||||
Serial.println("Test portamento terminé");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Réinitialise tous les paramètres CV
|
||||
*/
|
||||
void resetAllCV() {
|
||||
dac_cv_reset_values();
|
||||
cv_modulation_set_vibrato(false);
|
||||
cv_modulation_reset_cv_values(); // Remet les CV modifiées par souris au centre
|
||||
Serial.println("Tous les paramètres CV réinitialisés (y compris modifications souris)");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche l'état détaillé de tous les systèmes
|
||||
*/
|
||||
void showFullSystemState() {
|
||||
uint16_t cv1, cv2;
|
||||
bool gate_state;
|
||||
|
||||
Serial.println("\n=== ÉTAT COMPLET DU SYSTÈME ===");
|
||||
|
||||
// État des CV et GATE
|
||||
dac_cv_get_state(&cv1, &cv2, &gate_state);
|
||||
Serial.print("CV1: ");
|
||||
Serial.print(cv1);
|
||||
Serial.print(" | CV2: ");
|
||||
Serial.print(cv2);
|
||||
Serial.print(" | GATE: ");
|
||||
Serial.println(gate_state ? "ON" : "OFF");
|
||||
|
||||
// Statistiques de performance
|
||||
cv_modulation_debug_performance_stats();
|
||||
|
||||
// Rapport FM complet
|
||||
cv_modulation_debug_fm_report();
|
||||
|
||||
Serial.println("==============================\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Analyse une commande avec paramètres
|
||||
*/
|
||||
bool parseCommandWithParams(String command, String& base_cmd, float& param1, uint16_t& param2) {
|
||||
int space_index = command.indexOf(' ');
|
||||
if (space_index == -1) {
|
||||
base_cmd = command;
|
||||
return false;
|
||||
}
|
||||
|
||||
base_cmd = command.substring(0, space_index);
|
||||
String params = command.substring(space_index + 1);
|
||||
|
||||
int comma_index = params.indexOf(',');
|
||||
if (comma_index != -1) {
|
||||
param1 = params.substring(0, comma_index).toFloat();
|
||||
param2 = params.substring(comma_index + 1).toInt();
|
||||
return true;
|
||||
} else {
|
||||
param1 = params.toFloat();
|
||||
param2 = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gère les commandes série pour le debug CV/FM
|
||||
*/
|
||||
void handleSerialCommands() {
|
||||
if (Serial.available()) {
|
||||
String command = Serial.readStringUntil('\n');
|
||||
command.trim();
|
||||
command.toLowerCase();
|
||||
|
||||
// Analyse des paramètres pour les commandes avancées
|
||||
String base_cmd;
|
||||
float param1 = 0;
|
||||
uint16_t param2 = 0;
|
||||
bool has_params = parseCommandWithParams(command, base_cmd, param1, param2);
|
||||
|
||||
// === COMMANDES FM ===
|
||||
if (command == "fm debug on") {
|
||||
enableFMDebug();
|
||||
}
|
||||
else if (command == "fm debug off") {
|
||||
disableFMDebug();
|
||||
}
|
||||
else if (command == "fm report") {
|
||||
showFMReport();
|
||||
}
|
||||
else if (command == "fm stats") {
|
||||
showFMPerformanceStats();
|
||||
}
|
||||
else if (command == "fm test") {
|
||||
testFMModulation(3000);
|
||||
}
|
||||
else if (command == "fm test long") {
|
||||
testFMModulation(10000);
|
||||
}
|
||||
else if (base_cmd == "fm test" && has_params) {
|
||||
testFMModulation((uint32_t)(param1 * 1000)); // Paramètre en secondes
|
||||
}
|
||||
|
||||
// === COMMANDES VIBRATO ===
|
||||
else if (command == "vibrato on") {
|
||||
enableVibrato();
|
||||
}
|
||||
else if (command == "vibrato off") {
|
||||
disableVibrato();
|
||||
}
|
||||
else if (base_cmd == "vibrato config" && has_params) {
|
||||
if (param2 > 0) {
|
||||
configureVibrato(param1, param2);
|
||||
} else {
|
||||
cv_modulation_set_vibrato_params(param1, 100); // Profondeur par défaut
|
||||
Serial.print("Vibrato fréquence mise à jour: ");
|
||||
Serial.print(param1);
|
||||
Serial.println("Hz");
|
||||
}
|
||||
}
|
||||
|
||||
// === COMMANDES PORTAMENTO ===
|
||||
else if (base_cmd == "portamento speed" && has_params) {
|
||||
configurePortamentoSpeed(param1);
|
||||
}
|
||||
else if (command == "portamento test") {
|
||||
testPortamento();
|
||||
}
|
||||
|
||||
// === COMMANDES CV ===
|
||||
else if (command == "cv state") {
|
||||
debugCVState();
|
||||
}
|
||||
else if (command == "cv reset") {
|
||||
resetAllCV();
|
||||
}
|
||||
else if (base_cmd == "cv direct" && has_params) {
|
||||
// Format: "cv direct 1,2048" pour canal 1, valeur 2048
|
||||
dac_cv_write_direct((uint32_t)param1, (uint16_t)param2);
|
||||
Serial.print("CV");
|
||||
Serial.print((int)param1);
|
||||
Serial.print(" mise à ");
|
||||
Serial.println(param2);
|
||||
}
|
||||
|
||||
// === COMMANDES SYSTÈME ===
|
||||
else if (command == "system state") {
|
||||
showFullSystemState();
|
||||
}
|
||||
else if (command == "system reset") {
|
||||
resetAllCV();
|
||||
cv_modulation_set_fm_debug(false);
|
||||
Serial.println("Système CV complet réinitialisé");
|
||||
}
|
||||
else if (command == "test full") {
|
||||
runFullCVTest();
|
||||
}
|
||||
else if (command == "calibrate") {
|
||||
calibrateCVOutputs();
|
||||
}
|
||||
|
||||
// === AIDE ===
|
||||
else if (command == "help" || command == "?") {
|
||||
Serial.println("\n=== COMMANDES DEBUG COMPLÈTES ===");
|
||||
Serial.println("--- MODULATION FM ---");
|
||||
Serial.println("fm debug on/off - Active/désactive le debug FM optimisé");
|
||||
Serial.println("fm report - Rapport FM complet");
|
||||
Serial.println("fm stats - Statistiques performance");
|
||||
Serial.println("fm test [sec] - Test FM (durée optionnelle en sec)");
|
||||
Serial.println("fm test long - Test FM long (10s)");
|
||||
Serial.println("");
|
||||
Serial.println("--- VIBRATO ---");
|
||||
Serial.println("vibrato on/off - Active/désactive le vibrato");
|
||||
Serial.println("vibrato config f,d - Configure fréquence(Hz) et profondeur");
|
||||
Serial.println(" Ex: vibrato config 5.0,200");
|
||||
Serial.println("");
|
||||
Serial.println("--- PORTAMENTO ---");
|
||||
Serial.println("portamento speed s - Configure vitesse (0.1-10.0)");
|
||||
Serial.println("portamento test - Test automatique portamento");
|
||||
Serial.println("");
|
||||
Serial.println("--- CONTRÔLE CV ---");
|
||||
Serial.println("cv state - État actuel des CV et GATE");
|
||||
Serial.println("cv reset - Réinitialise toutes les CV");
|
||||
Serial.println("cv direct c,v - Écrit valeur directe sur canal");
|
||||
Serial.println(" Ex: cv direct 1,2048");
|
||||
Serial.println("");
|
||||
Serial.println("--- SYSTÈME ---");
|
||||
Serial.println("system state - État complet du système");
|
||||
Serial.println("system reset - Réinitialisation complète");
|
||||
Serial.println("test full - Test complet de toutes les fonctionnalités");
|
||||
Serial.println("calibrate - Calibrage des sorties CV (paliers)");
|
||||
Serial.println("help ou ? - Affiche cette aide");
|
||||
Serial.println("=================================\n");
|
||||
}
|
||||
else if (command.length() > 0) {
|
||||
Serial.print("Commande inconnue: '");
|
||||
Serial.print(command);
|
||||
Serial.println("'. Tapez 'help' pour l'aide complète.");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Boucle principale du programme.
|
||||
*/
|
||||
void loop() {
|
||||
// Gestion des commandes série pour le debug (STM32 uniquement)
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
handleSerialCommands();
|
||||
#endif
|
||||
|
||||
if (deviceState.keyboard_present) {
|
||||
// Serial.println("Gestion du clavier...");
|
||||
handleKeyboard();
|
||||
delay(POLL_DELAY);
|
||||
}
|
||||
|
||||
if (deviceState.mouse_present) {
|
||||
// Serial.println("Gestion de la souris...");
|
||||
handleMouse();
|
||||
delay(POLL_DELAY);
|
||||
}
|
||||
|
||||
// Traitement continu des modulations CV (portamento, vibrato, FM)
|
||||
#ifdef ARDUINO_ARCH_STM32
|
||||
dac_cv_process_modulations();
|
||||
|
||||
// Vérifier le timeout et réinitialiser les CV si nécessaire
|
||||
if (last_mouse_activity > 0 && (millis() - last_mouse_activity) > CV_RESET_TIMEOUT) {
|
||||
dac_cv_reset_values();
|
||||
last_mouse_activity = 0; // Éviter de répéter la réinitialisation
|
||||
Serial.println("CV réinitialisées (timeout inactivité souris).");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Unit Testing 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/page/plus/unit-testing.html
|
||||
@@ -0,0 +1,5 @@
|
||||
#include <cstdint>
|
||||
|
||||
void delay(uint16_t) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
|
||||
typedef struct {
|
||||
uint8_t modifiers;
|
||||
uint8_t reserved;
|
||||
uint8_t keys[6];
|
||||
} KeyReport;
|
||||
|
||||
class Keyboard_ {
|
||||
private:
|
||||
KeyReport _keyReport;
|
||||
void sendReport(KeyReport *keys) {};
|
||||
public:
|
||||
Keyboard_(void) {};
|
||||
void begin(void) {};
|
||||
void end(void) {};
|
||||
size_t write(uint8_t k) { return 0; };
|
||||
size_t press(uint8_t k) { return 0; };
|
||||
size_t release(uint8_t k) { return 0; };
|
||||
void releaseAll(void) {};
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
typedef enum {
|
||||
HID_KEYBOARD,
|
||||
HID_MOUSE
|
||||
} HID_Interface;
|
||||
|
||||
void HID_Composite_Init(HID_Interface device) {
|
||||
|
||||
}
|
||||
|
||||
void HID_Composite_DeInit(HID_Interface device) {
|
||||
|
||||
}
|
||||
|
||||
void HID_Composite_keyboard_sendReport(uint8_t *report, uint16_t len) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#define private public
|
||||
#pragma GCC diagnostic ignored "-Wc++11-extensions"
|
||||
|
||||
#include <unity.h>
|
||||
#include "adb_devices.h"
|
||||
#include "hid_keyboard.h"
|
||||
|
||||
// void setUp(void) {
|
||||
// // set stuff up here
|
||||
// }
|
||||
|
||||
// void tearDown(void) {
|
||||
// // clean stuff up here
|
||||
// }
|
||||
|
||||
void test_key_report_empty(void) {
|
||||
hid_key_report k = {0};
|
||||
|
||||
for (uint8_t i = 0; i < 6; i++)
|
||||
TEST_ASSERT_EQUAL(0, k.keys[i]);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, k.modifiers);
|
||||
}
|
||||
|
||||
void test_hid_keyboard_add_key_to_report(void) {
|
||||
hid_key_report k = {0};
|
||||
|
||||
TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 1));
|
||||
TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 2));
|
||||
TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 3));
|
||||
TEST_ASSERT_TRUE(hid_keyboard_add_key_to_report(&k, 2));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, k.keys[0]);
|
||||
TEST_ASSERT_EQUAL(2, k.keys[1]);
|
||||
TEST_ASSERT_EQUAL(3, k.keys[2]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[3]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[4]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[5]);
|
||||
}
|
||||
|
||||
void test_hid_keyboard_remove_key_from_report(void) {
|
||||
hid_key_report k = {0};
|
||||
|
||||
uint8_t initial_keys[] = {7, 8, 9, 0, 0, 0};
|
||||
for (uint8_t i = 0; i < 6; i++) {
|
||||
k.keys[i] = initial_keys[i];
|
||||
}
|
||||
|
||||
hid_keyboard_remove_key_from_report(&k, 7);
|
||||
hid_keyboard_remove_key_from_report(&k, 9);
|
||||
hid_keyboard_remove_key_from_report(&k, 7);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, k.keys[0]);
|
||||
TEST_ASSERT_EQUAL(8, k.keys[1]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[2]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[3]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[4]);
|
||||
TEST_ASSERT_EQUAL(0, k.keys[5]);
|
||||
}
|
||||
|
||||
void test_adb_kb_keypress(void) {
|
||||
uint8_t kp_bin[2] = {0b10000001, 0b00001011};
|
||||
|
||||
adb_kb_keypress kp_stru;
|
||||
kp_stru.released0 = true;
|
||||
kp_stru.key0 = 1;
|
||||
kp_stru.released1 = false;
|
||||
kp_stru.key1 = 11;
|
||||
|
||||
uint16_t kp_stru_bin = *((uint16_t*)&kp_stru);
|
||||
|
||||
TEST_ASSERT_EQUAL(kp_bin, kp_stru_bin);
|
||||
}
|
||||
|
||||
void test_adb_kb_modifiers() {
|
||||
uint8_t mod_bin[2] = {0b00110101, 0b11000010};
|
||||
adb_kb_modifiers mod_stru = *((adb_kb_modifiers*)&mod_bin);
|
||||
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.reserved0);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.backspace);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.caps_lock);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.reset);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.control);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.shift);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.option);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.command);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.num_lock);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.scroll_lock);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.reserved1);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.led_scroll);
|
||||
TEST_ASSERT_EQUAL(1, mod_stru.led_caps);
|
||||
TEST_ASSERT_EQUAL(0, mod_stru.led_num);
|
||||
}
|
||||
|
||||
void test_adb_command() {
|
||||
uint8_t cmd_bin = 0b01101001;
|
||||
adb_command cmd_stru = *((adb_command*)&cmd_bin);
|
||||
|
||||
TEST_ASSERT_EQUAL(6, cmd_stru.addr);
|
||||
TEST_ASSERT_EQUAL(2, cmd_stru.cmd);
|
||||
TEST_ASSERT_EQUAL(1, cmd_stru.reg);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_key_report_empty);
|
||||
RUN_TEST(test_hid_keyboard_add_key_to_report);
|
||||
RUN_TEST(test_hid_keyboard_remove_key_from_report);
|
||||
|
||||
RUN_TEST(test_adb_kb_keypress);
|
||||
RUN_TEST(test_adb_kb_modifiers);
|
||||
RUN_TEST(test_adb_command);
|
||||
UNITY_END();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
# Smoke Tests
|
||||
|
||||
## Objectif
|
||||
Valider rapidement la compilabilité et la cohérence de configuration.
|
||||
|
||||
## Commandes recommandées
|
||||
- `pio run`
|
||||
- `cmake -S . -B build && cmake --build build` (si CMake est présent)
|
||||
|
||||
## Critères de réussite
|
||||
- Build sans erreur bloquante.
|
||||
- Configuration cible cohérente.
|
||||
Reference in New Issue
Block a user