- 新增光泵磁力仪驱动模块,通过 USART2 中断接收 BCD 编码数据,采样率 115200bps - 重构数据包架构:引入标准包与扩展包(含光泵数据)两种类型,通过帧头魔数区分 - 新增 DataPacketWithOptic_t、CorrectedDataPacketWithOptic_t 两种扩展数据包类型 - 数据存储改为通用字节流写入(方案Y),支持任意包类型混流存储 - 将编译期配置集中到 app_config.h,包括 UART 输出、SD 存储、GPS 位置等开关 - 移除 ADC_SYNC GPIO 引脚配置,释放 PA2 用于 USART2_TX - 主循环 ProcessAdcData 改为按需选择数据包类型,光泵数据快照在 ADC 中断前完成 - 新增 USART2 错误回调处理,支持接收异常时自动恢复
560 lines
16 KiB
C
560 lines
16 KiB
C
#include "data_storage.h"
|
||
#include "system_monitor.h"
|
||
#include "config_manager.h"
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
|
||
/**
|
||
* @brief 初始化数据存储模块
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_Init(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 初始化句柄
|
||
memset(handle, 0, sizeof(DataStorageHandle_t));
|
||
|
||
// 初始化双缓冲区
|
||
for (int i = 0; i < 2; i++) {
|
||
handle->buffers[i].index = 0;
|
||
handle->buffers[i].state = BUFFER_IDLE;
|
||
memset(handle->buffers[i].data, 0, DATA_STORAGE_BUFFER_SIZE);
|
||
}
|
||
|
||
// 设置活动缓冲区为0
|
||
handle->active_buffer = 0;
|
||
handle->flush_buffer = 1;
|
||
handle->flush_in_progress = 0;
|
||
|
||
// 创建新的会话文件夹(每次上电创建新文件夹)
|
||
if (DataStorage_CreateSessionFolder(handle) != HAL_OK) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
handle->stats.state = DATA_STORAGE_IDLE;
|
||
handle->initialized = 1;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 停止数据记录
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_StopRecording(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
if (handle->stats.state != DATA_STORAGE_RECORDING) {
|
||
return HAL_OK; // 没有在记录中
|
||
}
|
||
|
||
// 刷新所有缓冲区
|
||
for (int i = 0; i < 2; i++) {
|
||
if (handle->buffers[i].index > 0) {
|
||
handle->buffers[i].state = BUFFER_READY_TO_FLUSH;
|
||
DataStorage_FlushBuffer(handle, i);
|
||
}
|
||
}
|
||
|
||
// 强制同步所有数据到SD卡
|
||
f_sync(&handle->file);
|
||
|
||
// 关闭文件
|
||
f_close(&handle->file);
|
||
|
||
handle->stats.state = DATA_STORAGE_IDLE;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 写入数据包到存储
|
||
* @param handle: 数据存储句柄指针
|
||
* @param packet: 数据包指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_WriteData(DataStorageHandle_t *handle, const DataPacket_t *packet)
|
||
{
|
||
if (handle == NULL || packet == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
if (handle->stats.state != DATA_STORAGE_RECORDING) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer];
|
||
|
||
// 检查当前活动缓冲区空间
|
||
if (active_buf->index + sizeof(DataPacket_t) > DATA_STORAGE_BUFFER_SIZE) {
|
||
// 切换缓冲区
|
||
if (DataStorage_SwitchBuffer(handle) != HAL_OK) {
|
||
handle->stats.error_count++;
|
||
return HAL_ERROR;
|
||
}
|
||
active_buf = &handle->buffers[handle->active_buffer];
|
||
}
|
||
|
||
// 复制数据到活动缓冲区
|
||
memcpy(&active_buf->data[active_buf->index], packet, sizeof(DataPacket_t));
|
||
active_buf->index += sizeof(DataPacket_t);
|
||
active_buf->state = BUFFER_WRITING;
|
||
handle->stats.total_samples++;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 通用字节流写入,支持任意包长度(方案Y混流存储)
|
||
* @param handle: 数据存储句柄指针
|
||
* @param data: 数据缓冲区指针
|
||
* @param size: 写入字节数
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_WriteRawBytes(DataStorageHandle_t *handle, const uint8_t *data, uint16_t size)
|
||
{
|
||
if (handle == NULL || data == NULL || !handle->initialized || size == 0) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
if (handle->stats.state != DATA_STORAGE_RECORDING) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer];
|
||
|
||
if (active_buf->index + size > DATA_STORAGE_BUFFER_SIZE) {
|
||
if (DataStorage_SwitchBuffer(handle) != HAL_OK) {
|
||
handle->stats.error_count++;
|
||
return HAL_ERROR;
|
||
}
|
||
active_buf = &handle->buffers[handle->active_buffer];
|
||
}
|
||
|
||
memcpy(&active_buf->data[active_buf->index], data, size);
|
||
active_buf->index += size;
|
||
active_buf->state = BUFFER_WRITING;
|
||
handle->stats.total_samples++;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 写入校正后的数据包到存储
|
||
* @param handle: 数据存储句柄指针
|
||
* @param packet: 数据包指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_WriteCorrectedData(DataStorageHandle_t *handle, const CorrectedDataPacket_t *packet)
|
||
{
|
||
if (handle == NULL || packet == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
if (handle->stats.state != DATA_STORAGE_RECORDING) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer];
|
||
|
||
// 检查当前活动缓冲区空间
|
||
if (active_buf->index + sizeof(CorrectedDataPacket_t) > DATA_STORAGE_BUFFER_SIZE) {
|
||
// 切换缓冲区
|
||
if (DataStorage_SwitchBuffer(handle) != HAL_OK) {
|
||
handle->stats.error_count++;
|
||
return HAL_ERROR;
|
||
}
|
||
active_buf = &handle->buffers[handle->active_buffer];
|
||
}
|
||
|
||
// 复制校正后的数据到活动缓冲区
|
||
memcpy(&active_buf->data[active_buf->index], packet, sizeof(CorrectedDataPacket_t));
|
||
active_buf->index += sizeof(CorrectedDataPacket_t);
|
||
active_buf->state = BUFFER_WRITING;
|
||
handle->stats.total_samples++;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 刷新缓冲区到文件
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_Flush(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 处理后台刷新任务
|
||
DataStorage_ProcessBackgroundTasks(handle);
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 创建新的数据文件
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_CreateNewFile(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 生成文件名 (基于时间戳),文件存储在当前会话文件夹中
|
||
uint32_t timestamp = HAL_GetTick();
|
||
snprintf(handle->stats.current_filename, sizeof(handle->stats.current_filename),
|
||
"%s%s%08lX.dat", handle->current_session_path, DATA_STORAGE_FILE_PREFIX, timestamp);
|
||
|
||
// 创建并打开文件
|
||
FRESULT res = f_open(&handle->file, handle->stats.current_filename,
|
||
FA_CREATE_ALWAYS | FA_WRITE);
|
||
|
||
if (res != FR_OK) {
|
||
handle->stats.error_count++;
|
||
SystemMonitor_ReportSDWriteError(); // 报告文件创建错误
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
handle->stats.file_count++;
|
||
handle->stats.current_file_size = 0;
|
||
SystemMonitor_ReportSDFileCreated(); // 报告文件创建成功
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取数据存储统计信息
|
||
* @param handle: 数据存储句柄指针
|
||
* @param stats: 统计信息结构体指针
|
||
* @retval None
|
||
*/
|
||
void DataStorage_GetStats(DataStorageHandle_t *handle, DataStorageStats_t *stats)
|
||
{
|
||
if (handle == NULL || stats == NULL || !handle->initialized) {
|
||
return;
|
||
}
|
||
|
||
memcpy(stats, &handle->stats, sizeof(DataStorageStats_t));
|
||
}
|
||
|
||
/**
|
||
* @brief 开始数据记录
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_StartRecording(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
if (handle->stats.state == DATA_STORAGE_RECORDING) {
|
||
return HAL_OK; // 已经在记录中
|
||
}
|
||
|
||
// 创建新文件
|
||
if (DataStorage_CreateNewFile(handle) != HAL_OK) {
|
||
handle->stats.state = DATA_STORAGE_ERROR;
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 重置双缓冲区状态
|
||
for (int i = 0; i < 2; i++) {
|
||
handle->buffers[i].index = 0;
|
||
handle->buffers[i].state = BUFFER_IDLE;
|
||
}
|
||
handle->active_buffer = 0;
|
||
handle->flush_buffer = 1;
|
||
handle->flush_in_progress = 0;
|
||
|
||
handle->stats.state = DATA_STORAGE_RECORDING;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 刷新指定缓冲区到文件
|
||
* @param handle: 数据存储句柄指针
|
||
* @param buffer_index: 缓冲区索引 (0 或 1)
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_FlushBuffer(DataStorageHandle_t *handle, uint8_t buffer_index)
|
||
{
|
||
if (handle == NULL || !handle->initialized || buffer_index > 1) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
DataBuffer_t *buffer = &handle->buffers[buffer_index];
|
||
|
||
// 检查缓冲区状态和数据
|
||
if (buffer->state != BUFFER_READY_TO_FLUSH || buffer->index == 0) {
|
||
return HAL_OK; // 没有数据需要刷新
|
||
}
|
||
|
||
// 标记缓冲区正在刷新
|
||
buffer->state = BUFFER_FLUSHING;
|
||
|
||
UINT bytes_written;
|
||
FRESULT res = f_write(&handle->file, buffer->data, buffer->index, &bytes_written);
|
||
|
||
if (res != FR_OK || bytes_written != buffer->index) {
|
||
handle->stats.error_count++;
|
||
buffer->state = BUFFER_READY_TO_FLUSH; // 恢复状态以便重试
|
||
SystemMonitor_ReportSDWriteError(); // 报告SD卡写入错误
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 优化:减少同步频率以提高性能
|
||
// 使用静态变量记录刷新次数
|
||
static uint32_t flush_count = 0;
|
||
flush_count++;
|
||
|
||
// 每10次刷新才同步一次,或者文件即将达到最大大小时同步
|
||
if (flush_count % 10 == 0 ||
|
||
handle->stats.current_file_size + bytes_written >= DATA_STORAGE_FILE_MAX_SIZE) {
|
||
f_sync(&handle->file);
|
||
}
|
||
|
||
// 更新统计信息
|
||
handle->stats.current_file_size += bytes_written;
|
||
SystemMonitor_ReportSDWrite(bytes_written); // 报告SD卡写入成功
|
||
|
||
// 重置缓冲区
|
||
buffer->index = 0;
|
||
buffer->state = BUFFER_IDLE;
|
||
|
||
// 检查文件大小是否超过限制
|
||
if (handle->stats.current_file_size >= DATA_STORAGE_FILE_MAX_SIZE) {
|
||
f_close(&handle->file);
|
||
DataStorage_CreateNewFile(handle);
|
||
}
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 处理后台任务(异步刷新缓冲区)
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval None
|
||
*/
|
||
void DataStorage_ProcessBackgroundTasks(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return;
|
||
}
|
||
|
||
// 检查是否有缓冲区需要刷新
|
||
for (int i = 0; i < 2; i++) {
|
||
if (handle->buffers[i].state == BUFFER_READY_TO_FLUSH) {
|
||
// 刷新缓冲区
|
||
DataStorage_FlushBuffer(handle, i);
|
||
break; // 一次只处理一个缓冲区,避免阻塞太久
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 切换活动缓冲区
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_SwitchBuffer(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
DataBuffer_t *current_buf = &handle->buffers[handle->active_buffer];
|
||
|
||
// 标记当前缓冲区准备刷新
|
||
if (current_buf->index > 0) {
|
||
current_buf->state = BUFFER_READY_TO_FLUSH;
|
||
}
|
||
|
||
// 切换到另一个缓冲区
|
||
uint8_t next_buffer = (handle->active_buffer == 0) ? 1 : 0;
|
||
DataBuffer_t *next_buf = &handle->buffers[next_buffer];
|
||
|
||
// 检查目标缓冲区是否可用
|
||
if (next_buf->state == BUFFER_FLUSHING) {
|
||
// 目标缓冲区正在刷新,等待完成
|
||
handle->stats.error_count++;
|
||
SystemMonitor_ReportSDBufferFull(); // 报告缓冲区满
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 切换活动缓冲区
|
||
handle->active_buffer = next_buffer;
|
||
next_buf->index = 0;
|
||
next_buf->state = BUFFER_WRITING;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查缓冲区是否有足够空间可用
|
||
* @param handle: 数据存储句柄指针
|
||
* @param required_size: 需要的空间大小(字节)
|
||
* @retval 1: 缓冲区可用, 0: 缓冲区不可用
|
||
*/
|
||
uint8_t DataStorage_IsBufferAvailable(DataStorageHandle_t *handle, uint32_t required_size)
|
||
{
|
||
if (handle == NULL || !handle->initialized) {
|
||
return 0;
|
||
}
|
||
|
||
// 如果未在记录状态,返回不可用
|
||
if (handle->stats.state != DATA_STORAGE_RECORDING) {
|
||
return 0;
|
||
}
|
||
|
||
DataBuffer_t *active_buf = &handle->buffers[handle->active_buffer];
|
||
|
||
// 检查当前活动缓冲区是否有足够空间
|
||
if (active_buf->index + required_size <= DATA_STORAGE_BUFFER_SIZE) {
|
||
return 1; // 当前缓冲区有足够空间
|
||
}
|
||
|
||
// 当前缓冲区空间不足,检查是否可以切换到另一个缓冲区
|
||
uint8_t next_buffer = (handle->active_buffer == 0) ? 1 : 0;
|
||
DataBuffer_t *next_buf = &handle->buffers[next_buffer];
|
||
|
||
// 如果另一个缓冲区正在刷新或准备刷新,则不可用
|
||
if (next_buf->state == BUFFER_FLUSHING || next_buf->state == BUFFER_READY_TO_FLUSH) {
|
||
return 0; // 无法切换,缓冲区不可用
|
||
}
|
||
|
||
// 另一个缓冲区可用
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* @brief 创建新的会话文件夹
|
||
* @param handle: 数据存储句柄指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_CreateSessionFolder(DataStorageHandle_t *handle)
|
||
{
|
||
if (handle == NULL) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 从配置管理器获取并递增会话序号
|
||
uint32_t session_number = Config_IncrementSessionNumber();
|
||
|
||
// 生成会话文件夹名(基于序号)
|
||
snprintf(handle->current_session_path, sizeof(handle->current_session_path),
|
||
"%s/%s%06lu", DATA_STORAGE_BASE_PATH, DATA_STORAGE_FOLDER_PREFIX, session_number);
|
||
|
||
// 创建基础数据目录(如果不存在)
|
||
FRESULT res = f_mkdir(DATA_STORAGE_BASE_PATH);
|
||
if (res != FR_OK && res != FR_EXIST) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 创建会话文件夹
|
||
res = f_mkdir(handle->current_session_path);
|
||
if (res != FR_OK && res != FR_EXIST) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 保存更新后的配置(包含新的会话序号)
|
||
if (Config_Save() != HAL_OK) {
|
||
// 即使保存失败,也继续使用该文件夹
|
||
// 这不是致命错误
|
||
}
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 从文件加载会话序号
|
||
* @param session_number: 用于存储序号的指针
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_LoadSessionNumber(uint32_t *session_number)
|
||
{
|
||
if (session_number == NULL) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
FIL file;
|
||
FRESULT res;
|
||
UINT bytes_read;
|
||
char buffer[16];
|
||
|
||
// 打开PARAM.TXT文件
|
||
res = f_open(&file, DATA_STORAGE_PARAM_FILE, FA_READ);
|
||
if (res != FR_OK) {
|
||
// 文件不存在,返回初始序号0
|
||
*session_number = 0;
|
||
return HAL_OK;
|
||
}
|
||
|
||
// 读取序号
|
||
res = f_read(&file, buffer, sizeof(buffer) - 1, &bytes_read);
|
||
if (res != FR_OK) {
|
||
f_close(&file);
|
||
*session_number = 0;
|
||
return HAL_OK;
|
||
}
|
||
|
||
// 添加字符串结束符
|
||
buffer[bytes_read] = '\0';
|
||
|
||
// 关闭文件
|
||
f_close(&file);
|
||
|
||
// 转换为数字
|
||
*session_number = (uint32_t)atoi(buffer);
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 保存会话序号到文件
|
||
* @param session_number: 要保存的序号
|
||
* @retval HAL_StatusTypeDef
|
||
*/
|
||
HAL_StatusTypeDef DataStorage_SaveSessionNumber(uint32_t session_number)
|
||
{
|
||
FIL file;
|
||
FRESULT res;
|
||
UINT bytes_written;
|
||
char buffer[16];
|
||
|
||
// 创建或覆盖PARAM.TXT文件
|
||
res = f_open(&file, DATA_STORAGE_PARAM_FILE, FA_CREATE_ALWAYS | FA_WRITE);
|
||
if (res != FR_OK) {
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 将序号转换为字符串
|
||
snprintf(buffer, sizeof(buffer), "%lu", session_number);
|
||
|
||
// 写入序号
|
||
res = f_write(&file, buffer, strlen(buffer), &bytes_written);
|
||
if (res != FR_OK || bytes_written != strlen(buffer)) {
|
||
f_close(&file);
|
||
return HAL_ERROR;
|
||
}
|
||
|
||
// 关闭文件
|
||
f_close(&file);
|
||
|
||
return HAL_OK;
|
||
}
|