feat(audio): API capture micro RX hal_i2s

Ajoute hal_i2s_capture_begin/read_frame/end : scratch PSRAM,
downmix L+R->mono, energie RMS. Sonde app_main valide le canal.
This commit is contained in:
clement
2026-06-19 10:27:02 +02:00
parent 04a12e9351
commit f3b92bf6e3
3 changed files with 64 additions and 5 deletions
+48
View File
@@ -9,6 +9,9 @@
#include "driver/gpio.h"
#include "driver/i2s_std.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include <stdlib.h>
#define TAG "hal_i2s"
@@ -18,6 +21,10 @@
static i2s_chan_handle_t s_spk_handle = NULL;
static i2s_chan_handle_t s_mic_handle = NULL;
#define HAL_I2S_CAP_FRAME_SAMPLES 320
#define HAL_I2S_CAP_IN_BYTES (HAL_I2S_CAP_FRAME_SAMPLES * 2 * (int)sizeof(int16_t))
static int16_t *s_rx_scratch = NULL;
void hal_audio_pa_set(bool enable)
{
gpio_set_level(PLIP_PA_ENABLE, enable ? 1 : 0);
@@ -95,3 +102,44 @@ esp_err_t hal_i2s_init(void)
}
return ESP_OK;
}
esp_err_t hal_i2s_capture_begin(void)
{
if (!s_mic_handle) {
ESP_LOGE(TAG, "capture_begin: pas de canal RX");
return ESP_ERR_INVALID_STATE;
}
if (s_rx_scratch) return ESP_OK; /* idempotent */
s_rx_scratch = heap_caps_malloc(HAL_I2S_CAP_IN_BYTES, MALLOC_CAP_SPIRAM);
if (!s_rx_scratch) s_rx_scratch = malloc(HAL_I2S_CAP_IN_BYTES);
if (!s_rx_scratch) return ESP_ERR_NO_MEM;
ESP_LOGI(TAG, "capture_begin: pret (full-duplex, TX actif)");
return ESP_OK;
}
int hal_i2s_capture_read_frame(int16_t *mono_out, int n_samples, int64_t *rms_sq_out)
{
if (!s_rx_scratch || !s_mic_handle) return -1;
size_t bytes_read = 0;
esp_err_t ret = i2s_channel_read(s_mic_handle, s_rx_scratch,
HAL_I2S_CAP_IN_BYTES, &bytes_read,
pdMS_TO_TICKS(100));
if (ret != ESP_OK || bytes_read == 0) return 0; /* timeout */
int n = (int)(bytes_read / (2 * sizeof(int16_t)));
if (n > n_samples) n = n_samples;
int64_t rms_sq = 0;
for (int i = 0; i < n; i++) {
int32_t l = s_rx_scratch[i * 2];
int32_t r = s_rx_scratch[i * 2 + 1];
int16_t mono = (int16_t)((l + r) / 2);
mono_out[i] = mono;
rms_sq += (int64_t)mono * mono;
}
if (rms_sq_out) *rms_sq_out = (n > 0) ? rms_sq / n : 0;
return n;
}
void hal_i2s_capture_end(void)
{
if (s_rx_scratch) { free(s_rx_scratch); s_rx_scratch = NULL; }
}
+6
View File
@@ -21,6 +21,12 @@ i2s_chan_handle_t hal_i2s_mic_handle(void);
/* Enable/disable the speaker power amplifier (GPIO21, active HIGH). */
void hal_audio_pa_set(bool enable);
/* Microphone capture (RX). capture_begin allocates a scratch buffer; read_frame
* reads one 20 ms frame, downmixes L+R to mono, and reports mean energy. */
esp_err_t hal_i2s_capture_begin(void);
int hal_i2s_capture_read_frame(int16_t *mono_out, int n_samples, int64_t *rms_sq_out);
void hal_i2s_capture_end(void);
#ifdef __cplusplus
}
#endif
+10 -5
View File
@@ -17,14 +17,19 @@ void app_main(void)
* avec la tache tone (i2s_channel_write qui tourne en parallele). */
ESP_ERROR_CHECK(slic_init());
audio_router_init();
audio_router_dialtone_start();
ESP_LOGI(TAG, "tonalite de numerotation demarree (combine decroche pour ecouter)");
/* Sonde temporaire de polarite hook (a retirer en Task 6). */
/* Sonde RMS micro (valide chemin LIN2/RIN2) — a retirer en Task 6. */
ESP_ERROR_CHECK(hal_i2s_capture_begin());
int16_t cap[320];
for (int i = 0; i < 20; i++) {
ESP_LOGI(TAG, "HOOK sonde: offhook=%d (niveau brut SHK)", (int)slic_is_offhook());
int64_t rms_sq = 0;
int got = hal_i2s_capture_read_frame(cap, 320, &rms_sq);
ESP_LOGI(TAG, "MIC sonde: offhook=%d frame=%d rms_sq=%lld",
(int)slic_is_offhook(), got, (long long)rms_sq);
vTaskDelay(pdMS_TO_TICKS(500));
}
ESP_LOGI(TAG, "boot OK — Phase 3 Task 1 SLIC KS0835");
audio_router_dialtone_start();
ESP_LOGI(TAG, "tonalite de numerotation demarree (combine decroche pour ecouter)");
ESP_LOGI(TAG, "boot OK — Phase 3 Task 2 capture micro hal_i2s");
while (true) {
ESP_LOGI(TAG, "heartbeat");
vTaskDelay(pdMS_TO_TICKS(5000));