零知ESP32--XR2206波形监视器
项目概述
本项目用一块 ESP32 与 XR2206 函数信号发生器 组成一台简易的频率计 + 真实波形监视器:
- 测频:XR2206 的方波输出接入 ESP32 的 GPIO34,使用 ESP32 硬件脉冲计数器(PCNT)对脉冲累加,再用"闸门计数法"换算成频率。
- 波形显示:XR2206 的当前波形输出(正弦 / 三角 / 方波)接入 GPIO35,通过 ADC 真实采样,在 ST7789 屏幕上画出"真实 ADC 波形",附带上升沿触发、自动量程与削波检测。
- 双界面:按 GPIO27 键在 数据界面(大字频率) 与 波形界面(滚动示波) 之间切换。
- 网页监视:ESP32 开启软 AP(SSID
ESP32-XR2206),手机连上热点后访问http://192.168.4.1,可看到频率数字 + 由真实采样数据绘制成网页 canvas 波形(300ms 轮询刷新)。
项目亮点
- PCNT 硬件计数测频,几乎不占 CPU:脉冲计数交给 ESP32 硬件外设完成,主循环只需要几十毫秒读取一次计数。
- 自适应闸门:闸门时长会在 1s ~ 4s 之间自动调整——计数太少自动加倍闸门提高精度,计数太多恢复 1s 保证响应速度。
- 真实 ADC 波形,不是"画出来的正弦":屏幕和网页都显示 GPIO35 的真实采样点,方波就是方波、削波就提示削波。
- 触发与防混叠兼顾:采集前先"摸底"找电平范围,用中值 + 滞回做上升沿触发,保证每帧波形起点一致;采样间隔又按频率自适应,高频不延时连续采。
- 自动量程 + 削波报警:波形幅度过小时自动切全量程;当采样值大量贴着 0 / 4095 两端时判定为输入削波,屏幕红色提示
CLIP! turn AMP down。 - 网页端与屏幕端双界面:软 AP + HTTP JSON 接口,手机浏览器即可实时看频率与波形,无需额外上位机软件。
- 代码分层极简:FreqCounter / ModeButton / Display / WebUI 四个类各司其职,主程序只负责初始化与调度。
项目难点及解决方案
| 难点 | 具体表现 | 解决方案(代码对应) |
|---|---|---|
| 16 位计数溢出 | PCNT 计数器寄存器是 int16,上限 32767,高频时几十毫秒就溢出丢数 | update() 每 50ms 搬运一次 readAndClear(),累计到 32 位 accumCount_(FreqMeter.cpp update) |
| 固定闸门无法兼顾精度与响应 | 闸门太长高频响应慢,太短低频精度差 | 自适应闸门:count<100 且闸门<4s 则翻倍;count>1000 则恢复 1s(update() 末尾) |
| 波形画面抖动 / 起点乱 | 不触发直接采,每帧波形相位随机,画面乱跳 | captureRealScope() 先 64 次采样摸 max/min,取中值 + 1/20 滞回做上升沿触发(等待 40ms),保证每帧从同相位开始 |
| 波形残影叠加 | 新波形叠在旧波形上,画面重影 | 绘制前 tft.fillRect() 整块清空绘图区,再画边框与中线 |
| 输入削波或幅度贴边 | 输入超出 ADC 量程被"削平",正弦被削成方波 | nearLo<80 / nearHi>4015 各超过 1/8 样本判为削波,波形转红色并提示调低幅度;量程自适应加 1/12 边距 |
| 混叠与采样率矛盾 | 频率高时延时采样会混叠,频率低时又要等 | sampleIntervalUs():目标覆盖约 2 个周期;<5Hz 固定 500us;>20kHz 不延时连续采样 |
| 按键抖动 / 长按误触 | 机械按键抖动导致一次变两次 | 按下瞬间记录 downMs_,抬起时要求按住时长 >30ms 且 <800ms 才记短按(ModeButton::update()) |
| 屏幕与 WiFi 库 depcomp | 编译期可能报 FS/WiFi/WebServer 与 TFT_eSPI 相互冲突 | 包含顺序固定:FS.h、WiFi.h、WebServer.h 在前,TFT_eSPI.h 在后 |
| 大批量数据上网页 | 200 个采样点 + 频率要每 300ms 传输 | 网页只请求一个 /api/wave,返回紧凑 JSON(字符串预留 reserve(1600)),JS 用 canvas 绘制 |
一、硬件系统部分
1.1 硬件清单
| 器件 | 型号/规格 | 用途 |
|---|---|---|
| 主控 | ESP32 开发板 | 测频、ADC 采样、显示驱动、软 AP |
| 扩展板 | ESP32扩展板 | 方便接线 |
| 信号发生器 | XR2206 函数信号发生器 | 被测信号源(输出方波/正弦/三角,可调频率、幅度) |
| 显示 | ST7789 240×240 SPI 彩屏 | 本地数据显示 |
| 分压 | 电阻分压 | 把 XR2206 输出幅度降到 0~3.3V 时序之内,保护 ESP32 模拟输入 |
| 连接 | 杜邦线 / 面包板 | 接线 |
1.2 接线方案
| ESP32 引脚 | 作用 | 连接对象 | 代码宏 |
|---|---|---|---|
| GPIO34 | 测频输入(方波) | XR2206 方波输出 → 分压 → 此脚 | PIN_FREQ_IN |
| GPIO35 | 真实 ADC 波形采样 | XR2206 波形输出(任意波形)→ 分压 → 此脚 | PIN_WAVE_ADC |
| GND | 共地 | XR2206、ESP32、屏幕、按键全部共地 | — |
⚠️ 分压电路横向:XR2206 输出幅度可能超过 3.3V,必须分压到 ≤3.3V 才能接 GPIO34/GPIO35,否则可能损坏 ESP32。
1.3 硬件连接图

