跳转至

代码

Warning

以下代码应基于发布代码中的代码,可能已更新。

arch_common.h

/**
 * @file arch_common.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief 实时处理架构的通用数据结构和定义
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 */

#pragma once

#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * COMMON DATA STRUCTURES
 * ============================================================================ */

/**
 * @brief 实时处理样本数据结构
 * @note 与 offline_sensing_sample_t 兼容,便于集成
 */
typedef struct
{
    float x;                    ///< X轴加速度 (g)
    float y;                    ///< Y轴加速度 (g)
    float z;                    ///< Z轴加速度 (g)
    float temp;                 ///< 温度 (°C)
    uint64_t timestamp_us;      ///< 时间戳(微秒)
} rt_process_sample_t;

/**
 * @brief 实时处理结果结构
 */
typedef struct
{
    float processed_x;          ///< 处理后的X轴加速度
    float processed_y;          ///< 处理后的Y轴加速度
    float processed_z;          ///< 处理后的Z轴加速度
    float features[8];          ///< 提取的特征(可扩展)
    uint64_t process_time_us;   ///< 处理时间(微秒)
} rt_process_result_t;

/**
 * @brief 架构类型枚举
 */
typedef enum
{
    RT_ARCH_PRODUCER_CONSUMER = 0,  ///< 架构1:Producer-Consumer 队列
    RT_ARCH_DMA_DOUBLE_BUFFER,      ///< 架构2:DMA + 双缓冲 + 多任务
    RT_ARCH_DMA_DUAL_CORE           ///< 架构3:DMA + 双核分工
} rt_process_arch_type_t;

/**
 * @brief 性能统计结构
 */
typedef struct
{
    uint32_t total_samples;         ///< 采集的样本总数
    uint32_t processed_samples;     ///< 已处理的样本数
    uint32_t dropped_samples;       ///< 丢弃的样本数(队列/缓冲区满)
    float avg_acquisition_time_us;  ///< 每个样本的平均采集时间
    float avg_process_time_us;      ///< 每个样本的平均处理时间
    float cpu_usage_core0;          ///< Core 0 的 CPU 使用率(%)
    float cpu_usage_core1;          ///< Core 1 的 CPU 使用率(%)(用于双核架构)
    uint64_t last_sample_time_us;   ///< 最后一个样本的时间戳
} rt_process_stats_t;

/**
 * @brief 架构状态结构
 */
typedef struct
{
    bool is_running;                ///< 架构是否正在运行
    rt_process_arch_type_t arch_type; ///< 当前架构类型
    float sampling_frequency_hz;     ///< 当前采样频率
    uint32_t queue_usage;           ///< 队列使用情况(用于 producer-consumer)
    uint32_t buffer_usage;          ///< 缓冲区使用情况(用于 DMA 架构)
} rt_process_status_t;

/**
 * @brief 实时处理配置结构
 */
typedef struct
{
    float sampling_frequency_hz;     ///< 采样频率(Hz,默认:100.0)
    uint32_t queue_size;             ///< Producer-Consumer 的队列大小(默认:50)
    bool enable_mqtt;                ///< 启用 MQTT 输出(默认:false)
    bool enable_serial;              ///< 启用串口输出(默认:true)
    bool enable_accel_detection;     ///< 启用加速度检测和 LCD 反馈(默认:false)
    const char *mqtt_topic;         ///< MQTT 主题(默认:NULL 使用默认主题)
} rt_process_config_t;

#ifdef __cplusplus
}
#endif

real-time-process-arch.h

/**
 * @file real-time-process-arch.h
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief 实时处理架构的统一接口
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 * @details
 * 本头文件为三种实时处理架构提供统一接口:
 * 1. Producer-Consumer 环形缓冲区(默认)
 * 2. DMA + 双缓冲 + 多任务
 * 3. DMA + 双核分工
 *
 * 架构选择通过编译时 RT_PROCESS_ARCH_TYPE 宏完成。
 * 默认:RT_ARCH_PRODUCER_CONSUMER
 */

