固件 0.1.1-c3:I²C 不应答不再被报成「没初始化 / 忙」(关电回 busy 的根因)
- 根因:IDF 5.4 的 i2c_master_transmit / receive / transmit_receive 在从机 NACK / SCL 超时时返回 ESP_ERR_INVALID_STATE,HAL 照翻成 HAL_ERR_STATE(= 没 init);命令层 from_hal 又把 STATE 当成 「执行器忙」⇒ PCA9555 不应答时 power off 回 busy(eFuse 其实已关、意图已清),I2C_LOST 错误码是 5 - hal_i2c_esp.c:传输函数用 map_xfer_err(INVALID_STATE → NAK);总线没建起来仍单独报 STATE - cmd_exec.c:新增 from_bus,set_voltage / vbus / power 的器件失败一律 io;from_hal 只留给执行器 - sim:sim_i2c_inject_err_ch() 可指定注入的错误码;test_cmd 加 STATE 注入 ⇒ 必须 io 不是 busy (改回 from_hal 时这段确实红)。26 个 ctest 全绿 - 板 B(台架镜像,CH1 强制在位,未提交):power off 回 io、CH1 关掉;I2C_LOST 错误码 5 → 2。现跑 0.1.1-c3 Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
@@ -25,6 +25,17 @@ static hal_err_t map_err(esp_err_t e)
|
||||
}
|
||||
}
|
||||
|
||||
/* 传输函数专用。⚠️ IDF 5.4 的 i2c_master_transmit / receive / transmit_receive 在**从机 NACK**
|
||||
(以及硬件 SCL 超时)时返回的是 ESP_ERR_INVALID_STATE,不是 ESP_FAIL(i2c_master.c
|
||||
s_i2c_synchronous_transaction:「status != DONE ⇒ INVALID_STATE」)。照 map_err 翻成
|
||||
HAL_ERR_STATE(= 没 init)的话,真板上所有「器件不应答」都长得像「没初始化」——
|
||||
2026-09-27 实测:I2C_LOST 事件的错误码是 5,`power` 关电回 `busy`。sim 回的是 NAK,宿主机测不出来。
|
||||
总线没建起来的情况在调它之前已经单独返回 HAL_ERR_STATE 了(dev_of)。 */
|
||||
static hal_err_t map_xfer_err(esp_err_t e)
|
||||
{
|
||||
return (e == ESP_ERR_INVALID_STATE) ? HAL_ERR_NAK : map_err(e);
|
||||
}
|
||||
|
||||
hal_err_t hal_i2c_init(const hal_i2c_cfg_t *cfg)
|
||||
{
|
||||
if (cfg == NULL || cfg->sda_gpio < 0 || cfg->scl_gpio < 0) {
|
||||
@@ -83,7 +94,7 @@ hal_err_t hal_i2c_write(uint8_t addr, const uint8_t *data, size_t len)
|
||||
return err;
|
||||
}
|
||||
hal_i2c_acct_add(len, 0);
|
||||
return map_err(i2c_master_transmit(dev, data, len, I2C_XFER_TIMEOUT_MS));
|
||||
return map_xfer_err(i2c_master_transmit(dev, data, len, I2C_XFER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
hal_err_t hal_i2c_read(uint8_t addr, uint8_t *data, size_t len)
|
||||
@@ -94,7 +105,7 @@ hal_err_t hal_i2c_read(uint8_t addr, uint8_t *data, size_t len)
|
||||
return err;
|
||||
}
|
||||
hal_i2c_acct_add(0, len);
|
||||
return map_err(i2c_master_receive(dev, data, len, I2C_XFER_TIMEOUT_MS));
|
||||
return map_xfer_err(i2c_master_receive(dev, data, len, I2C_XFER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
hal_err_t hal_i2c_write_read(uint8_t addr, const uint8_t *wr, size_t wr_len,
|
||||
@@ -106,7 +117,7 @@ hal_err_t hal_i2c_write_read(uint8_t addr, const uint8_t *wr, size_t wr_len,
|
||||
return err;
|
||||
}
|
||||
hal_i2c_acct_add(wr_len, rd_len);
|
||||
return map_err(i2c_master_transmit_receive(dev, wr, wr_len, rd, rd_len,
|
||||
return map_xfer_err(i2c_master_transmit_receive(dev, wr, wr_len, rd, rd_len,
|
||||
I2C_XFER_TIMEOUT_MS));
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,11 @@ static sim_i2c_dev_t *must_ch(int ch, uint8_t addr)
|
||||
|
||||
void sim_i2c_set_reg(uint8_t addr, uint8_t reg, uint16_t val) { must(addr)->regs[reg] = val; }
|
||||
uint16_t sim_i2c_get_reg(uint8_t addr, uint8_t reg) { return must(addr)->regs[reg]; }
|
||||
void sim_i2c_inject_nak(uint8_t addr, int count) { must(addr)->nak_left = count; }
|
||||
void sim_i2c_inject_nak(uint8_t addr, int count)
|
||||
{
|
||||
must(addr)->nak_left = count;
|
||||
must(addr)->fail_err = HAL_OK;
|
||||
}
|
||||
void sim_i2c_set_present(uint8_t addr, bool present) { must(addr)->present = present; }
|
||||
|
||||
void sim_i2c_set_reg_ch(int ch, uint8_t addr, uint8_t reg, uint16_t val)
|
||||
@@ -191,6 +195,12 @@ uint16_t sim_i2c_get_reg_ch(int ch, uint8_t addr, uint8_t reg)
|
||||
void sim_i2c_inject_nak_ch(int ch, uint8_t addr, int count)
|
||||
{
|
||||
must_ch(ch, addr)->nak_left = count;
|
||||
must_ch(ch, addr)->fail_err = HAL_OK;
|
||||
}
|
||||
void sim_i2c_inject_err_ch(int ch, uint8_t addr, int count, hal_err_t err)
|
||||
{
|
||||
must_ch(ch, addr)->nak_left = count;
|
||||
must_ch(ch, addr)->fail_err = err;
|
||||
}
|
||||
void sim_i2c_set_present_ch(int ch, uint8_t addr, bool present)
|
||||
{
|
||||
@@ -237,7 +247,7 @@ static hal_err_t take(uint8_t addr, sim_i2c_dev_t **out)
|
||||
if (d->nak_left > 0) {
|
||||
d->nak_left--;
|
||||
}
|
||||
return HAL_ERR_NAK;
|
||||
return d->fail_err != HAL_OK ? d->fail_err : HAL_ERR_NAK;
|
||||
}
|
||||
*out = d;
|
||||
return HAL_OK;
|
||||
|
||||
@@ -57,6 +57,7 @@ typedef struct sim_i2c_dev {
|
||||
uint8_t reg_width; /* 1 或 2 字节。2 字节按**大端**——INA226 / DAC63204 都是 */
|
||||
bool present; /* false = 器件失联(A15 降级矩阵用),事务一律 NAK */
|
||||
int nak_left; /* >0:接下来 n 次事务 NAK 后自动恢复;<0:一直 NAK;0:正常 */
|
||||
hal_err_t fail_err; /* 注入的失败返回什么;0 = HAL_ERR_NAK(sim_i2c_inject_err_ch) */
|
||||
uint16_t regs[SIM_I2C_REGS];
|
||||
uint8_t ptr; /* 器件内部的寄存器指针,跨事务保持(真器件就是这个行为) */
|
||||
uint32_t n_write; /* 事务计数,可用来断言「驱动真的访问过它 / 没多访问」 */
|
||||
@@ -98,6 +99,9 @@ void sim_i2c_inject_nak(uint8_t addr, int count);
|
||||
void sim_i2c_set_present(uint8_t addr, bool present);
|
||||
void sim_i2c_inject_nak_ch(int ch, uint8_t addr, int count);
|
||||
void sim_i2c_set_present_ch(int ch, uint8_t addr, bool present);
|
||||
/* 同 inject_nak_ch,但失败时返回 `err`。模拟 ESP 驱动把「不应答」报成别的码
|
||||
(IDF 5.4 的 NACK 是 ESP_ERR_INVALID_STATE,HAL 修之前会变成 HAL_ERR_STATE)。 */
|
||||
void sim_i2c_inject_err_ch(int ch, uint8_t addr, int count, hal_err_t err);
|
||||
|
||||
/* **假总线上的事务是要花时间的**(默认开):每次事务把虚拟时钟往前拨
|
||||
`hal_i2c_txn_bits() / 频率` 那么多微秒。没有这一条,t_safety 的一个周期在 sim 里
|
||||
|
||||
在新工单中引用
屏蔽一个用户