Skip to content

I2C 协议 ​

仅适用于型号名带 -I2C 的产品(例如 HI05-I2C)。

适用型号与引脚 ​

模块是 I2C 从机,主机一般是 MCU。I2C 上能做两件事:读写寄存器,读取最新样本的 HI83 帧。HI91 和 ASCII 命令只在串口上提供,批量配置、调试和固件升级请用 COM1,见配置模块和固件升级。

项说明
引脚SCL、SDA 的引脚号见产品硬件手册。I2C 占用了 CAN 的引脚,所以 -I2C 型号没有 CAN
上拉模块上没有上拉电阻,由主机板提供
型号名串口发 LOG VERSION 看 PNAME=,或经 I2C 读寄存器 0x0070–0x0077

地址与速率 ​

项出厂说明
I2C 设备地址(7 位)80(0x50)与 Modbus 从站 ID 是同一个值,存放在寄存器 0x0005(MD_ID);可设 0x08–0x77
总线速率—100 kHz 或 400 kHz,不支持 1 MHz(Fm+)
时钟拉伸—主机必须支持,并允许单次拉伸至少 20 ms

写入新地址后立即生效,旧地址随即不再应答。超出 0x08–0x77 的值模块不接受,读回仍是原地址。

下面把设备地址从 0x50 改为 0x51。[0x50] 是 I2C 设备地址;每次写入的前两个字节是寄存器地址:

text
→ [0x50] 写 00 05 00 51     寄存器 0x0005 ← 0x0051
→ [0x51] 写 00 05 01        请求读寄存器 0x0005,共 1 个
← [0x51] 读 00 51           读回 0x0051,新地址已生效
→ [0x51] 写 00 00 00 00     寄存器 0x0000(CTRL)← 0x0000,保存配置

恢复出厂(寄存器 0x0000 写 0x0001,见恢复出厂)或复位后,先用原地址探测,没有应答再用 0x50。

寻址与读写 ​

寄存器地址按 16 位字编号,每个字 2 字节,大端(高字节在前);连续读取时地址自动递增。

字地址区域内容
0x0000–0x0FFF寄存器与 Modbus 共用一张表,含义见寄存器列表;SN_STR 和 HI83_MAP 在本页说明
0x1000 起HI83 帧最新样本的完整 HI83 帧,含帧头和 CRC

模块按一次写事务的长度判断它是什么请求:

写入的字节含义
地址H 地址L只设读指针。指针设为 0x1000 时,同时请求最新样本的 HI83 帧
地址H 地址L 个数请求读寄存器,个数 1–32
地址H 地址L 数据…写寄存器,数据为偶数字节,一次最多 32 个寄存器

读事务从指针所指的字开始,返回最近一次读请求或 HI83 请求的结果。超出结果末尾的字节读为 0;指针不在结果范围内时,整次读到的都是 0。接收缓冲小的主机可以分几次读完一帧。

请求和读取各用一次独立事务:写请求,发 STOP,再发起读。不建议不发 STOP 就用重复起始直接转入读,因为模块执行请求期间会拉住 SCL,对主机的时钟拉伸容忍度要求更高。

读取数据帧 ​

读一帧分三步:向 0x1000 发请求,读 6 字节帧头,再读剩下的部分。每次读到的都是当时最新的样本,I2C 上没有输出周期。

  • 帧头第 2、3 字节(buf[2]、buf[3],小端)是数据域长度,整帧长度 = 6 + 数据域长度。剩下的部分把指针设为 0x1003 后读取;已知帧长时也可以一次读完。
  • 帧格式与串口 HI83 相同,字段按每帧的 data_bitmap 解析,帧长随字段变化,规则见 HI83,CRC 见 CRC 校验。
  • 出厂字段见出厂默认配置,帧长按帧长示例估算。改字段用串口命令 LOG HI83 MAP(见 HI83 字段选择),或写本页的 HI83_MAP 寄存器(见配置寄存器)。两者是同一个位图,生效和保存规则相同。
  • 帧里的时间戳是该样本的采样时刻,各接口的区别见时间与时间戳。主机读得比模块采样快时,会重复读到同一个样本,用 system_time_us(HI83 bit5)去重,所以改字段时请保留 bit5。
  • 上电初期还没有样本,读到的全是 0(首字节不是 0x5A),丢弃即可,姿态何时可用见上电过程。
  • 读取频率建议:400 kHz 下不超过 200 Hz,100 kHz 下不超过 100 Hz。以 74 字节的帧为例,400 kHz 下仅数据传输就需约 1.7 ms,还不含请求和模块编码的时间。