1.4 实物连接图

二、软件架构设计
2.1 系统初始化(setup())
初始化顺序(ESP32_XR2206_Simple.ino setup())
├─ Serial.begin(115200) 启动串口调试
├─ counter.begin(34) 初始化 PCNT(硬件计数 + 滤波 + 清零)
├─ modeBtn.begin(27) 按键输入上拉
├─ display.begin() TFT 初始化 / 旋转 + ADC 设置(12bit,11dB) + 绘制数据界面骨架
└─ web.begin(&counter) 软 AP 模式 + 注册路由 + 启动 HTTP
要点:
- ADC 配置集中:
display.begin()里调用analogReadResolution(12); analogSetAttenuation(ADC_11db);后面所有analogRead()均为 12 位(0~4095),量程约 0~3.3V 的俾斯。 - web 回调持指针:
web.begin(&counter)传入频率计的指针,网页接口实时读取最新频率。
2.2 主循环逻辑(loop())
loop() 每一轮:
├─ counter.update() // 搬计数 + 判断闸门结束 → 算频率
├─ web.update() // 处理网页 HTTP 请求(异步轮询)
├─ modeBtn.update() // 读取按键,检测短按事件
├─ 若短按 → mode 切换(MODE_DATA ↔ MODE_WAVE),forceRedraw=true
└─ 屏幕刷新调度:
interval = (mode==WAVE) ? 50ms : 200ms
若 forceRedraw 或超过 interval → display.render(模式, 频率, AP地址)
- 刷新率按模式分:波形界面 50ms(快速更波形),数据界面 200ms(大数字不用太勤)。
- 强制重绘:切换模式的按键瞬间除
lastMode变化外,还用forceRedraw保证重新画一遍新界面的"骨架"。
三、代码拆分讲解
3.1 头文件(FreqMeter.h)
把配置宏与接口集中,四个类对应四个功能模块:
#define PIN_FREQ_IN 34 // 测频输入
#define PIN_WAVE_ADC 35 // 波形 ADC
#define PIN_MODE_BTN 27 // 切换按键
#define WIFI_AP_SSID "ESP32-XR2206" // AP 名
#define WIFI_AP_PASS "12345678" // AP 密码
#define SCOPE_SAMPLES 200 // 波形采样点总数
类职责:
| 类 | 职责 | 关键成员 |
|---|---|---|
FreqCounter |
闸门计数测频 | gateLengthMs_(闸门时长)、accumCount_(累计脉冲)、freqHz_ |
ModeButton |
短按检测 | wasDown_/downMs_/shortEvent_ |
Display |
TFT 渲染 | render(mode, hz, ip, forceRedraw) |
WebUI |
软 AP + HTTP | begin(FreqCounter*) / ipString() |
3.2 频率计(FreqCounter)
初始化——配置 PCNT 单元 0:GPIO34 输入、仅上升沿计数(PCNT_COUNT_INC)、清零计数上下限 ±外层控制在 int16 范围,再加 100 个单位输入滤波:
cfg.pos_mode = PCNT_COUNT_INC; // 上升沿 +1
cfg.neg_mode = PCNT_COUNT_DIS; // 下降沿不计
cfg.counter_h_lim = 32767; // 计数上限(防 int16 溢出)
pcnt_set_filter_value(PCNT_UNIT, 100); // 滤除毛刺
闸门测频流程(update())——本项目最核心的换算逻辑:
// 每 50ms 搬运一次计数,防止 16 位计数溢出
if (elapsed >= 50) accumCount_ += readAndClear();
// 闸门结束:频率 = 累计脉冲数 / 实际闸门时间
if (elapsed >= gateLengthMs_) {
freqHz_ = accumCount_ * 1000.0f / elapsed;
// 自适应闸门:计数<100 翻倍闸门;>1000 恢复 1s
if (accumCount_ < 100 && gateLengthMs_ < 4000) gateLengthMs_ *= 2;
else if (accumCount_ > 1000 && gateLengthMs_ > 1000) gateLengthMs_ = 1000;
accumCount_ = 0;
gateStartMs_ = now;
}
测出的是实际经过时间(
elapsed毫秒),用真实计数 ÷ 真实耗时,避免了闸门边界误差。
3.3 按键(ModeButton)
上拉输入 + 边沿检测 + 时间窗判定:
if (down && !wasDown_) { downMs_ = now; } // 按下瞬间记录时刻
else if (!down && wasDown_) { // 抬起
if (now - downMs_ > 30 && now - downMs_ < 800)
shortEvent_ = true; // 30ms~800ms = 短按
}
wasDown_ = down;
3.4 显示(Display)
- 数据界面:大号数字(6号字)+ 单位 + AP 地址 + 提示条。
- 波形界面:
REAL ADC标题、绘图区边框(x:20, y:55, 宽=采样点数, 高140)、垂直中线、底部频率与提示。
频率格式化 splitFreq():
≤0 → "---"
≥100kHz → 2 位小数 kHz ≥1000Hz → 3 位小数 kHz
否则 → 1 位小数 Hz(原值)
3.5 采集与波形绘制(captureRealScope / drawRealScope)——核心难点处理
第一步:摸底找电平:连续读 64 次,找 min/max。
第二步:设定触发阈值:mid = (min+max)/2,滞回宽 = 幅跨度/20(下限 15)。可适配带直流偏置的正弦/三角波(因为用的是相对中值,不是绝对 0V)。
第三步:等上升沿:波形先低于 mid-滞回,再穿过 mid+滞回 才启动采样(40ms 超时)。
第四步:按频率自适应采样间隔:
if (hz < 5) return 500; // 极低频 固定 500us
if (hz > 20000) return 0; // 超高频 连续采样
interval = 周期(μs) * 2 / 200; // 目标窗口 = 2 个周期
clamp(0, 2000) // 最长 2000us
第五步:自动量程与削波检测:幅度跨度 <40 用全量程;否则上下留 1/12 边距。统计贴边样本(<80 和 >4015),两端都超 1/8 判为 削波 → 画红色 + 提示 CLIP!。
坐标系换算(ADC 值 → 屏幕 Y):
y = SCOPE_Y + SCOPE_H - 1 - (v - lo) * (SCOPE_H - 2) / span;
(lo/span 为自动量程后的下限与跨度,SCOPE_H-2 留 1 像素上下边界。)
3.6 网页模块(WebUI)
- 软 AP:
WiFi.mode(WIFI_AP); WiFi.softAP(SSID, PASS); - 路由:
/返回内嵌 HTML;/api/wave与/api/status都返回 同一份 JSON(实测频率 + 200 个采样点)。 - 网页 JS 每 300ms
fetch('/api/wave'),canvas 画网格线 + 波形曲线。
{"measured":1000.5,"samples":[1234,1333,...,1024]}
四、操作过程及数据展示
4.1 操作步骤
-
接线:按 1.2 表格连接 XR2206、ESP32、按键、屏幕,确保共地。
-
烧录:零知派导入工程,选择
零知ESP32烧录,串口监视器 115200 波特率。 -
上电:屏幕先显示"数据界面";串口打印:
ESP32-XR2206 meter + real ADC scope WiFi AP: ESP32-XR2206 http://192.168.4.1 -
手机端浏览:手机连接 WiFi
ESP32-XR2206(密码12345678),浏览器打开192.168.4.1,即可看到网页频率与实时波形(每 300ms 刷新)。 -
切换界面:短按 GPIO27 按键,屏幕在
数据 ↔ 波形间切换。 -
调节信号源:旋转 XR2206 频率/幅度旋钮,观测频率数字与波形变化。
4.2 数据展示
XRR2206 输出不同频率时的屏幕 / 网页显示效果(以实测为准):
| 输入设置 | 屏显数据 | 网页波形 | 闸门行为 |
|---|---|---|---|
| 低频(约 50 Hz) | 50.0 Hz |
完整几个周期 | 闸门自动加倍拉长至 1s/2/4s 提精度 |
| 中频(约 1 kHz) | 1.000 kHz |
2 个周期窗口 | 默认 1s 闸门,3 位小数 |
| 高频(约 10 kHz) | 10.000 kHz |
高频密集波 | 1s 闸门 |
| 超高频(>20 kHz) | 100.320 kHz |
连续采样,可能混叠 | 采样间隔=0,连续采集 |
| 幅度过大 | 数值不变,波顶/底贴边 | 波形上下削平 | 屏幕提示 CLIP!,波形变红 |
观察点:低频段数字末尾会因闸门变化"慢慢变稳定";幅度太大时屏幕右下角变红提示,此时应调小 XR2206 输出幅度。
4.3 演示视频
零知ESP32--XR2206波形监视器
五、技术原理
5.1 工作原理
频率测量 = 硬件计数 + 闸门时间法
- PCNT 硬件在 34 引脚每次上升沿
+1,不做软件轮询(省 CPU)。 - 软件每 50ms 搬运一次计数值到 32 位累加器,解决 16 位溢出。
- 闸门(默认 1s)结束后:频率 = 累计脉冲 ÷ 实际闸门毫秒 × 1000。
- 自适应:统计过程中脉冲太少(<100 个)就加倍闸门,让计数更多 → 相对误差降低;脉冲过多(>1000 个)说明取样足够,闸门回到 1s 保持响应。
波形显示 = 真实 ADC 采样 + 软件触发 + 自动缩放
采样流程:
摸底(min/max) → mid=(min+max)/2 → 滞回判定(跨中线) → 等上升沿 → 间隔采样×200点
绘制流程:
清空区域 → 自动量程(留边距) → ADC值→屏幕Y映射 → 折线绘制 → 削波检测→红色
- 触发保证波形稳定不滚屏;自动量程避免幅度小波形贴边;削波检测保护使用者了解幅度是否过载。
- 采样率自适应:目标是在 200 个采样点里尽量铺满约 2 个周期;低频把采样拉长(塞多个周期),高频只能连续采(受 ADC 转换时间与主循环延时限制,高频可能混叠是正常现象)。
5.2 工作模式配置
软件模式
| 模式 | 枚举值 | 界面 | 刷新间隔 |
|---|---|---|---|
| 数据模式 | MODE_DATA = 0 |
大字频率 + 单位 + AP IP | 200ms |
| 波形模式 | MODE_WAVE = 1 |
标题 + 示波器波形 + 频率 | 50ms |
切换:GPIO27 短按(30~800ms)在两者间翻转;长按 >800ms 不计。
硬件参数配置(改宏即可适配不同板子 / 接线):
| 宏 | 默认 | 作用 |
|---|---|---|
PIN_FREQ_IN |
34 | 测频输入脚(须为 PCNT 可接的引脚) |
PIN_WAVE_ADC |
35 | ADC 采样脚(GPIO32-39 等 ADC1 允许,内部 µF 电压范围允许) |
PIN_MODE_BTN |
27 | 按键脚(输入上拉) |
WIFI_AP_SSID/PASS |
ESP32-XR2206 / 12345678 | 软 AP 凭据 |
SCOPE_SAMPLES |
200 | 波形点数(与绘图区宽度一致) |
| ADC 分辨率 | 12bit | analogReadResolution(12) |
| ADC 倍率 | 11dB | analogSetAttenuation(ADC_11db),量程约 0~3.3V |
PCNT 选择注意:GPIO34/35 为模拟 ADC 通道(不可用于 PCNT?实际上 34/35 是纯输入脚,可用于 PCNT;而 GPIO27 不能重复用于 PCNT 而同时被 ADC 使用等)。若换脚,须查阅 ESP32 PC NT 引脚表确保该 GPIO 支持 PCNT 通道。
六、常见问题指引
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 频率一直为 0 / 不变 | 方波没接对 / 分压太弱 / 输入脚不对 | 检查 GPIO34 接入、分压、接地;串口观察 |
| 频率跳变严重 | 滤波太弱 / 没共地 / 共模噪声 | 检查接线电感;滤波值已开 100;确认 XR2206 GND 与 ESP826 共地 |
| 屏幕波形残影/跳 | 触发没生效 | 排查贡献:检查信号幅度、滞回配合;触顶时间 40ms 超时则等不到波形 |
屏幕显示 CLIP! |
输入幅度过大 / 削波 | 调小 XR2206 输出幅度(或重新分压) |
| 高频波形紊乱/混叠 | 采样率低于奈奎斯特 | 属正常高频现象,可观察频率值;网页提示"高频可能混叠" |
| 网页打不开 | 手机没连热点 / IP 错 / 密码错 | 连 ESP32-XR2206,密码 12345678,浏览 http://192.168.4.1;串口看 AP 是否启动 |
| 屏幕白屏/花屏 | ST7789 SPI 接线或 User_Setup.h 引脚配置不符 | 核对 SCL/SDA/RES/DC/CS 与 User_Setup.h;检查供电电流 |
| 按键没反应 | 接线 / 按压时间窗口 | 确认接住 GPIO27 与 GND;按 30~800ms |
| 低频精度不足 | 数据太少 | 等闸门自适应至 4s(此时数据更新慢但更准) |
| .ino 编译报 TFT_eSPI 冲突 | 库包含顺序 | 保持 FS.h/WiFi.h/WebServer.h 在 TFT_eSPI.h 之前 |
更多推荐


所有评论(0)