#pragma once

#include "arch_common.h"  // Includes rt_process_config_t
#include "node_acc_adxl355.h"
#include "esp_err.h"

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * COMPILE-TIME ARCHITECTURE SELECTION
 * ============================================================================ */

/**
 * @brief 编译时架构类型选择
 * @note 默认:RT_ARCH_PRODUCER_CONSUMER
 * @note 要更改:在包含此头文件之前定义 RT_PROCESS_ARCH_TYPE
 */
#ifndef RT_PROCESS_ARCH_TYPE
#define RT_PROCESS_ARCH_TYPE RT_ARCH_PRODUCER_CONSUMER
#endif

/* ============================================================================
 * FUNCTION DECLARATIONS
 * ============================================================================ */

/**
 * @brief 初始化实时处理架构
 * @param config 配置结构(NULL 使用默认配置)
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_init(const rt_process_config_t *config);

/**
 * @brief 设置 ADXL355 传感器句柄(启动前必须调用)
 * @param handle ADXL355 传感器句柄指针
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_set_sensor_handle(adxl355_handle_t *handle);

/**
 * @brief 启动实时处理
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_start(void);

/**
 * @brief 停止实时处理
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_stop(void);

/**
 * @brief 反初始化实时处理架构
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_deinit(void);

/**
 * @brief 获取当前状态
 * @param status 状态输出参数
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_get_status(rt_process_status_t *status);

/**
 * @brief 获取性能统计
 * @param stats 统计输出参数
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_get_stats(rt_process_stats_t *stats);

/**
 * @brief 运行时更新采样频率
 * @param frequency_hz 新的采样频率(Hz)
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_set_frequency(float frequency_hz);

/**
 * @brief 检查实时处理是否正在运行
 * @param is_running 输出参数:true 表示正在运行,false 表示未运行
 * @return 成功返回 ESP_OK,失败返回错误代码
 */
esp_err_t rt_process_is_running(bool *is_running);

/**
 * @brief 启用或禁用加速度检测和 LCD 反馈
 * @param enable true 启用检测,false 禁用
 * @return 成功返回 ESP_OK,失败返回错误代码
 * @note 适用于所有三种架构
 */
esp_err_t rt_process_set_accel_detection(bool enable);

#ifdef __cplusplus
}
#endif

real-time-process-arch.cpp

/**
 * @file real-time-process-arch.cpp
 * @author SHUAIWEN CUI (SHUAIWEN001@e.ntu.edu.sg)
 * @brief 实时处理架构的统一接口实现
 * @version 1.0
 * @date 2025-01-17
 * @copyright Copyright (c) 2025
 *
 * @details
 * 本文件提供统一接口,根据 RT_PROCESS_ARCH_TYPE 编译时宏将调用路由到
 * 选定的架构实现。
 */

#include "real-time-process-arch.h"

// 包含选定的架构实现
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    #include "arch_producer_consumer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    #include "arch_dma_double_buffer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    #include "arch_dma_dual_core.h"
#else
    #error "Unknown RT_PROCESS_ARCH_TYPE"
#endif

