系统优化方案

STM32L0智慧供暖RTU固件的系统优化方案:通过UART DMA传输、8倍过采样、脉冲处理从中断移到主循环与中断优先级重配,使中断执行时间从15-25μs降至5μs以下、CPU占用降低75-80%,并附验证与排障步骤。

2026-07-04
系统优化性能提升嵌入式

STM32L0 系统优化方案:动手前先把账算清

为什么做这轮优化

这份方案是给我们智慧供暖RTU固件(STM32L0,负责脉冲计量和UART上报)定的一轮中期优化。当时现场的痛点是:脉冲一多,中断里又是滤波又是异常判断,CPU 被拖得喘不过气,UART 上报还会偶尔掉数据、延迟抖得厉害。方案的核心就三条——把 UART 改成 DMA 搬运、把过采样从16倍降到8倍、把脉冲处理从中断挪到主循环,同时理顺中断优先级。目标很直白:CPU 用得更少、传输更快更稳,但脉冲计量精度一分不降。下面是这套改动的完整拆解和落地后的效果指标。

1. UART 数据改用 DMA 搬运

1.1 这一层解决什么

UART 发送改走 DMA:数据搬运不再占 CPU 的工夫,整体传输效率也上去了。

1.2 配置与代码改动

硬件配置
代码改动

改动文件 main.c:


DMA_HandleTypeDef hdma_usart2_tx;

改动文件 stm32l0xx_hal_msp.c:


/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();

/* DMA interrupt init */
HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);

/* Configure DMA for USART2 TX */
hdma_usart2_tx.Instance = DMA1_Channel7;
hdma_usart2_tx.Init.Request = DMA_REQUEST_4;
hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart2_tx.Init.Mode = DMA_NORMAL;
hdma_usart2_tx.Init.Priority = DMA_PRIORITY_HIGH;
HAL_DMA_Init(&hdma_usart2_tx);

__HAL_LINKDMA(huart, hdmatx, hdma_usart2_tx);

改动文件 stm32l0xx_it.c:


void DMA1_Channel4_5_6_7_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&hdma_usart2_tx);
}

1.3 使用示例


uint8_t tx_buffer[64];
// Fill buffer with data
HAL_UART_Transmit_DMA(&huart2, tx_buffer, sizeof(tx_buffer));

1.4 带来的收益

2. 过采样从 16 倍降到 8 倍

2.1 为什么这样调

UART 过采样从 16 倍降到 8 倍,接收精度和抗噪不受影响,处理开销却省下来一截。

2.2 改哪里

改动文件 main.c:


huart2.Init.OverSampling = UART_OVERSAMPLING_8;

2.3 收益

2.4 要权衡的地方

3. 脉冲处理优化

3.1 思路

脉冲处理做了重点优化:从中断里抽出大部分活,降低中断负载,系统响应也跟着快起来。

3.2 核心机制

3.2.1 暂停/恢复机制

volatile uint8_t pulse_processing_enabled = 1;
volatile uint8_t uart_tx_busy = 0;

void pause_pulse_processing(void)
{
  pulse_processing_enabled = 0;
}

void resume_pulse_processing(void)
{
  pulse_processing_enabled = 1;
}

void uart_tx_callback(void)
{
  uart_tx_busy = 0;
  resume_pulse_processing();
}
3.2.2 降低处理频率

uint16_t pulse_process_counter = 0;
#define PULSE_PROCESS_INTERVAL 10

void process_raw_pulse_data(void)
{
  if (!pulse_processing_enabled)
  {
    return;
  }

  if (raw_pulse_index == 0)
  {
    return;
  }

  pulse_process_counter++;
  
  if (pulse_process_counter >= PULSE_PROCESS_INTERVAL)
  {
    pulse_process_counter = 0;
    
    uint16_t pulse_to_process = raw_pulse_buffer[0];
    
    data_processing_pipeline(pulse_to_process);
    
    for (uint16_t i = 1; i < raw_pulse_index; i++)
    {
      raw_pulse_buffer[i - 1] = raw_pulse_buffer[i];
    }
    raw_pulse_index--;
  }
}

3.3 收益

4. 架构重构:处理挪到主循环

4.1 思路

做了次架构重构:把脉冲处理从中断上下文搬进主循环,中断里只留最轻的活。

4.2 优化前


