传感器 · 六轴IMU · UNO R3

ICM-42670-P:接线、烧程序、看数据

照着接好4根线,把测试程序传到UNO里,就能在电脑上看加速度、角速度和温度。

Quick Facts

  • 接线:VCC、GND、SDA、SCL共4根
  • 程序:网页烧录或Arduino IDE
  • 看数据:加速度、角速度、芯片温度

先准备好

需要一块UNO R3、这款ICM-42670-P模块、一根能传数据的USB线和4根连接线。接好后,可以在电脑上看到加速度、转动速度和芯片温度。

先按下面接线,再选一种方式烧程序。已经烧过本页程序的UNO,直接看第3步。

1. 接线:先接这4根

先拔掉UNO的USB线,照表接好,再插回USB。

模块上的字接UNO哪里
VCC5V
GNDGND
SDAA4(或SDA)
SCLA5(或SCL)

AD0、CS、INT1、INT2先不接。这块模块默认地址是0x69,使用本页程序时,不要把AD0接地。

上面的5V接法只适用于本页这款带3.3V稳压器的模块。稳压器只给电源降压,SDA/SCL不要另加5V上拉;本页程序已关闭UNO内部上拉。换用其他程序或主控前,要确认信号电平,必要时加I²C电平转换。

2. 烧程序:选一种方式

烧录会覆盖UNO原来的程序,需要保留的请先备份。烧录时关闭其他串口窗口。

方式一:网页烧录

  1. 用电脑Chrome或Edge打开下面的烧录页。
  2. 勾选确认项,点“1 选择串口”,选中UNO对应的串口。
  3. 点“2 烧录并校验”,等到提示完成。
  4. 点“读取数据”,看下面的数值是否刷新。
方式二:Arduino IDE烧录下载程序包,打开后上传到UNO
  1. 下载程序包,解压后打开 ICM42670P_UNO_Diagnostic.ino。
  2. 在Arduino IDE里选择开发板 Arduino Uno,端口选择插上UNO后出现的那个。若提示缺少开发板支持包,安装Arduino AVR Boards。本程序不需要另外安装传感器库。
  3. 点“上传”,等到提示上传完成。
  4. 打开串口监视器,把波特率设成 115200,就能看数据。

3. 查看数据:这样判断正常不正常

网页看:连接UNO后点“读取数据”,加速度、角速度和温度应该持续刷新。如果程序已用IDE烧好,网页只需选串口、读取数据,不用重烧。看网页数据前,先关掉IDE串口监视器,否则串口会被占住。

你怎么动模块正常会看到什么
放在桌面不动加速度比较稳定,三个轴不用都为0,因为会测到重力。让Z轴竖直时,Z一般接近+1g或−1g,X、Y接近0。
慢慢倾斜X、Y、Z的加速度数值跟着姿态变化。
转动后停下转动时角速度变化,停下后回到0附近。静止时有少量偏差很常见,精确测量前需要校零。
查看温度这是芯片自己的温度,不是室温。

这款模块已用UNO读取过,通信和数据更新正常。本程序显示的是加速度和角速度,不会直接显示俯仰角或方向角。

IDE里一串逗号和数字怎么看?波特率115200,重点看S开头的数据行

数据行以 S, 开头,会不断增加。每行最后7个数,依次是:温度、加速度X/Y/Z、角速度X/Y/Z。加速度单位是g,角速度单位是°/秒。

上电时若看到 WHO_AM_I,0x67,说明认到了42670-P。前面的 BOOT、CONFIG 是启动信息,不是测量值。想省去看原始数字,可以关闭IDE串口监视器,改用网页“读取数据”。

遇到问题,先查这里

找不到UNO串口先查USB线和连接

换一根能传数据的USB线,重新插拔UNO,再打开串口列表。如果电脑仍没有识别到串口,检查这块UNO所用USB串口芯片的驱动是否装好。

烧录失败或提示串口被占用关掉其他串口窗口

关闭Arduino串口监视器及其他正在读串口的软件,再重新连接。使用IDE时确认开发板选的是Arduino Uno;网页方式遇到问题,可以改用上面的IDE方式。

烧好了,但没有数据检查四根线和程序

确认SDA接A4、SCL接A5,电源和地没有接反。AD0先不要接地,这个程序使用0x69。确认烧的是本页42670-P程序,42688的程序不能直接照用。IDE里若出现E或FAIL开头的信息,把完整信息和接线照片发给客服。

