ESP32的脉冲计数器

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

自己GPIO17 产生 1Mhz方波。

端接GPIO 16-》17引脚。

#include <Arduino.h>
#include <driver/pcnt.h>

#define PCNT_UNIT      PCNT_UNIT_0
#define PCNT_INPUT_PIN 16  // 接收端:改用 GPIO 16 (板上标注通常为 RX2)
#define PWM_OUTPUT_PIN 17  // 发送端:改用 GPIO 17 (板上标注通常为 TX2)

volatile int16_t last_count = 0;
volatile bool data_ready = false;
hw_timer_t *timer = NULL;

void IRAM_ATTR onTimer() {
    pcnt_get_counter_value(PCNT_UNIT, (int16_t*)&last_count);
    pcnt_counter_clear(PCNT_UNIT); 
    data_ready = true;
}

void setup() {
    Serial.begin(115200);
    delay(1000); 

    // 1. 在 GPIO 17 上产生一个 1MHz, 50% 占空比的方波
    ledcAttach(PWM_OUTPUT_PIN, 1000000, 1); 
    ledcWrite(PWM_OUTPUT_PIN, 1);           

    // 2. 配置 PCNT 硬件计数器(接收端 GPIO 16)
    pcnt_config_t pcnt_config = {0};
    pcnt_config.pulse_gpio_num = PCNT_INPUT_PIN;
    pcnt_config.ctrl_gpio_num = -1; 
    pcnt_config.unit = PCNT_UNIT;
    pcnt_config.channel = PCNT_CHANNEL_0;
    
    pcnt_config.pos_mode = PCNT_COUNT_INC;   
    pcnt_config.neg_mode = PCNT_COUNT_DIS;   
    pcnt_config.lctrl_mode = PCNT_MODE_KEEP; 
    pcnt_config.hctrl_mode = PCNT_MODE_KEEP; 
    
    pcnt_config.counter_h_lim = 20000;  
    pcnt_config.counter_l_lim = -20000; 
    pcnt_unit_config(&pcnt_config);

    // 3. 关闭滤波器
    pcnt_filter_disable(PCNT_UNIT); 

    pcnt_counter_clear(PCNT_UNIT);
    pcnt_counter_resume(PCNT_UNIT);

    // 4. 配置定时器(10ms 采样)
    timer = timerBegin(1000000); 
    timerAttachInterrupt(timer, &onTimer); 
    timerAlarm(timer, 10000, true, 0);    
    
    Serial.println("--- ESP32 1MHz PCNT 测试开始 ---");
    Serial.println("请用一根杜邦线连接:G17 (TX2) <---> G16 (RX2)");
}

void loop() {
    if (data_ready) {
        data_ready = false;
        
        int16_t count_10ms = last_count; 
        uint32_t frequency = count_10ms * 100; 

        Serial.print("10ms Count: ");
        Serial.print(count_10ms);
        Serial.print(" | Frequency: ");
        Serial.print(frequency);
        Serial.println(" Hz");
    }
}

测量10Mhz

#include <Arduino.h>
#include <driver/pcnt.h>

#define PCNT_UNIT PCNT_UNIT_0
#define PCNT_INPUT_PIN 16  // 接收端:GPIO 16 (RX2)
#define PWM_OUTPUT_PIN 17  // 发送端:GPIO 17 (TX2)

volatile int16_t last_hw_count = 0;
volatile bool data_ready = false;
hw_timer_t *timer = NULL;

// 针对 100ms 刷新的滑动滤波缓冲区(10个样本组合成1秒的物理总量)
const int FILTER_SIZE = 10;
uint32_t freq_history[FILTER_SIZE] = {0};
int filter_index = 0;

