feat(fw/health): добавлено моргание светодиода

This commit is contained in:
2025-12-26 02:46:06 +03:00
parent 2a14a36797
commit eaa0e0a3eb
5 changed files with 147 additions and 39 deletions

View File

@@ -3,6 +3,7 @@
#define configCHECK_FOR_STACK_OVERFLOW 2
#define configUSE_MALLOC_FAILED_HOOK 1
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0

15
firmware/App/Inc/health.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef HEALTH_H
#define HEALTH_H
#include <stdbool.h>
#include <stdint.h>
void health_kick_watchdog(void);
void health_init_watchdog(void);
void health_update_led(float freq_hz, float rms_dbfs);
void health_led_task(void *param);
#endif // HEALTH_H

68
firmware/App/Src/health.c Normal file
View File

@@ -0,0 +1,68 @@
#include "health.h"
#include <math.h>
#include "FreeRTOS.h"
#include "stm32f1xx.h"
#include "task.h"
// Default to "Heartbeat" mode (1 Hz)
static volatile uint32_t led_period_ms = 3000;
void health_init_watchdog(void) {
IWDG->KR = 0x5555;
IWDG->PR = 0x04; // Prescaler /64 -> 625 Hz
IWDG->RLR = 1875; // ~3 seconds
IWDG->KR = 0xCCCC;
IWDG->KR = 0xAAAA;
}
void health_kick_watchdog(void) {
IWDG->KR = 0xAAAA;
}
void health_update_led(float freq_hz, float rms_dbfs) {
if (rms_dbfs < -35.0f) {
led_period_ms = 3000;
return;
}
if (freq_hz < 100.0f) freq_hz = 100.0f;
if (freq_hz > 6000.0f) freq_hz = 6000.0f;
// 100..8000 Hz -> 1..5 Hz blink (period 1000..200 ms)
float t = (log10f(freq_hz) - log10f(100.0f)) /
(log10f(6000.0f) - log10f(100.0f)); // 0..1
float blink_hz = 1.0f + t * 20.0f; // 1..5
uint32_t period = (uint32_t)(3000.0f / blink_hz); // 1000..200 ms
if (period < 200) period = 50;
if (period > 3000) period = 3000;
led_period_ms = period;
}
void health_led_task(void *param) {
(void)param;
// 1) Включаем тактирование GPIOC и настраиваем PC13 как выход push-pull
// 2MHz
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
GPIOC->CRH |= GPIO_CRH_MODE13_1; // 2 MHz output, push-pull (CNF=00)
// LED off initially (Blue Pill LED is active-low)
GPIOC->ODR |= GPIO_ODR_ODR13;
while (1) {
uint32_t period = led_period_ms;
uint32_t on_ms = 40;
if (on_ms > period / 2) on_ms = period / 2;
uint32_t off_ms = period - on_ms;
// LED active-low: 0 = ON, 1 = OFF
GPIOC->BSRR = GPIO_BSRR_BR13; // ON
vTaskDelay(pdMS_TO_TICKS(on_ms)); // держим заметный импульс
GPIOC->BSRR = GPIO_BSRR_BS13; // OFF
vTaskDelay(pdMS_TO_TICKS(off_ms)); // пауза
health_kick_watchdog();
}
}

View File