静止时数值不全是0重力和少量零偏是正常的

加速度会测到重力,不是静止就全为0。角速度静止时应在0附近,少量偏差需要校零;如果完全不刷新或一动也不变,再检查接线和程序。

原理图与贴装图

原理图供电、接口与芯片连接

原理图保留原工程的ICM-42688标注,42670-P的接线按本页说明。

当前ICM-42688模块原工程原理图的电路区域
点图打开原版PDF,可放大查看。

42670-P:7脚为FSYNC,不用时接地;9脚只作INT2。当前PCB的7脚已接地。

贴装图R/C/LED位号与交互定位
按已核对PCB实际坐标绘制的顶面贴装位置图
17件位号全部标出。顶视、不镜像;黄色圆点为U1原生焊盘1,LED1旁标注焊盘1=GND。
位号标称值/器件
C10.1uF
C22.2uF
C310nF
C41uF
C51uF
H1PZ254V-11-08P
LDO1ME6211C33M5G-N
LED10402 LED(颜色待确认)
R12.7K
R210K
R310K
R410K
R510K
R60R
R70R
R80R
U1ICM-42688-P

点击交互图中的位号可找元件位置。贴装前请核对实物方向;LED的颜色标注不一致,请以实物为准。

程序和图纸下载

查看程序源码需要修改程序时再展开
// ICM-42670-P diagnostic, UNO R3 / ATmega328P, 2026-10-17.
// Register definitions: TDK DS-000451 v1.2, pp. 48, 52-59, 68-71.
// Wiring: module VCC -> UNO 5V (module LDO), GND -> GND,
// module SDA -> A4, SCL -> A5. AD0 and CS use module 3.3V pull-ups.
// PUD disables AVR internal pull-ups BEFORE Wire.begin().
// Never drive the sensor's CS/AD0/INT pins from UNO 5V GPIO.
// This sketch does not certify 5V/3.3V logic-level compatibility.
#include <Wire.h>
#include <avr/io.h>

static const uint8_t ADDRESS = 0x69;
static const uint8_t WHO_AM_I = 0x75;
static bool bigEndian = true;
static bool ready = false;
static uint32_t sampleCount = 0;
static uint32_t lastReady = 0;

bool readRegs(uint8_t reg, uint8_t *data, uint8_t length) {
  Wire.clearWireTimeoutFlag();
  Wire.beginTransmission(ADDRESS);
  Wire.write(reg);
  uint8_t err = Wire.endTransmission(false);
  if (err != 0) {
    Serial.print(F("E,read_address,")); Serial.print(reg, HEX);
    Serial.print(','); Serial.println(err);
    return false;
  }
  uint8_t got = Wire.requestFrom(ADDRESS, length, (uint8_t)true);
  if (got != length || Wire.getWireTimeoutFlag()) {
    while (Wire.available()) Wire.read();
    Serial.print(F("E,read_length,")); Serial.print(reg, HEX);
    Serial.print(','); Serial.println(got);
    return false;
  }
  for (uint8_t i = 0; i < length; ++i) data[i] = Wire.read();
  return true;
}

bool writeReg(uint8_t reg, uint8_t value) {
  Wire.clearWireTimeoutFlag();
  Wire.beginTransmission(ADDRESS);
  Wire.write(reg); Wire.write(value);
  uint8_t err = Wire.endTransmission();
  if (err || Wire.getWireTimeoutFlag()) {
    Serial.print(F("E,write,")); Serial.print(reg, HEX);
    Serial.print(','); Serial.println(err);
    return false;
  }
  return true;
}

bool verifyReg(uint8_t reg, uint8_t expected) {
  uint8_t value = 0;
  if (!readRegs(reg, &value, 1)) return false;
  Serial.print(F("CONFIG,0x")); Serial.print(reg, HEX);
  Serial.print(F(",0x")); Serial.println(value, HEX);
  return value == expected;
}

int16_t wordAt(const uint8_t *data) {
  uint16_t value = bigEndian ? ((uint16_t)data[0] << 8) | data[1]
                             : ((uint16_t)data[1] << 8) | data[0];
  return (int16_t)value;
}

