chore(hw): stage local changes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
L'électron rare
2026-03-11 00:12:29 +01:00
parent 1c51e3c8f3
commit 20b841655e
10 changed files with 2290 additions and 2 deletions
+33
View File
@@ -0,0 +1,33 @@
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
+48
View File
@@ -219,3 +219,51 @@ 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 -->
+2 -2
View File
@@ -16,8 +16,8 @@ build_flags =
-D STM32H7A3xx
-D STM32H743xx
-D HAL_PCD_MODULE_ENABLED
-D HAL_DAC_MODULE_ENABLED
-D HAL_TIM_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
+140
View File
@@ -0,0 +1,140 @@
/**
* @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
+845
View File
@@ -0,0 +1,845 @@
/**
* @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
+188
View File
@@ -0,0 +1,188 @@
/**
* @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
+77
View File
@@ -0,0 +1,77 @@
/**
* @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
+34
View File
@@ -0,0 +1,34 @@
/**
* @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
+911
View File
@@ -0,0 +1,911 @@
/**
* @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
+12
View File
@@ -0,0 +1,12 @@
# 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.