STM_ATEM/Core/Src/main.c
zhoujie 9a2d543fcb feat(system-monitor): 新增SD卡存储监控功能并优化系统配置
- 新增SD卡存储监控功能,包含写入次数、错误次数、缓冲区满次数、总写入字节数和文件数量等统计指标
- 新增SD卡监控API函数,包括报告写入、报告错误、报告缓冲区满和报告文件创建
- 在data_storage.c中集成SD卡监控功能,在关键操作点添加监控报告
- 新增详细的使用文档《SD_Storage_Monitoring_Guide.md》,说明监控指标、API使用、集成方法和故障诊断
- 优化系统调试输出,在DebugOutput_PrintSystemStats中增加SD卡统计信息显示,包括基本统计、SD卡统计和计算指标(平均写入大小、错误率)
- 调整SDIO时钟分频器从2改为1以提高SD卡通信速度
- 调整TIM2中断优先级从12改为3以提高定时器响应优先级
- 更新STM32CubeMX配置文件(.ioc)以反映SDIO时钟分频和TIM2中断优先级的更改
- 注释掉USB连接状态检测代码以简化主循环处理
- 优化ADC数据就绪中断处理,每2次触发一次DMA读取,并在DMA读取超时时报告数据溢出
- 移除ProcessAdcData函数中已注释的RS485发送代码
- 将数据溢出报告从ProcessAdcData函数移至HAL_GPIO_EXTI_Callback函数中的DMA读取超时处理
2026-02-06 23:58:56 +08:00

