feat(FFT): добавлено FFT, выделение основной частоты
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <string.h>
|
||||
#include "FreeRTOS.h"
|
||||
#include "audio_adc.h"
|
||||
#include "audio_processor.h" // НОВОЕ
|
||||
#include "queue.h"
|
||||
#include "stm32f1xx.h"
|
||||
#include "task.h"
|
||||
@@ -10,23 +11,25 @@
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
|
||||
(void)xTask;
|
||||
(void)pcTaskName;
|
||||
// Мигаем LED очень быстро при переполнении стека
|
||||
while (1) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
for (volatile int i = 0; i < 50000; i++);
|
||||
}
|
||||
}
|
||||
|
||||
// === Структура данных для очереди ===
|
||||
// === Структуры данных ===
|
||||
|
||||
// НОВОЕ: пакет с результатами FFT
|
||||
typedef struct {
|
||||
uint16_t min_val;
|
||||
uint16_t max_val;
|
||||
uint16_t avg_val;
|
||||
float rms_dbfs;
|
||||
float peak_hz;
|
||||
float peak_mag;
|
||||
uint8_t clipped;
|
||||
uint32_t buffer_num;
|
||||
} audio_stats_packet_t;
|
||||
} audio_metrics_packet_t;
|
||||
|
||||
static QueueHandle_t audio_stats_queue = NULL;
|
||||
static QueueHandle_t audio_metrics_queue = NULL;
|
||||
static volatile uint32_t buffer_counter = 0;
|
||||
|
||||
// === System Clock ===
|
||||
|
||||
@@ -51,37 +54,26 @@ void SystemClock_Config(void) {
|
||||
SystemCoreClock = 72000000;
|
||||
}
|
||||
|
||||
// === Audio Callback ===
|
||||
// === Audio Callback (НОВОЕ: копируем в очередь для обработки) ===
|
||||
|
||||
// Буфер для копирования из ISR
|
||||
static audio_sample_t processing_buffer[AUDIO_BUFFER_SIZE];
|
||||
|
||||
void audio_buffer_ready(audio_sample_t *buffer, uint32_t size) {
|
||||
static uint32_t buffer_counter = 0;
|
||||
buffer_counter++;
|
||||
|
||||
// Мигаем LED при каждом вызове
|
||||
// Мигаем LED
|
||||
if (buffer_counter % 5 == 0) { GPIOC->ODR ^= GPIO_ODR_ODR13; }
|
||||
|
||||
uint16_t min_val = 4095;
|
||||
uint16_t max_val = 0;
|
||||
uint32_t sum = 0;
|
||||
// Копируем данные (ISR должен быть быстрым)
|
||||
memcpy(processing_buffer, buffer, size * sizeof(audio_sample_t));
|
||||
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
uint16_t val = buffer[i];
|
||||
if (val < min_val) min_val = val;
|
||||
if (val > max_val) max_val = val;
|
||||
sum += val;
|
||||
}
|
||||
|
||||
if (buffer_counter % 10 == 0) {
|
||||
audio_stats_packet_t packet = {
|
||||
.min_val = min_val,
|
||||
.max_val = max_val,
|
||||
.avg_val = (uint16_t)(max_val - min_val),
|
||||
.buffer_num = buffer_counter};
|
||||
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xQueueSendFromISR(
|
||||
audio_stats_queue,
|
||||
&packet,
|
||||
// Сигналим задаче обработки через notification
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
extern TaskHandle_t audio_process_task_handle;
|
||||
if (audio_process_task_handle != NULL) {
|
||||
vTaskNotifyGiveFromISR(
|
||||
audio_process_task_handle,
|
||||
&xHigherPriorityTaskWoken);
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
@@ -97,60 +89,110 @@ void usb_device_task(void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
// НОВОЕ: задача обработки FFT
|
||||
TaskHandle_t audio_process_task_handle = NULL;
|
||||
|
||||
void audio_process_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
// Инициализация процессора
|
||||
if (!audio_processor_init()) {
|
||||
// Ошибка FFT init
|
||||
while (1) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
|
||||
audio_metrics_t metrics;
|
||||
|
||||
while (1) {
|
||||
// Ждём сигнала от ISR
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
|
||||
// Обработка 512 сэмплов
|
||||
if (audio_processor_process_512(processing_buffer, &metrics)) {
|
||||
// Отправляем только каждый 10-й (10 Hz)
|
||||
if (buffer_counter % 10 == 0) {
|
||||
audio_metrics_packet_t packet = {
|
||||
.rms_dbfs = metrics.rms_dbfs,
|
||||
.peak_hz = metrics.peak_hz,
|
||||
.peak_mag = metrics.peak_mag,
|
||||
.clipped = metrics.clipped,
|
||||
.buffer_num = buffer_counter};
|
||||
xQueueSend(audio_metrics_queue, &packet, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cdc_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
char tx_buffer[256];
|
||||
uint32_t heartbeat_counter = 0;
|
||||
uint32_t last_buffer_count = 0;
|
||||
|
||||
while (1) {
|
||||
heartbeat_counter++;
|
||||
|
||||
// Heartbeat + ДИАГНОСТИКА регистров каждые 100 циклов
|
||||
// Heartbeat каждые 100 циклов
|
||||
if (heartbeat_counter % 100 == 0 && tud_cdc_connected()) {
|
||||
uint32_t current_buffer_count = audio_adc_get_buffer_count();
|
||||
|
||||
int len = snprintf(
|
||||
tx_buffer,
|
||||
sizeof(tx_buffer),
|
||||
"HB:%lu Q:%u BC:%lu | "
|
||||
"TIM3_CR1:%lX TIM3_CNT:%lu | "
|
||||
"ADC1_CR2:%lX ADC1_SR:%lX | "
|
||||
"DMA_CCR:%lX DMA_CNDTR:%lu\r\n",
|
||||
"HB:%lu Q:%u BC:%lu\r\n",
|
||||
heartbeat_counter,
|
||||
(unsigned)uxQueueMessagesWaiting(audio_stats_queue),
|
||||
current_buffer_count,
|
||||
TIM3->CR1,
|
||||
TIM3->CNT,
|
||||
ADC1->CR2,
|
||||
ADC1->SR,
|
||||
DMA1_Channel1->CCR,
|
||||
DMA1_Channel1->CNDTR);
|
||||
(unsigned)uxQueueMessagesWaiting(audio_metrics_queue),
|
||||
current_buffer_count);
|
||||
|
||||
if (tud_cdc_write_available() >= len) {
|
||||
tud_cdc_write(tx_buffer, len);
|
||||
if (len > 0 && tud_cdc_write_available() >= (uint32_t)len) {
|
||||
tud_cdc_write(tx_buffer, (uint32_t)len);
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
|
||||
last_buffer_count = current_buffer_count;
|
||||
}
|
||||
|
||||
// Остальной код без изменений
|
||||
audio_stats_packet_t packet;
|
||||
if (xQueueReceive(audio_stats_queue, &packet, pdMS_TO_TICKS(10)) ==
|
||||
// Метрики FFT
|
||||
audio_metrics_packet_t packet;
|
||||
if (xQueueReceive(audio_metrics_queue, &packet, pdMS_TO_TICKS(10)) ==
|
||||
pdPASS) {
|
||||
// fixed-point:
|
||||
// RMS: dBFS * 10 (один знак после запятой)
|
||||
int32_t rms_x10 = (int32_t)(packet.rms_dbfs * 10.0f);
|
||||
int32_t rms_int = rms_x10 / 10;
|
||||
int32_t rms_frac = rms_x10 % 10;
|
||||
if (rms_frac < 0) rms_frac = -rms_frac;
|
||||
|
||||
// Freq: Hz * 10 (один знак после запятой)
|
||||
int32_t freq_x10 = (int32_t)(packet.peak_hz * 10.0f);
|
||||
int32_t freq_int = freq_x10 / 10;
|
||||
int32_t freq_frac = freq_x10 % 10;
|
||||
if (freq_frac < 0) freq_frac = -freq_frac;
|
||||
|
||||
// Mag: *1000 (три знака после запятой)
|
||||
int32_t mag_x1000 = (int32_t)(packet.peak_mag * 1000.0f);
|
||||
int32_t mag_int = mag_x1000 / 1000;
|
||||
int32_t mag_frac = mag_x1000 % 1000;
|
||||
if (mag_frac < 0) mag_frac = -mag_frac;
|
||||
|
||||
int len = snprintf(
|
||||
tx_buffer,
|
||||
sizeof(tx_buffer),
|
||||
"Buf:%lu Min:%u Max:%u Avg:%u\r\n",
|
||||
"Buf:%lu RMS:%ld.%01ld dBFS Freq:%ld.%01ld Hz Mag:%ld.%03ld "
|
||||
"Clip:%u\r\n",
|
||||
packet.buffer_num,
|
||||
packet.min_val,
|
||||
packet.max_val,
|
||||
packet.avg_val);
|
||||
(long)rms_int,
|
||||
(long)rms_frac,
|
||||
(long)freq_int,
|
||||
(long)freq_frac,
|
||||
(long)mag_int,
|
||||
(long)mag_frac,
|
||||
(unsigned)packet.clipped);
|
||||
|
||||
if (tud_cdc_connected() && len > 0 &&
|
||||
tud_cdc_write_available() >= len) {
|
||||
tud_cdc_write(tx_buffer, len);
|
||||
if (len > 0 && tud_cdc_connected() &&
|
||||
tud_cdc_write_available() >= (uint32_t)len) {
|
||||
tud_cdc_write(tx_buffer, (uint32_t)len);
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
}
|
||||
@@ -177,28 +219,24 @@ void led_task(void *param) {
|
||||
}
|
||||
}
|
||||
|
||||
// === Задача инициализации аудио ===
|
||||
void audio_init_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
// Индикация старта инициализации (мигнем 3 раза быстро)
|
||||
// Индикация старта
|
||||
for (int i = 0; i < 3; i++) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
if (!audio_adc_init(audio_buffer_ready)) {
|
||||
// Ошибка: мигаем очень быстро
|
||||
while (1) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
vTaskDelay(pdMS_TO_TICKS(50));
|
||||
}
|
||||
}
|
||||
|
||||
// Запускаем ADC после старта FreeRTOS
|
||||
audio_adc_start();
|
||||
|
||||
// Индикация успешного запуска (мигнем 5 раз медленно)
|
||||
for (int i = 0; i < 5; i++) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
@@ -207,8 +245,6 @@ void audio_init_task(void *param) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// === USB Reset ===
|
||||
|
||||
void force_usb_reset(void) {
|
||||
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||
GPIOA->CRH &= ~GPIO_CRH_CNF12;
|
||||
@@ -245,9 +281,9 @@ int main(void) {
|
||||
|
||||
tusb_init();
|
||||
|
||||
// Очередь создаем ДО старта планировщика
|
||||
audio_stats_queue = xQueueCreate(10, sizeof(audio_stats_packet_t));
|
||||
if (audio_stats_queue == NULL) {
|
||||
// НОВОЕ: очередь для метрик FFT
|
||||
audio_metrics_queue = xQueueCreate(10, sizeof(audio_metrics_packet_t));
|
||||
if (audio_metrics_queue == NULL) {
|
||||
while (1) {
|
||||
GPIOC->ODR ^= GPIO_ODR_ODR13;
|
||||
for (volatile int i = 0; i < 100000; i++);
|
||||
@@ -266,6 +302,15 @@ int main(void) {
|
||||
xTaskCreate(led_task, "led", 128, NULL, 1, NULL);
|
||||
xTaskCreate(audio_init_task, "audio_init", 128, NULL, 2, NULL);
|
||||
|
||||
// НОВОЕ: задача обработки FFT (высокий приоритет, большой стек для FFT)
|
||||
xTaskCreate(
|
||||
audio_process_task,
|
||||
"audio_proc",
|
||||
512,
|
||||
NULL,
|
||||
configMAX_PRIORITIES - 2,
|
||||
&audio_process_task_handle);
|
||||
|
||||
vTaskStartScheduler();
|
||||
|
||||
while (1);
|
||||
|
||||
Reference in New Issue
Block a user