feat(protocol): add packet protocol
This commit is contained in:
40
firmware/App/Inc/protocol.h
Normal file
40
firmware/App/Inc/protocol.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef PROTOCOL_H
|
||||
#define PROTOCOL_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Protocol Constants
|
||||
#define PROTOCOL_SOF 0xAA
|
||||
#define PACKET_TYPE_AUDIO 0x02
|
||||
#define PACKET_LEN_V1 0x08 // Payload length (excluding SOF, TYPE, LEN, CRC)
|
||||
#define PACKET_TOTAL_SIZE 12
|
||||
|
||||
// CRC8-ATM Constants
|
||||
#define CRC8_POLY 0x07
|
||||
#define CRC8_INIT 0x00
|
||||
|
||||
/**
|
||||
* @brief Calculates CRC-8/ATM over the data buffer.
|
||||
* Polynomial: x^8 + x^2 + x + 1 (0x07)
|
||||
* Init: 0x00, RefIn: false, RefOut: false, XorOut: 0x00
|
||||
* @param data Pointer to data buffer
|
||||
* @param len Length of data
|
||||
* @return Calculated CRC8
|
||||
*/
|
||||
uint8_t crc8_atm(const uint8_t *data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Encodes the audio metric packet into the wire format.
|
||||
* @param buf Output buffer (must be at least 12 bytes)
|
||||
* @param timestamp_ms Timestamp in milliseconds
|
||||
* @param rms_dbfs RMS value in dBFS (float)
|
||||
* @param freq_hz Peak frequency in Hz (float)
|
||||
*/
|
||||
void protocol_pack_v1(
|
||||
uint8_t *buf,
|
||||
uint32_t timestamp_ms,
|
||||
float rms_dbfs,
|
||||
float freq_hz);
|
||||
|
||||
#endif // PROTOCOL_H
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "FreeRTOS.h"
|
||||
#include "audio_adc.h"
|
||||
#include "audio_processor.h" // НОВОЕ
|
||||
#include "protocol.h"
|
||||
#include "queue.h"
|
||||
#include "stm32f1xx.h"
|
||||
#include "task.h"
|
||||
@@ -19,13 +20,12 @@ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
|
||||
|
||||
// === Структуры данных ===
|
||||
|
||||
// НОВОЕ: пакет с результатами FFT
|
||||
typedef struct {
|
||||
float rms_dbfs;
|
||||
float peak_hz;
|
||||
float peak_mag;
|
||||
uint8_t clipped;
|
||||
uint32_t buffer_num;
|
||||
uint32_t timestamp_ms;
|
||||
} audio_metrics_packet_t;
|
||||
|
||||
static QueueHandle_t audio_metrics_queue = NULL;
|
||||
@@ -119,7 +119,8 @@ void audio_process_task(void *param) {
|
||||
.peak_hz = metrics.peak_hz,
|
||||
.peak_mag = metrics.peak_mag,
|
||||
.clipped = metrics.clipped,
|
||||
.buffer_num = buffer_counter};
|
||||
.timestamp_ms = xTaskGetTickCount(),
|
||||
};
|
||||
xQueueSend(audio_metrics_queue, &packet, 0);
|
||||
}
|
||||
}
|
||||
@@ -128,83 +129,37 @@ void audio_process_task(void *param) {
|
||||
|
||||
void cdc_task(void *param) {
|
||||
(void)param;
|
||||
|
||||
char tx_buffer[256];
|
||||
uint32_t heartbeat_counter = 0;
|
||||
// Buffer for the FR-1.4 packet (12 bytes)
|
||||
uint8_t tx_buffer[PACKET_TOTAL_SIZE];
|
||||
|
||||
while (1) {
|
||||
heartbeat_counter++;
|
||||
// Check if USB is connected
|
||||
if (tud_cdc_connected()) {
|
||||
audio_metrics_packet_t packet;
|
||||
|
||||
// Heartbeat каждые 100 циклов
|
||||
if (heartbeat_counter % 100 == 0 && tud_cdc_connected()) {
|
||||
uint32_t current_buffer_count = audio_adc_get_buffer_count();
|
||||
// Wait for data from DSP task
|
||||
if (xQueueReceive(
|
||||
audio_metrics_queue,
|
||||
&packet,
|
||||
pdMS_TO_TICKS(10)) == pdPASS) {
|
||||
// Pack data according to FR-1.4 spec
|
||||
protocol_pack_v1(
|
||||
tx_buffer,
|
||||
packet.timestamp_ms,
|
||||
packet.rms_dbfs,
|
||||
packet.peak_hz);
|
||||
|
||||
int len = snprintf(
|
||||
tx_buffer,
|
||||
sizeof(tx_buffer),
|
||||
"HB:%lu Q:%u BC:%lu\r\n",
|
||||
heartbeat_counter,
|
||||
(unsigned)uxQueueMessagesWaiting(audio_metrics_queue),
|
||||
current_buffer_count);
|
||||
|
||||
if (len > 0 && tud_cdc_write_available() >= (uint32_t)len) {
|
||||
tud_cdc_write(tx_buffer, (uint32_t)len);
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
}
|
||||
|
||||
// Метрики 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 RMS:%ld.%01ld dBFS Freq:%ld.%01ld Hz Mag:%ld.%03ld "
|
||||
"Clip:%u\r\n",
|
||||
packet.buffer_num,
|
||||
(long)rms_int,
|
||||
(long)rms_frac,
|
||||
(long)freq_int,
|
||||
(long)freq_frac,
|
||||
(long)mag_int,
|
||||
(long)mag_frac,
|
||||
(unsigned)packet.clipped);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Echo
|
||||
if (tud_cdc_available()) {
|
||||
uint8_t buf[64];
|
||||
uint32_t count = tud_cdc_read(buf, sizeof(buf));
|
||||
if (tud_cdc_connected() && count > 0) {
|
||||
tud_cdc_write(buf, count);
|
||||
tud_cdc_write_flush();
|
||||
// Write to USB CDC
|
||||
// Check available space just in case
|
||||
if (tud_cdc_write_available() >= sizeof(tx_buffer)) {
|
||||
tud_cdc_write(tx_buffer, sizeof(tx_buffer));
|
||||
tud_cdc_write_flush();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Flush queue if USB not connected to prevent stalling DSP task
|
||||
// or just sleep longer.
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
firmware/App/Src/protocol.c
Normal file
51
firmware/App/Src/protocol.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "protocol.h"
|
||||
#include <math.h>
|
||||
|
||||
uint8_t crc8_atm(const uint8_t *data, size_t len) {
|
||||
uint8_t crc = CRC8_INIT;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
if (crc & 0x80) {
|
||||
crc = (crc << 1) ^ CRC8_POLY;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void protocol_pack_v1(
|
||||
uint8_t *buf,
|
||||
uint32_t timestamp_ms,
|
||||
float rms_dbfs,
|
||||
float freq_hz) {
|
||||
// Header
|
||||
buf[0] = PROTOCOL_SOF;
|
||||
buf[1] = PACKET_TYPE_AUDIO;
|
||||
buf[2] = PACKET_LEN_V1;
|
||||
|
||||
// Payload: Timestamp (4 bytes, Little Endian)
|
||||
buf[3] = (uint8_t)(timestamp_ms & 0xFF);
|
||||
buf[4] = (uint8_t)((timestamp_ms >> 8) & 0xFF);
|
||||
buf[5] = (uint8_t)((timestamp_ms >> 16) & 0xFF);
|
||||
buf[6] = (uint8_t)((timestamp_ms >> 24) & 0xFF);
|
||||
|
||||
// Payload: RMS_DB (2 bytes, Little Endian, x10, int16)
|
||||
// Range check implicit by int16 cast, but clamping is safer
|
||||
// Spec: -40..80 dB -> -400..800
|
||||
// Note: Since DSP returns dBFS (negative), we just send it as is.
|
||||
// E.g. -60.5 dB -> -605.
|
||||
int16_t rms_fixed = (int16_t)(rms_dbfs * 10.0f);
|
||||
buf[7] = (uint8_t)(rms_fixed & 0xFF);
|
||||
buf[8] = (uint8_t)((rms_fixed >> 8) & 0xFF);
|
||||
|
||||
// Payload: FREQ_HZ (2 bytes, Little Endian, uint16)
|
||||
uint16_t freq_fixed = (uint16_t)freq_hz;
|
||||
buf[9] = (uint8_t)(freq_fixed & 0xFF);
|
||||
buf[10] = (uint8_t)((freq_fixed >> 8) & 0xFF);
|
||||
|
||||
// CRC8 (Calculated over bytes 1..10: TYPE, LEN, Payload)
|
||||
buf[11] = crc8_atm(&buf[1], 10);
|
||||
}
|
||||
Reference in New Issue
Block a user