void setup() {
  // The AVR Wire core normally enables pull-ups to the MCU's 5V supply.
  MCUCR |= _BV(PUD);
  Serial.begin(115200);
  Wire.begin();
  digitalWrite(SDA, LOW); digitalWrite(SCL, LOW);
  Wire.setClock(100000);
  Wire.setWireTimeout(25000, false);
  delay(100);
  Serial.println(F("BOOT,ICM42670P_UNO_DIAG_v1,115200,I2C100k,AVR_PULLUPS_OFF"));
  Serial.println(F("EXPECT,address=0x69,WHO=0x67,accel=2g,gyro=250dps,ODR=100Hz"));
  for (uint8_t address = 0x68; address <= 0x69; ++address) {
    Wire.beginTransmission(address);
    uint8_t err = Wire.endTransmission();
    Serial.print(F("PROBE,0x")); Serial.print(address, HEX);
    Serial.print(','); Serial.println(err);
  }
  uint8_t identity = 0;
  if (!readRegs(WHO_AM_I, &identity, 1)) return;
  Serial.print(F("WHO_AM_I,0x")); Serial.println(identity, HEX);
  if (identity != 0x67) {
    Serial.println(F("FAIL,unexpected_identity,no_sensor_configuration_written"));
    return;
  }
  if (!writeReg(0x02, 0x10)) return; // SOFT_RESET_DEVICE_CONFIG.
  delay(20);
  uint8_t status = 0;
  if (!readRegs(0x3A, &status, 1) || !(status & 0x10)) {
    Serial.println(F("FAIL,reset_done_missing")); return;
  }
  if (!readRegs(WHO_AM_I, &identity, 1) || identity != 0x67) return;
  if (!writeReg(0x20, 0x69)) return; // Gyro +/-250 dps, 100Hz.
  if (!writeReg(0x21, 0x69)) return; // Accel +/-2g, 100Hz.
  uint8_t interfaceConfig = 0;
  if (!readRegs(0x35, &interfaceConfig, 1)) return;
  bigEndian = (interfaceConfig & 0x10) != 0;
  Serial.print(F("SENSOR_ENDIAN,"));
  Serial.println(bigEndian ? F("BIG") : F("LITTLE"));
  if (!writeReg(0x1F, 0x0F)) return; // Both sensors low-noise mode.
  delay(100); // No writes for >200us; gyro startup >45ms.
  if (!verifyReg(0x20, 0x69) || !verifyReg(0x21, 0x69) ||
      !verifyReg(0x1F, 0x0F)) {
    Serial.println(F("FAIL,configuration_readback")); return;
  }
  Serial.println(F("HEADER,ms,seq,temp_raw,ax_raw,ay_raw,az_raw,gx_raw,gy_raw,gz_raw,temp_C,ax_g,ay_g,az_g,gx_dps,gy_dps,gz_dps"));
  ready = true;
  lastReady = millis();
}

void loop() {
  if (!ready) { delay(1000); return; }
  delay(50); // Report about 20Hz; sensor runs at 100Hz.
  uint8_t status = 0;
  if (!readRegs(0x39, &status, 1)) return;
  if (!(status & 1)) {
    if (millis() - lastReady > 1000) {
      Serial.println(F("E,no_data_ready")); lastReady = millis();
    }
    return;
  }
  uint8_t buffer[14];
  if (!readRegs(0x09, buffer, sizeof(buffer))) return;
  int16_t raw[7];
  for (uint8_t i = 0; i < 7; ++i) {
    raw[i] = wordAt(buffer + 2 * i);
    if (raw[i] == INT16_MIN) {
      Serial.println(F("E,invalid_sample_0x8000")); return;
    }
  }
  lastReady = millis();
  Serial.print(F("S,")); Serial.print(lastReady);
  Serial.print(','); Serial.print(++sampleCount);
  for (uint8_t i = 0; i < 7; ++i) {
    Serial.print(','); Serial.print(raw[i]);
  }
  Serial.print(','); Serial.print(raw[0] / 128.0f + 25.0f, 3);
  for (uint8_t i = 1; i <= 3; ++i) {
    Serial.print(','); Serial.print(raw[i] / 16384.0f, 5);
  }
  for (uint8_t i = 4; i <= 6; ++i) {
    Serial.print(','); Serial.print(raw[i] * (250.0f / 32768.0f), 4);
  }
  Serial.println();
}

参考资料

官方资料与图纸来源需要了解芯片细节时再看

TDK ICM-42670-P官方资料 · TDK官方Arduino示例

原理图来自“ICM-42688陀螺仪模块”原工程,作者标注HexaSilicon;贴装图按同工程元件位置整理。42670-P与42688-P的程序不同,请下载本页提供的42670-P程序。