STM_ATEM/User/ltc2508_driver.c
zhoujie 29cc814384 feat(scripts): 新增光泵磁力仪数据支持与混流解析
- 新增混流解析函数 `parse_mixed_stream()`,支持标准包与光泵包交替出现的二进制文件
- 新增光泵磁力仪模拟器 `OpticMagSimulatorThread`,以可配置频率发送BCD帧
- 新增光泵磁场显示标签页,支持文件模式与实时模式下的磁场曲线绘制
- 新增 `flash_counter` 模块,用于NOFX模式下文件序号持久化

♻️ refactor(data_storage): 重构数据存储目录与文件命名策略

- 会话目录改为按GPS日期命名(YYYYMMDD),无GPS时使用NOFX目录
- 文件命名改为基于GPS时间(HHMMSS)或Flash计数器(C开头),消除序号文件依赖
- 移除 `config_manager` 依赖和 `PARAM.TXT` 序号文件读写逻辑
- 新增 `GPS_ParseGPRMC()` 解析函数,提取日期字段用于目录命名

🐛 fix(ltc2508_driver): 修复DMA启动失败时资源泄漏问题

- SPI2/SPI3 DMA启动失败时主动停止已启动的DMA,防止资源泄漏
- 将SPI1的dummy发送缓冲区改为static,确保DMA传输期间数据有效

🐛 fix(rs485_driver): 修复DMA发送缓冲区可能被覆盖的问题

- 引入静态发送缓冲区 `s_tx_buf`,隔离调用方数据与DMA传输,防止TIM2 ISR覆盖

♻️ refactor(ATEMParse): 重构数据结构定义与解析逻辑

- 移除硬编码的V1/V2 dtype,改为动态构建 `make_dtype()` 函数
- 新增数据格式选项(GPS经纬度/光泵),支持灵活配置
- 新增文件模式下的分页浏览功能(滑块/上一页/下一页)
- 优化实时模式下的光泵数据显示与表格列过滤
2026-06-14 23:06:25 +08:00

325 lines
9.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 是否初始化
* @retval 0-未初始化 1-已初始化
*/
uint32_t LTC2508_IsInited()
{
return (g_hspi1 != NULL && g_hspi2 != NULL && 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 作为从机只接收;必须在 SPI1主机启动时钟前挂好 DMA否则从机会丢失前几个时钟
if (HAL_SPI_Receive_DMA(g_hspi2, (uint8_t*)current_buffer->data[1], LTC2508_DATA_LEN) != 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) != HAL_OK)
{
current_buffer->state = LTC2508_BUFFER_EMPTY;
HAL_SPI_DMAStop(g_hspi2);
g_ltc2508_stats.dma_error_count++;
g_ltc2508_stats.error_count++;
g_ltc2508_stats.last_error = LTC2508_ERROR_DMA;
return LTC2508_ERROR_DMA;
}
// staticDMA 传输异步完成,必须保证缓冲区生命周期覆盖整个传输过程
static uint16_t s_dummy_tx[LTC2508_DATA_LEN] = {0};
if (HAL_SPI_TransmitReceive_DMA(g_hspi1, (uint8_t*)s_dummy_tx, (uint8_t*)current_buffer->data[0], LTC2508_DATA_LEN) != HAL_OK)
{
current_buffer->state = LTC2508_BUFFER_EMPTY;
HAL_SPI_DMAStop(g_hspi2);
HAL_SPI_DMAStop(g_hspi3);
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;
}
}
}