feat(protocol): add packet protocol
This commit is contained in:
136
client/src/protocol_parser.py
Normal file
136
client/src/protocol_parser.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
FR-1.4 Data Transmission Protocol Parser (v1)
|
||||||
|
12-byte binary packet: [0xAA][TYPE=0x02][LEN=8][TIMESTAMP(4)][RMS_DB(2)][FREQ_HZ(2)][CRC8(1)]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import struct
|
||||||
|
from typing import Optional, NamedTuple
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
|
||||||
|
class AudioMetrics(NamedTuple):
|
||||||
|
"""Parsed audio metrics packet"""
|
||||||
|
timestamp_ms: int
|
||||||
|
rms_db: float
|
||||||
|
freq_hz: int
|
||||||
|
valid: bool = True
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ProtocolStats:
|
||||||
|
"""Protocol statistics"""
|
||||||
|
packets_received: int = 0
|
||||||
|
crc_errors: int = 0
|
||||||
|
length_errors: int = 0
|
||||||
|
range_errors: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ProtocolParser:
|
||||||
|
"""
|
||||||
|
Stream parser for FR-1.4 protocol with automatic resynchronization.
|
||||||
|
"""
|
||||||
|
|
||||||
|
SOF = 0xAA
|
||||||
|
TYPE_AUDIO_V1 = 0x02
|
||||||
|
PAYLOAD_LEN = 0x08
|
||||||
|
PACKET_SIZE = 12
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.buffer = bytearray()
|
||||||
|
self.stats = ProtocolStats()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _crc8_atm(data: bytes) -> int:
|
||||||
|
"""CRC-8/ATM: poly=0x07, init=0x00, refin=false, refout=false, xorout=0x00"""
|
||||||
|
crc = 0x00
|
||||||
|
for byte in data:
|
||||||
|
crc ^= byte
|
||||||
|
for _ in range(8):
|
||||||
|
if crc & 0x80:
|
||||||
|
crc = ((crc << 1) ^ 0x07) & 0xFF
|
||||||
|
else:
|
||||||
|
crc = (crc << 1) & 0xFF
|
||||||
|
return crc
|
||||||
|
|
||||||
|
def feed(self, data: bytes) -> list[AudioMetrics]:
|
||||||
|
"""
|
||||||
|
Feed incoming bytes, return list of parsed packets.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data: Raw bytes from serial port
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of successfully parsed AudioMetrics
|
||||||
|
"""
|
||||||
|
self.buffer.extend(data)
|
||||||
|
packets = []
|
||||||
|
|
||||||
|
while len(self.buffer) >= self.PACKET_SIZE:
|
||||||
|
# Find SOF
|
||||||
|
sof_idx = self.buffer.find(self.SOF)
|
||||||
|
if sof_idx == -1:
|
||||||
|
# No SOF found, discard all but last byte
|
||||||
|
self.buffer = self.buffer[-1:]
|
||||||
|
break
|
||||||
|
|
||||||
|
# Discard bytes before SOF
|
||||||
|
if sof_idx > 0:
|
||||||
|
self.buffer = self.buffer[sof_idx:]
|
||||||
|
|
||||||
|
# Need at least 3 bytes for SOF + TYPE + LEN
|
||||||
|
if len(self.buffer) < 3:
|
||||||
|
break
|
||||||
|
|
||||||
|
packet_type = self.buffer[1]
|
||||||
|
payload_len = self.buffer[2]
|
||||||
|
|
||||||
|
# Validate TYPE and LEN
|
||||||
|
if packet_type != self.TYPE_AUDIO_V1 or payload_len != self.PAYLOAD_LEN:
|
||||||
|
self.stats.length_errors += 1
|
||||||
|
self.buffer.pop(0) # Remove false SOF, retry
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Full packet size = SOF(1) + TYPE(1) + LEN(1) + PAYLOAD(8) + CRC(1) = 12
|
||||||
|
total_len = 3 + payload_len + 1
|
||||||
|
if len(self.buffer) < total_len:
|
||||||
|
break # Wait for more data
|
||||||
|
|
||||||
|
packet = bytes(self.buffer[:total_len])
|
||||||
|
|
||||||
|
# Verify CRC (over bytes 1..10: TYPE, LEN, payload)
|
||||||
|
crc_data = packet[1:11]
|
||||||
|
expected_crc = packet[11]
|
||||||
|
calculated_crc = self._crc8_atm(crc_data)
|
||||||
|
|
||||||
|
if calculated_crc != expected_crc:
|
||||||
|
self.stats.crc_errors += 1
|
||||||
|
self.buffer.pop(0) # Remove bad packet, retry
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Parse payload (little-endian)
|
||||||
|
timestamp_ms, rms_db_x10, freq_hz = struct.unpack_from('<IhH', packet, 3)
|
||||||
|
|
||||||
|
# Convert and validate ranges
|
||||||
|
rms_db = rms_db_x10 / 10.0
|
||||||
|
valid = True
|
||||||
|
if not (-40.0 <= rms_db <= 80.0) or not (100 <= freq_hz <= 8000):
|
||||||
|
self.stats.range_errors += 1
|
||||||
|
valid = False
|
||||||
|
|
||||||
|
self.stats.packets_received += 1
|
||||||
|
packets.append(AudioMetrics(
|
||||||
|
timestamp_ms=timestamp_ms,
|
||||||
|
rms_db=rms_db,
|
||||||
|
freq_hz=freq_hz,
|
||||||
|
valid=valid
|
||||||
|
))
|
||||||
|
|
||||||
|
# Remove processed packet
|
||||||
|
self.buffer = self.buffer[total_len:]
|
||||||
|
|
||||||
|
return packets
|
||||||
|
|
||||||
|
def get_stats(self) -> ProtocolStats:
|
||||||
|
"""Get current statistics"""
|
||||||
|
return self.stats
|
||||||
56
client/src/receiver.py
Normal file
56
client/src/receiver.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Example client for FR-1.4 protocol
|
||||||
|
"""
|
||||||
|
import serial
|
||||||
|
import time
|
||||||
|
from protocol_parser import ProtocolParser
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
SERIAL_PORT = "/dev/ttyACM0"
|
||||||
|
BAUDRATE = 115200
|
||||||
|
|
||||||
|
parser = ProtocolParser()
|
||||||
|
|
||||||
|
try:
|
||||||
|
with serial.Serial(SERIAL_PORT, BAUDRATE, timeout=1) as ser:
|
||||||
|
print(f"Connected to {SERIAL_PORT}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Read available data
|
||||||
|
if ser.in_waiting > 0:
|
||||||
|
data = ser.read(ser.in_waiting)
|
||||||
|
|
||||||
|
# Parse packets
|
||||||
|
packets = parser.feed(data)
|
||||||
|
for pkt in packets:
|
||||||
|
if pkt.valid:
|
||||||
|
print(
|
||||||
|
f"[{pkt.timestamp_ms:010d}] "
|
||||||
|
f"RMS: {pkt.rms_db:+6.1f} dB "
|
||||||
|
f"Freq: {pkt.freq_hz:4d} Hz"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"[WARN] Invalid packet: {pkt}")
|
||||||
|
|
||||||
|
# Show stats every 100 packets
|
||||||
|
stats = parser.get_stats()
|
||||||
|
if stats.packets_received % 100 == 0 and stats.packets_received > 0:
|
||||||
|
print(
|
||||||
|
f"Stats: RX={stats.packets_received} "
|
||||||
|
f"CRC_err={stats.crc_errors} "
|
||||||
|
f"LEN_err={stats.length_errors} "
|
||||||
|
f"Range_err={stats.range_errors}"
|
||||||
|
)
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\nExiting...")
|
||||||
|
except serial.SerialException as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
import serial
|
|
||||||
|
|
||||||
# Настройки порта
|
|
||||||
SERIAL_PORT = "/dev/ttyACM0"
|
|
||||||
BAUD_RATE = 115200
|
|
||||||
|
|
||||||
|
|
||||||
def read_serial_data():
|
|
||||||
try:
|
|
||||||
# Открытие порта. Timeout позволяет прерывать блокирующее чтение.
|
|
||||||
ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
|
|
||||||
print(f"Connected to {SERIAL_PORT}")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if ser.in_waiting > 0:
|
|
||||||
# Чтение строки, декодирование и удаление пробелов
|
|
||||||
line = ser.readline().decode("utf-8", errors="replace").strip()
|
|
||||||
if line:
|
|
||||||
print(f"[RX]: {line}")
|
|
||||||
else:
|
|
||||||
# Небольшая пауза, чтобы не грузить CPU ПК
|
|
||||||
time.sleep(0.01)
|
|
||||||
|
|
||||||
except serial.SerialException as e:
|
|
||||||
print(f"Error opening serial port: {e}")
|
|
||||||
print(
|
|
||||||
"Hint: Check if /dev/ttyACM0 exists and you have permissions (group uucp)."
|
|
||||||
)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("\nExiting...")
|
|
||||||
except:
|
|
||||||
print("\nAn error occure!")
|
|
||||||
finally:
|
|
||||||
if "ser" in locals() and ser.is_open:
|
|
||||||
ser.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
read_serial_data()
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import serial
|
|
||||||
import time
|
|
||||||
|
|
||||||
port = serial.Serial("/dev/ttyACM0", 115200, timeout=1)
|
|
||||||
print(f"Connected to {port.name}")
|
|
||||||
|
|
||||||
port.write(b"test\n")
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
# Читаем 5 секунд
|
|
||||||
start = time.time()
|
|
||||||
while time.time() - start < 5:
|
|
||||||
if port.in_waiting:
|
|
||||||
data = port.read(port.in_waiting)
|
|
||||||
print(f"RX: {data.decode('utf-8', errors='replace')}", end="")
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
port.close()
|
|
||||||
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 "FreeRTOS.h"
|
||||||
#include "audio_adc.h"
|
#include "audio_adc.h"
|
||||||
#include "audio_processor.h" // НОВОЕ
|
#include "audio_processor.h" // НОВОЕ
|
||||||
|
#include "protocol.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "stm32f1xx.h"
|
#include "stm32f1xx.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
@@ -19,13 +20,12 @@ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) {
|
|||||||
|
|
||||||
// === Структуры данных ===
|
// === Структуры данных ===
|
||||||
|
|
||||||
// НОВОЕ: пакет с результатами FFT
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
float rms_dbfs;
|
float rms_dbfs;
|
||||||
float peak_hz;
|
float peak_hz;
|
||||||
float peak_mag;
|
float peak_mag;
|
||||||
uint8_t clipped;
|
uint8_t clipped;
|
||||||
uint32_t buffer_num;
|
uint32_t timestamp_ms;
|
||||||
} audio_metrics_packet_t;
|
} audio_metrics_packet_t;
|
||||||
|
|
||||||
static QueueHandle_t audio_metrics_queue = NULL;
|
static QueueHandle_t audio_metrics_queue = NULL;
|
||||||
@@ -119,7 +119,8 @@ void audio_process_task(void *param) {
|
|||||||
.peak_hz = metrics.peak_hz,
|
.peak_hz = metrics.peak_hz,
|
||||||
.peak_mag = metrics.peak_mag,
|
.peak_mag = metrics.peak_mag,
|
||||||
.clipped = metrics.clipped,
|
.clipped = metrics.clipped,
|
||||||
.buffer_num = buffer_counter};
|
.timestamp_ms = xTaskGetTickCount(),
|
||||||
|
};
|
||||||
xQueueSend(audio_metrics_queue, &packet, 0);
|
xQueueSend(audio_metrics_queue, &packet, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,83 +129,37 @@ void audio_process_task(void *param) {
|
|||||||
|
|
||||||
void cdc_task(void *param) {
|
void cdc_task(void *param) {
|
||||||
(void)param;
|
(void)param;
|
||||||
|
// Buffer for the FR-1.4 packet (12 bytes)
|
||||||
char tx_buffer[256];
|
uint8_t tx_buffer[PACKET_TOTAL_SIZE];
|
||||||
uint32_t heartbeat_counter = 0;
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
heartbeat_counter++;
|
// Check if USB is connected
|
||||||
|
if (tud_cdc_connected()) {
|
||||||
|
audio_metrics_packet_t packet;
|
||||||
|
|
||||||
// Heartbeat каждые 100 циклов
|
// Wait for data from DSP task
|
||||||
if (heartbeat_counter % 100 == 0 && tud_cdc_connected()) {
|
if (xQueueReceive(
|
||||||
uint32_t current_buffer_count = audio_adc_get_buffer_count();
|
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(
|
// Write to USB CDC
|
||||||
tx_buffer,
|
// Check available space just in case
|
||||||
sizeof(tx_buffer),
|
if (tud_cdc_write_available() >= sizeof(tx_buffer)) {
|
||||||
"HB:%lu Q:%u BC:%lu\r\n",
|
tud_cdc_write(tx_buffer, sizeof(tx_buffer));
|
||||||
heartbeat_counter,
|
tud_cdc_write_flush();
|
||||||
(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();
|
|
||||||
}
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ C_SOURCES = \
|
|||||||
App/Src/main.c \
|
App/Src/main.c \
|
||||||
App/Src/audio_adc.c \
|
App/Src/audio_adc.c \
|
||||||
App/Src/audio_processor.c \
|
App/Src/audio_processor.c \
|
||||||
|
App/Src/protocol.c \
|
||||||
App/Src/usb_descriptors.c \
|
App/Src/usb_descriptors.c \
|
||||||
App/Src/system_stm32f1xx.c \
|
App/Src/system_stm32f1xx.c \
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user