[AD2][Pascal] I2C - Digital I/O i obsługa protokołów | WaveForms SDK w Pascal | cz.3-B

Tutaj umieszczamy tematy związane z językami programowania niepasującymi do innych działów.
Regulamin forum
Temat prosimy poprzedzić nazwą języka umieszczonego w nawiasach kwadratowych np. [Pascal].
Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

[AD2][Pascal] I2C - Digital I/O i obsługa protokołów | WaveForms SDK w Pascal | cz.3-B

Postautor: tasza » wtorek 18 gru 2018, 22:13

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ The Birthday Massacre ⚡ ☘ ⚡ Red Stars ♪ ♩ ♫
https://youtu.be/GDxYFzxAkWg



Tryptyk Protokolarny

część druga - I2C


Magistrali I2C przedstawiać raczej nie muszę, wiadomo że znana pod alternatywną nazwą TWI (Two Wire Interface) jest jednym z popularniejszych sposobów wewnętrznej komunikacji pomiędzy komponentami systemu mikroprocesorowego. Puzderko AD2 i jego WaveForms stanowią bardzo efektywne narzędzia do analizy przebiegów na szynie I2C, to nie podlega dyskusji. Uważam jednak, że warto również poznać możliwości AD2 SDK w zakresie czynnego udziału w takiej komunikacji, udziału jako master – nadzorca. I o tym dalej.

Będziemy pracować z ekspanderem I/O typu PCF8574 wraz z garstką LED-ów oraz pamięcią EEPROM 24C512:

00_elementy.jpg


Noty katalogowe tu:

:arrow: https://www.nxp.com/docs/en/data-sheet/ ... F8574A.pdf
:arrow: http://ww1.microchip.com/downloads/en/d ... oc1116.pdf

Schemat układu doświadczalnego jest banalny, całość podłączona jak poniżej:

01_schem_led_pam.png


Czerwone gwiazdy

Zaczniemy od WaveForms i tego, co udostępniają nam okienka aplikacji – proszę, oto dla wprawy test zapisu bajtu danych do ekspandera PCF8574 celem wyświetlenia wzorka na cudnych staropolskich diodkach LED jarzących się przyjemną czerwienią.

02_led.jpg


Zauważmy, że dla A2,A1,A0 zwartych do masy jako adres kostki podajemy wartość 0x20, czyli wersję 7-bitową adresu sprzętowego, zgodnie z prawą kolumną w ` Table 4.PCF8574 address map`. Wniosek z tego taki, że bit determinujący zapis/odczyt aplikacja WaveForms dodaje sobie sama, przekształcając wewnętrznie (<< )podaną wartość na efektywny adres 0x40 do zapisu lub 0x41 do odczytu.

Po chwili zabawy z okienkami naszkicujemy program w Pascal, zestaw funkcji do obsługi I2C, podobnie jak dla UART jest sensownie zaprojektowany i zapewnia obsługę typowych przypadków jakie mogą nas spotkać przy pacy z magistralą: konfiguracja + inicjalizacja, odczyt bajtu, zapis bajtu, sekwencja zapis-odczyt danych wielobajtowych.

Do migania ledami wystarczy nam operacja zapisu jednego bajtu do kostki pod zadany adres, czyli kluczowa część programiku z użyciem funkcji FDwfDigitalI2cWrite lub FDwfDigitalI2cWriteOne będzie wyglądała następująco:

i2c_pcf8574_1.lpr pisze:

Kod: Zaznacz cały

FDwfDigitalI2cRateSet( hAd2, 500 ); // taktowaie na przykład 500Hz
FDwfDigitalI2cSclSet( hAd2, 0 );     // SCL - DIO_0
FDwfDigitalI2cSdaSet( hAd2, 1);      // SDA = DIO_1

FDwfDigitalI2cClear( hAd2, @nak );
if nak = 0 then
begin
  writeln( 'I2C err, możliwy problem z zasilaniem lub rezystorami pull-up na SDA/SCL :(' );
  exit;
end;

repeat
 for i := 0 to length( pattern )-1 do
   begin
     //FDwfDigitalI2cWrite( hAd2, $40, @pattern[ i ], 1, @nak );
     //lub
     FDwfDigitalI2cWriteOne( hAd2, $40, pattern[ i ], @nak );
     delay ( 100 );
   end;