@@ -3,6 +3,7 @@
#include "FreeRTOS.h"
#include "audio_adc.h"
#include "audio_processor.h" // НОВОЕ
#include "health.h"
#include "protocol.h"
#include "queue.h"
#include "stm32f1xx.h"
@@ -18,6 +19,22 @@ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
}
}
void vApplicationMallocFailedHook(void) {
taskDISABLE_INTERRUPTS();
while (1) {
GPIOC->ODR ^= GPIO_ODR_ODR13;
for (volatile int i = 0; i < 200000; i++) {}
}
}
static void panic_blink_forever(uint32_t delay_ms) {
// PC13 уже сконфигурирован в main
while (1) {
GPIOC->ODR ^= GPIO_ODR_ODR13;
vTaskDelay(pdMS_TO_TICKS(delay_ms));
}
}
// === Структуры данных ===
typedef struct {
@@ -63,7 +80,7 @@ void audio_buffer_ready(audio_sample_t *buffer, uint32_t size) {
buffer_counter++;
// Мигаем LED
if (buffer_counter % 5 == 0) { GPIOC->ODR ^= GPIO_ODR_ODR13; }
/* if (buffer_counter % 5 == 0) { GPIOC->ODR ^= GPIO_ODR_ODR13; } */
// Копируем данные (ISR должен быть быстрым)
memcpy(processing_buffer, buffer, size * sizeof(audio_sample_t));
@@ -89,47 +106,57 @@ 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));
vTaskDelay(pdMS_TO_TICKS(50));
health_kick_watchdog();
}
}
audio_metrics_t metrics;
TickType_t last_wake = xTaskGetTickCount();
const TickType_t period = pdMS_TO_TICKS(100); // 10 Hz
while (1) {
// Ждём сигнала от ISR
// 1) Ждём хотя бы один новый буфер от ISR
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// Обработка 512 сэмплов
// 2) Выкидываем накопившиеся уведомления, чтобы не пытаться "догонять"
// прошлое
while (ulTaskNotifyTake(pdTRUE, 0) > 0) {}
// 3) Обрабатываем самый свежий буфер (processing_buffer
// перезаписывается в ISR)
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,
.timestamp_ms = xTaskGetTickCount(),
};
xQueueSend(audio_metrics_queue, &packet, 0);
}
health_update_led(metrics.peak_hz, metrics.rms_dbfs);
audio_metrics_packet_t packet = {
.rms_dbfs = metrics.rms_dbfs,
.peak_hz = metrics.peak_hz,
.peak_mag = metrics.peak_mag,
.clipped = metrics.clipped,
.timestamp_ms = xTaskGetTickCount(),
};
(void)xQueueSend(audio_metrics_queue, &packet, 0);
}
health_kick_watchdog();
// ограничиваем частоту обработки
vTaskDelayUntil(&last_wake, period);
}
}
void cdc_task(void *param) {
(void)param;
// Buffer for the FR-1.4 packet (12 bytes)
// Buffer for packet (12 bytes)
uint8_t tx_buffer[PACKET_TOTAL_SIZE];
while (1) {
@@ -164,16 +191,6 @@ void cdc_task(void *param) {
}
}
void led_task(void *param) {
(void)param;
while (1) {
GPIOC->BSRR = GPIO_BSRR_BR13;
vTaskDelay(pdMS_TO_TICKS(500));
GPIOC->BSRR = GPIO_BSRR_BS13;
vTaskDelay(pdMS_TO_TICKS(500));
}
}
void audio_init_task(void *param) {
(void)param;
@@ -215,9 +232,9 @@ void force_usb_reset(void) {
int main(void) {
SystemClock_Config();
// LED
// LED GPIO (will be managed by health_led_task)
RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
GPIOC->CRH &= ~GPIO_CRH_CNF13;
GPIOC->CRH &= ~(GPIO_CRH_MODE13 | GPIO_CRH_CNF13);
GPIOC->CRH |= GPIO_CRH_MODE13_1;
force_usb_reset();
@@ -225,18 +242,19 @@ int main(void) {
// USB
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, 6);
NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 6);
NVIC_SetPriority(USBWakeUp_IRQn, 6);
NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
NVIC_EnableIRQ(USBWakeUp_IRQn);
tusb_init();
// НОВОЕ: очередь для метрик FFT
// Initialize watchdog BEFORE starting tasks
health_init_watchdog();
// FFT queue
audio_metrics_queue = xQueueCreate(10, sizeof(audio_metrics_packet_t));
if (audio_metrics_queue == NULL) {
while (1) {
@@ -245,7 +263,7 @@ int main(void) {
}
}
// Задачи
// Create tasks
xTaskCreate(
usb_device_task,
"usbd",
@@ -254,10 +272,10 @@ int main(void) {
configMAX_PRIORITIES - 1,
NULL);
xTaskCreate(cdc_task, "cdc", 320, NULL, configMAX_PRIORITIES - 2, NULL);
xTaskCreate(led_task, "led", 128, NULL, 1, NULL);
xTaskCreate(audio_init_task, "audio_init", 128, NULL, 2, NULL);
// НОВОЕ: задача обработки FFT (высокий приоритет, большой стек для FFT)
xTaskCreate(health_led_task, "health_led", 128, NULL, 1, NULL);
xTaskCreate(audio_init_task, "audio_init", 128, NULL, 2, NULL);
xTaskCreate(
audio_process_task,
"audio_proc",
@@ -266,9 +284,14 @@ int main(void) {
configMAX_PRIORITIES - 2,
&audio_process_task_handle);
if (xTaskCreate(health_led_task, "health_led", 128, NULL, 1, NULL) !=
pdPASS) {
panic_blink_forever(100);
}
vTaskStartScheduler();
while (1);
while (1); // Should never reach here
}
// === USB Handlers ===