- 新增性能监控模块(performance_monitor),用于实时跟踪系统性能指标 - 添加串口调试输出功能,支持系统状态和性能统计的定期输出 - 实现双缓冲机制,提升ADC数据采集和存储的实时性 - 优化数据存储模块,支持校正后数据的存储和双缓冲管理 - 增强错误处理机制,完善中断回调函数和系统错误恢复 ♻️ refactor(ltc2508): 重构ADC驱动支持双缓冲 - 将ADC数据存储从单缓冲区重构为双缓冲区结构 - 新增缓冲区状态管理和自动切换机制 - 优化DMA传输完成回调,支持多缓冲区处理 - 提供缓冲区获取和释放的API接口 📝 docs(performance): 新增性能评估报告和使用指南 - 创建STM32F405性能评估报告,详细分析系统性能指标 - 编写双缓冲机制使用指南,说明实现原理和使用方法 - 添加LTC2508驱动使用示例代码 🐛 fix(dma): 调整DMA中断优先级 - 将DMA2_Stream7中断优先级从9调整为6,优化中断响应 - 更新STM32CubeMX配置文件中的中断优先级设置 🔧 chore(config): 优化系统配置和代码结构 - 添加串口调试输出控制开关和间隔配置 - 清理中断处理文件,移除重复的回调函数定义 - 增强错误处理函数,添加系统状态恢复机制
311 lines
9.4 KiB
C
311 lines
9.4 KiB
C
#include "ltc2508_driver.h"
|
||
#include <string.h> // For memset
|
||
|
||
// 全局变量定义 - 双缓冲区实现
|
||
volatile LTC2508_BufferTypeDef g_adc_buffers[LTC2508_BUFFER_COUNT] = {0};
|
||
volatile uint8_t g_adc_data_ready_flag = 0;
|
||
volatile uint8_t g_dma_complete_count = 0;
|
||
volatile uint8_t g_current_write_buffer = 0;
|
||
volatile uint8_t g_current_read_buffer = 0;
|
||
|
||
// 错误统计信息
|
||
LTC2508_StatsTypeDef g_ltc2508_stats = {0};
|
||
|
||
// SPI 句柄指针
|
||
static SPI_HandleTypeDef *g_hspi1 = NULL;
|
||
static SPI_HandleTypeDef *g_hspi2 = NULL;
|
||
static SPI_HandleTypeDef *g_hspi3 = NULL;
|
||
|
||
/**
|
||
* @brief 初始化 LTC2508 驱动
|
||
* @param hspi1: SPI1 句柄指针
|
||
* @param hspi2: SPI2 句柄指针
|
||
* @param hspi3: SPI3 句柄指针
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_Init(SPI_HandleTypeDef *hspi1, SPI_HandleTypeDef *hspi2, SPI_HandleTypeDef *hspi3)
|
||
{
|
||
// 参数检查
|
||
if (hspi1 == NULL || hspi2 == NULL || hspi3 == NULL) {
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_INIT;
|
||
return LTC2508_ERROR_INIT;
|
||
}
|
||
|
||
g_hspi1 = hspi1;
|
||
g_hspi2 = hspi2;
|
||
g_hspi3 = hspi3;
|
||
g_adc_data_ready_flag = 0;
|
||
g_dma_complete_count = 0;
|
||
g_current_write_buffer = 0;
|
||
g_current_read_buffer = 0;
|
||
|
||
// 初始化双缓冲区
|
||
for (int i = 0; i < LTC2508_BUFFER_COUNT; i++) {
|
||
memset((void*)&g_adc_buffers[i], 0, sizeof(LTC2508_BufferTypeDef));
|
||
g_adc_buffers[i].state = LTC2508_BUFFER_EMPTY;
|
||
g_adc_buffers[i].dma_complete_count = 0;
|
||
}
|
||
|
||
// 重置统计信息
|
||
memset(&g_ltc2508_stats, 0, sizeof(g_ltc2508_stats));
|
||
|
||
// 可以在这里添加对 LTC2508 的配置,如果需要通过 SPI 发送配置字
|
||
// 例如:HAL_SPI_Transmit(g_hspi1, &config_word, 1, 100);
|
||
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 触发三路 SPI 通过 DMA 读取数据
|
||
* @param None
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_TriggerDmaRead(void)
|
||
{
|
||
// 检查SPI句柄是否有效
|
||
if (g_hspi1 == NULL || g_hspi2 == NULL || g_hspi3 == NULL) {
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_INIT;
|
||
return LTC2508_ERROR_INIT;
|
||
}
|
||
|
||
// 检查当前写入缓冲区是否可用
|
||
volatile LTC2508_BufferTypeDef *current_buffer = &g_adc_buffers[g_current_write_buffer];
|
||
|
||
if (current_buffer->state == LTC2508_BUFFER_EMPTY)
|
||
{
|
||
// 设置缓冲区状态为填充中
|
||
current_buffer->state = LTC2508_BUFFER_FILLING;
|
||
current_buffer->dma_complete_count = 0;
|
||
current_buffer->timestamp = HAL_GetTick();
|
||
|
||
// SPI2 和 SPI3 作为从机只接收
|
||
if (HAL_SPI_Receive_DMA(g_hspi2, (uint8_t*)current_buffer->data[1], LTC2508_DATA_LEN * 2) != HAL_OK)
|
||
{
|
||
current_buffer->state = LTC2508_BUFFER_EMPTY;
|
||
g_ltc2508_stats.dma_error_count++;
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_DMA;
|
||
return LTC2508_ERROR_DMA;
|
||
}
|
||
|
||
if (HAL_SPI_Receive_DMA(g_hspi3, (uint8_t*)current_buffer->data[2], LTC2508_DATA_LEN * 2) != HAL_OK)
|
||
{
|
||
current_buffer->state = LTC2508_BUFFER_EMPTY;
|
||
g_ltc2508_stats.dma_error_count++;
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_DMA;
|
||
return LTC2508_ERROR_DMA;
|
||
}
|
||
|
||
// SPI1 作为主机收发,先发一个 dummy 数据触发时钟
|
||
uint16_t dummy_tx[LTC2508_DATA_LEN] = {0}; // 可以是任意值
|
||
if (HAL_SPI_TransmitReceive_DMA(g_hspi1, (uint8_t*)dummy_tx, (uint8_t*)current_buffer->data[0], LTC2508_DATA_LEN * 2) != HAL_OK)
|
||
{
|
||
current_buffer->state = LTC2508_BUFFER_EMPTY;
|
||
g_ltc2508_stats.dma_error_count++;
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_DMA;
|
||
return LTC2508_ERROR_DMA;
|
||
}
|
||
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
return LTC2508_ERROR_TIMEOUT; // 缓冲区不可用
|
||
}
|
||
|
||
/**
|
||
* @brief SPI DMA 传输完成回调函数
|
||
* @param hspi: SPI 句柄指针
|
||
* @retval None
|
||
*/
|
||
void LTC2508_DmaComplete_Callback(SPI_HandleTypeDef *hspi)
|
||
{
|
||
if (hspi == g_hspi1 || hspi == g_hspi2 || hspi == g_hspi3)
|
||
{
|
||
volatile LTC2508_BufferTypeDef *current_buffer = &g_adc_buffers[g_current_write_buffer];
|
||
|
||
// 增加当前缓冲区的DMA完成计数
|
||
current_buffer->dma_complete_count++;
|
||
|
||
// 检查是否所有SPI的DMA传输都完成
|
||
if (current_buffer->dma_complete_count >= NUM_LTC2508)
|
||
{
|
||
// 标记当前缓冲区为准备就绪
|
||
current_buffer->state = LTC2508_BUFFER_READY;
|
||
g_ltc2508_stats.total_samples++;
|
||
|
||
// 自动切换到下一个缓冲区
|
||
LTC2508_SwapBuffers();
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief SPI DMA 传输错误回调函数
|
||
* @param hspi: SPI 句柄指针
|
||
* @retval None
|
||
*/
|
||
void LTC2508_ErrorCallback(SPI_HandleTypeDef *hspi)
|
||
{
|
||
if (hspi == g_hspi1 || hspi == g_hspi2 || hspi == g_hspi3)
|
||
{
|
||
volatile LTC2508_BufferTypeDef *current_buffer = &g_adc_buffers[g_current_write_buffer];
|
||
|
||
g_ltc2508_stats.dma_error_count++;
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_DMA;
|
||
|
||
// 重置当前缓冲区状态
|
||
current_buffer->state = LTC2508_BUFFER_EMPTY;
|
||
current_buffer->dma_complete_count = 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 验证ADC数据有效性
|
||
* @param buffer: 缓冲区指针
|
||
* @param channel: ADC通道 (0-2)
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_ValidateData(LTC2508_BufferTypeDef *buffer, uint8_t channel)
|
||
{
|
||
if (buffer == NULL || channel >= NUM_LTC2508) {
|
||
return LTC2508_ERROR_DATA_INVALID;
|
||
}
|
||
|
||
// 检查数据是否为全0或全1 (可能的错误状态)
|
||
uint32_t combined_data = ((uint32_t)buffer->data[channel][0] << 16) | buffer->data[channel][1];
|
||
|
||
if (combined_data == 0x00000000 || combined_data == 0xFFFFFFFF) {
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_DATA_INVALID;
|
||
return LTC2508_ERROR_DATA_INVALID;
|
||
}
|
||
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取统计信息
|
||
* @param stats: 统计信息结构体指针
|
||
* @retval None
|
||
*/
|
||
void LTC2508_GetStats(LTC2508_StatsTypeDef *stats)
|
||
{
|
||
if (stats != NULL) {
|
||
memcpy(stats, &g_ltc2508_stats, sizeof(LTC2508_StatsTypeDef));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 重置统计信息
|
||
* @param None
|
||
* @retval None
|
||
*/
|
||
void LTC2508_ResetStats(void)
|
||
{
|
||
memset(&g_ltc2508_stats, 0, sizeof(LTC2508_StatsTypeDef));
|
||
}
|
||
|
||
/**
|
||
* @brief 缓冲区切换函数
|
||
* @param None
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_SwapBuffers(void)
|
||
{
|
||
uint8_t next_write_buffer = (g_current_write_buffer + 1) % LTC2508_BUFFER_COUNT;
|
||
|
||
// 检查下一个写入缓冲区是否可用
|
||
if (g_adc_buffers[next_write_buffer].state == LTC2508_BUFFER_EMPTY) {
|
||
g_current_write_buffer = next_write_buffer;
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
// 如果下一个缓冲区不可用,说明缓冲区溢出
|
||
g_ltc2508_stats.buffer_overflow_count++;
|
||
g_ltc2508_stats.error_count++;
|
||
g_ltc2508_stats.last_error = LTC2508_ERROR_BUFFER_OVERFLOW;
|
||
return LTC2508_ERROR_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取当前写入缓冲区索引
|
||
* @param None
|
||
* @retval uint8_t 当前写入缓冲区索引
|
||
*/
|
||
uint8_t LTC2508_GetCurrentWriteBuffer(void)
|
||
{
|
||
return g_current_write_buffer;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取当前读取缓冲区索引
|
||
* @param None
|
||
* @retval uint8_t 当前读取缓冲区索引
|
||
*/
|
||
uint8_t LTC2508_GetCurrentReadBuffer(void)
|
||
{
|
||
return g_current_read_buffer;
|
||
}
|
||
|
||
/**
|
||
* @brief 检查指定缓冲区是否准备就绪
|
||
* @param buffer_index: 缓冲区索引
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_IsBufferReady(uint8_t buffer_index)
|
||
{
|
||
if (buffer_index >= LTC2508_BUFFER_COUNT) {
|
||
return LTC2508_ERROR_DATA_INVALID;
|
||
}
|
||
|
||
if (g_adc_buffers[buffer_index].state == LTC2508_BUFFER_READY) {
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
return LTC2508_ERROR_TIMEOUT;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取准备好的数据缓冲区
|
||
* @param buffer: 返回缓冲区指针
|
||
* @retval LTC2508_StatusTypeDef
|
||
*/
|
||
LTC2508_StatusTypeDef LTC2508_GetReadyBuffer(LTC2508_BufferTypeDef **buffer)
|
||
{
|
||
if (buffer == NULL) {
|
||
return LTC2508_ERROR_DATA_INVALID;
|
||
}
|
||
|
||
// 检查读取缓冲区是否有准备好的数据
|
||
if (g_adc_buffers[g_current_read_buffer].state == LTC2508_BUFFER_READY) {
|
||
g_adc_buffers[g_current_read_buffer].state = LTC2508_BUFFER_PROCESSING;
|
||
*buffer = (LTC2508_BufferTypeDef*)&g_adc_buffers[g_current_read_buffer];
|
||
return LTC2508_OK;
|
||
}
|
||
|
||
return LTC2508_ERROR_TIMEOUT; // 没有准备好的数据
|
||
}
|
||
|
||
/**
|
||
* @brief 释放缓冲区
|
||
* @param buffer_index: 缓冲区索引
|
||
* @retval None
|
||
*/
|
||
void LTC2508_ReleaseBuffer(uint8_t buffer_index)
|
||
{
|
||
if (buffer_index < LTC2508_BUFFER_COUNT) {
|
||
g_adc_buffers[buffer_index].state = LTC2508_BUFFER_EMPTY;
|
||
g_adc_buffers[buffer_index].dma_complete_count = 0;
|
||
|
||
// 如果释放的是当前读取缓冲区,切换到下一个
|
||
if (buffer_index == g_current_read_buffer) {
|
||
g_current_read_buffer = (g_current_read_buffer + 1) % LTC2508_BUFFER_COUNT;
|
||
}
|
||
}
|
||
}
|