Skip to content

refactor(ina226): rewrite driver for robustness and clarity#27364

Open
dakejahl wants to merge 2 commits into
mainfrom
dakejahl/ina226-rewrite
Open

refactor(ina226): rewrite driver for robustness and clarity#27364
dakejahl wants to merge 2 commits into
mainfrom
dakejahl/ina226-rewrite

Conversation

@dakejahl
Copy link
Copy Markdown
Contributor

Summary

Rewrite of the INA226 driver to modernize the code layout (state machine, namespaced constants, DEFINE_PARAMETERS, file consolidation) and pick up the same robustness fixes from #27359.

Problem

The INA226 driver shares the same anti-patterns the INA238 PR identified as causes of intermittent battery_status dropouts:

  • A single failed collect() flipped _initialized = false and re-ran Reset() inline on the next tick — at 10 Hz that lost a sample period on every isolated I2C glitch.
  • setConnected(false) could fire repeatedly per failed cycle, collapsing the intended ~2 s debounce.
  • Init failures destroyed the driver instance unless -k was set; with -k, the only recovery path was the same blocking re-init.
  • -t (battery index) was parsed with no range check; out-of-range values silently clamped to 1 inside Battery.
  • The INA226_CONFIG param exposed the raw config register to users, with a default that disagreed with the C #define default (param defaulted to triggered mode; macro defaulted to continuous).

Solution

  • Non-blocking UNINITIALIZED → RESET → CONFIGURE → MEASURE state machine. Each transition is its own RunImpl tick with an explicit ScheduleDelayed; init failures retry without losing the driver instance.
  • Tolerate ~2 s of consecutive collect() failures (MAX_CONSECUTIVE_FAILURES = DISCONNECT_DEBOUNCE_US / SAMPLE_INTERVAL_US) before a full reinit. Isolated glitches just skip a cycle.
  • SAMPLE_INTERVAL_US = 100 ms, comfortably above the default ADC period of ~75.3 ms (588 µs × 2 channels × 64 avg). MEASURE compensates for in-tick I2C time so each tick is exactly SAMPLE_INTERVAL_US apart.
  • CONFIGURE waits SAMPLE_INTERVAL_US + 5 ms before the first MEASURE read so the first averaged sample is ready.
  • checkConfigurationRotating() reads one of {CONFIGURATION, CALIBRATION} per cycle and compares it against the value we wrote, so an externally reset device is detected within two cycles.
  • -t arg validated against 1–3 at parse time; out-of-range values now exit with an error.
  • New perf counters ina226_bad_register and ina226_reinit for field diagnosis; current driver state is surfaced in ina226 status.
  • File layout: ina226_main.cpp folded into ina226.cpp. Constants/enums namespaced under ina226. Params switched to DEFINE_PARAMETERS.
  • INA226_CONFIG raw-register override is removed; the driver hardcodes continuous-conversion mode with the same 588 µs / 588 µs / 64-avg defaults the C macro previously specified.
  • AuterionAutostarter::ina226_probe() updated to the new namespaced constants.

Testing

Tested with an ARK PAB Power Module. Induced I2S bus glitches by momentarily shorting SDA/SCL to GND to trigger bus errors. Shorting for >2s triggers a full re-init.
https://review.px4.io/plot_app?log=015893d0-bb6a-47b9-8a7a-9e7dd19525cf
image

