Rušení I2C na čtyřmetrovém kabelu z I2S mikrofonu
Senzor VEML7700 na stole fungoval, na konci 4 m kabelu ztrácel většinu přenosů. Na vině byly hodiny I2S mikrofonu ve vedlejším páru.
Den předtím na stole fungoval světelný senzor VEML7700 na sto procent.
Pak jsem ho připájel do venkovního hubu meteostanice, na konec čtyřmetrového FTP kabelu, a většina čtení skončila hláškou no answer nebo read failed.
Hub nese tři čidla. Teplotu a vlhkost měří SHT45, světlo VEML7700 a hluk mikrofon INMP441. Na druhém konci kabelu sedí ESP32-WROOM-32 a BMP280. Senzory používají I2C, mikrofon I2S a všechno jde jedním kabelem se čtyřmi kroucenými páry:
| pár | vodiče |
|---|---|
| 1 | SD mikrofonu + GND |
| 2 | SDA + 3V3 |
| 3 | SCL + GND |
| 4 | SCK + WS mikrofonu |
Při návrhu jsem hlídal, aby hodiny mikrofonu nikde neběžely v jednom páru se SDA nebo SCL. Myslel jsem, že tím mám vystaráno.
Většina přenosů v háji
Diagnostický sketch vypisuje každé dvě sekundy řádek se stavem všech senzorů. Sken sběrnice našel všechny tři adresy (0x10 0x44 0x76), takže zapojení sedělo.
Jenže na 100 kHz prošlo čistě zhruba jedno čtení z dvanácti. Po snížení na 20 kHz to bylo 9 z 19 a tři z těch devíti vrátily nesmysl, třeba white 65391.
SHT45 a BMP280 na téže sběrnici přitom nenahlásily jedinou chybu.
Nabízel se vadný modul nebo studený spoj v hubu. Jenže ten modul den předtím na stole fungoval. Rozdíl mezi stolem a hubem byl hlavně v kabelu: kabel měl čtyři metry a ve vedlejším páru běžely hodiny mikrofonu.
Tak jsem hodiny mikrofonu zastavil. Se zastavenými hodinami prošlo 19 čtení z 19, na 20 kHz za 40 sekund.
Princip rušení
Na každý vzorek připadají dva 32bitové sloty (levý a pravý kanál), takže při vzorkování 16 kHz běží SCK na 16 000 × 64 = 1,024 MHz. To je padesátkrát rychlejší než I2C na 20 kHz, s ostrými hranami a bez přestávky.
Kroucený pár chrání před rušením jen tehdy, když v něm teče signál i jeho zpětný proud. Pár 4 ale nese dvoje hodiny a žádnou zem. Zpětný proud SCK i WS si tedy hledá cestu zemnicími vodiči v párech 1 a 3, a v páru 3 leží SCL. Osciloskop jsem na to neměl, takže je to moje vysvětlení, ne měření. Jisté je jen to, že se zastavenými hodinami chyby zmizely.
BMP280 sedí u ESP32 na krátkých drátech, takže jeho čistá čtení mě nepřekvapila. Proč ale chyby nehlásil SHT45 na konci téhož kabelu, nevím.
U VEML7700 je chyba o to zrádnější, že nemá CRC. SHT4x posílá k datům kontrolní součet, takže firmware poškozené čtení pozná. Hodnota als 65307 z VEML7700 odejde jako platné číslo.
Oprava: hodiny stojí, když se čte
Tahat nový kabel se mi nechtělo, takže oprava skončila ve firmwaru. Před každým čtením senzorů se úloha mikrofonu pozastaví a i2s_channel_disable() zastaví hodiny SCK i WS. Po čtení je i2s_channel_enable() zase rozběhne. V diagnostickém sketchi to vypadá takhle:
// I2C only with the mic clocks stopped
if (i2sReady) i2s_channel_disable(i2s.rxChan());
reportBmp();
reportSht();
reportVeml();
if (i2sReady) {
i2s_channel_enable(i2s.rxChan());
delay(WARMUP_MS);
i2s.readBytes((char*)frames, sizeof(frames)); // drop what the DMA held and the muted start
}
Zadarmo to není. INMP441 po rozběhu hodin mlčí 2^18 cyklů SCK, při 1,024 MHz tedy 256 ms, a firmware pro jistotu zahodí o něco víc. Spolu se samotným čtením na 20 kHz tak každé čtení senzorů stojí zhruba půl sekundy měření hluku. Stanice čte dvacetkrát za desetiminutové okno, takže v každém okně chybí asi deset sekund hluku. S tím se dá žít.
Zároveň jsem na SCK a WS nastavil nejnižší sílu budiče (GPIO_DRIVE_CAP_0), aby hrany byly pozvolnější. Nasadil jsem obojí naráz, takže nevím, jestli by slabší budič stačil sám.
A první verze měla díru. bmp.readPressure() zůstal mimo úsek se zastavenými hodinami.
BMP280 sice sedí u ESP32, ale SDA a SCL jsou pořád tytéž vodiče, které vedou čtyři metry kabelem. Došlo mi to až o commit později.
Diagnostika po opravě: ok=29 fail=0 bad=0 za 60 sekund.
Slabé místo zůstalo v kabelu
Zastavování hodin je jenom berlička. Do další revize hubu mám zapsané, že každý signál nad zhruba 100 kHz dostane v páru vlastní zem. I2S vést nejlépe samostatným kabelem, SCK, WS i SD každý v páru se zemí a stínění uzemněné na straně ESP32. Kdyby I2C zlobilo i pak, přijde na řadu diferenciální extender PCA9615. Nejčistší by ale bylo zpracovat signál z mikrofonu vlastním mikrokontrolérem přímo v hubu, nebo mikrofon přestěhovat k ESP32 a megahertzové hodiny po čtyřech metrech vůbec netahat.
Jedno pravidlo ovšem platí hned. Každý nový I2C senzor, který má viset na sdíleném kabelu, zkouším rovnou na celém kabelu a s běžícími hodinami mikrofonu. Stůl s krátkými dráty mi o rušení nic neřekne.