#ifdef __cplusplus
extern "C"
{
#endif

/* ============================================================================
 * UNIFIED INTERFACE IMPLEMENTATION
 * ============================================================================ */

esp_err_t rt_process_init(const rt_process_config_t *config)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_init(config);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_init(config);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_init(config);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_sensor_handle(adxl355_handle_t *handle)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_sensor_handle(handle);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_sensor_handle(handle);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_sensor_handle(handle);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_start(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_start();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_stop(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_stop();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_stop();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_stop();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_deinit(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_deinit();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_deinit();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_deinit();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_get_status(rt_process_status_t *status)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_get_status(status);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_get_status(status);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_get_status(status);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_get_stats(rt_process_stats_t *stats)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_get_stats(stats);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_get_stats(stats);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_get_stats(stats);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_frequency(float frequency_hz)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_frequency(frequency_hz);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_frequency(frequency_hz);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_frequency(frequency_hz);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_is_running(bool *is_running)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_is_running(is_running);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_is_running(is_running);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_is_running(is_running);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

esp_err_t rt_process_set_accel_detection(bool enable)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_set_accel_detection(enable);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_set_accel_detection(enable);
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_set_accel_detection(enable);
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

#ifdef __cplusplus
}
#endif

关键实现

编译时架构选择

统一接口使用编译时宏 RT_PROCESS_ARCH_TYPE 来选择使用哪个架构实现。这种方法:

  • 零运行时开销:无函数指针间接调用或运行时检查
  • 类型安全:编译时验证架构选择
  • 代码优化:编译器可以为选定的架构进行优化
  • 单一二进制:每个架构一个二进制(编译时选择)

统一接口路由

每个 API 函数使用预处理器条件将调用路由到相应的架构特定函数:

esp_err_t rt_process_start(void)
{
#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    return arch_pc_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    return arch_dma_db_start();
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    return arch_dma_dc_start();
#else
    return ESP_ERR_NOT_SUPPORTED;
#endif
}

架构特定头文件

实现文件根据选定的类型包含相应的架构头文件:

#if RT_PROCESS_ARCH_TYPE == RT_ARCH_PRODUCER_CONSUMER
    #include "arch_producer_consumer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DOUBLE_BUFFER
    #include "arch_dma_double_buffer.h"
#elif RT_PROCESS_ARCH_TYPE == RT_ARCH_DMA_DUAL_CORE
    #include "arch_dma_dual_core.h"
#else
    #error "Unknown RT_PROCESS_ARCH_TYPE"
#endif

使用示例

基本使用

#include "real-time-process-arch.h"
#include "node_acc_adxl355.h"

// 初始化传感器
adxl355_handle_t *adxl355_handle = adxl355_init(...);

// 配置实时处理
rt_process_config_t config = {
    .sampling_frequency_hz = 100.0f,
    .queue_size = 50,
    .enable_mqtt = false,
    .enable_serial = true,
    .enable_accel_detection = false,
    .mqtt_topic = NULL
};

// 初始化架构
esp_err_t ret = rt_process_init(&config);
if (ret != ESP_OK) {
    ESP_LOGE("App", "初始化实时处理失败");
    return;
}

// 设置传感器句柄
ret = rt_process_set_sensor_handle(adxl355_handle);
if (ret != ESP_OK) {
    ESP_LOGE("App", "设置传感器句柄失败");
    return;
}

// 启动处理
ret = rt_process_start();
if (ret != ESP_OK) {
    ESP_LOGE("App", "启动实时处理失败");
    return;
}

// ... 应用程序代码 ...

// 监控统计
rt_process_stats_t stats;
rt_process_get_stats(&stats);
ESP_LOGI("App", "总样本数: %lu, 已处理: %lu, 丢弃: %lu",
         stats.total_samples, stats.processed_samples, stats.dropped_samples);

// 停止处理
rt_process_stop();

// 反初始化
rt_process_deinit();

选择不同架构

// 选择架构2:DMA + 双缓冲
#define RT_PROCESS_ARCH_TYPE RT_ARCH_DMA_DOUBLE_BUFFER
#include "real-time-process-arch.h"

// 其余代码保持不变
rt_process_init(&config);
rt_process_set_sensor_handle(adxl355_handle);
rt_process_start();

运行时频率更新

// 运行时更新采样频率
esp_err_t ret = rt_process_set_frequency(200.0f);
if (ret != ESP_OK) {
    ESP_LOGE("App", "更新频率失败");
}

状态监控

rt_process_status_t status;
rt_process_get_status(&status);

ESP_LOGI("App", "运行中: %s, 频率: %.2f Hz, 队列使用: %lu",
         status.is_running ? "是" : "否",
         status.sampling_frequency_hz,
         status.queue_usage);