dakejahl added 2 commits May 17, 2026 18:33
Mirrors the INA238 rewrite (#27359):

- Non-blocking UNINITIALIZED -> RESET -> CONFIGURE -> MEASURE state
  machine. Each transition is its own RunImpl tick with an explicit
  ScheduleDelayed; init failures retry without losing the driver
  instance.
- Tolerate ~2 s of consecutive collect() failures
  (MAX_CONSECUTIVE_FAILURES = DISCONNECT_DEBOUNCE_US / SAMPLE_INTERVAL_US)
  before a full reinit. Isolated I2C glitches just skip a cycle instead
  of dropping battery_status.
- SAMPLE_INTERVAL_US = 100 ms, comfortably above the default ADC period
  of ~75.3 ms (588 us * 2 channels * 64 avg). MEASURE compensates for
  in-tick I2C time so each tick is exactly one interval apart.
- CONFIGURE waits SAMPLE_INTERVAL_US + 5 ms before the first MEASURE
  read so the first averaged sample is ready.
- checkConfigurationRotating() reads one of {CONFIGURATION, CALIBRATION}
  per cycle and compares it against the value we wrote, so an externally
  reset device is detected within two cycles.
- '-t' arg validated against 1-3 at parse time; out-of-range values now
  exit with an error.
- New perf counters ina226_bad_register and ina226_reinit for field
  diagnosis; current driver state is surfaced in 'ina226 status'.

File layout: ina226_main.cpp folded into ina226.cpp. Constants/enums
namespaced under ina226. Params switched to DEFINE_PARAMETERS, and the
INA226_CONFIG raw-register override is removed in favor of a hardcoded
continuous-conversion config.

AuterionAutostarter::ina226_probe() updated to the new namespaced
ina226::Register and ina226::MANFID / ina226::DIEID constants.
Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
The INA226 CONFIGURATION register has D15 (RST) self-clearing and
D14:D12 reserved, with D14 reading back as 1 regardless of what is
written. The verify step compared the raw read-back to the value
we wrote, so the check failed on every cycle and the driver
re-initialized itself every ~2 s. Mask the non-R/W bits before
comparing.

Signed-off-by: Jacob Dahl <dahl.jakejacob@gmail.com>
@github-actions github-actions Bot added kind:refactor Code cleanup or redesign without intended behavior change. scope:drivers Device drivers and hardware interfaces. scope:sensors Sensor pipeline, calibration, voting, or sensor validation. scope:parameters Parameter definitions, metadata, migration, or defaults. labels May 18, 2026
@dakejahl dakejahl requested review from AlexKlimaj, julianoes and mbjd May 18, 2026 01:19
@github-actions
Copy link
Copy Markdown
Contributor

🔎 FLASH Analysis

px4_fmu-v5x [Total VM Diff: 0 byte (0 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +16  +0.0%     +16    .text
    +123%    +196  +123%    +196    INA226::RunImpl()
    [NEW]    +122  [NEW]    +122    INA226::checkConfigurationRotating()
    +0.0%     +76  +0.0%     +76    [section .text]
    [NEW]     +62  [NEW]     +62    INA226::registerRead()
    [NEW]     +52  [NEW]     +52    INA226::registerWrite()
     +57%     +48   +57%     +48    INA226::print_status()
    [NEW]     +40  [NEW]     +40    INA226::updateParamsImpl()
     +28%     +28   +28%     +28    INA226::instantiate()
    +9.2%     +24  +9.2%     +24    ina226_main
     +80%     +16   +80%     +16    CSWTCH.58
    -0.1%      -8  -0.1%      -8    px4::parameters
    -0.0%     -12  -0.0%     -12    g_cromfs_image
    [DEL]     -24  [DEL]     -24    INA226::force_init()
    [DEL]     -34  [DEL]     -34    INA226::write()
    [DEL]     -36  [DEL]     -36    INA226::measure()
    [DEL]     -44  [DEL]     -44    INA226::start()
   -101.9%     -60 -101.9%     -60    [19 Others]
    [DEL]     -74  [DEL]     -74    INA226::read()
    [DEL]     -76  [DEL]     -76    INA226::setConnected()
   -77.8%    -112 -77.8%    -112    INA226::init()
   -30.0%    -168 -30.0%    -168    INA226::INA226()
  -0.1% -1.48Ki  [ = ]       0    .debug_abbrev
  -0.0%     -24  [ = ]       0    .debug_aranges
  -0.0%     -60  [ = ]       0    .debug_frame
  -0.1% -25.8Ki  [ = ]       0    .debug_info
  -0.0% -1.41Ki  [ = ]       0    .debug_line
    +500%      +5  [ = ]       0    [Unmapped]
    -0.0% -1.42Ki  [ = ]       0    [section .debug_line]
  +0.0%    +160  [ = ]       0    .debug_loclists
  +0.0%     +72  [ = ]       0    .debug_rnglists
    [DEL]      -2  [ = ]       0    [Unmapped]
    +0.0%     +74  [ = ]       0    [section .debug_rnglists]
  +0.1% +2.48Ki  [ = ]       0    .debug_str
  -0.4%      -1  [ = ]       0    .shstrtab
  +0.0%     +53  [ = ]       0    .strtab
    [NEW]     +41  [ = ]       0    INA226::checkConfigurationRotating()
    [DEL]     -25  [ = ]       0    INA226::force_init()
    [DEL]     -21  [ = ]       0    INA226::measure()
    [DEL]     -20  [ = ]       0    INA226::read()
    [NEW]     +46  [ = ]       0    INA226::registerRead()
    [NEW]     +46  [ = ]       0    INA226::registerWrite()
    [DEL]     -27  [ = ]       0    INA226::setConnected()
    [DEL]     -19  [ = ]       0    INA226::start()
    [NEW]     +68  [ = ]       0    INA226::updateParamsImpl()
    [DEL]     -20  [ = ]       0    INA226::write()
    -0.1%     -16  [ = ]       0    [section .strtab]
   -50.0%     -16  [ = ]       0    __memcpy_veneer
     +64%     +16  [ = ]       0    __nxsched_get_tcb_veneer
  -0.0%     -48  [ = ]       0    .symtab
    +100%     +32  [ = ]       0    CSWTCH.58
     +50%     +16  [ = ]       0    ConstLayer::contains()
   -33.3%     -16  [ = ]       0    ConstLayer::store()
     +50%     +32  [ = ]       0    INA226::RunImpl()
    [NEW]     +16  [ = ]       0    INA226::checkConfigurationRotating()
     +33%     +16  [ = ]       0    INA226::collect()
    [DEL]     -16  [ = ]       0    INA226::force_init()
   -20.0%     -16  [ = ]       0    INA226::init()
   -25.0%     -16  [ = ]       0    INA226::instantiate()
    [DEL]     -32  [ = ]       0    INA226::measure()
     +33%     +16  [ = ]       0    INA226::print_usage()
     +50%     +16  [ = ]       0    INA226::probe()
    [DEL]     -32  [ = ]       0    INA226::read()
    [NEW]     +32  [ = ]       0    INA226::registerRead()
    [NEW]     +16  [ = ]       0    INA226::registerWrite()
    [DEL]     -48  [ = ]       0    INA226::setConnected()
    [DEL]     -64  [ = ]       0    INA226::start()
    [NEW]     +32  [ = ]       0    INA226::updateParamsImpl()
    [DEL]     -32  [ = ]       0    INA226::write()
   -89.2%     +64  [ = ]       0    [11 Others]
    -0.6%     -64  [ = ]       0    [section .symtab]
  -0.1%     -16  -0.1%     -16    .ramfunc
     +14%      +1   +14%      +1    __up_restoreusartint_veneer
   -12.5%      -1 -12.5%      -1    ___ZNK6matrix10QuaternionIfE19rotateVectorInverseERKNS_7Vector3IfEE_veneer
    -1.7%      -4  -1.7%      -4    param_get
    -1.4%      -4  -1.4%      -4    param_get_default_value
   -25.0%      -4 -25.0%      -4    param_get_index
    -8.3%      -4  -8.3%      -4    param_get_system_default_value
  -0.1% -26.1Ki  [ = ]       0    TOTAL

px4_fmu-v6x [Total VM Diff: 104 byte (0.01 %)]
    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +104  +0.0%    +104    .text
    +123%    +196  +123%    +196    INA226::RunImpl()
    [NEW]    +122  [NEW]    +122    INA226::checkConfigurationRotating()
    +0.1%     +92  +0.1%     +92    [section .text]
    [NEW]     +62  [NEW]     +62    INA226::registerRead()
    [NEW]     +52  [NEW]     +52    INA226::registerWrite()
     +57%     +48   +57%     +48    INA226::print_status()
    [NEW]     +40  [NEW]     +40    INA226::updateParamsImpl()
     +28%     +28   +28%     +28    INA226::instantiate()
    +9.2%     +24  +9.2%     +24    ina226_main
    [NEW]     +16  [NEW]     +16    CSWTCH.58
   -97.9%      +8 -97.9%      +8    [2 Others]
    -0.0%      -8  -0.0%      -8    g_cromfs_image
    -0.1%      -8  -0.1%      -8    px4::parameters
    [DEL]     -24  [DEL]     -24    INA226::force_init()
    [DEL]     -34  [DEL]     -34    INA226::write()
    [DEL]     -36  [DEL]     -36    INA226::measure()
    [DEL]     -44  [DEL]     -44    INA226::start()
    [DEL]     -74  [DEL]     -74    INA226::read()
    [DEL]     -76  [DEL]     -76    INA226::setConnected()
   -77.8%    -112 -77.8%    -112    INA226::init()
   -30.0%    -168 -30.0%    -168    INA226::INA226()
  -0.1% -1.50Ki  [ = ]       0    .debug_abbrev
  -0.0%     -24  [ = ]       0    .debug_aranges
  -0.0%     -60  [ = ]       0    .debug_frame
  -0.1% -25.2Ki  [ = ]       0    .debug_info
  -0.0% -1.27Ki  [ = ]       0    .debug_line
   -33.3%      -2  [ = ]       0    [Unmapped]
    -0.0% -1.27Ki  [ = ]       0    [section .debug_line]
  +0.0%     +77  [ = ]       0    .debug_loclists
  +0.0%     +81  [ = ]       0    .debug_rnglists
    [NEW]      +3  [ = ]       0    [Unmapped]
    +0.0%     +78  [ = ]       0    [section .debug_rnglists]
  +0.1% +2.47Ki  [ = ]       0    .debug_str
  +0.9%      +2  [ = ]       0    .shstrtab
  +0.0%     +62  [ = ]       0    .strtab
    [NEW]     +10  [ = ]       0    CSWTCH.58
    [NEW]     +41  [ = ]       0    INA226::checkConfigurationRotating()
    [DEL]     -25  [ = ]       0    INA226::force_init()
    [DEL]     -21  [ = ]       0    INA226::measure()
    [DEL]     -20  [ = ]       0    INA226::read()
    [NEW]     +46  [ = ]       0    INA226::registerRead()
    [NEW]     +46  [ = ]       0    INA226::registerWrite()
    [DEL]     -27  [ = ]       0    INA226::setConnected()
    [DEL]     -19  [ = ]       0    INA226::start()
    [NEW]     +68  [ = ]       0    INA226::updateParamsImpl()
    [DEL]     -20  [ = ]       0    INA226::write()
    -0.1%     -27  [ = ]       0    [section .strtab]
    -0.0%      -1  [ = ]       0    do_not_explicitly_use_this_namespace::Param<>::update()
     +33%     +11  [ = ]       0    mode_util::nav_state_names
  -0.0%     -48  [ = ]       0    .symtab
    [NEW]     +32  [ = ]       0    CSWTCH.58
     +50%     +32  [ = ]       0    INA226::RunImpl()
    [NEW]     +16  [ = ]       0    INA226::checkConfigurationRotating()
     +33%     +16  [ = ]       0    INA226::collect()
    [DEL]     -16  [ = ]       0    INA226::force_init()
   -20.0%     -16  [ = ]       0    INA226::init()
   -25.0%     -16  [ = ]       0    INA226::instantiate()
    [DEL]     -32  [ = ]       0    INA226::measure()
     +33%     +16  [ = ]       0    INA226::print_usage()
     +50%     +16  [ = ]       0    INA226::probe()
    [DEL]     -32  [ = ]       0    INA226::read()
    [NEW]     +32  [ = ]       0    INA226::registerRead()
    [NEW]     +16  [ = ]       0    INA226::registerWrite()
    [DEL]     -48  [ = ]       0    INA226::setConnected()
    [DEL]     -64  [ = ]       0    INA226::start()
    [NEW]     +32  [ = ]       0    INA226::updateParamsImpl()
    [DEL]     -32  [ = ]       0    INA226::write()
  -2.3%    -104  [ = ]       0    [Unmapped]
  -0.1% -25.4Ki  +0.0%    +104    TOTAL

Updated: 2026-05-18T01:25:45

max: 65535
decimal: 1
increment: 1
reboot_required: true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odd, wasn't aware of this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind:refactor Code cleanup or redesign without intended behavior change. scope:drivers Device drivers and hardware interfaces. scope:parameters Parameter definitions, metadata, migration, or defaults. scope:sensors Sensor pipeline, calibration, voting, or sensor validation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants