押题二:Touch/ADC 参数终端

命中方向:Touch、自校准、ADC 稳定采样、定时器、RGB 分档、UART、NVS。

模拟任务书

  1. ADC 接电位器,输入范围 0–3.3 V。每 500 ms 采样一次。
  2. 每次采集 16 个样本,排序后去掉最高 2 个和最低 2 个,对剩余样本求平均并换算毫伏。
  3. 低于 1000 mV 显示绿色,1000–2199 mV 显示黄色,2200 mV 及以上显示红色并拉高 GPIO21。
  4. Touch 启动时自动采集基线,使用相对变化量判断触摸,必须兼容不同导电片。
  5. 每次有效触摸切换 LIVE/HOLD。HOLD 状态冻结最后一次有效 ADC 结果。
  6. 按键短按重新校准 Touch;长按恢复默认阈值。
  7. 串口可读 ADC/Touch 原始值,修改两个电压阈值、采样周期和 Touch 灵敏度并保存。

建议评分点

项目分值验收
ADC1 和输入安全10GPIO1,输入不超过 3.3 V
定时采样与 16 点去极值平均20波动明显小于单次采样
三档 RGB 与 GPIO 输出15跨越阈值时状态正确
Touch 基线、自适应阈值和消抖20换导电片后重新校准可用
LIVE/HOLD 状态机10HOLD 后转动电位器,显示保持
UART 参数与状态查询15越界参数被拒绝
NVS 和异常默认值10断电保存;损坏时恢复默认

直接使用

Arduino 工程:arduino_pio/mock_02_touch_adc。主要修改 include/contest_user_config.hsrc/task_logic.cpp

TOUCH CAL
TOUCH READ
ADC READ
SET ADC_LOW 1000
SET ADC_HIGH 2200
ADC AUTO 500
SAVE
STATUS

最可能的变题

  • 电位器改成光敏、电阻式传感器或电池电压。
  • 三档改成百分比、频率或 LED 亮度映射。
  • Touch 改为短触/长触或双击识别。
  • 要求 ADC 阈值附近增加迟滞,避免颜色频繁跳变。

现场修改指南:先判断要不要改

当前代码已经实现三档 ADC、迟滞和 LIVE/HOLD。现场阈值、采样周期和迟滞不同,优先用串口改参数,不要立即改源码。

题目一句话对应哪里

题目关键词函数/变量作用
开机默认迟滞beginTask()user1为 0 时设为 50 mV
短按重新校准shortPress()调用 calibrateTouch()
Touch 保持/恢复touchChanged()在 LIVE 与 HOLD 间切换
阈值和分档adcSample()使用低、高阈值和迟滞更新 level
颜色/GPIO21tick()低绿、中黄、高红,高档拉高 GPIO21
HOLD/HYST 命令command()解析题型专属参数

变量翻译

代码含义
display_mv当前用于显示和判断的毫伏值;HOLD 后不再更新
level0 低档、1 中档、2 高档
adc_low_mv低档和中档的分界
adc_high_mv中档和高档的分界
user1迟滞毫伏数,避免阈值附近反复跳档
mode0 LIVE,1 HOLD

最安全:先用串口改参数

现场要求命令是否改代码
阈值改为 800/2500 mVSET ADC_LOW 800
SET ADC_HIGH 2500
不需要
迟滞改为 80 mVHYST 80不需要
每 200 ms 采样ADC AUTO 200不需要
Touch 更灵敏SET TOUCH_DELTA 10不需要;数值越小越容易触发
保存以上参数SAVE不需要

输入安全:无论代码如何映射,GPIO1 电压都不能超过 3.3 V。测更高电压必须先使用电阻分压。

三种高概率变题:可直接替换

示例 A:改变低、中、高三档的颜色和高档输出

替换范围:完整替换 tick()

