chore(hw): stage local changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user