Dzisiaj przedstawię krok po kroku proces analizy nieznanego protokołu I2C na przykładzie kontrolera wyświetlacza 7-segmentowego z klawiaturą HD2015E znalezionego na płytce ze starego tunera DVB-T2. Celem projektu będzie zebranie informacji potrzebnych do wysterowania HD2015E z poziomu Arduino. Przechwycimy tu komunikację na liniach SDA/SCL analizatorem stanów logicznych kupionym za 40 zł, zdekodujemy ją jako I2C a potem spróbujemy odgadnąć znaczenie poszczególnych bajtów.
Założenia
W tym temacie zakładam, że:
- czytelnik posiada podstawową wiedzę na temat protokołu I2C bądź podobnych protokołów, wie, że opiera się on o dwie linie (zegar i dane), wie że I2C zapewnia komunikację w obie strony (zapis i odczyt z adresu), wie, że w zależności od tego czy odczytujemy czy wysyłamy dane ustawiony jest (bądź nie) bit Read/Write, itd
- czytelnik wie mniej więcej jak działa wyświetlacz 7-segmentowy, wie, że zapalane są osobne segmenty, że tworzą one cyfry, że zazwyczaj jeden bajt (8 bitów) mapuje się na jedną cyfrę i każdy bit odpowiada innemu segmentowi, itd
Zakup Salae
Ten analizator jest naprawdę bardzo tani, można go dostać za raptem 40 zł i to w naszym kraju - bez czekania na przesyłkę z Chin:
Zobaczmy, co mówi o nim sprzedawca:
Sprzedawca sam przyznaje, że to jest analizator "kompatybilny z Salae", czyli potocznie mówiąc klon. Raczej nie mam wobec tego zarzutów - za taką cenę... Sprzedawca również poleca link: http://www.saleae.com/downloads , ale ja miałem zainstalowane już Pulseview.
Instalacja Pulseview i sterowników
Mój wybór softu to PulseView, PulseView to interfejs graficzny dla Sigroka, aplikacja została napisana w środowisku QT i jest dostępna za darmo:
https://sigrok.org/wiki/PulseView
Pobrać ją można tutaj, zarówno w wersji 32, jak i w 64-bitowej:
https://sigrok.org/wiki/Downloads
PulseView pozwala przechwytywać dane na żywo z analizatora oraz odtwarzać wcześniej zapisane do pliku dane. Po uruchomieniu wita nas minimalistyczny interfejs:
Ale możliwe, że analizator nie będzie jeszcze wykrywany:
Wtedy należy zainstalować sterowniki, pobrałem je stąd:
https://zadig.akeo.ie/
Wybrałem na liście mój analizator i rozpocząłem instalację:
Przygotowanie sprzętu - sterownik LED HD2015E
W ramach demonstracji Salae przechwycimy komunikację I2C kontrolera wyświtelacza LED 4-cyfrowego, 7-segmentowego z dodatkową obsługą przycisków. Płytka z wyświetlaczem pochodzi z tego tematu:
Stary dekoder DVB-T2 - STB HD N6 - wnętrze, potencjał przeróbki na DIY zegar
Nawet bez znajomości wyprowadzeń układu łatwo jest poznać, co jest gdzie podłączone, bo akurat mamy wyprowadzone sygnały na pady. Podpisane jest zarówno zasilanie, jak i CLK i DAT:
Na zdjęciu poniżej widać, że CLK i DAT jest podłączone do nóżek HD2015E poprzez dwa rezystory.
Zostaje tylko podłączyć analizator - tylko w jaki sposób?
Przede wszystkim należy zadbać o bezpieczne zasilanie badanego układu, nie wszystkie przetwornice zapewniają izolację od sieci, dlatego polecam zasilać wszystko z tego samego jednego laptopa/komputera na którym wykonujemy analizę. Nie będę się u zagłębiać w tą tematykę, bo tutaj omawiany jest analizator Salae, ale dla dobrej zasady po prostu zbadałem, że zasilacz na płytce daje 5V i postanowiłem się wlutować z przewodem 5V na wyjście tego zasilacza:
Teraz trzeba podłączyć analizator. Podłączamy interesujące nas kanały i koniecznie też masę, musimy mieć wspólny poziom odniesienia.
Cały układ:
Całość będzie zasilana z USB jednego komputera, bez dostępu do sieci.
Przechwycenie i analiza
Uruchamiamy Pulseview i klikamy Run, tutaj:
Przechwytywanie powinno ruszyć. W razie problemów, warto spróbować zrestartować program, komputer i ew. reinstalować sterowniki, ale zakładam, że już je zainstalowaliście wcześniej.
W razie czego też warto zwiększyć gęstość próbkowania:
Początkowo miałem tak rzadkie próbkowanie ustawiane, że obserwowane I2C było zbyt szybkie by móc je odczytać.
Dopiero ustawienia jak poniżej dały mi rezultat:
Na zrzucie ekranu widać że co jakiś czas coś się dzieje. Przechwytywanie było włączone przy uruchomionym tunerze. Co takiego to może być? Podejrzewam, że to "polling" klawiatury. Wyświetlacz cały czas przedstawia numer kanału, więc nie trzeba go odświeżać, więc to pewnie nie wyświetlacz. Nie widzę natomiast by tu był pin przerwania od wciśnięcia klawisza (jakiś INT, interrupt), więc pewnie MCU odpytuje regularnie kontroler o stan klawiszy.
Teraz jeszcze pora odkodować zebrane dane. Możemy odkodować je jako I2C:
Dekodery Pulseview są tworzone przez społeczność. Wybieramy z listy I2C:
Na osi czasu pojawi się dekoder I2C, ale trzeba jeszcze się w niego wklikać w celu ustawienia linii SDA i SCL:
Ustawiłem wedle połączeń z płytki:
Od tego momentu rozpocznie się powolny proces dekodowania I2C. Cierpliwości! Przy długich osiach czasu może to troszkę potrwać.
Analiza komunikacji - stan idle
Poprzez "stan idle" mam na myśli sprzęt jest włączony ale nie wykonywane są żadne operacje. Wybrany jest po prostu jakiś kanał i użytkownik nic nie robi.
Mimo to co jakiś czas się coś się dzieje, coś przechwytujemy:
Powiększmy i zobaczmy co jest przesyłane:
Wygląda na to, że podawany jest jeden adres (to raczej nie jest adres urządzenia, tylko rejestru, pewnie tutaj ten protokół to nie jest de facto I2C, widywałem już wersje bez adresu urządzenia) a potem odczytywane są dane z tego adresu.
Podejrzewam, że jest to odpytywanie układu o stan klawiszy, tzw. "polling". Ten układ nie ma pinu INT/Interrupt by wystawić na niego informację o tym że coś się wydarzyło, więc MCU musi regularnie pytać go o stan klawiszy by zobaczyć, czy coś się zmieniło.
Stan klawiszy odczytywany jest z rejestru 0x27 (tudzież: komendą 0x27).
Odpowiedź 0x07 jest za każdym razem taka sama, ale czy zmieni się jak przytrzymam jakiś klawisz?
Analiza komunikacji - wciśnięty klawisz "następny kanał"
Zatem powtarzamy przechwycenie, tym razem trzymając wciśnięty klawisz "następny kanał", oczywiście klawisz na obudowie, nie na pilocie.
Teraz przechwytujemy grupowo po dwie sesje komunikacji razem
Przyjrzyjmy się im z bliska. Najpierw pakiet który podejrzewałem o obsługę klawiszy;
Zmienia się odczytana wartość. Rejestr to dalej 0x27, ale teraz odczytujemy 0x47 a nie 0x07.
Bitowo to jest:
- 0x07 -> 0000 0111
- 0x47 -> 0100 0111
Czyli jeden klawisz to jeden bit, zgodnie z oczekiwaniami. Pewnie wciśnięcie drugiego klawisza zapaliłoby inny bit.
Ale to nie wszystko, tu jest jeszcze drugi pakiet, przyjrzyjmy mu się:
Bardzo dużo tu się dzieje. Zasadniczo widzę tutaj:
- ustawienie rejestru o adresie 0x24 na wartość 0x71 (powtarza się)
- kolejne ustawianie rejestrów o adresach 0x34, 0x35, 0x36 itd na różne wartości
Obstawiałbym, że 0x24 to rejestr kontroli wyświetlacza a rejestry począwszy od 0x34 to rejestry wyświetlanych cyfr. Wyświetlacz jest na 4 cyfry i mamy tu kolejny zapisy do 0x34, 0x35, 0x36 i 0x37. Dodatkowo pierwsze dwie wartości pisane do tych rejestrów to 0x00, co zgadza się z tym co widzę na wyświetlaczu, bo widzę, że dwie pierwsze cyfry są zgaszone.
Tutaj to samo, ale w większym powiększeniu:
Aha, prawie bym zapomniał - dla wygody może jeszcze schowajmy zbędne linie:
Po prostu klikamy na nie i wybieramy opcję "Disable" by schować.
Ok, to teraz zobaczmy, jak wygląda wysłanie na wyświetlacz numeru kanału pierwszego, czyli trzy pierwsze cyfry zgaszone, a potem znak 1.
Specjalnie przewinąłem na ostatni kanał by móc jednym wciśnięciem klawisza wejść na kanał 1.
Rezultaty:
Idąc za ciosem, przechwyciłem też kolejne zmiany kanałów, zawsze o 1 dalej, aby móc porównać wysyłane kody segmentów.
Oto gotowe porównanie:
Co tutaj widzimy?
- dla 1, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x06
- dla 2, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x5B
- dla 3, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x4F
- dla 4, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x66
- dla 5, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x6D
- dla 6, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x7D
- dla 7, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x07
- dla 8, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x7F
- dla 9, rejestry są ustawiane na kolejno 0x00 0x00 0x00 0x6F
- dla 10, rejestry są ustawiane na kolejno 0x00 0x00 0x06 0x3F
Widać tutaj, że w przypadku 10 cyfra 1 pojawia się też miejsce wcześniej i ma tak samo jak na ostatnim miejscu kod 0x06.
Teraz możemy określić, które bity to które segmenty.
Zacznijmy może od porównania 0 (0x3F) i 8 (0x7F):
- 0x3F -> 0011 1111
- 0x7F -> 0111 1111
Zatem środkowa belka to siódmy bit.
Teraz może porównamy 5 i 6, bo 5 to 6 bez jednego segmentu:
- 0x6D -> 0110 1101
- 0x7D -> 0111 1101
Możemy zauważyć, że różnica między nimi to bit na pozycji 5:
W ten sposób możemy określić również role pozostałych segmentów.
Dodam jeszcze, że dziwi mnie to, że osobno wysyłany jest zawsze adres i osobno wartość cyfry. Przecież z reguły można wysłać tylko raz adres a potem kolejno bajty i będą one wpisywane do kolejnych komórek w pamięci. Czyżby HD2015 tego nie obsługiwał, a może po prostu twórca programu tego nie użył?
Analiza komunikacji - tryb standby
O dziwo najwięcej dzieje się na tej linii w stanie standby, to znaczy w sytuacji, gdy dekoder jest wyłączony. Wynika to z tego, że pojawia się wtedy zegar. W momencie przechwycenia pokazywał 14:29:
Ale na razie nie będę tego odkodowywać, bo to jest ustawianie cyfr takie samo jak omawiałem wcześniej:
Można by tu jeszcze poszukać, gdzie wysyłany jest dwukropek - być może ma osobny rejestr bądź doklejony jest do którejś z cyfr, wszakże wyświetlacz jest 7-segmentowy a rejestry są 8-bitowe, więc jeden wolny bit jest dostępny.
Podsumowanie
Bardzo mi się ten analizator stanów logicznych spodobał. Już jakiś czas z niego korzystam i jednocześnie bardzo żałuję, że nie miałem go te 5 czy ileś lat temu jak zaczynałem w ogóle przygodę z mikrokontrolerami. Z pewnością pomógłby mi on skuteczniej diagnozować potencjalne problemy z implementacją I2C czy tam innych protokołów.
Z aplikacji na komputer, Pulseview, też jestem bardzo zadowolony - można do niej nawet pisać pluginy w Pythonie, kiedyś już przerabiałem tak jeden skrypt (dekodowanie WS2812) by było zgodne z SM15155E, szczegóły tutaj.
Co do samego analizowanego protokołu, to pierwsze co rzuca się w oczy to brak adresowania układu, co sprawia, że nie jest on zgodny z I2C i nie umieścimy na magistrali kilku takich urządzeń. Bardzo mocno mi on przypomina protokół TM1637, czyżby to nie było praktycznie to samo?
Oprócz tego - bez zarzutów, bardzo prosty protokół, być może w następnej części spróbuję go zaimplementować na Arduino.
Czy korzystaliście z prezentowanego lub z podobnych analizatorów logicznych? Czy waszym zdaniem są przydatne? Zapraszam do dyskusji.
Fajne? Ranking DIY Pomogłem? Kup mi kawę.
