https://obrazki.elektroda.pl/5502422400_1590145798_thumb.jpg Witajcie moi drodzy Chciałbym zaprezentować tutaj drugą wersję mojego Arduino VGA Shield (nakładki na Arduino UNO generującej jednokolorowy obraz VGA i czarno-biały PAL/NTSC) - tym razem wykonaną za pomocą elementów SMD, i z dodatkowymi układami na pokładzie (expander portów MCP23016 oraz dwie kości EEPROM AT24CM02). Opis pierwszej wersji tego shielda znajdziecie tutaj: https://www.elektroda.pl/rtvforum/viewtopic.php?p=18496229#18496229 Co to za Arduino Shield? W dużym skrócie mój shield jest nakładką na Arduino UNO która pozwala bardzo łatwo generować jednokolorowy obraz na monitor VGA oraz obsługiwać klawiaturę PS/2. Opcjonalnie też dostępne jest wyjście sygnału PAL/NTSC dla telewizorów. Shield korzysta z biblioteki 'ASCII Video Terminal' dla PIC32MX250F128B autorstwa geoffg. Arduino UNO komunikuje się z tym shieldem poprzez UART na wybranych przez nas za pomocą lutowanej zworki pinach. Dodatkowo na pokładzie (nowość w tej wersji!) znajduje się: - expander portów MCP23016, dzięki czemu zyskujemy aż 16 dodatkowych IO! - dwie kości EEPROM AT24CM02, dzięki którym zyskujemy aż 512KB pamięci nieulotnej do własnych projektów! Obie nowości znajdują się na magistrali I2C od Arduino. Poniżej szczegółowo opiszę cały projekt, umieszczę proste przykłady użycia nowości na płytce a na koniec dam bardziej zaawansowane demka korzystające z poszczególnych funkcjonalności shielda. Projekt Arduino VGA Shield SMD Niniejszy projekt jest zasadniczo aktualizacją jego poprzedniej wersji która była wykonana w THT (montaż przewlekany): https://obrazki.elektroda.pl/2294289300_1588796118_thumb.jpg W obecnej wersji większość elementów jest w obudowach SMD, dodatkowo dzięki zaoszczędzonemu miejscu udało mi się na płytkę dodać: - złącze USB od PIC32 (do aktualizacji softu poprzez bootloader; może to dać też w przyszłości możliwość łatwego użycia tego shielda bez Arduino, ale na razie brakuje na PCB w tym celu regulatora 3.3V) - dwie kości pamięci EEPROM AT24CM02 na magistralę I2C od Arduino (standardowo piny 4 i 5) - 16-bitowy expander portów MCP23016 na magistralę I2C od Arduino (jak wyżej; piny 4 i 5) - rezystory pull-up na piny 4 i 5 (jak wyżej; magistrala I2C Arduino) W ten sposób obecna wersja wyszła tak: https://obrazki.elektroda.pl/8546300900_1588796382_thumb.jpg Całość open hardware i do pobrania poniżej. Schemat płytki w PDF: 1030456 Schemat płytki w PNG: 1030457 Pełny projekt płytki w Eagle (.sch + .brd): 1030455 Pliki Gerber które wyeksportowałem z Eagle (te same, których użyłem przy zleceniu wykonania gołego PCB płytkarni): 1030453 Tym razem obyło się bez żadnych błędów na płytce. Płytki standardowo zamówiłem w Chinach (10 sztuk - ktoś chętny na kilka?) a jak przybyły to wziąłem się do lutowania. Lutowanie Arduino VGA Shield w wersji SMD Opiszę tutaj szczegółowo jak zlutowałem tę płytkę w warunkach domowych. Na początek przygotowałem wszystkie elementy: https://obrazki.elektroda.pl/8220448100_1588804224_thumb.jpg https://obrazki.elektroda.pl/6139364300_1588804228_thumb.jpg I tym razem nawet nie używałem złącza VGA (DSUB25) z starej płyty głównej od komputera, tylko miałem zakupione przez internet. Do lutowania użyłem mojej wiernej najtańszej lutownicy kolbowej i na tę okazję zmieniłem jej grot na grot ścięty (choć pewnie lepszy byłby grot ścięty typu minifala, ten z wyżłobionym 'zbiorniczkiem' na cynę): https://obrazki.elektroda.pl/4748971600_1588804324_thumb.jpg Zacząłem lutowanie od najmniejszych elementów: https://obrazki.elektroda.pl/3753769700_1588804437_thumb.jpg Dałem też wtedy cynową zworkę na sygnał "B" (Blue) koło złącza VGA, czyli wybrałem niebieski kolor czcionki. Szybko przyszła pora na złącza: https://obrazki.elektroda.pl/8356330500_1588804501_thumb.jpg https://obrazki.elektroda.pl/6638436500_1588804722_thumb.jpg Złącze DSUB25 czyli VGA oraz MDC6 (aka 'mini din') czyli PS/2: https://obrazki.elektroda.pl/6815685300_1588804785_thumb.jpg Na zdjęciu można zobaczyć, że wciąż nie przylutowałem EEPROMów - to dlatego, że dopiero czekałem na paczkę z nimi. Jak przyszły to uzupełniłem ubytek: https://obrazki.elektroda.pl/3204394600_1588804882_thumb.jpg https://obrazki.elektroda.pl/1382751400_1588804914_thumb.jpg Potem jeszcze uzupełniłem ubytek złącza RCA (od sygnału PALNTSC); nie miałem takiego na składzie więc wylutowałem z jakiegoś fragmentu urządzenia elektronicznego, to chyba był odtwarzacz kaset VHS: https://obrazki.elektroda.pl/7021071000_1588805047_thumb.jpg https://obrazki.elektroda.pl/9088830900_1588805043_thumb.jpg https://obrazki.elektroda.pl/5706716800_1588805051_thumb.jpg Złącze RCA: https://obrazki.elektroda.pl/4076050200_1588805209_thumb.jpg I oto końcowy efekt (już po wyczyszczeniu z nadmiaru topnika): https://obrazki.elektroda.pl/1416338600_1588846009_thumb.jpg https://obrazki.elektroda.pl/6408921300_1588846191_thumb.jpg Warto jeszcze pamiętać, że przed użyciem należy zrobić ze spoiwa lutowniczego następujące zworki: - zworka pinu RX shielda z Arduino - zworka pinu TX shielda z Arduino - zworka pinu RESET z Shielda (łączy RESET od PIC32 z RESET od Arduino) - zworka wyboru koloru VGA z Shielda - dwie zworki na magistrali I2C dla EEPROMów Przed użyciem shielda trzeba oczywiście też wgrać mu firmware na PIC32MX250F128B za pomocą PICKIT3 (lub innego programatora) poprzez złącze ICSP dostępne na płytce. Test generowanego sygnału VGA z różnymi monitorami i telewizorem W temacie dotyczącym poprzedniej wersji mojego shielda (tutaj: https://www.elektroda.pl/rtvforum/viewtopic.php?p=18496229#18496229 ) pojawiła się uwaga, że nie działa on z częścią monitorów. Postanowiłem to zweryfikować. Użyłem do tego: - telewizora LG42LE4500 - monitora DELL 1703FP - monitora ASUS VH196S - monitora HP L1706 - monitora SyncMaster 740N Zapraszam do obejrzenia zdjęć z testów. Test 1 - monitor DELL 1703FP: https://obrazki.elektroda.pl/9890789800_1588852185_thumb.jpg https://obrazki.elektroda.pl/5803001300_1588852224_thumb.jpg Tabliczka znamionowa użytego monitora: https://obrazki.elektroda.pl/2073302500_1588783980_thumb.jpg Test 2 - telewizor LG42LE4500: https://obrazki.elektroda.pl/5747425500_1588852640_thumb.jpg https://obrazki.elektroda.pl/7081934500_1588852638_thumb.jpg Tabliczka znamionowa użytego telewizora: https://obrazki.elektroda.pl/7031910900_1588783997_thumb.jpg Test 3 - monitor ASUS VH196S: https://obrazki.elektroda.pl/2275459400_1588854942_thumb.jpg https://obrazki.elektroda.pl/3360872300_1588854987_thumb.jpg Tabliczka znamionowa użytego monitora: https://obrazki.elektroda.pl/5879373100_1588854839_thumb.jpg Test 4 - HP L1706. https://obrazki.elektroda.pl/7060437300_1588855274_thumb.jpg https://obrazki.elektroda.pl/2953685300_1588855274_thumb.jpg Tabliczka znamionowa użytego monitora: https://obrazki.elektroda.pl/6106436200_1588855202_thumb.jpg Test 5 - SyncMaster 740N. Ten monitor naprawiałem tutaj: https://www.elektroda.pl/rtvforum/topic3688191.html , wymieniłem w nim kondensatory elektrolityczne. Dzięki temu może dalej działać i brać udział w testach mojego shielda: https://obrazki.elektroda.pl/4246636900_1588855412_thumb.jpg https://obrazki.elektroda.pl/2066575900_1588855446_thumb.jpg Tabliczka znamionowa użytego monitora: https://obrazki.elektroda.pl/1258439900_1588855496_thumb.jpg Podsumowując, shield VGA przetestowałem z czterema monitorami i jednym telewizorem i w każdym przypadku obraz był poprawnie wyświetlany. Sygnał widziany jest przez wszystkie urządzenia jako 640x480, mimo iż w dokumentacje użytej biblioteki jest podana nieco inna rozdzielczość. Po prostu shield w sprytny sposób sobie z tym radzi zostawiając puste marginesy wokół obrazu (co zresztą widać na większości zdjęć z jego działania). Nie znalazłem ani jednego urządzenia przez którego sygnał VGA z mojego shielda nie był wspierany, a jego timingi (31.5kHz i 60Hz) są zgodne z tym, co jest w kodzie: https://obrazki.elektroda.pl/8567581200_1590314726_thumb.jpg Test generowanego sygnału PAL Generowanie czarno-białego obrazu PAL (oraz NTSC - też jest wspierany) może się przydać gdy chcemy obsłużyć jakiś starszy telewizor, pewnie jeszcze CRT, który nie posiada złącza VGA. Może to być podyktowane chociażby sentymentem - niektórzy lubią korzystać ze starszego sprzętu nawet dla samej satysfakcji korzystania z niego. Z tego powodu przetestowałem też jak shield radzi sobie z generowaniem sygnału PAL. Sygnał PAL ze shielda podłącza się za pomocą przewodu RCA: https://obrazki.elektroda.pl/9451123200_1590143186_thumb.jpg Wystarczy pojedynczy sygnał video, audio nie jest oferowane przez ten shield. Test PAL 1 - telewizor LG42LE4500: Podłączenie: https://obrazki.elektroda.pl/2258585600_1590146184_thumb.jpg https://obrazki.elektroda.pl/9368920000_1590146199_thumb.jpg https://obrazki.elektroda.pl/3942760900_1590146205_thumb.jpg Obraz na ekranie: https://obrazki.elektroda.pl/9427440500_1590146322_thumb.jpg Obraz generowany przez shield jest tutaj rzecz jasna czarno-biały; zworka od koloru (wybór R/G/B) jest tylko dla VGA. Test PAL 2 - stary telewizor CRT TX-21AT1P: Ten pierwszy test PAL z nowym, płaskim telewizorem LG42LE4500 poniekąd mija się z celem, bo użyty tam telewizor ma złącze VGA (i to nie jedno!) więc nic nie stoi na przeszkodzie, by go podłączyć normalnie. Z tego powodu też przetestowałem tryb PAL z telewizorem który jest o wiele bardziej z tym trybem związanym - ze starym, ciężkim telewizorem kineskopowym, dokładniej 'Panasonic Colour TV' model TX-21AT1P: https://obrazki.elektroda.pl/8911258000_1590161461_thumb.jpg Tabliczka znamionowa tego telewizora: https://obrazki.elektroda.pl/8935412200_1590161531_thumb.jpg https://obrazki.elektroda.pl/7483420000_1590161541_thumb.jpg Sygnał PAL podłączyłem z przodu telewizora do złącz RCA: https://obrazki.elektroda.pl/9148315700_1590161702_thumb.jpg Warto tu wspomnieć, że nawet jeśli telewizor nie ma złącz RCA to sygnał PAL można też podać poprzez SCART - w przewodzie SCART jest jeden pin odpowiedzialny za wejście 'composite video'. Sygnał PAL oczywiście tutaj nie jest modulowany i odbieramy go w trybie AV: https://obrazki.elektroda.pl/6474478000_1590161796_thumb.jpg (Aczkolwiek jakby się postarać i użyć np. modulatora ze starego odtwarzacza VHS to można by prosto odbierać sygnał ze shielda poprzez złącze antenowe; choć wtedy jest ryzyko, że będziemy 'siać' sygnał po okolicy; do tej pory pamiętam jak kilkanaście lub więcej lat temu odbierałem przez przypadek na telewizorze obraz z konsoli do gier sąsiada). Na telewizorze uruchomiłem demko, które opisane jest nieco niżej w tekście. Rezultat: https://obrazki.elektroda.pl/3756502800_1590162046_thumb.jpg Uwaga - co jeśli monitor pokazuje 'nieobsługiwany format VGA'? Chciałbym tu tylko jeszcze przypomnieć, że shield w celu obsługi zarówno standardu PAL jak i VGA musi w sprytny sposób między nimi przełączać. Nie generuje dwóch sygnałów naraz. Shield przy starcie (po sygnale RESET lub podaniu zasilania) sam określa, czy monitor VGA jest podłączony i jeśli nie, to przechodzi w tryb PAL. Określa to poprzez badanie stanu na pinie RA4. Więc jeśli podłączymy monitor VGA w trakcie do shielda to powinniśmy wcisnąć przycisk RESET na Arduino lub wyłączyć i włączyć zasilanie - w przeciwnym razie shield będzie już 'nastawiony na PAL' i monitor VGA nic nie wyświetli. RESET z Arduino jest połączony z RESET (aka MCLR) z PIC32 z płytki shielda, ale należy pamiętać by na płytce shielda dać tam zworkę by oba układy resetowały się razem. (O tym, że mam wspomnieć o tym problemie przypomniał mi Bartek, który odkupił ode mnie jedno PCB poprzedniej wersji shielda - za przetestowanie układu i zwrócenie uwagi tu dziękuję) Test magistrali I2C - skan dostępnych urządzeń Na samym początku na mojej płytce uruchomiłem prosty program całkowicie niezwiązany z VGA - skaner I2C. Program ten pochodzi z oficjalnej strony Arduino: https://playground.arduino.cc/Main/I2cScanner/ i po prostu wykrywa wszystkie urządzenia podłączone na magistralę I2C, czyli w przypadku użytego przeze mnie Arduino UNO na jego piny 4 i 5. Program drukuje adresy I2C znalezionych urządzeń na port szeregowy/terminal Arduino. #include <Wire.h> //include Wire.h library void setup() { Wire.begin(); // Wire communication begin Serial.begin(9600); // The baudrate of Serial monitor is set in 9600 while (!Serial); // Waiting for Serial Monitor Serial.println("\nI2C Scanner"); } void loop() { byte error, address; //variable for error and I2C address int nDevices; Serial.println("Scanning..."); nDevices = 0; for (address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) Serial.print("0"); Serial.print(address, HEX); Serial.println(" !"); nDevices++; } else if (error == 4) { Serial.print("Unknown error at address 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); // wait 5 seconds for the next I2C scan } Program skompilowałem i wgrałem na Arduino z założonym moim shieldem. Rezultat sketchu: https://obrazki.elektroda.pl/8259841700_1588789861_thumb.jpg Widzimy, że skan I2C znalazł aż 9 urządzeń! Ale czy na pewno? Na powyższym zrzucie ekranu: - adres 0x20 to port expander MCP23016 - adresy 0x50, 0x51, 0x52, 0x53 to pierwszy AT24CM02 (przypominam: w adresie urządzenia AT24CM02 dwa bity odpowiadają za jego 'wewnętrzny adres' pamięci; dwa bity dają nam 4 możliwe adresy) - adresy 0x54, 0x55, 0x56, 0x57 to drugi AT24CM02 Czyli wszystko jest okej - dwie pamięci EEPROM i jeden expander portów. Poniżej zaprezentuję już jak można ich użyć. Lutowanie przejściówki do expandera portów (dla płytki stykowej) Na płytce znajduje się 16-pinowe złącze 2.54mm (2 rzędy po 8 pinów) od MCP23016: https://obrazki.elektroda.pl/3189098400_1588846402_thumb.jpg Oferuje ono dodatkowe 16 pinów IO którymi możemy sterować przez I2C. Polecam używać go w połączeniu z takim przewodem (najlepiej też zakupić odpowiednie gniazdo pod ten przewód by nie dało się go wsadzić odwrotnie, ale ja takiego pod ręką nie miałem): https://obrazki.elektroda.pl/1013751400_1588799286_thumb.jpg https://obrazki.elektroda.pl/6126385500_1588799285_thumb.jpg Do tego sugeruję zlutować sobie przejściówkę z tego przewodu do płytki stykowej/prototypowej. Płytka stykowa ma rozstaw pól 2.54mm pasujący do goldpinów, za wyjątkiem środkowego przedziałka płytki który ma akurat 5.08mm, czyli tyle ile wąski rozstaw pinów z obudowy DIP. Aż prosi się to wykorzystać. Opiszę tutaj jak - na początek trzeba przygotować goldpiny 1x8 (dwie sztuki) i 2x8 (jedna sztuka) oraz płytkę wierconą: https://obrazki.elektroda.pl/8689529400_1588799587_thumb.jpg https://obrazki.elektroda.pl/3694746900_1588799587_thumb.jpg Następnie warto dodać topnik na płytkę, by się lepiej lutowało: https://obrazki.elektroda.pl/4560970200_1588799647_thumb.jpg Dalej umieścić na naszej płytce pierwszy rząd goldpinów (on będzie wchodzić w płytkę stykową), ale odwrotnie niż zazwyczaj: https://obrazki.elektroda.pl/3611174500_1588799703_thumb.jpg Teraz trzeba ją przylutować - najwygodniej bardzo cienką końcówką. Przy lutowaniu pilnować, by goldpiny były prostopadle do płytki: https://obrazki.elektroda.pl/8970239000_1588799870_thumb.jpg Pierwszy lut gotowy: https://obrazki.elektroda.pl/5596099700_1588799906_thumb.jpg Następnie trzeba dokończyć wszystkie luty i można od drugiej strony płytki dać złącze 2x8 2.54mm goldpin i zacząć łączyć odpowiednie piny: https://obrazki.elektroda.pl/1829572700_1588799984_thumb.jpg Dalej polecam delikatnie przylutować drugi pojedynczy rząd goldpinów który wejdzie w płytkę stykową: https://obrazki.elektroda.pl/6292228500_1588800085_thumb.jpg https://obrazki.elektroda.pl/3709848100_1588800087_thumb.jpg Następnie wystarczy zmostkować odpowiednie piny za pomocą lutownicy. Jeszcze tylko trzeba odciąć zbędny fragment PCB: https://obrazki.elektroda.pl/8851617300_1588800172_thumb.jpg Przejściówka gotowa: https://obrazki.elektroda.pl/5095248300_1588800212_thumb.jpg Idealnie pasuje do wybranego przeze mnie przewodu (choć ponownie tu Wam polecam użyć nie zwykłych goldpinów 2x8, lecz złącza pod ten konkretny kabelek; wtedy unikniecie ryzyka podłączenia go odwrotnie): https://obrazki.elektroda.pl/1732464200_1588800500_thumb.jpg Przejściówka expandera portów gotowa. Tak wygląda razem z Shieldem i Arduino: https://obrazki.elektroda.pl/3922228300_1588800544_thumb.jpg Następne cztery akapity przedstawią testy MCP23016 w oparciu o te połączenie. Test expandera portu - MCP23016 - użyta biblioteka Na początku do obsługi expandera portów MCP23016 sterowanego poprzez I2C próbowałem użyć tej biblioteki od Adafruit (mimo iż jest ona dla MCP23017 - liczyłem na podobieństwo układów): https://obrazki.elektroda.pl/9380545400_1588790023_thumb.jpg Ale nie zadziałała, więc ostatecznie użyłem biblioteki CyMCP23016 autorstwa Chris Brunner aka cyrusbuilt, stąd: https://github.com/cyrusbuilt/CyMCP23016 Kopia zapasowa repozytorium (na czas pisania tematu): 1030477 Bibliotekę dodałem do Arduino poprzez Sketch->Include Library->Add .ZIP Library. Test expandera portu - MCP23016 - układ testowy Do przygotowania układu testowego MCP23016 użyłem płytki stykowej i wcześniej opisanej przejściówki i kabelka, do tego kilka rezystorów: https://obrazki.elektroda.pl/7024523100_1588802931_thumb.jpg https://obrazki.elektroda.pl/2680579400_1588802952_thumb.jpg Do tego oczywiście trzeba osobno podpiąć masę (i zasilanie, jeśli go potrzebujemy) https://obrazki.elektroda.pl/4391025300_1588803055_thumb.jpg Test expandera portu - MCP23016 - blink LED Pierwszym przykładem użycia MCP23016 jaki przygotowałem jest proste miganie jedną diodą poprzez ten expander portów, poprzez I2C. Wybrałem pin GP0.0, w tej bibliotece oznaczany MCP23016_PIN_GPIO0_0. Kod prawie w całości pochodzi z przykładu biblioteki z Githuba: /** * Basic input/output test for MCP23016 expander. */ #include <Arduino.h> #include "CyMCP23016.h" CyMCP23016 mcp; void setup() { Serial.begin(9600); Serial.println(("start")); // Init MCP23016 at the default address. This assumes we are running on // an ATmel AVR-based arduino, like the Arduino Uno. mcp.begin(); Serial.println(("begin")); // Set Pin 0 on Port 0 as an output. mcp.pinMode(MCP23016_PIN_GPIO0_0, OUTPUT); Serial.println(("pm")); } void loop() { delay(1000); // Set the pin HIGH and read back the state. mcp.digitalWrite(MCP23016_PIN_GPIO0_0, HIGH); uint8_t val = mcp.digitalRead(MCP23016_PIN_GPIO0_0); Serial.print(F("Pin 0.0 is ")); Serial.println(val == HIGH ? "HIGH" : "LOW"); delay(1000); // Set the pin LOW and read back the state. mcp.digitalWrite(MCP23016_PIN_GPIO0_0, LOW); val = mcp.digitalRead(MCP23016_PIN_GPIO0_0); Serial.print(F("Pin 0.0 is ")); Serial.println(val == HIGH ? "HIGH" : "LOW"); } Kod tylko przełącza stan na pierwszym pinie expandera i dodatkowo wysyła jego stan na UART. Rezultat w Serial Monitor: https://obrazki.elektroda.pl/7086169000_1588791010_thumb.jpg Rezultat na filmie: https://filmy.elektroda.pl/92_1588794181.mp4 W taki sam sposób możemy sterować wszystkimi 16 pinami od MCP23016. Test expandera portu - MCP23016 - prosty licznik binarny Drugim, bardziej zaawansowanym przykładem użycia MCP23016 jaki tu zaprezentuję jest prosty licznik binarny zrealizowany na 8 diodach LED (czyli 8-bitowy). Do tego użyłem całego portu GP0: https://obrazki.elektroda.pl/6748085500_1588798418_thumb.jpg Odpowiednio zmodyfikowałem też kod przykładu. Do ustawienia odpowiednich diod LED użyłem pętli for oraz operatorów bitowych, całość sketchu jest do wglądu poniżej: /** * Basic input/output test for MCP23016 expander. */ #include <Arduino.h> #include "CyMCP23016.h" CyMCP23016 mcp; int counter = 0; void setup() { Serial.begin(9600); Serial.println(("start")); mcp.begin(); Serial.println(("begin")); // Set Pin 0 on Port 0 as an output. for(int i = 0; i < 8; i++) { mcp.pinMode(i, OUTPUT); mcp.digitalWrite(i, LOW); } delay(1000); Serial.println(("pm")); } void loop() { delay(250); for(int i = 0; i < 8; i++) { mcp.digitalWrite(i, counter & (1<<(i))); } counter++; } Powyższy kod powinien być zrozumiały nawet dla początkujących z Arduino, jedyną z pozoru trudną rzeczą w nim jest 'wyłuskanie' bitu za pomocą counter & (1<<(i)). Ta linijka po prostu sprawdza, czy i-ty bit zmiennej counter jest zapalony. Rezultat na filmie: https://filmy.elektroda.pl/15_1588793680.mp4 Wszystko działa poprawnie. Można by jeszcze sprawdzić czy wszystko jest okej z drugim portem od MCP23016, ale uznałem, że to już jest zbędne. Test pamięci EEPROM - AT24CM02 - użyta biblioteka Do obsługi pamięci EEPROM a dokładniej dwóch kości AT24CM02 użyłem biblioteki autorstwa Rushikesh Patel aka luffykesh z Githuba, tej: https://github.com/luffykesh/AT24Cx Kopia zapasowa/snapshot repo na czas pisania artykułu: 1030460 Bibliotekę dodałem do Arduino poprzez Sketch->Include Library->Add .ZIP Library. Test pamięci EEPROM - AT24CM02 - test zapisu i odczytu Na początek sprawdziłem czy w ogóle komunikacja z AT24CM02 działa poprawnie. W tym celu użyłem przykładowego kodu z wybranej biblioteki, jedynie tylko zmodyfikowałem go by korzystał z konkretnie AT24CM02 (z pierwszego na PCB, o adresie 0x50): /* * * Read and write demo of the AT24CX library * Written by Christian Paul, 2014-11-24 * * */ // include libraries #include <Wire.h> #include <AT24CX.h> // EEPROM object AT24CM02 mem; // setup void setup() { // serial init Serial.begin(9600); Serial.println("AT24CX read/write demo"); Serial.println("----------------------"); } // main loop void loop() { // read and write byte Serial.println("Write 42 to address 12"); mem.write(12, 42); Serial.println("Read byte from address 12 ..."); byte b = mem.read(12); Serial.print("... read: "); Serial.println(b, DEC); Serial.println(); // read and write integer Serial.println("Write 65000 to address 15"); mem.writeInt(15, 65000); Serial.println("Read integer from address 15 ..."); unsigned int i = mem.readInt(15); Serial.print("... read: "); Serial.println(i, DEC); Serial.println(); // read and write long Serial.println("Write 3293732729 to address 20"); mem.writeLong(20, 3293732729UL); Serial.println("Read long from address 20 ..."); unsigned long l = mem.readLong(20); Serial.print("... read: "); Serial.println(l, DEC); Serial.println(); // read and write long Serial.println("Write 1111111111 to address 31"); mem.writeLong(31, 1111111111); Serial.println("Read long from address 31 ..."); unsigned long l2 = mem.readLong(31); Serial.print("... read: "); Serial.println(l2, DEC); Serial.println(); // read and write float Serial.println("Write 3.14 to address 40"); mem.writeFloat(40, 3.14); Serial.println("Read float from address 40 ..."); float f = mem.readFloat(40); Serial.print("... read: "); Serial.println(f, DEC); Serial.println(); // read and write double Serial.println("Write 3.14159265359 to address 50"); mem.writeDouble(50, 3.14159265359); Serial.println("Read double from address 50 ..."); double d = mem.readDouble(50); Serial.print("... read: "); Serial.println(d, DEC); Serial.println(); // read and write char Serial.print("Write chars: '"); char msg = "This is a message"; Serial.print(msg); Serial.println("' to address 200"); mem.writeChars(200, msg, sizeof(msg)); Serial.println("Read chars from address 200 ..."); char msg2; mem.readChars(200, msg2, sizeof(msg2)); Serial.print("... read: '"); Serial.print(msg2); Serial.println("'"); Serial.println(); // write array of bytes Serial.println("Write array of 80 bytes at address 1000"); byte xy = {0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,8,8,8,9,9,9, // 10 x 3 = 30 10,11,12,13,14,15,16,17,18,19, // 10 120,121,122,123,124,125,126,127,128,129, // 10 130,131,132,133,134,135,136,137,138,139, // 10 200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219}; // 20 mem.write(1000, (byte*)xy, sizeof(xy)); // read bytes with multiple steps Serial.println("Read 80 single bytes starting at address 1000"); for (int i=0; i<sizeof(xy); i++) { byte sb = mem.read(1000+i); Serial.print(" = "); Serial.println(sb); } Serial.println(); // read bytes with one step Serial.println("Read 80 bytes with one operation at address 1000"); byte z; memset(&z, 32, sizeof(z)); mem.read(1000, z, sizeof(z)); for (int i=0; i<sizeof(z); i++) { Serial.print(" = "); Serial.println(z); } // stop while (1==1) {delay(1000);} } https://obrazki.elektroda.pl/7951808100_1588847183_thumb.jpg Następnie zmodyfikowałem ten kod tak, by przetestować użycie dwóch EEPROM jednocześnie. W celu weryfikacji czy na pewno kod korzysta z dwóch różnych kości postanowiłem zapisać na ten sam adres różne wartości (dla pierwszej kości: 42, dla drugiej: 15) i potem je odczytać i sprawdzić czy są zachowywane. /* * * Read and write demo of the AT24CX library * Written by Christian Paul, 2014-11-24 * * */ // include libraries #include <Wire.h> #include <AT24CX.h> // EEPROM object AT24CM02 mem0(0); AT24CM02 mem1(1); // setup void setup() { // serial init Serial.begin(9600); Serial.println("AT24CX read/write demo"); Serial.println("----------------------"); } // main loop void loop() { // read and write byte Serial.println(" Write 42 to address 12"); mem0.write(12, 42); Serial.println(" Write 15 to address 12"); mem1.write(12, 15); Serial.println(" Read byte from address 12 ..."); byte b0 = mem0.read(12); Serial.print(" ... read: "); Serial.println(b0, DEC); Serial.println(" Read byte from address 12 ..."); byte b1 = mem1.read(12); Serial.print(" ... read: "); Serial.println(b1, DEC); // stop while (1==1) {delay(1000);} } Rezultat sketchu: https://obrazki.elektroda.pl/9200007400_1588847521_thumb.jpg Jak widać obie pamięci EEPROM działają poprawnie. W ten sposób zyskujemy całe dodatkowe 512KB (dwie kości po 256KB) pamięci! Małe demko - drukowanie kodów klawiszy na ekranie Teraz przedstawię proste demko już bezpośrednio związane z głównymi atutami mojej nakładki, czyli generowaniem obrazu VGA i obsługą klawiatury PS/2. Poniższe demko odbiera klawisze wciśnięte na klawiaturze i wyświetla ich kody na ekranie - za wyjątkiem strzałek, które są kodowane jako kilka kolejnych wartości i trzeba je w specjalny sposób wykrywać. Każda wciśnięcie jednej ze strzałek daje na UART następujące kody: * Up Arrow - 27 91 65 * Down Arrow - 27 91 66 * Right Arrow 27 91 67 * Left Arrow 27 91 68 Strzałki w poniższym demku są wykrywane poprawne i informacje o ich wciśnięciu są wyświetlane jako tekst. UWAGA: Poniższe demko poprawnie obsługuje Caps Lock i wpisywanie znaków z Shift, ale tego nie ma w kodzie na Arduino, gdyż dzieje się to w samym shieldzie i na klawiaturze. Pełny kod demka: /* * Up Arrow - 27 91 65 * Down Arrow - 27 91 66 * Right Arrow 27 91 67 * Left Arrow 27 91 68 * */ #if 0 #define USE_ARDUINO_RXTX #endif #ifdef USE_ARDUINO_RXTX #define vgaSerial Serial #else #include <SoftwareSerial.h> SoftwareSerial mySerial(2, 3); // RX, TX #define vgaSerial mySerial #endif void SCR_Reset() { vgaSerial.print((char)27); vgaSerial.print("c"); } void SCR_PrintLn(const char *s) { vgaSerial.println(s); } void SCR_Print(const char *s) { vgaSerial.print(s); } void SCR_ClearScreen() { vgaSerial.print((char)27); vgaSerial.print("[2J"); } void SCR_SetPos(int x, int y) { vgaSerial.print((char)27); vgaSerial.print("["); vgaSerial.print(x); vgaSerial.print(";"); vgaSerial.print(y); vgaSerial.print("H"); } void setup() { // Open serial communications and wait for port to open: Serial.begin(57600); while (!Serial) { ; // wait for serial port to connect. Needed for Native USB only } Serial.println("started vga"); #ifndef USE_ARDUINO_RXTX //mySerial.begin(1200); // mySerial.begin(57600); // mySerial.begin(38400); mySerial.begin(19200); //mySerial.begin(115200); mySerial.listen(); #endif delay(10); SCR_Reset(); SCR_ClearScreen(); // put your setup code here, to run once: SCR_PrintLn("Arduino VGA Shield Key Code Tester"); //SCR_SetPos(5,5); //SCR_Print("A"); } char buffer ; //whatever void OnPress_ArrowDown() { sprintf (buffer, "Pressed DOWN ARROW"); SCR_PrintLn(buffer); } void OnPress_ArrowRight() { sprintf (buffer, "Pressed RIGHT ARROW"); SCR_PrintLn(buffer); } void OnPress_ArrowLeft() { sprintf (buffer, "Pressed LEFT ARROW"); SCR_PrintLn(buffer); } void OnPress_ArrowUp() { sprintf (buffer, "Pressed UP ARROW"); SCR_PrintLn(buffer); } void OnPress_Key(char inByte) { sprintf (buffer, "Pressed code %i (visual %c)", (int)inByte, inByte); SCR_PrintLn(buffer); } void loop() { // put your main code here, to run repeatedly: while (vgaSerial.available() > 0) { char inByte = vgaSerial.read(); if(inByte == 27) { while (vgaSerial.available() == 0) { } vgaSerial.read(); while (vgaSerial.available() == 0) { } char inByte2 = vgaSerial.read(); if(inByte2 == 65) { OnPress_ArrowUp(); } if(inByte2 == 66) { OnPress_ArrowDown(); } if(inByte2 == 68) { OnPress_ArrowLeft(); } if(inByte2 == 67) { OnPress_ArrowRight(); } } else { OnPress_Key(inByte); } } } Rezultat działania: https://obrazki.elektroda.pl/9093480600_1589293381_thumb.jpg Shift i Caps-Lock są poprawnie obsługiwane, a same klawisze są bezbłędnie odbierane (za wyjątkiem pewnej sytuacji - ale o tym w następnym akapicie). Dlaczego lepiej korzystać z baud 19200 a nie np. z 1200? Tutaj chciałbym zwrócić uwagę na to jak duże znaczenie ma świadomy wybór odpowiedniego baud komunikacji UART ze shieldem. Nie można wybrać zbyt dużego baud, bo ogranicza nas zdolność SoftwareSerial z Arduino. Przy około 38400 mogą się zacząć problemy. To raczej wiadomo, podobne informacje powtarzają się na forum Arduino: https://forum.arduino.cc/index.php?topic=528912.0 Ale nie można też wybrać zbyt małego baud, bo wtedy będziemy gubić znaki lub co gorsza otrzymywać błędne dane. Łatwo to pokazać uruchamiając demo 'test klawiszy' i wciskając (trzymając cały czas) klawisz strzałki, który wysyłany jest przez UART jako trzy znaki. Zdjęcie demka 'odbiór klawiszy' przy baud 1200: https://obrazki.elektroda.pl/9787153500_1589293837_thumb.jpg Zdjęcie demka 'odbiór klawiszy' przy baud 19200: https://obrazki.elektroda.pl/2876768200_1589293876_thumb.jpg Widzicie różnicę? Wniosek: nieodpowiednio dobrany baud może naprawdę namieszać, również gdy wybierzemy jego zbyt małą wartość. Na zdjęciu widać, że raz nawet wysłała się wartość '109' która odpowiada klawiszowi 'm', co z pewnością mogłoby mieć niepożądany skutek jeśli byśmy w naszym programie korzystali zarówno ze strzałek jak i z tego klawisza. Zaawansowane edytor tekstu Teraz zaprezentuję nieco bardziej zaawansowane demko oparte o mojego shielda, a mianowicie prosty edytor tekstu. Edytor tekstu będzie oferować: - obsługa klawisza Shift - obsługa klawisza Caps Lock - obsługa klawisza Delete (usuwanie znaku po kursorze) - obsługa klawisza Backspace (usuwanie znaku przed kursorem) - obsługa klawisza Enter (dodawanie nowej linii) Część tej funkcjonalności jest po stronie Arduino, a część na shieldzie. Rzeczy takie jak poruszanie kursorem, obsługa bufora tekstu, dodawanie znaków, usuwanie ich itp. są po stronie Arduino. Zdjęcia z działania dema-edytora tekstu na monitorze VGA i klawiaturze PS2: https://obrazki.elektroda.pl/5248090800_1589747497_thumb.jpg https://obrazki.elektroda.pl/5745421400_1589747497_thumb.jpg https://obrazki.elektroda.pl/5153392700_1589747496_thumb.jpg https://obrazki.elektroda.pl/5951234900_1589747502_thumb.jpg https://obrazki.elektroda.pl/5538928900_1589747504_thumb.jpg Edytor tekstu można by znacznie ulepszyć, ograniczyć bardziej zbędne odświeżanie pełnego ekranu lub po prostu przenieść w większej części na PICa. Skecz Arduino do pobrania: 1030452 Test edytora tekstu na starym telewizorze CRT TX-21AT1P W ramach uzupełnienia poprzedniego akapitu przedstawiam tu filmik z działania edytora tekstu w trybie PAL na telewizorze kineskopowym: https://filmy.elektroda.pl/13_1590143145.mp4 Zbędne 'pełne odświeżenie' ekranu w trakcie dodawania nowej linii do tekstu można by usunąć w kodzie programu, ale nie to było celem tego demka. Zaawansowane demko - system plików na EEPROM Przedstawione tutaj demko opracowałem w celu dokładniejszego przetestowania obu pamięci EEPROM znajdujących się na płytce (te pamięci pozwalają nam zapisać dane które nie utracą się nawet po całkowitym odłączeniu zasilania od Arduino). Demko to stanowi podstawową wersję systemu plików, który wspiera podstawowe operacje na plikach i katalogach (tworzenie ich, usuwanie, dodawanie danych, przeglądanie, czyszczenie), pozwala też zagnieżdżać katalogi/pliki w innych katalogach tworząc strukturę drzewa. System oczywiście też jest na różne sposoby ograniczony - brakuje dynamicznej alokacji pamięci dla danych pliku, choć jest ona dostępna dla samych katalogów i plików (używane są ponownie zwolnione sloty). Demko obsługuje się poprzez Serial Monitor od Arduino który pełni dla niego funkcję linii komend i wspiera następujące polecenia: - dirls - wyświetla zawartość bieżącego katalogu - mkdir - tworzy katalog o danej nazwie - rmdir - usuwa katalog z zawartością - resetfs - resetuje EEPROMy do pustego systemu plików - resetfile - resetuje plik do stanu pustego (tj. ustawia jego długość na zero) - rmfile - usuwa plik - cd - przechodzi do danego folderu - mkfile - tworzy pusty plik o danej nazwie - append - dodaje do danego pliku dany ciąg znaków - show - pokazuje dany plik w konsoli Poniżej umieszczam zrzuty ekranu z testów powyższego systemu plików; myślę, że ich zawartość nie wymaga komentarza. Testy komend mkdir, ls, mkfile, append, ls: https://obrazki.elektroda.pl/9208051700_1589744617_thumb.jpg Testy komend: cd, mkdir, mkfile: https://obrazki.elektroda.pl/5551405800_1589744899_thumb.jpg Testy komend: rmdir, rmfile, resetfs: https://obrazki.elektroda.pl/7737634500_1589744985_thumb.jpg Testy komend: show, mkfile, append: https://obrazki.elektroda.pl/9026338300_1589745050_thumb.jpg Demko do pobrania: 1030449 Kompilacja wsadu z kodu źródłowego Jeśli tylko chcecie złożyć i używać tego shielda z Arduino to nie musicie samodzielnie kompilować kodu na PICa, potrzebny dla niego .hex załączam tutaj: 1030451 Ale jeśli ktoś chce zmodyfikować samo działanie shielda, to kod można łatwo skompilować w środowisku MPLAB X IDE v5.20: https://obrazki.elektroda.pl/1438991700_1590314916_thumb.jpg Przy użyciu kompilatora XC32 (v2.20): https://obrazki.elektroda.pl/6079210300_1590315087_thumb.jpg Dodatkowo do kompilacji wymagana jest biblioteka PIC32 Peripheral Library (słynne plib.h wraz z innymi nagłówkami), którą ściąga się osobno ze strony Microchipa: https://www.microchip.com/SWLibraryWeb/product.aspx?product=PIC32%20Peripheral%20Library Gdy już ją zainstalujemy, to wszystko poprawnie się kompiluje: https://obrazki.elektroda.pl/2965134100_1590410028_thumb.jpg Kompilować wsad można w dwóch trybach: - tryb dla bootloadera (do wgrania wsadu przez USB za pomocą PIC32UBL.exe) - wtedy w projekcie załączamy 32MX250F128B.ld - tryb bez bootloadera (do wgrania wsadu przez ICSP) - wtedy w projekcie wykluczamy plik 32MX250F128B.ld Więcej informacji na ten temat znajdziecie w oryginalnej paczce z kodem źródłowym: 1030459 Dalszy rozwój projektu - dalsza minimalizacja? Obecna wersja pozostawia jeszcze pole do popisu i możliwość ulepszenia, m.in. dlatego że np. użyty PIC32 jest w wersji PIC32MX250F128B-50I/SO, czyli obudowa SOIC: https://obrazki.elektroda.pl/7523413500_1588796498_thumb.jpg A ten sam mikrokontroler jest jeszcze dostępny w obudowach SSOP (PIC32MX250F128B-50I/SS): https://obrazki.elektroda.pl/9127079900_1588796563_thumb.jpg ... oraz w QFN (PIC32MX250F128B-50I/ML): https://obrazki.elektroda.pl/2453157800_1588796599_thumb.jpg Wszystkie obudowy naszego PICa (kolejno: QFN, SSOP, SOIC i SPDIP) umieściłem na obrazku poniżej: https://obrazki.elektroda.pl/2428852500_1588857711_thumb.jpg Dalszy rozwój projektu - co można by lepiej zrobić? Projekt (i też jego wykonanie - luty) nie jest idealny i dużo można by jeszcze ulepszyć. - można by dać slot na kartę microSD na płytce (podłączony do SPI od Arduino) - można by (jak wspomniałem wyżej) użyć mniejszych obudów elementów i zyskać w ten sposób miejsce na coś więcej na płytce - można by zastąpić MCP23016 (wymagający zewnętrznego rezystora i kondensatora na pinie CLK) poprzez nieco lepszy MCP23017 (który nie wymaga tych dwóch elementów) - można by też dać tam mocniejszego PICa i/lub iść w stronę kolorowego VGA - umieszczone tu przykłady możny by ulepszyć i zoptymalizować, lecz nie to było ich celem (to tylko demonstracja możliwości shielda, nie miała być wydajna) Możliwe zatem, że za jakiś czas zrobię trzecią wersję shielda. Tabela załączników Dla wygody czytelników umieszczam tutaj raz jeszcze odnośniki do załączników które pojawiły się w temacie: NazwaOpisZałącznik Źródła Eagle projektuPliki .sch i .brd które można edytować w Eagle. Użyjcie tego, jeśli chcecie zrobić własną, zmodyfikowaną wersję tego shielda. 1030455 Pliki Gerber projektuTe pliki wysyłamy płytkarni jeśli chcemy by nam wyprodukowali PCB pod ten projekt. Nie polecam ich edytować. 1030453 Arduino skecz - demo edytora tekstuPełny kod edytora tekstu zrealizowanego na tym shieldzie dla Arduino w formacie .ino. Wymaga podłączonej klawiatury i monitora/telewizora do działania. 1030452 Arduino skecz - demo klawiszyPełny kod testera klawiszy PS/2 z obsługą strzałek zrealizowanego na tym shieldzie dla Arduino w formacie .ino. Wymaga podłączonej klawiatury i monitora/telewizora do działania. 1030450 Arduino skecz - demo system plików na EEPROMPełny kod prostego systemu plików na dwóch kościach EEPROM na magistrali I2C Arduino wraz z linią komend na UART. Ten projekt nie korzysta wcale z VGA i nie korzysta z klawiatury PS/2. Kod w formacie .ino 1030449 Skompilowane firmware dla PICa z ShieldaNiezbędny wsad do jednorazowego wgrania na PIC32MX250F128B znajdującego się na pokładzie shielda. Można go wgrać np. za pomocą PICKIT3. 1030451 Kod źródłowy shieldaKod C firmware shielda na PIC32MX250F128B dla MPLAB. Projekt autorstwa geoffg. 1030459 Datasheet AT24CM02Dla dociekliwych - nota katalogowa użytej kości pamięci 1030458 Datasheet MCP23016Dla dociekliwych - nota katalogowa użytego expandera portów 1030454 Biblioteka AT24CXBiblioteka AT24CX z Githuba - kopia zapasowa, wersja ta którą przetestowałem i wykorzystałem do tego projektu. Tę paczkę .ZIP można łatwo dodać do Arduino poprzez 'Add .ZIP Library...'. 1030460 Biblioteka CyMCP23016Biblioteka CyMCP23016 z Githuba - kopia zapasowa, wersja ta którą przetestowałem i wykorzystałem do tego projektu. Tę paczkę .ZIP można łatwo dodać do Arduino poprzez 'Add .ZIP Library...'. 1030477 Podsumowanie Jestem bardzo zadowolony z drugiej wersji mojego shielda. Przejście z montażu przewlekanego na powierzchniowy pozwoliło mi zmieścić na nim nieco więcej niż w pierwszej wersji. Dzięki oparciu o Arduino UNO całość jest wygodna i prosta w użyciu nawet dla początkujących. Umieszczone dodatkowo na płytce układy (MCP23016 i AT24CM02) też posiadają gotowe biblioteki dla Arduino dostępne w sieci, co sprawia, że obsługa mojego shielda sprowadza się do składania programów niemalże jak z klocków, a użycie klawiatury PS/2 oraz możliwość wyświetlania jednokolorowego obrazu VGA oraz czarno-białego obrazu PAL/NTSC nadaje całości nieco 'oldschoolowy' klimat.
%
M48
M72
T01C0.02362
T02C0.03346
T03C0.03543
T04C0.04000
T05C0.05500
T06C0.08700
T07C0.09055
T08C0.11811
T09C0.12598
%
T01
X118619Y187128
X135619Y182628
X142619Y162628
X124119Y154628
X118319Y163628
X122119Y140628
X100619Y134128
X89119Y121628
X88119Y112628
X77619Y112628
X87619Y96128
X102619Y104128
X112669Y111178
X105619Y115128
X122619Y118928
X133019Y117828
X135619Y106128
X140619Y107128
X143119Y95128
X127119Y95128
X107719Y86228
X108319Y80228
X141619Y78628
X152619Y81128
X167119Y80628
X168119Y96828
X171119Y103128
X162619Y103628
X158119Y109128
X153119Y117628
X148119Y117628
X166819Y117828
X170119Y111628
X183119Y114128
X191619Y112628
X191619Y118128
X191619Y123128
X204119Y120128
X204119Y113128
X210619Y113128
X214619Y105128
X215119Y99128
X221619Y99128
X228619Y111628
X236619Y112128
X241619Y116128
X246619Y119628
X251719Y114628
X256319Y117228
X261619Y115128
X271119Y121128
X281619Y120128
X299619Y112628
X281619Y137628
X281119Y146128
X277119Y153628
X287619Y158628
X232619Y118628
X223619Y120128
X196619Y105628
X196619Y99128
X228619Y82628
X235619Y78628
X235619Y73628
X246619Y65628
X253619Y62128
X245619Y52128
X239619Y46628
X234119Y46628
X230619Y56628
X217619Y59128
X215119Y69628
X219619Y75128
X249619Y80628
X268119Y68128
X267119Y60928
X291619Y47128
X285619Y34128
X273619Y36628
X272119Y44128
X247119Y30628
X218619Y36628
X219119Y42128
X190119Y39628
X190119Y45128
X189119Y58628
X189119Y65128
X179119Y64628
X179119Y58628
X174119Y29628
X181119Y27628
X115119Y44128
X113119Y52628
X125619Y54128
X167619Y133128
X183619Y140128
X166619Y145128
X150119Y140128
X296619Y88128
T02
X113119Y204628
X123119Y204628
X133119Y204628
X143119Y204628
X153119Y204628
X163119Y204628
X173119Y204628
X183119Y204628
X193119Y204628
X203119Y204628
X219119Y204628
X229119Y204628
X239119Y204628
X249119Y204628
X259119Y204628
X269119Y204628
X279119Y204628
X289119Y204628
X289119Y14628
X279119Y14628
X269119Y14628
X259119Y14628
X249119Y14628
X239119Y14628
T03
X80619Y167085
X70619Y162585
X80619Y158085
X70619Y153585
X80619Y149085
X70619Y144585
X80619Y140085
X70619Y135585
X80619Y131085
X70619Y126585
X60619Y131085
X60619Y140085
X60619Y149085
X60619Y158085
X60619Y167085
X117957Y24628
X135280Y24628
X276013Y86743
X285855Y86743
X285855Y95010
X285855Y105246
X285855Y113514
X276209Y113514
T04
X149119Y172628
X159119Y172628
X169119Y172628
X179119Y172628
X195619Y154128
X205619Y154128
X215619Y154128
X225619Y154128
X235619Y154128
X245619Y154128
X255619Y154128
X265619Y154128
X265619Y144128
X255619Y144128
X245619Y144128
X235619Y144128
X225619Y144128
X215619Y144128
X205619Y144128
X195619Y144128
X134519Y136928
X94119Y68128
X104119Y68128
X114119Y68128
X124119Y68128
X134119Y68128
X154119Y51128
X164119Y51128
X174119Y51128
X174119Y41128
X164119Y41128
X154119Y41128
X159119Y14628
X169119Y14628
X179119Y14628
X189119Y14628
X199119Y14628
X209119Y14628
X219119Y14628
X206119Y76828
X196119Y76828
T05
X78119Y57128
T06
X78119Y79128
X48119Y57128
X78119Y35128
T07
X297666Y73357
X300816Y100128
X297666Y126703
T08
X70619Y197341
X70619Y98916
T09
X99119Y204628
X94119Y14628
X299119Y34628
X299119Y144628
M30