这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【GROVECREATORKIT测评】3、综合2-模拟输入输出

共1条 1/1 1 跳转至页

【GROVECREATORKIT测评】3、综合2-模拟输入输出

高工
2026-10-04 15:58:47     打赏

一、评测目标

        验证用 Grove Rotary Angle Sensor(旋转角度传感器 / 电位器) 作为模拟输入,将 A2 引脚采集到的电压实时映射为 Grove Green LED 的 PWM 占空比,验证整套"模拟采集 → 占空比输出"链路在 Nano RP2040 Connect 上的可行性、线性度和稳定性。

具体回答三个问题:

  1. ADC 实际分辨率是不是 10 bit?满量程是 1023 还是 4095?

  2. 旋钮角度(0°–300°)和 PWM 占空比(0%–100%)的对应关系是否线性?

  3. 在没有外部分压的情况下,A2 引脚上能否稳定达到接近 3.3 V 的满量程电压?

二、硬件清单

物料型号 / 规格数量备注
主控板Seeed Studio Arduino Nano RP2040 Connect1RP2040 双核,板载 3.3V LDO
模拟输入Seeed Grove – Rotary Angle Sensor110 kΩ 线性电位器,机械角度 300°,D1 模拟输出
PWM 输出Seeed Grove – Green LED1板载 LED + 限流电位器,Grove D2 接 PWM 信号
连接线Grove 4‑pin 通用线2红=VCC、黑=GND、黄=SIG、白=NC
测量仪表万用表1验证 A2 引脚实际电压

Grove 接口定义:

线色含义
黑色GND
红色VCC(板子供 3.3V,本模块兼容)
黄色SIG(D4 PWM输出 / A2 模拟输入)
白色NC(本例不使用)

三、接线图

  • Rotary Angle Sensor 接 Nano RP2040 Connect 的 A2 母座(模拟输入)。

  • Green LED 接 D4 母座(PWM 输出)。

  • 两块模块的 VCC 都接到板载 3.3V,不接 5V,因为 Nano RP2040 Connect 的 ADC 输入上限就是 3.3 V;超过会烧 IO。

四、软件设计

4.1 关键引脚

const uint8_t  ADC_PIN = 16;   // 丝印 A2 → Arduino D16 (p28 / GPIO28)
const uint8_t  PWM_PIN = 4;    // 丝印 D4 → Arduino D4  (p16 / GPIO16)

        踩坑记录:ArduinoCore‑mbed 的 variant.cpp 把丝印 A2 的"数字引脚号"定为 16(对应 GPIO28),直接写 A2 也可以,但写常量时建议统一用 D16 / D4 这种和丝印对齐的编号,避免把 16 误读成 GPIO16(D4)。

4.2 ADC 分辨率

const float    VREF_NOMINAL    = 3.3f;
const uint16_t ADC_NOMINAL_MAX = 1023;   // 实测:这块板 analogRead() 是 10 bit

  • 实测确认:旋钮拧到最大时,万用表测得 A2 = 3.30 V,串口 adc=1020。

  • 1020 ≈ 1023(差 4 个码值是分压损耗 + ADC 噪声),说明这块板的 analogRead() 实际返回 10 bit,不是 12 bit。

4.4 测试代码

#include <Arduino.h>
#include "pinDefinitions.h"   // p16 / p18 等 PinName 常量(可选)

const uint8_t  ADC_PIN         = 16;   // 丝印 A2  -> Arduino D16 (p28)
const uint8_t  PWM_PIN         = 4;    // 丝印 D4  -> Arduino D4  (p16)

const float    VREF_NOMINAL    = 3.3f;
const uint16_t ADC_NOMINAL_MAX = 1023;

#define ENABLE_CALIBRATION    1
const uint32_t CALIB_MS        = 5000;   // 5 秒校准窗口
const uint16_t ADC_FLOOR       = 8;      // adc < 8 视为接近 0 V(防止信号源没接时被锁成 0)

const uint8_t  PWM_MAX         = 255;

const uint32_t LOG_INTERVAL_MS = 100;
const uint8_t  DUTY_DELTA_PCT  = 1;