主机读帧示例 ​

下面的函数读一帧并校验 CRC。三个平台函数和 hipnuc_frame_crc_ok()(见 CRC 校验)需自行实现。

c
#include <stdint.h>
#include <stddef.h>

#define I2C_ADDR 0x50u   /* 7-bit device address = MD_ID */

/* Platform hooks: one whole transfer (START, address, data, STOP).
 * Return 0 when done, 1 when NACKed (module busy), below 0 on a bus error. */
extern int i2c_write(uint8_t addr7, const uint8_t *buf, size_t n);
extern int i2c_read(uint8_t addr7, uint8_t *buf, size_t n);
extern void delay_ms(uint32_t ms);
extern int hipnuc_frame_crc_ok(const uint8_t *frame, uint16_t len);  /* len = payload length */

/* Retry a NACKed transfer every 1 ms, for at most 1 s. */
static int xfer(int is_read, uint8_t *buf, size_t n)
{
    for (int i = 0; i <= 1000; i++) {
        int rc = is_read ? i2c_read(I2C_ADDR, buf, n) : i2c_write(I2C_ADDR, buf, n);
        if (rc <= 0)
            return rc;
        delay_ms(1);
    }
    return 1;                                   /* still busy after 1 s */
}

static int set_pointer(uint16_t word)
{
    uint8_t b[2] = { (uint8_t)(word >> 8), (uint8_t)word };
    return xfer(0, b, 2);
}

/* Returns the frame length, 0 when there is no sample yet, -1 on error. */
int hi83_read(uint8_t *buf, size_t cap)
{
    uint16_t len;
    if (cap < 6)
        return -1;
    if (set_pointer(0x1000) != 0 || xfer(1, buf, 6) != 0)   /* 0x1000: newest sample */
        return -1;
    if (buf[0] != 0x5A || buf[1] != 0xA5)
        return 0;                               /* all zeros before the first sample */
    len = (uint16_t)(buf[2] | buf[3] << 8);
    if (len == 0 || len > 512 || 6u + len > cap)
        return -1;
    if (set_pointer(0x1003) != 0 || xfer(1, buf + 6, len) != 0)  /* word 3 = byte 6 */
        return -1;
    return hipnuc_frame_crc_ok(buf, len) ? (int)(6u + len) : -1;
}

配置寄存器 ​

寄存器的地址、类型和含义与 Modbus 相同,SN_STR、HI83_MAP 两项在本页说明。I2C 一次最多读写 32 个寄存器。常用项:

寄存器名称类型读写生效说明
0x0000CTRLu16W立即写 0x0000 保存,0x0001 恢复出厂,0x00FF 重启。恢复出厂前先看恢复出厂
0x0005MD_IDu16R/W立即I2C 设备地址,见地址与速率
0x0006 / 0x0007 / 0x00A6PROFILE / COORD / URFRu16R/W重启工作模式、输出数据轴向、安装方向,取值见 Modbus 寄存器列表
0x0008–0x000B地磁校准u16W / R立即见地磁校准寄存器
0x00A5ATT_RSTu16W立即姿态零点,取值见 Modbus 寄存器列表。改过工作模式、输出数据轴向或安装方向后的操作顺序见设置顺序
0x0070–0x0082版本信息—R—型号、固件和引导程序版本,见读取版本信息
0x0408–0x040FSN_STRASCIIR—产品序列号,即 LOG VERSION 中 SN= 的前 16 个字符。每个寄存器 2 个字符,高字节在前,不足的部分读为 0
0x00B8–0x00B9HI83_MAPu32R/W立即HI83 的 data_bitmap,高 16 位在 0x00B8,低 16 位在 0x00B9。只能置位 HI83 位定义里的公开位,并保留 bit5(system_time_us);自动保存