until false;


Cały projekt w lokalizacji: :arrow: https://github.com/bienata/AnalogDiscov ... _pcf8574_1

Zauważmy, że we wszelkich wywołaniach SDK adres podajemy w formie 8-bitowej, czyli dla naszego ekspandera to będzie 0x40, operujemy wartością do zapisu jako podstawową. Jedynkę na najmłodszym bicie potrzebną do ustalenia adresu do odczytu SDK doda sobie samo (bitowe OR), gdy uzna to za konieczne. To jest pewna niekonsekwencja względem WaveForms i należy o tym drobiazgu pamiętać.

Tradycyjnie krótki filmik z pobłyskującej czerwono instalacji:

https://youtu.be/qNPdQLCzmaY

I dla rozbudzenia tych przysypiających zagadka - co intrygującego/charakterystycznego/nietypowego jest na poniższych oscylogramach przedstawiających przebiegi SDA/SCL?

03_writebyte_osc.png


Tak, dżenderuśny Rigol potrafi pokazać adres w trybie Normal (7-bit) oraz w trybie R/W (8-bit), no fajnie.
Ale co jeszcze? Czas start.

Chora róża

Taki wiersz drzewiej pan William Blake napisał - `The Sick Rose` i w tych klimatach następne demko będzie, gdzie zapoznamy się z operacjami odczytu jedno- i wielobajtowego z pamięci EEPROM. Kostka 24C512 została zaprogramowana pewną zawartością przy pomocy oryginalnego chińskiego (a nie jakiegoś tam europejskiego klona) programatora typu TL866A, fotografia pamiątkowa dla ilustracji:

04_programmer.jpg


Dodatkowa informacja taka, że wpisana zawartość uplasowana jest od adresu 0x0000, zawiera kody ASCII w kwocie około 3kB, jako znacznik końca przyjęłam wartość 0xFF, która nie występuje w treści przekazu, za to konsekwentnie wypełnia nieużywany, dalszy obszar pamięci EEPROM.

Choćby pobieżna lektura specyfikacji kostki 24C512 wykaże, że możliwe jest kilka wariantów odczytu danych, z bardziej praktycznych to odczyt bajtu precyzyjnie wskazanego przez 16-bitowy adres oraz odczyt całego ciągłego bloku danych wskazanego przez adres początkowy. Każda metoda ma swoje zalety, wady oczywiście też – popatrzmy zatem na pierwszy przykład:

i2c_24c512_1.lpr pisze:

Kod: Zaznacz cały

for i := 0 to $FFFF do
  begin
       address [0] := (i shr 8) and $ff;
       address [1] := i and $ff;
       FDwfDigitalI2cWriteRead (hAd2, $A0, @address, 2, @data, 1, @nak );
       if data = $a then continue;
       if data = $ff then break;
       write( char(data) );
  end;


Tu mamy odczyt bajt po bajcie i jak nietrudno przewidzieć narzut (pojadę fachowo – overhead) komunikacji jest dość znaczny. Popatrzmy na oscylogram elementarnej transakcji: aby pozyskać jeden bajt danych musimy przesłać wejściowe 2 bajty kontekstu – adres wewnątrz kostki.

05_read_byte.png


Łatwo zgadnąć, że ten sposób czytania jest kosztowny czasowo, z drugiej strony - działamy precyzyjnie.

Drugi wariant odczytu naszej zawartości to transmisja blokowa. Tu na wstępie dialogu z kostką pamięci podajemy jej adres startowy, następnie odbieramy do bufora cały ciągły blok danych, to tak ogólnie. A ponieważ szukamy w treści naszego znacznika 0xFF to nie wciągniemy wszystkiego co jest niczym kreskę nosem, ale podzielimy zabawę na mniejsze bloki po na przykład 256 bajtów. Tu widać wadę - zapewne kiedyś zaczytamy z kostki paczkę danych, z których większość już nas nie będzie interesować. Programik przyjmuje postać:

i2c_24c512_1.lpr pisze:

Kod: Zaznacz cały

  done := false;
  for i := 0 to $ff do
  begin
       address [0] := i;
       address [1] := 0; // czytamy paczkami po 256 bajtow
       FDwfDigitalI2cWriteRead( hAd2, $A0, @address, 2, @dataBuff, $ff, @nak );
       for x := 0 to $ff do
       begin
              if dataBuff[x] = $a then continue;
              if dataBuff[x] = $ff then
              begin
                   done := true;
                   break;
              end;
              write( char(dataBuff[x]) );
       end;
       if done then break;
  end;


Kompletny projekt tu: :arrow: https://github.com/bienata/AnalogDiscov ... c_24c512_1

Po odebraniu każdego bloku jest on wypisywany na ekran, znacznik 0xFF kończy demonstrację. Oscylogram jednej sekwencji odczytu bloku danych widać poniżej jak i zysk – ile danych dostajemy w zmian za dwubajtowy adres początkowy, który jest już gdzieś hen po lewej stronie diagramu, tu widać złapany jeden z cykli odczytu sekwencyjnego (sequential read):

06_seq_read.png


I ponownie w nawiązaniu do zagadki - skąd wiadomo, że to właśnie odczyt? No skąd?

No i proszę oto finalny materiał filmowy, przy okazji rozjaśni się, skąd moje wczorajsze rojenia o zmianie czcionki w oknie Console Lazarusa.

https://youtu.be/e30jwzvuMJ0

Zauważmy jak czasowo prezentuje się działanie pierwszej, a jak drugiej pętli czytającej kostkę. Narzut samej pętli napędzającej odczyt jest znikomy w porównaniu z czasem konsumowanym przez synchroniczne wywołanie FDwfDigitalI2cWriteRead taktowanej w sumie niewielkim sygnałem zegarowym.

W podsumowaniu

Powyższa pisanina chyba dość dobrze pokazuje z jaką łatwością można w Pascalu (czy innym języku, dla którego dostępnej jest WaveForms SDK) tworzyć własne programiki wykorzystujące AD2 w roli nadzorcy (mastera) komunikacji I2C. W praktyce otwiera to drogę do szybkiego testowania czy poznawania (research) układów z interfejsem I2C, a w sytuacjach awaryjnych umożliwia np. doraźne odczytanie zawartości kostki EEPROM czy zapisanie jej nowym wsadem. No i jak w poprzednim przypadku – mamy sporą elastyczność w wyborze pinów na linie SDA i SCL, to pozwoli uniknąć przekładania kabelków przy pracy z kilkoma układami jednocześnie na przykład podczas testów porównawczych.


#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)

Awatar użytkownika
tasza
Geek
Geek
Posty: 1082
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [AD2][Pascal] I2C - Digital I/O i obsługa protokołów | WaveForms SDK w Pascal | cz.3-B

Postautor: tasza » czwartek 20 gru 2018, 13:17

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ The Birthday Massacre ⚡ ☘ ⚡ Under Your Spell ♪ ♩ ♫
https://youtu.be/FhBIqvzQMGY


Heh, no widzę że wszyscy już świętami żyją i na podejście do zagadki chętnych jakoś zabrakło.
Bo myśl straszliwą, że ona zbyt trudna była to póki co odpędzam, niczym namolne aniołki grzebiące w prezentach pod choinką.

Konkretnie to rozchodziło mi się o niekonsekwentną wartość napięcia niskiego stanu logicznego dla sygnału SDA podczas wymiany danych pomiędzy AD2 i kostką PCF8574. Zobaczmy jeszcze jak to wygląda bardziej dynamicznie:

https://youtu.be/Rrb389kRmzg

I bierzemy sprawę pod lupę:

00_glass.jpg


Barbioskop, po ustawieniu kursorów w interesujących nas miejscach przebiegu pokazuje następujące wartości:

01_20181220_093419_candy.png


Ponieważ wiemy, jak wygląda elementarna transakcja zapisu I2C, możemy z łatwością ustalić, które urządzenie odpowiada za wskazane fragmenty przebiegu. Widoczny powyżej żółty sygnał SCL pełni tu rolę statysty, ułatwiając obserwację, a w/g kursorów mamy kolejno:

* poziom B, o wartości 400mV wymusza AD2 podając kolejne zera bieżącego bajtu danych
* poziom A, o wartości 40mV to stan L wymuszony przez PCF-a, to jest bit ACK, potwierdzający przyjęcie ramki danych

