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

如何使用STM32测量接入模拟信号的电压周期及平均值

STM32 PC0模拟信号采集与参数计算实现方案

基础配置说明

  • 本实现基于STM32 HAL库,适配STM32F1/F4主流系列,其他系列仅需修改对应外设寄存器参数即可使用
  • 硬件连接:待测模拟信号直接接入PC0引脚(若信号幅值超过3.3V需先做分压,负电压信号需先做电平抬升)
  • 核心实现逻辑:
    • ADC采样获取PC0引脚的模拟量数值,换算为实际电压
    • 边沿检测法捕获信号跳变沿,结合定时器计数计算信号周期
    • 滑动窗口滤波法计算连续采样的电压平均值,降低噪声干扰

核心代码实现

1. 宏定义与全局变量声明

// 基础参数配置
#define ADC_VREF            3.3f    // 参考电压,按实际硬件连接调整
#define ADC_RESOLUTION      4095.0f // 12位ADC量程
#define WINDOW_SIZE         20      // 平均值滑动窗口大小
#define EDGE_THRESHOLD      1.65f   // 边沿检测阈值,默认取参考电压1/2,按需调整

// 全局变量
uint16_t adc_sample_buf[WINDOW_SIZE] = {0};
uint8_t buf_index = 0;
uint32_t last_edge_tick = 0;
float signal_period = 0;
uint8_t edge_flag = 0;
ADC_HandleTypeDef hadc1;
TIM_HandleTypeDef htim2;

2. 外设初始化代码

需要先初始化GPIO、ADC、基础定时器三个外设,核心配置逻辑如下:

void MX_Peripherals_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  // 开时钟:GPIOC、ADC1、TIM2
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_ADC1_CLK_ENABLE();
  __HAL_RCC_TIM2_CLK_ENABLE();

  // PC0配置为模拟输入模式
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  // ADC1配置:12位分辨率,单通道非扫描模式
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  HAL_ADC_Init(&hadc1);

  // ADC通道10(对应PC0)配置,采样时间28.5个周期
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_10;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  // TIM2配置:计数频率1MHz,用于周期计时
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = (HAL_RCC_GetPCLK1Freq() / 1000000) - 1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0xFFFFFFFF;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_Base_Init(&htim2);
  HAL_TIM_Base_Start(&htim2);

  // 启动ADC转换
  HAL_ADC_Start(&hadc1);
}

3. 电压采集与平均值计算函数

// 获取ADC原始采样值
uint16_t get_adc_raw(void)
{
  HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
  return HAL_ADC_GetValue(&hadc1);
}

// 换算为实际电压并更新滑动窗口,返回平均值
float get_voltage_avg(void)
{
  uint16_t raw_val = get_adc_raw();

  // 更新滑动窗口
  adc_sample_buf[buf_index] = raw_val;
  buf_index = (buf_index + 1) % WINDOW_SIZE;

  // 计算平均值
  uint32_t sum = 0;
  for(uint8_t i=0; i<WINDOW_SIZE; i++)
  {
    sum += adc_sample_buf[i];
  }
  return (sum * ADC_VREF) / (WINDOW_SIZE * ADC_RESOLUTION);
}

4. 信号周期计算函数

float get_signal_period(void)
{
  float current_voltage = get_adc_raw() * ADC_VREF / ADC_RESOLUTION;

  // 上升沿检测:当前电压超过阈值,上一次采样低于阈值
  if(current_voltage > EDGE_THRESHOLD && edge_flag == 0)
  {
    edge_flag = 1;
    uint32_t current_tick = TIM2->CNT;
    if(last_edge_tick != 0)
    {
      // 定时器计数频率1MHz,计数值差即为us数,换算为s
      signal_period = (current_tick - last_edge_tick) / 1000000.0f;
    }
    last_edge_tick = current_tick;
  }
  else if(current_voltage < EDGE_THRESHOLD)
  {
    edge_flag = 0;
  }

  return signal_period;
}

调用示例

int main(void)
{
  HAL_Init();
  SystemClock_Config(); // 按实际芯片配置系统时钟
  MX_Peripherals_Init();

  float voltage_avg = 0;
  float period = 0;

  while(1)
  {
    voltage_avg = get_voltage_avg();
    period = get_signal_period();

    // 此处可添加串口打印逻辑输出参数
    HAL_Delay(10);
  }
}

注意事项

  • 若输入信号噪声较大,可在PC0引脚对地接100nF滤波电容,或在软件中增加多次采样取均值的逻辑
  • 边沿检测阈值EDGE_THRESHOLD需要根据实际输入信号的幅值调整,建议设置为信号峰峰值的中间值
  • 采样频率需高于待测信号最高频率的5倍以上,若测量高频信号可适当缩短ADC采样时间,提高采样率
  • 测量低于100Hz的低频信号时,可替换定时器为SysTick计时,无需额外占用定时器资源

内容的提问来源于stack exchange,提问作者Alperen Eroğlu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 05:27:04