void tick(contest::ContestApp &app, uint32_t) {
    if (app.touchActive()) {
        app.setRgb(45, 45, 45);       // 正在触摸:白色
    } else if (level == 0) {
        app.setRgb(0, 0, 70);         // 低档:蓝色
    } else if (level == 1) {
        app.setRgb(0, 65, 0);         // 中档:绿色
    } else {
        app.setRgb(90, 0, 0);         // 高档:红色
    }
    app.setDigitalOutput(level == 2); // 只有高档拉高 GPIO21
}

只要求高档亮灯、不要求 RGB 随 Touch 变白时,可以删除最前面的 if (app.touchActive())分支并从 level开始判断。

示例 B:三级改四级,固定分界 800/1600/2400 mV

此示例保持 HOLD 功能,但改用三个固定分界。先保留文件顶部的 uint8_t level,再替换下面两个完整函数。

完整替换 adcSample()

void adcSample(contest::ContestApp &app, uint32_t millivolts) {
    if (app.config().mode == 1) return; // HOLD 时保持旧结果

    display_mv = millivolts;
    if (millivolts < 800) level = 0;
    else if (millivolts < 1600) level = 1;
    else if (millivolts < 2400) level = 2;
    else level = 3;

    app.emit("EVENT ADC MV=%lu LEVEL=%u HOLD=0",
             static_cast<unsigned long>(display_mv), level);
}

完整替换 tick()

void tick(contest::ContestApp &app, uint32_t) {
    if (level == 0) app.setRgb(0, 0, 50);
    else if (level == 1) app.setRgb(0, 60, 0);
    else if (level == 2) app.setRgb(70, 30, 0);
    else app.setRgb(90, 0, 0);

    app.setDigitalOutput(level == 3);
}

注意:这个入门版本没有迟滞。题目明确要求迟滞时,应继续使用原三级算法,或者为三个分界分别写上升/下降阈值。

示例 C:Touch 只进入 HOLD,长按才恢复 LIVE

替换范围:完整替换 touchChanged();现有 longPress()已经会恢复 LIVE,无需再改。

void touchChanged(contest::ContestApp &app, bool active) {
    if (!active) return;

    app.setMode(1);             // 只进入 HOLD,不再来回切换
    app.emit("STATE HOLD=1");
}

如果题目改成“Touch 期间保持,松开恢复”,则使用:

void touchChanged(contest::ContestApp &app, bool active) {
    app.setMode(active ? 1 : 0);
    app.emit("STATE HOLD=%u", app.config().mode);
}
示例 D:高于 2800 mV 时每 500 ms 蜂鸣一次

1. 在文件顶部的 level变量后加入:

uint32_t last_alarm_ms = 0;

2. 把 tick()的第二个参数从省略名称改为 now_ms,并在函数末尾加入节流判断:

void tick(contest::ContestApp &app, uint32_t now_ms) {
    if (app.touchActive()) {
        app.setRgb(45, 45, 45);
    } else if (level == 0) {
        app.setRgb(0, 55, 0);
    } else if (level == 1) {
        app.setRgb(75, 30, 0);
    } else {
        app.setRgb(80, 0, 0);
    }
    app.setDigitalOutput(level == 2);

    if (display_mv >= 2800 &&
        now_ms - last_alarm_ms >= 500) {
        last_alarm_ms = now_ms;
        app.startBeep(2200, 80);
    }
}

公共 startBeep()会自动遵守静音设置,因此这里不必再读取 muted

改完后的最短验收

  1. 编译后输入 ADC READ,确认毫伏值随电位器变化。
  2. 输入 SET ADC_LOW 1000SET ADC_HIGH 2200,缓慢跨越阈值,观察颜色和 GPIO21。
  3. 触摸后转动电位器,HOLD 时 DISPLAY_MV应保持;恢复 LIVE 后继续变化。
  4. 在阈值附近轻微转动,开启迟滞时颜色不应快速来回跳。
  5. 输入 STATUS核对 LEVELHOLDHYST_MV

不要改:公共库里的 16 点采样和 Touch 消抖。除非任务书明确改变滤波算法,否则只改本页列出的题型函数。