HI83_MAP 的两个寄存器必须在同一次写事务里一起写(4 个数据字节),只写一个会被忽略。设置 HI83_MAP = 0x0000003B 并读回:

text
→ [0x50] 写 00 B8 00 00 00 3B     寄存器 0x00B8–0x00B9 ← 0x0000003B
→ [0x50] 写 00 B8 02              请求读寄存器 0x00B8,共 2 个
← [0x50] 读 00 00 00 3B           读回 0x0000003B

读回一致后,再看下一帧帧头的 data_bitmap 是否相同。

读寄存器可以复用主机读帧示例里的 xfer:

c
/* Read n (1..32) 16-bit registers from word address reg; words are big-endian on the wire. */
int i2c_reg_read(uint16_t reg, uint16_t *out, uint8_t n)
{
    uint8_t req[3] = { (uint8_t)(reg >> 8), (uint8_t)reg, n };
    uint8_t raw[64];
    if (n < 1 || n > 32)
        return -1;
    if (xfer(0, req, 3) != 0 || xfer(1, raw, (size_t)n * 2) != 0)
        return -1;
    for (uint8_t i = 0; i < n; i++)
        out[i] = (uint16_t)((raw[2 * i] << 8) | raw[2 * i + 1]);
    return 0;
}

配置完成后,向寄存器 0x0000 写 0x0000 保存;有需要重启生效的项,再写 0x00FF 重启,然后按错误处理与重试重新初始化。保存和生效时机的通则见配置流程与保存。

警告

模块不接受某个写入值时不报错,I2C 事务照常完成。关键参数写完一定读回,不要拿写事务成功当作配置已生效(通则见配置流程与保存)。

错误处理与重试 ​

NACK 先按忙处理。 模块执行请求(读写寄存器、编码 HI83 帧)时不应答地址。主机每 1 ms 重试一次被 NACK 的那次事务,最长 1 s。读事务被 NACK 时只重试读本身,不要重发读请求。

写 Flash 同样会让模块忙:写入需要保存到 Flash 的寄存器时,模块通常 NACK 十几毫秒,遇到 Flash 整理时可达几百毫秒,按上面的规则重试。其他任务操作 Flash 时,SCL 也可能被拉住十几毫秒,这属于时钟拉伸,由主机硬件等待,见地址与速率的拉伸要求。

复位之后。 上电、重启或恢复出厂后,模块约 0.4–0.6 s 不应答;复位后最初约 100 ms 内,SCL、SDA 引脚可能还不是 I2C 功能。主机这样做:

  1. 停止所有总线访问,等待至少 150 ms。
  2. 读寄存器 0x0005(MD_ID)。没有应答就按 NACK 规则重试,最长 1 s;读回值等于设备地址后再正常访问。
  3. 总线上有其他主机时,各主机在模块复位前后都要让总线空闲至少 150 ms,期间不要拉低 SDA 或 SCL。

一次事务要快。 一次事务(START 到 STOP)要在 50 ms 内完成。主机中途停顿超过 50–100 ms,模块会放弃这次事务并复位自己的 I2C 外设。出错或超时后等 100 ms 再重试。只有确认 SDA 被拉低时才补 9 个时钟脉冲。

校验数据。 HI83 帧带 CRC,CRC 错就丢掉这一帧,下次再读。寄存器读取没有校验,总线受干扰时读事务可能提前结束,读到全 0xFF,所以关键参数读两次确认。线束较长或同时接有串口时,要保证模块与主机可靠共地。

地址不应答时,依次确认:型号带 -I2C、主机板有上拉、地址等于寄存器 0x0005 的值(出厂 0x50)、复位后已按上面的规则重试至少 1 s。读到全 0,说明还没有样本,或指针不在结果范围内,按读取数据帧的三步重新请求。

最后更新:

IMU · AHRS · INS · RTK 技术支持中心