void setup() {
  // PWM 输出
  pinMode(PWM_PIN, OUTPUT);
  analogWrite(PWM_PIN, 0);              // 初始占空比 = 0%

  // 模拟输入(analogRead 内部已配置为输入,无需显式 pinMode)

  // 调试串口
  Serial.begin(115200);
  // USB CDC 必须等到主机端"打开串口监视器"才被视为就绪。
  // 为了不让程序在没有打开监视器时永久阻塞,这里加 2 s 超时。
  uint32_t t0 = millis();
  while (!Serial && (millis() - t0) < 2000) { /* 等枚举或超时 */ }

  Serial.println();
  Serial.println("=== A2 (analog) -> D4 (PWM) ready ===");
  Serial.println("    format: V_in  duty%  raw_adc  pwm_raw  adc_full");

#if ENABLE_CALIBRATION
  Serial.println();
  Serial.print("[CALIB] 接下来 ");
  Serial.print(CALIB_MS / 1000);
  Serial.println(" 秒:请把 A2 信号源调到你能给的 **最大值**,保持住。");
  Serial.println("         程序会自动把它记作本机 ADC 满量程,再换算电压 / 占空比。");
  Serial.println("         (不需要这个功能就把源码里 ENABLE_CALIBRATION 改成 0)");
  // 在校准窗口内不输出 PWM,避免"低电压小灯"误读
  analogWrite(PWM_PIN, 0);
#endif

  // 用 D4 闪一下作为 setup() 心跳 —— 不开串口监视器也能肉眼确认固件在跑
  analogWrite(PWM_PIN, 64);            // ~25%
  delay(100);
  analogWrite(PWM_PIN, 0);
}

uint32_t lastLogMs   = 0;
uint8_t  lastDutyPct = 0xFF;           // 强制首次一定会打

uint16_t adcFull      = ADC_NOMINAL_MAX;  // 校准中持续刷新
uint32_t calibStartMs = 0;
bool     calibrated   = false;

void loop() {
  // 1) 读 A2(12 bit)
  uint16_t adcRaw = analogRead(ADC_PIN);

  // 2) 校准窗口:持续跟踪 adc 极大值作为本机满量程
  if (!calibrated) {
    if (calibStartMs == 0) {
      calibStartMs = millis();
    }
    if (adcRaw > adcFull) {
      adcFull = adcRaw;
    }
    if ((millis() - calibStartMs) >= CALIB_MS) {
      // 校准结束。给一个合理下限,避免信号源根本没接 / 一直 0 时
      // adcFull 也被锁在 0,下面所有映射都会爆。
      if (adcFull < ADC_FLOOR) {
        adcFull = ADC_FLOOR;
      }
      calibrated = true;
      Serial.println();
      Serial.print("[CALIB] done. adcFull=");
      Serial.print(adcFull);
      Serial.print(" (此即信号源最大值对应的 ADC 码值;打印的 V_in 由此换算)");
      Serial.println();
    }
  }
  uint8_t  pwmRaw  = (uint8_t)((uint32_t)adcRaw * PWM_MAX / adcFull);
  analogWrite(PWM_PIN, pwmRaw);
  uint8_t  dutyPct = (uint8_t)((uint32_t)pwmRaw * 100 / PWM_MAX);
  float    vIn     = (adcRaw * VREF_NOMINAL) / adcFull;

  // 3) 节流打印
  uint32_t now = millis();
  if (now - lastLogMs >= LOG_INTERVAL_MS) {
    lastLogMs = now;
    if (dutyPct == lastDutyPct) {
      Serial.print("V_in=");
      Serial.print(vIn, 3);
      Serial.print(" V   adc=");
      Serial.print(adcRaw);
      Serial.print("   adcFull=");
      Serial.println(adcFull);
    } else {
      Serial.print("V_in=");
      Serial.print(vIn, 3);
      Serial.print(" V   duty=");
      Serial.print(dutyPct);
      Serial.print("%   adc=");
      Serial.print(adcRaw);
      Serial.print("   pwm_raw=");
      Serial.print(pwmRaw);
      Serial.print("   adcFull=");
      Serial.println(adcFull);
      lastDutyPct = dutyPct;
    }
  }

  // 简单周期 ≈ 10 ms(采样 + PWM + 串口)
  delay(10);
}


五、测试效果

eepw-2.gif





关键词: GROVE     测评     模拟    

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]