Powstało zatem pytanie - skąd ta różnica i dodatkowo: czy może być potencjalnie groźna dla stabilności transmisji.

Jako uzupełniająca informacja i gdyby ktoś jednak chwycił za kalkulator, oba rezystorki pull-up na I2C, na okazję tej pisanki zmniejszyłam do około 2.5kΩ, aby różnice na ekranie były jeszcze wyraźniejsze.

No i teraz spójrzmy na sposób sterowania linią SDA ze stałoprądowego punktu widzenia, poglądowy schemat poniżej:

02_sch2.png


Może wygląda to nieco zaskakująco, ale właśnie to tak jest zrobione - linie Digital I/O puderniczki AD2 opatrzone są maleńkimi termistorami PTC o wartości 220Ω, to zabezpieczenie układu FPGA przed prądowym przeciążeniem wejść/wyjść.
Całość widać w pełnej krasie na schemacie w lokalizacji: :arrow: https://reference.digilentinc.com/refer ... digital_io

Sygnały cyfrowe z AD2 widzimy poprzez te niewielkie rezystancję i jak widać ma to pewne konsekwencje.

Dla stanu logicznego H nic ciekawego się nie dzieje, zostawmy to. Ale gdy AD2 wymusza niski stan logiczny, zamyka się tranzystor Q1 i na wyprowadzeniu DIO_x pudełeczka poziom napięcia opisuje wzorek na zwykły dzielnik: R1/(R1+R2). Przyjmując za Ucc 5V, napięcie dla L(AD2) będzie 5*220/(2500+220) czyli około 400mV. I zauważmy, tyle mniej więcej pokazał Rigol kursorem B.

Dodatkowe spostrzeżenie takie, że widoczne na Digilentowym schemacie stadko diodek zabezpieczających wejścia D19...D27 w tym rozważanym przypadku możemy spokojnie pominąć, ponieważ dla L na wyjściu AD2 pracują one zaporowo, taką polaryzację zapewnia im wewnętrzne 3.3V pokładowego zasilania.

Gdy dla odmiany to kostka PCF wymusza L logiczne, swoim zwartym tranzystorem Q2 - ujmując niezbyt wyszukanie - robi na SDA twardą glebę, zapewniając wartość L(PCF) na poziomie kilkudziesięciu miliwoltów. Widoczna u mnie wartość 40mV to zakładam wynik parametrów wyjścia układu scalonego (a dokładnie siedzącego tam MOSFET-a) no i pasożytniczego spadku napięcia na plątaninie cherlawych kabelków pracujących w roli rozproszonego GND, czyli...jak zwykle.

Z powyższego wniosek prosty jest taki, że przy odrobinie uporu zmniejszając bez sensu pull-up będziemy w stanie doprowadzić do sytuacji, że zbyt wysoki poziom L sygnału SDA sparaliżuje transmisję.

Drugi, że możemy potraktować AD2 jako źródło napięcia (impulsów), a całość magistrali I2C jako czwórnik R-C, gdzie R ma te 220Ω a wszelkie pojemności na szynie stanowią rzeczone C. Tu powstaje zagadnienie - jakie będzie pasmo przenoszenia takiego układu i przy jakiej częstotliwości stromość zboczy przestanie być akceptowalna do zastosowań jakby cyfrowych. O tym zagadnieniu z resztą wspomina wskazany w linku powyżej fragment dokumentacji AD2.

I na koniec - jeżeli oscylogram pokazuje serie 'wyższych' stanów L i pojedyncze, bliskie 0V impulsy - to dane nadaje AD2 do PCF, a owy je tylko mocnym ACK-iem potwierdza. Jeżeli mamy w seriach dobre, bliskie 0V stany L i rachityczne, podniesione o kilkaset mV impulsy potwierdzeń - to właśnie nadaje PCF do AD2, który to stara się każdą ramkę potwierdzić (vide: oscylogram z sequential read)

No i po zagadce.
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
______________________________________________ ____ ___ __ _ _ _ _
Kończysz tworzyć dopiero, gdy umierasz. (Marina Abramović)


Wróć do „Inne języki programowania”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 3 gości