745 lines
20 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.

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "fatfs.h"
#include "sdio.h"
#include "spi.h"
#include "tim.h"
#include "usart.h"
#include "usb_device.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ltc2508_driver.h"
#include "rs485_driver.h"
#include "data_packet.h"
#include "correction.h"
#include "data_storage.h"
#include "system_monitor.h"
#include <stdio.h>
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
// 串口输出控制开关
#define ENABLE_UART_DEBUG_OUTPUT 1
#define DEBUG_OUTPUT_INTERVAL_MS 1000 // 调试输出间隔(毫秒)
// 监控功能宏开关
#define ENABLE_SYSTEM_MONITOR 1 // 系统监控开关
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
// 外部SPI句柄声明
extern SPI_HandleTypeDef hspi1;
extern SPI_HandleTypeDef hspi2;
extern SPI_HandleTypeDef hspi3;
extern TIM_HandleTypeDef htim2;
extern UART_HandleTypeDef huart1;
extern UART_HandleTypeDef huart3;
// 校正参数
CorrectionParams_t g_correction_params;
// 数据包
DataPacket_t g_data_packet;
CorrectedDataPacket_t g_corrected_packet;
// 数据存储句柄
DataStorageHandle_t g_data_storage;
// 系统状态
static uint8_t g_recording_enabled = 0;
// USB连接状态管理
static uint8_t g_usb_connected = 0;
static uint8_t g_fatfs_mounted_for_sampling = 0;
static uint32_t g_last_usb_check = 0;
// 性能监控和调试输出
static uint32_t g_last_debug_output = 0;
static uint8_t g_debug_output_enabled = ENABLE_UART_DEBUG_OUTPUT;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
static void StartRecording(void);
static void StopRecording(void);
static void ProcessAdcData(void);
// USB连接状态管理函数
static uint8_t CheckUSBConnectionStatus(void);
static void HandleUSBConnectionChange(void);
static HAL_StatusTypeDef MountFileSystemForSampling(void);
static void UnmountFileSystemForSampling(void);
static void DebugOutput_Init(void);
static void DebugOutput_SendString(const char* str);
static void DebugOutput_PrintSystemStats(void);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/**
* @brief 开始数据记录
* @retval None
*/
static void StartRecording(void)
{
if (!g_recording_enabled) {
if (DataStorage_StartRecording(&g_data_storage) == HAL_OK) {
g_recording_enabled = 1;
}
}
}
/**
* @brief 停止数据记录
* @retval None
*/
static void StopRecording(void)
{
if (g_recording_enabled) {
g_recording_enabled = 0;
DataStorage_StopRecording(&g_data_storage);
}
}
/**
* @brief 处理ADC数据
* @retval None
*/
static void ProcessAdcData(void)
{
// 检查ADC数据是否准备就绪
LTC2508_BufferTypeDef *ready_buffer = NULL;
if (LTC2508_GetReadyBuffer(&ready_buffer) == LTC2508_OK && ready_buffer != NULL)
{
#if ENABLE_SYSTEM_MONITOR
SystemMonitor_IncrementSampleCount();
#endif
// 1. 从双缓冲区获取数据并合并 (高位16位在前)
int32_t raw_adc[NUM_LTC2508];
for (uint8_t i = 0; i < NUM_LTC2508; i++) {
raw_adc[i] = (int32_t)(((uint32_t)ready_buffer->data[i][0] << 16) | ready_buffer->data[i][1]);
}
// 3. 应用校正算法
CorrectionResult_t correction_result;
uint8_t correction_applied = 0;
if (g_correction_params.params_valid &&
Apply_Correction(raw_adc[0], raw_adc[1], raw_adc[2],
&correction_result, &g_correction_params) == HAL_OK) {
// 4a. 打包校正后的数据
PackCorrectedData(&g_corrected_packet,
correction_result.corrected_x,
correction_result.corrected_y,
correction_result.corrected_z);
correction_applied = 1;
// 发送校正后的数据包
// RS485_SendData((uint8_t*)&g_corrected_packet, sizeof(CorrectedDataPacket_t));
} else {
// 4b. 校正失败或未启用,使用原始数据
PackData(&g_data_packet, raw_adc[0], raw_adc[1], raw_adc[2]);
// 发送原始数据包
RS485_SendData((uint8_t*)&g_data_packet, sizeof(DataPacket_t));
}
// 6. 存储数据到SD卡 (如果启用记录)
if (g_recording_enabled) {
if (correction_applied) {
// 存储校正后的数据
DataStorage_WriteCorrectedData(&g_data_storage, &g_corrected_packet);
} else {
// 存储原始数据
DataStorage_WriteData(&g_data_storage, &g_data_packet);
}
}
// 7. 释放已处理的缓冲区
LTC2508_ReleaseBuffer(LTC2508_GetCurrentReadBuffer());
} else {
}
}
/**
* @brief 初始化调试输出
* @retval None
*/
static void DebugOutput_Init(void)
{
// USART3已在MX_USART3_UART_Init()中初始化
if (g_debug_output_enabled) {
DebugOutput_SendString("\r\n=== System Debug Output Initialized ===\r\n");
}
}
/**
* @brief 通过USART3发送字符串
* @param str: 要发送的字符串
* @retval None
*/
static void DebugOutput_SendString(const char* str)
{
if (!g_debug_output_enabled || str == NULL) {
return;
}
HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), 100);
}
/**
* @brief 输出系统监控统计信息
* @retval None
*/
static void DebugOutput_PrintSystemStats(void)
{
#if ENABLE_SYSTEM_MONITOR
if (!g_debug_output_enabled) {
return;
}
char buffer[256];
SystemMonitorStats_t sys_stats;
SystemMonitor_GetStats(&sys_stats);
// 打印基本统计信息
snprintf(buffer, sizeof(buffer),
"\r\n=== System Stats ===\r\n"
"Total Samples: %lu\r\n"
"Data Overflow: %lu\r\n",
sys_stats.total_samples,
sys_stats.data_overflow_count);
DebugOutput_SendString(buffer);
// 打印SD卡存储监控信息
snprintf(buffer, sizeof(buffer),
"=== SD Card Stats ===\r\n"
"SD Write Count: %lu\r\n"
"SD Write Errors: %lu\r\n"
"SD Buffer Full: %lu\r\n"
"SD Total Bytes: %lu\r\n"
"SD File Count: %lu\r\n",
sys_stats.sd_write_count,
sys_stats.sd_write_error_count,
sys_stats.sd_buffer_full_count,
sys_stats.sd_total_bytes_written,
sys_stats.sd_file_count);
DebugOutput_SendString(buffer);
// 计算并打印统计指标
if (sys_stats.sd_write_count > 0) {
uint32_t avg_write_size = sys_stats.sd_total_bytes_written / sys_stats.sd_write_count;
snprintf(buffer, sizeof(buffer),
"Avg Write Size: %lu bytes\r\n",
avg_write_size);
DebugOutput_SendString(buffer);
}
if (sys_stats.sd_write_count + sys_stats.sd_write_error_count > 0) {
uint32_t total_attempts = sys_stats.sd_write_count + sys_stats.sd_write_error_count;
uint32_t error_rate = (sys_stats.sd_write_error_count * 100) / total_attempts;
snprintf(buffer, sizeof(buffer),
"Write Error Rate: %lu%%\r\n",
error_rate);
DebugOutput_SendString(buffer);
}
DebugOutput_SendString("====================\r\n");
#endif
}
/**
* @brief 检查USB连接状态
* @retval 1: USB已连接, 0: USB未连接
*/
static uint8_t CheckUSBConnectionStatus(void)
{
// 通过检查USB设备状态来判断是否连接到PC
extern USBD_HandleTypeDef hUsbDeviceFS;
// 检查USB设备是否已配置枚举完成
if (hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED) {
return 1; // USB已连接并配置
}
return 0; // USB未连接或未配置
}
/**
* @brief 处理USB连接状态变化
* @retval None
*/
static void HandleUSBConnectionChange(void)
{
uint8_t current_usb_status = CheckUSBConnectionStatus();
if (current_usb_status != g_usb_connected) {
g_usb_connected = current_usb_status;
if (g_usb_connected) {
// USB连接停止数据采集卸载文件系统用于USB存储
DebugOutput_SendString("USB Connected: Stopping data acquisition\r\n");
// 停止数据记录
StopRecording();
// 卸载用于采样的文件系统
UnmountFileSystemForSampling();
} else {
// USB断开重新挂载文件系统开始数据采集
DebugOutput_SendString("USB Disconnected: Starting data acquisition\r\n");
// 挂载文件系统用于数据采集
if (MountFileSystemForSampling() == HAL_OK) {
// 重新初始化数据存储
if (DataStorage_Init(&g_data_storage) == HAL_OK) {
StartRecording();
}
}
}
}
}
/**
* @brief 为数据采集挂载文件系统
* @retval HAL_OK: 成功, HAL_ERROR: 失败
*/
static HAL_StatusTypeDef MountFileSystemForSampling(void)
{
extern FATFS SDFatFS;
extern char SDPath[4];
extern SD_HandleTypeDef hsd;
if (g_fatfs_mounted_for_sampling) {
return HAL_OK; // 已经挂载
}
// 初始化SD卡
if (HAL_SD_Init(&hsd) != HAL_OK) {
DebugOutput_SendString("SD card init failed\r\n");
return HAL_ERROR;
}
// FRESULT format_result = f_mkfs(SDPath, FM_FAT32, 0, NULL, 0);
// 尝试挂载文件系统
FRESULT mount_result = f_mount(&SDFatFS, SDPath, 1);
if (mount_result != FR_OK) {
if (mount_result == FR_NO_FILESYSTEM)
{
DebugOutput_SendString("No filesystem found, formatting...\r\n");
// 格式化为FAT32
FRESULT format_result = f_mkfs(SDPath, FM_FAT32, 0, NULL, 0);
if (format_result == FR_OK) {
DebugOutput_SendString("Format successful, remounting...\r\n");
mount_result = f_mount(&SDFatFS, SDPath, 1);
if (mount_result != FR_OK) {
DebugOutput_SendString("Remount after format failed\r\n");
return HAL_ERROR;
}
} else {
DebugOutput_SendString("Format failed\r\n");
return HAL_ERROR;
}
} else {
DebugOutput_SendString("Mount failed with other error\r\n");
return HAL_ERROR;
}
}
g_fatfs_mounted_for_sampling = 1;
DebugOutput_SendString("Filesystem mounted for sampling\r\n");
return HAL_OK;
}
/**
* @brief 卸载用于数据采集的文件系统
* @retval None
*/
static void UnmountFileSystemForSampling(void)
{
extern FATFS SDFatFS;
extern char SDPath[4];
if (!g_fatfs_mounted_for_sampling) {
return; // 已经卸载
}
// 卸载文件系统
f_mount(NULL, SDPath, 0);
g_fatfs_mounted_for_sampling = 0;
DebugOutput_SendString("Filesystem unmounted for USB mode\r\n");
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
HAL_Delay(200);
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SDIO_SD_Init();
MX_SPI1_Init();
MX_SPI2_Init();
MX_SPI3_Init();
MX_USART1_UART_Init();
MX_FATFS_Init();
MX_USB_DEVICE_Init();
MX_USART3_UART_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
// SD_NAND_Init_And_Mount();
// SD_Test_Write();
// SD_Test_Read();
// 初始化系统监控
#if ENABLE_SYSTEM_MONITOR
SystemMonitor_Init();
#endif
// 初始化调试输出
DebugOutput_Init();
// 初始化RS485通信
RS485_Init(&huart1, RS485_DE_RE_PORT, RS485_DE_RE_PIN);
// 初始化校正参数
Init_CorrectionParams(&g_correction_params);
Load_CorrectionParams_FromFlash(&g_correction_params);
// 检查初始USB连接状态并相应初始化
HAL_Delay(2000);
g_usb_connected = CheckUSBConnectionStatus();
if (!g_usb_connected) {
// USB未连接挂载文件系统用于数据采集
if (MountFileSystemForSampling() == HAL_OK) {
// 初始化数据存储
if (DataStorage_Init(&g_data_storage) == HAL_OK) {
// 开始数据记录
StartRecording();
}
}
} else {
// USB已连接不进行数据采集
DebugOutput_SendString("USB connected at startup - data acquisition disabled\r\n");
while(1)
{
;
}
}
// 启动TIM2定时器用于1ms周期的ADC数据处理
if (HAL_TIM_Base_Start_IT(&htim2) != HAL_OK) {
Error_Handler();
}
// 初始化LTC2508驱动
if (LTC2508_Init(&hspi1, &hspi2, &hspi3) != LTC2508_OK) {
Error_Handler();
}
// 触发信号引脚初始化
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
// 定期任务
uint32_t current_tick = HAL_GetTick();
// USB连接状态检测 (每500ms检测一次)
// if (current_tick - g_last_usb_check >= 500) {
// HandleUSBConnectionChange();
// g_last_usb_check = current_tick;
// }
// 处理数据存储后台任务 (轮询方式)
if (g_recording_enabled) {
DataStorage_ProcessBackgroundTasks(&g_data_storage);
}
// 定期输出调试信息 (每1秒输出一次)
if (g_debug_output_enabled && (current_tick - g_last_debug_output >= DEBUG_OUTPUT_INTERVAL_MS)) {
DebugOutput_PrintSystemStats();
g_last_debug_output = current_tick;
}
// ADC采样由PA1外部中断触发不在主循环中触发
// 可以在这里添加其他低优先级任务
}
// 停止数据记录
StopRecording();
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/**
* @brief 外部中断回调函数 - ADC数据就绪信号
* @param GPIO_Pin: 触发中断的GPIO引脚
* @retval None
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static uint32_t cnt = 0;
if(LTC2508_IsInited() == 0) return;
cnt ++;
if(cnt % 2 == 0)
{
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
if (GPIO_Pin == ADC_DRY_Pin) {
// ADC数据就绪触发DMA读取
if(LTC2508_ERROR_TIMEOUT == LTC2508_TriggerDmaRead())
{
// 数据来不及处理
#if ENABLE_SYSTEM_MONITOR
SystemMonitor_ReportDataOverflow();
#endif
}
}
// HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}
}
/**
* @brief SPI DMA传输完成回调函数
* @param hspi: SPI句柄指针
* @retval None
*/
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
// 调用LTC2508驱动的DMA完成回调
LTC2508_DmaComplete_Callback(hspi);
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
// 调用LTC2508驱动的DMA完成回调
LTC2508_DmaComplete_Callback(hspi);
}
/**
* @brief SPI错误回调函数
* @param hspi: SPI句柄指针
* @retval None
*/
void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
{
// 调用LTC2508驱动的错误回调
LTC2508_ErrorCallback(hspi);
}
/**
* @brief TIM2回调函数 - 1ms定时器中断处理ADC数据
* @param htim: TIM句柄指针
* @retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2) {
// ADC是4KHz采样率定时器是1KHz需要在每次1ms中断中处理多个ADC数据
// 循环处理所有可用的ADC数据直到没有新数据为止
uint8_t processed_count = 0;
const uint8_t max_process_per_interrupt = 8; // 限制每次中断最多处理的数据量,避免中断时间过长
while (processed_count < max_process_per_interrupt) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
ProcessAdcData();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
processed_count++;
}
}
}
/**
* @brief UART传输完成回调函数
* @param huart: UART句柄指针
* @retval None
*/
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart1) {
// RS485传输完成回调
RS485_TxCpltCallback(huart);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
// 停止所有DMA传输
HAL_SPI_DMAStop(&hspi1);
HAL_SPI_DMAStop(&hspi2);
HAL_SPI_DMAStop(&hspi3);
// 停止数据记录
g_recording_enabled = 0;
DataStorage_StopRecording(&g_data_storage);
// 禁用中断并进入无限循环
__disable_irq();
while (1)
{
// 可以在这里添加LED指示或其他错误指示
HAL_Delay(500);
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */