CoreS3: fix external 5V output not turning off / re-enable being cancelled on USB-C without battery - #352
Merged
Conversation
… output control setExtOutput(false) wrote BUS_OUT_EN and BOOST_EN in one two-byte transaction, so BUS_OUT_EN dropped while the bus was still at 5V. BUS_OUT_EN=0 turns the bus input-side switch on; with the bus still at 5V that switch never enters reverse blocking and USB VBUS keeps feeding BUS_OUT. The 5V stayed on after the disable, and without a battery the TS reading (a divider of BUS_OUT) stayed above 2.0V so every later setExtOutput(true) was cancelled until a power cycle. - On BUS_OUT_EN 1->0: write BOOST_EN=0 first, wait 200 ms for the bus to discharge (measured boundary: 20 ms loaded, 80 ms unloaded), re-read the expander and clear only the requested bit. On an I2C failure re-read the latch and recover from the actual state, retrying with read-back up to 3 times. - Other paths write one register at a time, BOOST_EN before the output enable when turning on and after it when turning off, and stop if the first write fails. - Serialize all AW9523 output-port writers in M5Unified (setExtOutput, setUsbOutput, Power_Class::begin, the speaker amplifier enable) with a mutex and run the no-battery protection check inside the same critical section. - Protection check: skip it when BUS_OUT_EN is already 1 (the 5V on TS is our own output), treat I2C failures and invalid ADC values as unsafe, and when it reads unsafe release the lock and re-check every 20 ms for up to 1 s so a re-enable right after a disable is not cancelled by the stale TS reading. External bus power keeps TS high, so it is still cancelled. Requests carry a generation number so an enable that is still waiting yields to a later disable.
Open
2 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On CoreS3 / CoreS3 SE,
setExtOutput(false)wrote BUS_OUT_EN (AW9523 P0_1) and BOOST_EN (P1_7) in a single two-byte transaction, so BUS_OUT_EN dropped before the boost converter stopped. BUS_OUT_EN=0 turns the BUS input-side switch (U19) on; when the bus is still at 5V at that moment, the switch never enters reverse-current blocking and USB VBUS keeps feeding BUS_OUT. The visible symptoms:setExtOutput(false).setExtOutput(true)is cancelled withsetExtPower(true) is canceled.until the device is power-cycled. This is the recovery-impossible path behind Documentation: CoreS3 Requires Battery for Grove/MBUS Power When USB Powered M5CoreS3#72 (USB-C only, no battery).Fix
setExtOutput,setUsbOutput,Power_Class::begin, the CoreS3 speaker amplifier enable) with one mutex, and run the no-battery protection check inside the same critical section so a concurrent disable cannot invalidate the check.output_power=true). It reads the raw registers and treats an I2C read failure or an invalid ADC value as unsafe (fail closed). When it reads unsafe it releases the lock, waits 20 ms and re-checks for up to 1 s, because the TS ADC lags the real voltage by several hundred ms and would otherwise cancel a re-enable issued right after a disable; external bus power keeps TS high, so the protection still cancels in that case. Requests carry a generation number so an enable that is still waiting yields to a later disable.Measured discharge boundary on a CoreS3 (no battery, USB-C): 10 ms fails / 20 ms works with a Grove unit attached, 70 ms fails / 80 ms works with no load. 200 ms leaves margin.
Notes
setExtOutput(false)now blocks for about 200 ms when the output was enabled, andsetExtOutput(true)without a battery may block for up to 1 s while the protection check waits for TS to settle (both documented in the header).setExtOutput(true)called before the TS ADC has caught up) is unchanged by this PR.