void ADC1_COMP_IRQHandler(void)
{
  // Raw data acquisition
  uint16_t t = __HAL_TIM_GET_COUNTER(&htim22);
  __HAL_TIM_SET_COUNTER(&htim22, 0);
  
  // Data storage
  if ((t > 50) && (t < 2000))
  {
    if (index < 300)
      p[index++] = t;
    else
    {
      index = 0;
      p[index++] = t;
    }
    
    process_pulse(t);  // COMPLEX PROCESSING IN INTERRUPT
  }
  
  // Data validity check
  if ((ok == 0) && (index >= 100))
    ok = 1;
    
  HAL_COMP_IRQHandler(&hcomp2);
}

4.3 优化后


void ADC1_COMP_IRQHandler(void)
{
  // Raw data acquisition ONLY
  uint16_t t = __HAL_TIM_GET_COUNTER(&htim22);
  __HAL_TIM_SET_COUNTER(&htim22, 0);
  
  if ((t > 50) && (t < 2000))
  {
    // Store raw data in buffer
    if (index < 300)
      p[index++] = t;
    else
    {
      index = 0;
      p[index++] = t;
    }
    
    // Store in processing buffer
    if (raw_pulse_index < 300)
      raw_pulse_buffer[raw_pulse_index++] = t;
    else
    {
      raw_pulse_index = 0;
      raw_pulse_buffer[raw_pulse_index++] = t;
    }
  }
  
  // Data validity check
  if ((ok == 0) && (index >= 100))
    ok = 1;
    
  HAL_COMP_IRQHandler(&hcomp2);
}

4.4 在主循环里处理


while (1)
{
  process_raw_pulse_data();  // Process in main loop
  
  if (need_pwm_update)
  {
    uint32_t pwm_value = calculate_pwm_from_current(400);
    update_pwm_output(pwm_value);
    need_pwm_update = 0;
  }
  
  // Other tasks...
}

4.5 收益

5. 数据处理流水线标准化

5.1 总体思路

把数据处理抽成一条标准流水线,每个环节各管一段、边界分得清清楚楚,后面想改哪段都不牵连别的。

5.2 流水线分四段

第1段:原始数据采集

void ADC1_COMP_IRQHandler(void)
{
  uint16_t t = __HAL_TIM_GET_COUNTER(&htim22);
  __HAL_TIM_SET_COUNTER(&htim22, 0);
  
  if ((t > 50) && (t < 2000))
  {
    raw_pulse_buffer[raw_pulse_index++] = t;
  }
}
第2段:数字滤波

uint16_t moving_average(uint16_t new_value)
{
  static uint16_t window[8] = {0};
  static uint8_t window_index = 0;
  static uint8_t window_filled = 0;
  uint32_t sum = 0;
  uint8_t i;

  window[window_index] = new_value;
  window_index = (window_index + 1) % 8;

  if (window_index == 0)
  {
    window_filled = 1;
  }

  uint8_t count = window_filled ? 8 : window_index;

  for (i = 0; i < count; i++)
  {
    sum += window[i];
  }

  return (uint16_t)(sum / count);
}
第3段:基线更新

void update_baseline(uint16_t pulse)
{
  pulse_history[history_index] = pulse;
  history_index = (history_index + 1) % 8;

  if (history_index == 0 && !baseline_valid)
  {
    baseline_valid = 1;
  }

  if (baseline_valid)
  {
    baseline_pulse = median_filter(pulse_history, 8);
    if (baseline_pulse != 0)
    {
      para.O_Hz = calculate_frequency_from_pulse(baseline_pulse);
    }
  }
}
第4段:异常检测

uint8_t is_abnormal_pulse(uint16_t pulse, uint16_t baseline_pulse)
{
  if (baseline_pulse == 0) return 0;

  uint16_t upper_limit = baseline_pulse + (baseline_pulse * 30 / 100);
  uint16_t lower_limit = baseline_pulse - (baseline_pulse * 30 / 100);

  return (pulse > upper_limit) || (pulse < lower_limit);
}

5.3 四段串起来


void data_processing_pipeline(uint16_t raw_pulse)
{
  // Stage 1: Validation
  if (!validate_pulse_width(raw_pulse))
  {
    return;
  }

  // Stage 2: Digital Filtering
  uint16_t filtered_value = moving_average(raw_pulse);
  
  // Stage 3: Baseline Management
  if (startup_phase)
  {
    startup_count++;

    if (startup_count >= 20)
    {
      startup_phase = 0;
      baseline_valid = 1;
      baseline_pulse = filtered_value;
      last_valid_pulse = baseline_pulse;
      last_valid_time = t_ms;

      if (filtered_index < 300)
      {
        filtered_p[filtered_index++] = baseline_pulse;
      }
      else
      {
        filtered_index = 0;
        filtered_p[filtered_index++] = baseline_pulse;
      }
    }
  }
  else
  {
    // Stage 4: Anomaly Detection
    if (!is_abnormal_pulse(raw_pulse, baseline_pulse))
    {
      update_baseline(filtered_value);
      last_valid_pulse = filtered_value;
      last_valid_time = t_ms;

      if (filtered_index < 300)
      {
        filtered_p[filtered_index++] = filtered_value;
      }
      else
      {
        filtered_index = 0;
        filtered_p[filtered_index++] = filtered_value;
      }
    }
  }
}

