Fix display freeze: move I2C work out of interrupt context #8
Reference in New Issue
Block a user
Delete Branch "claude-fable/metronom:fix/i2c-delay-in-isr-freeze"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Wyświetlacz potrafi się zawiesić (rotacja staje, odczyty przestają się aktualizować, pomaga tylko reset) — powtarzalnie przy aktualizacji temperatury w dół (np. 25.08), a nie losowo.
Przyczyna
Biblioteka
grove_alphanumeric_displaypo każdym zapisie do wyświetlacza wywołujedelay(100)(pole_ms = 100ustawiane wsetTubeType), aupdateTube()robi dwa takie zapisy z wnętrza przerwania TC3 (TasksHandler). Wewnątrz ISR przerwanie SysTick (napędzającemillis()/micros()) nie może się wykonać, więcmicros()przestaje normalnie rosnąć i oscyluje piłokształtnie w oknie ~1 ms.Pętla
delay()na SAMD:przez przekręcanie arytmetyki unsigned prawie zawsze kończy się natychmiast (dlatego urządzenie zwykle działa godzinami), ale gdy
startzostanie spróbkowany dokładnie na dnie tej piły, warunek nie spełni się nigdy — pętla nieskończona w ISR, twardy zwis.Dlaczego powtarzalnie przy tej samej wartości i kierunku zmiany: SysTick (1 ms), TC3 (20 ms — dokładna wielokrotność) i I2C chodzą z jednego zegara 48 MHz, więc faza wejścia w przerwanie jest zatrzaśnięta, a czas dojścia do
delay()zależy co do mikrosekund od ścieżki kodu. Spadek i wzrost wartości to różne ścieżki (histereza 0.03 → inny skok wartości,strcmpkończy się na innym znaku), więc jedna konkretna ścieżka trafia w fatalne okno za każdym razem.Ten sam problem dotyczy
delay(100)w re-inicieSeeed_BME280po nieudanej transmisji orazupdateTube()wołanego z przerwania przycisku.Naprawa
Przerwania tylko ustawiają flagi (
sensorsDue,displayNeedsUpdate,tubeRotated); odczyty BME280, aktualizacja wyświetlacza i przestawianie interwału rotacji wykonują się wloop(), gdziedelay()działa normalnie.Skompilowane czysto dla Seeeduino Zero (
arduino-cli, core Seeeduino:samd 1.8.6) — bez dostępu do sprzętu, do weryfikacji na urządzeniu.🤖 Generated with Claude Code
Update — feedback od Piotra (właściciela repo) na sprzęcie z logowaniem:
To częściowo koryguje pierwotną (czysto czasową) diagnozę: zawieszenie jest danio-zależne, nie tylko fazowe. Spójniejsza hipoteza łączona:
'8'w foncie 14-segmentowym (0x4778) zapala najwięcej segmentów ze wszystkich cyfr (8; dla porównania'1'=2,'0'=6,'7'=3).'8'przysetBrightness(15)(maks) → szczyt chwilowego poboru prądu.Wire.endTransmission()(orazdelay(100)w re-inicie BME280) blokuje się, a w przerwaniu jest to trwały zawis. To tłumaczy: czemu nie losowo, czemu nie zawsze, i czemu pomaga tylko reset.Wniosek: fix z tego PR (I2C poza ISR) prawdopodobnie nadal pomaga — zmienia trwały zawis w jedną zgubioną klatkę — ale może nie usunąć źródła, jeśli to brownout sprzętowy.
Dorzuciłem do PR osobny commit (
6abd900) obniżającysetBrightness(15) -> 4jako tani, odwracalny test:Wire.setTimeout()/procedura odblokowania magistrali;Piotr — skoro masz już logowanie, log przed każdym
writeBytes/endTransmissionpokaże, na którym dokładnie wywołaniu staje (czy wendTransmission, czy wdelay) i przygwoździ to ostatecznie.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.