You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F429I中SRAM读取缓冲区的低延迟无丢数实现方案问询

STM32F4 低延迟无丢包的总线数据读取与处理方案

需求背景

在STM32F4平台上,通过GPIO下降沿中断触发从总线系统读取缓冲区数据,要求:

  • 主循环中处理数据,且处理延迟尽可能低
  • 中断触发间隔仅500ns,绝对不能丢失任何中断,因此开关中断的方式不可行

初步实现方案

变量声明

#define BUFFER_SIZE         ((uint32_t)0x0100)
#define WRITE_READ_ADDR     ((uint32_t)0x0800)

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
SRAM_HandleTypeDef hsram1;
FMC_NORSRAM_TimingTypeDef SRAM_Timing;

/* Read/Write Buffers */
volatile uint16_t aRxBuffer[BUFFER_SIZE];
volatile uint8_t iowr_set = 0; // 补充缺失的状态标记变量

主循环代码

while (1)
{
    if(iowr_set)
    {
        iowr_set = 0;
        for (int i = 0; i < BUFFER_SIZE; i++)
        {
            processData(aRxBuffer[i]);
        }
    }
}

中断回调函数

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_6) { // IORD 触发引脚
        HAL_SRAM_Read_16b(&hsram1, (uint32_t *)(SRAM_BANK_ADDR + WRITE_READ_ADDR), aRxBuffer, BUFFER_SIZE);
        iowr_set = 1;
    }
}

现有方案的核心隐患

当前方案存在致命风险:中断回调内的HAL_SRAM_Read_16b操作耗时远超500ns。即使STM32F4的FMC配置最快时序,读取256个16位数据(BUFFER_SIZE=0x100)的时间也远大于中断间隔,下一个中断触发时前一次中断未执行完毕,会直接导致中断丢失,完全违反需求。

优化实现方案

1. 双缓冲区切换,剥离中断内的大流量读取

使用两组缓冲区,中断仅负责切换缓冲区标记,耗时的读取操作交给主循环执行,避免中断阻塞:

#define BUFFER_SIZE         ((uint32_t)0x0100)
#define WRITE_READ_ADDR     ((uint32_t)0x0800)

volatile uint16_t bufferA[BUFFER_SIZE];
volatile uint16_t bufferB[BUFFER_SIZE];
volatile uint8_t active_buffer = 0; // 0: 当前总线写入的缓冲区,1: 待处理的缓冲区
volatile uint8_t new_data_ready = 0;

// 中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_6) {
        // 仅切换缓冲区标记,操作耗时可忽略
        active_buffer = active_buffer ^ 1;
        new_data_ready = 1;
    }
}

// 主循环
while (1) {
    if (new_data_ready) {
        new_data_ready = 0;
        // 读取非当前写入的缓冲区数据
        volatile uint16_t *target_buf = (active_buffer == 0) ? bufferB : bufferA;
        HAL_SRAM_Read_16b(&hsram1, (uint32_t *)(SRAM_BANK_ADDR + WRITE_READ_ADDR), (uint16_t*)target_buf, BUFFER_SIZE);
        // 处理数据
        for (int i = 0; i < BUFFER_SIZE; i++) {
            processData(target_buf[i]);
        }
    }
}

注:此方式要求总线数据在两次中断间隔内不会被覆盖,若总线持续刷新数据,需结合DMA做后台读取。

2. 启用DMA自动完成数据读取

利用FMC的DMA请求功能,配置DMA在中断触发时自动完成SRAM数据读取,中断仅负责启动传输,DMA完成后标记数据就绪:

// DMA配置示例(需根据硬件调整通道)
DMA_HandleTypeDef hdma_fmc;

void MX_DMA_Init(void) {
    __HAL_RCC_DMA2_CLK_ENABLE();
    hdma_fmc.Instance = DMA2_Stream0;
    hdma_fmc.Init.Channel = DMA_CHANNEL_0;
    hdma_fmc.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_fmc.Init.PeriphInc = DMA_PINC_ENABLE;
    hdma_fmc.Init.MemInc = DMA_MINC_ENABLE;
    hdma_fmc.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_fmc.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_fmc.Init.Mode = DMA_NORMAL;
    hdma_fmc.Init.Priority = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(&hdma_fmc);
    __HAL_LINKDMA(&hsram1, hdma, hdma_fmc);
}

// 中断回调
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_6) {
        // 启动DMA传输,无需等待完成
        HAL_SRAM_Read_DMA(&hsram1, (uint32_t *)(SRAM_BANK_ADDR + WRITE_READ_ADDR), (uint16_t*)aRxBuffer, BUFFER_SIZE);
    }
}

// DMA传输完成回调
void HAL_SRAM_RxCpltCallback(SRAM_HandleTypeDef *hsram) {
    iowr_set = 1;
}

// 主循环保持原逻辑
while (1) {
    if(iowr_set)
    {
        iowr_set = 0;
        for (int i = 0; i < BUFFER_SIZE; i++)
        {
            processData(aRxBuffer[i]);
        }
    }
}

3. 系统级优化

  • 将EXTI中断优先级设置为最高,避免被其他中断抢占
  • 确保STM32F4运行在最高时钟频率(168MHz),减少指令执行周期
  • 精简processData函数,仅保留必要预处理,复杂逻辑移至主循环空闲时段或低优先级任务

内容的提问来源于stack exchange,提问作者Andre Ahmed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 17:47:32