6. 中断优先级重排

6.1 为什么重排

中断优先级重新理了一遍,核心就一条:UART 通信必须排最高,传输一个比特都丢不得。

6.2 优先级怎么配

改动文件 stm32l0xx_hal_msp.c:


// UART2 - Highest priority (0,0)
HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);

// DMA - Highest priority (0,0)
HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0, 0);

// ADC1_COMP - Medium priority (1,0)
HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 1, 0);

// TIM2 - Lowest priority (2,0)
HAL_NVIC_SetPriority(TIM2_IRQn, 2, 0);

6.3 为什么这么定

  1. USART2 (0,0):关键通信,丢一个字节都不行
  2. DMA (0,0):高速搬数据,对时间敏感
  3. ADC1_COMP (1,0):测脉冲,能容忍一点延迟
  4. TIM2 (2,0):系统计时,优先级放最低

7. 性能指标前后对比

7.1 优化前

7.2 优化后

7.3 改进汇总

8. 使用要点

8.1 UART 用 DMA 收发


// Example: Send data using DMA
uint8_t tx_data[128];
// Prepare data...

// Pause pulse processing during critical transmission
pause_pulse_processing();
uart_tx_busy = 1;

// Start DMA transmission
HAL_StatusTypeDef status = HAL_UART_Transmit_DMA(&huart2, tx_data, sizeof(tx_data));

if (status != HAL_OK)
{
  // Handle error
  uart_tx_busy = 0;
  resume_pulse_processing();
}

// Processing will resume automatically when transmission completes

8.2 监视脉冲处理


// Check if pulse processing is enabled
if (pulse_processing_enabled)
{
  // Pulse processing is active
}
else
{
  // Pulse processing is paused (e.g., during UART transmission)
}

// Check raw pulse buffer status
if (raw_pulse_index > 0)
{
  // There are pulses waiting to be processed
}

8.3 调整处理频率


// Modify processing interval as needed
// Current: Process every 10th pulse
#define PULSE_PROCESS_INTERVAL 10

// For more frequent processing (every 5th pulse):
#define PULSE_PROCESS_INTERVAL 5

// For less frequent processing (every 20th pulse):
#define PULSE_PROCESS_INTERVAL 20

9. 测试与验证

9.1 单元测试

9.2 集成测试

9.3 性能测试

9.4 压力测试

10. 踩坑排障

10.1 常见的坑

症状一:UART 丢数据

现象是 UART 数据丢帧或错乱。排查思路如下:

  1. Verify DMA configuration
  2. Check interrupt priorities
  3. Ensure buffer sizes are adequate
  4. Monitor CPU utilization
症状二:脉冲处理延迟

现象是脉冲测不准。排查思路如下:

  1. Reduce PULSEPROCESSINTERVAL
  2. Check pulseprocessingenabled flag
  3. Verify rawpulsebuffer status
  4. Monitor interrupt execution time
症状三:系统不稳定

现象是莫名其妙复位或闪退。排查思路如下:

  1. Check stack usage
  2. Verify interrupt nesting
  3. Review memory allocation
  4. Monitor watchdog timers

11. 往后还能往哪走

11.1 短期(1-2周)

11.2 中期(1-2月)

11.3 长期(3-6月)

12. 结论

这轮中期优化把系统的性能、可靠性和可维护性都实实在在地提了一档。DMA 传输、脉冲处理优化加标准化流水线这一套,既保住了产线上的稳定运行,也给后续升级留好了地基。

主要成果

下一步

  1. 做完测试与验证
  2. 发布到生产环境
  3. 盯住性能指标
  4. 收集现场反馈
  5. 规划下一步增强

这套方案从设计到落地就是上面的完整记录,2026-02-23 当天定稿,代码已经实现完成,眼下正卡在测试验证阶段。


还想接着看

方案咬合着后面的验收流程,接着看这几篇就能把闭环补满:

如果你们也在给嵌入式采集固件做性能优化,欢迎按页尾方式联系我们聊聊方案怎么定。