// 定时器采样中断(每 100ms 触发一次,非常安全)
void IRAM_ATTR onTimer() {
  // 直接读取硬件计数值(100KHz在100ms内只有10000个脉冲,绝不溢出)
  pcnt_get_counter_value(PCNT_UNIT, (int16_t *)&last_hw_count);
  
  // 清空计数器,准备下一轮
  pcnt_counter_clear(PCNT_UNIT);
  
  data_ready = true;
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  // 1. 发送端测试信号:生成一个 100 KHz (0.100 MHz) 的自测信号
  #if ESP_ARDUINO_VERSION_MAJOR >= 3
    ledcAttach(PWM_OUTPUT_PIN, 1000000, 10); // 100KHz
    ledcWrite(PWM_OUTPUT_PIN, 512);          // 50% 占空比
  #else
    ledcSetup(0, 100000, 10);
    ledcAttachPin(PWM_OUTPUT_PIN, 0);
    ledcWrite(0, 512);
  #endif

  // 2. 配置 PCNT 硬件计数器
  pcnt_config_t pcnt_config = { 0 };
  pcnt_config.pulse_gpio_num = PCNT_INPUT_PIN;
  pcnt_config.ctrl_gpio_num = -1;
  pcnt_config.unit = PCNT_UNIT;
  pcnt_config.channel = PCNT_CHANNEL_0;

  pcnt_config.pos_mode = PCNT_COUNT_INC;
  pcnt_config.neg_mode = PCNT_COUNT_DIS;
  pcnt_config.lctrl_mode = PCNT_MODE_KEEP;
  pcnt_config.hctrl_mode = PCNT_MODE_KEEP;

  // 100KHz测量的安全边界
  pcnt_config.counter_h_lim = 32000;
  pcnt_config.counter_l_lim = -32000;
  pcnt_unit_config(&pcnt_config);

  // 3. 100KHz 属于中低频,可以开启硬件滤波器滤除杂波(设为 100 个采样时钟周期)
  pcnt_set_filter_value(PCNT_UNIT, 100);
  pcnt_filter_enable(PCNT_UNIT);

  pcnt_counter_clear(PCNT_UNIT);
  pcnt_counter_resume(PCNT_UNIT);

  // 4. 配置定时器(100ms 采样一次,兼顾响应速度与精度)
  #if ESP_ARDUINO_VERSION_MAJOR >= 3
    timer = timerBegin(1000000); 
    timerAttachInterrupt(timer, &onTimer);
    timerAlarm(timer, 100000, true, 0); // 100000us = 100ms 采样窗口
  #else
    timer = timerBegin(0, 80, true); 
    timerAttachInterrupt(timer, &onTimer, true);
    timerAlarmWrite(timer, 100000, true);
    timerAlarmEnable(timer);
  #endif

  Serial.println("--- ESP32 100KHz 滤波优化版测试开始 ---");
}

int t = 0;
void loop() {
  if (data_ready) {
    data_ready = false;

    // 100ms 内的硬件脉冲数,乘以 10 得到一秒的赫兹(Hz)基准
    uint32_t current_hz = (uint32_t)last_hw_count * 10; 

    // 将新数据存入滑动窗口
    freq_history[filter_index] = current_hz;
    filter_index = (filter_index + 1) % FILTER_SIZE;

    // 计算滑动平均值,平滑 1 KHz 级别的微小抖动
    uint64_t sum_hz = 0;
    for (int i = 0; i < FILTER_SIZE; i++) {
      sum_hz += freq_history[i];
    }
    uint32_t final_hz = sum_hz / FILTER_SIZE;

    if (final_hz > 0) {
      t++;
      // 将 Hz 转换为 MHz
      double mhz = (double)final_hz / 1000.0; 

      Serial.print(t);
      Serial.print(" freq: ");
      // 完美输出类似 0.100 MHz 的格式
      Serial.print(mhz, 3); 
      Serial.println(" kHz");
    }
  }
}

实战派 ESP32-S3,双模无线开发板

ESP32-S3 原生支持 ESP-IDF,WiFi + 蓝牙一次搞定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值