sobota, 27 sierpnia 2016

FreeCAD: TechDraw (rzuty 2D), elementy bazowe PartDesign

Ta krótka notka ma za zadanie przedstawić kilka ciekawych zmian i nowych funkcji programu FreeCAD.

Elementy bazowe i ciało (Body) modułu PartDesign

Wcześniej każdy nowy szkic musiał być umieszczony na geometrii istniejącej części. Stawało się to często bardzo niewygodne, wystarczy sobie wyobrazić dodanie segmentu do wałka przez obrót - na jednej z podstaw trzeba było wyciągnąć najpierw jakiś prostopadłościan, a dopiero potem narysować na nim docelowy szkic.

Niedawno został wprowadzony kontener Body, który likwiduje ten problem. Szkice mogą być umieszczane na dowolnych powierzchniach płaskich, a operacje z nich powstałe oddziałują na całą geometrię wewnątrz jednego kontenera.


Zadanie jeszcze bardziej ułatwiają elementy bazowe: płaszczyzny, osie i punkty. Więcej o budowie nowego PartDesign można przeczytaj na forum tutaj.

TechDraw - rzuty 2D

Wcześniej mieliśmy oficjalny moduł dokumentacji 2D tworzonej z modeli 3D nazwany Drawing i nieoficjalny Drawing Dimensioning. Pierwszy z nich nie umożliwiał łatwego wymiarowania ani tworzenia przekrojów, drugi dawał sobie radę z wymiarami, ale nie były one aktualizowane po zmianie kształtu części.

Nowy TechDraw rozwiązuje wszystkie te problemy. Wątek na forum.

 
Jak można zauważyć na powyższym zrzucie, jeszcze nie wszystko da się ustawić tak aby odpowiadało to zasadom rysunku technicznego. No, ale minęło dopiero półtora miesiąca od pierwszego wydania TechDraw - jest on cały czas dopracowywany. 

Nowości w module MES (FEM)

Moduł wspomagający obliczenia metodą elementów skończonych jest tak szybko i tak szeroko rozbudowywany, że trudno byłoby streści wszystkie zmiany.

Na pewno warto zwrócić uwagę na symulacje termomechaniczne opisane w tym wątku na forum.
 

Inną ciekawostką jest zaawansowany postprocesing z wykorzystaniem VTK - więcej tutaj.

Instalacja freecad-daily na Ubuntu

Opisane powyżej nowości (prócz postprocesingu VTK,który wymaga kompilacji ze źródeł) można znaleźć w prekompilowanych paczkach freecad-daily. 

Tematy warte uwagi w przyszłości

"Robust References" Redux - rozwiązanie problemu topologicznego nazewnictwa. Do tej pory było tak, że program do utrzymywania referencji numerował geometrię danego typu nie przejmując się jej położeniem czy znaczeniem. Przykład: płytka z kilkoma otworami, w jednym z nich robimy fazę. Potem zmniejszamy liczbę otworów, co się dzieje z fazą? Pojawia się na jednej z pozostałych krawędzi części, niekoniecznie w którymś z otworów. Ten problem powinien już niedługo być przeszłością.


update on FreeCAD + OpenFOAM fluid dynamic computation - pierwsze kroki w kierunku integracji solwera OpenFOAM i symulacji przepływu płynów.

piątek, 10 czerwca 2016

Otwarta fotogrametria: openMVG i openMVS

Fotogrametria pozwala na przeniesienie kształtu rzeczywistego do wirtualnego środowiska 3D za sprawą zaledwie kilku zdjęć i mocy obliczeniowej, której jest pod dostatkiem w w twoim czy moim domu. Specjalnie odniosłem się do naszych komputerów, bo chcę pokazać, że nie trzeba wykorzystywać oprogramowania w stylu Autodesk 123D Catch i obliczeń "w chmurze".

Nie potrzebny będzie też żaden drogi sprzęt, ani nawet wysokiej klasy aparat cyfrowy. Zdjęcia na potrzeby wygenerowania modelu rękawiczki, który powinieneś widzieć poniżej, zostały zrobione tym antykiem. Kosztował on mnie  450 zł jakieś 12 lat temu. Telefon, który masz w kieszeni, pewnie robi znacznie lepsze zdjęcia.


Uwaga: poniższy tekst nie jest tutorialem, a raczej luźnymi notatkami, które mają tak samo służyć mi za kilka miesięcy lub lat, jak i innym czytelnikom. Może przestawiać nieoptymalny przebieg procesu.

Jeśli nie widzisz obracalnego modelu 3D (bo przeglądarka nie obsługuje WebGL lub serwis ShareMy3D padł), to poniżej znajduje się statyczny zrzut ekranu.


Oprogramowanie - kompilacja

Opis bazuje na założeniu, że użytkownik korzysta z jakiejś współczesnej dystrybucji Linuksa. W moim przypadku jest to Kubuntu 16.04 64-bit.

Utworzyłem jeden katalog dla całego oprogramowania do fotogrametrii. W tym katalogu skompilowałem dwa pakiety:
  1. openMVG, zgodnie z oficjalnymi instrukcjami,
  2. openMVS, zgodnie z oficjalnymi instrukcjami,
  3. dodatkowo zainstalowałem MeshLab - wygodną przeglądarkę i edytor chmur punktów oraz modeli 3D, znajdziesz go w swoim ulubionym menedżerze pakietów.
O ile openMVG nie powinno sprawić problemów, to openMVS może zrobić niemiłą niespodziankę.
Pierwszym problemem w Ubuntu 16.04 jest niestabilna wersja (3.3-beta) pakietu libeigen-dev. Obszedłem ten problem pożyczając od Debiana paczkę deb w wersji 3.2.
Drugim okazało się vcglib i błąd związany z przestrzenią nazw:
error: ‘isinf’ was not declared in this scope
error: ‘isnan’ was not declared in this scope
Wystarczyło odszukać plik vcglib/vcg/math/base.h i zamienić
template int IsNAN(T t) { return isnan(t) || isinf(t); }
na
template int IsNAN(T t) { return std::isnan(t) || std::isinf(t); }

Ogólny model postępowania

Pakiet oprogramowania openMVG będzie karmiony wykonanymi przez nas zdjęciami, próbując znaleźć położenie punktów składających się na obiekt i stworzyć rzadką chmurę. Etap ten opisany jest w dokumentacji SfM: Structure from Motion. Chmura punktów będzie podstawą do pracy openMVS, które kolejno zagęści chmurę, utworzy siatkę wielokątów i w końcu oteksturuje ją.

Na początek potrzebujemy kilku zdjęć przy dobrym oświetleniu, bez użycia flesza, najlepiej z niezbyt dużym przesunięciem między nimi.

Sprytnym trikiem jest podłożenie pod obiekt czegoś o charakterystycznym wzorze, jak gazeta, czy (w moim przypadku) mapa. To znacznie poprawia skuteczność algorytmów SfM. Problematyczne okazują się za to obiekty o znacznych powierzchniach w jednolitym kolorze, odbijających światło lub częściowo przezroczystych.

Proces krok po kroku

Zakładam że openMVS i openMVG zostały skompilowane w wspólnym katalogu:
ls -l
razem 28
drwxrwxr-x  7 adi adi 4096 cze 10 20:49 ceres_build
drwxrwxr-x 12 adi adi 4096 cze  9 18:54 ceres-solver
drwxrwxr-x  6 adi adi 4096 cze  9 19:44 openMVG
drwxrwxr-x 12 adi adi 4096 cze  9 21:07 openMVG_Build
drwxrwxr-x  8 adi adi 4096 cze  9 18:42 openMVS
drwxrwxr-x  8 adi adi 4096 cze  9 19:41 openMVS_build
drwxrwxr-x  9 adi adi 4096 cze  9 18:34 vcglib
Wykonałem 10 zdjęć rękawiczki położonej na mapie Polski i wrzuciłem je (niezbyt elegancko) do katalogu openMVG_Build/software/SfM/lapka_in

Kolejny krok to uruchomienie skryptu Pythona:
python SfM_GlobalPipeline.py lapka_in lapka_global_out
I już tutaj pojawił się pierwszy zgrzyt. OpenMVG poinformowało o braku modelu aparatu cyfrowego w bazie:
Warning & Error messages:
IM000217" model "Photosmart M407" doesn't exist in the database
Wystarczyło otworzyć plik openMVG/src/openMVG/exif/sensor_width_database/sensor_width_camera_database.txt i dodać odpowiednią pozycję.

W konsoli powinniśmy zobaczyć m.in.:
Structure estimation took (s): 1.861.
#landmark found: 12600
...Generating SfM_Report.html
Found a sfm_data scene with:
 #views: 10
 #poses: 10
 #intrinsics: 1
 #tracks: 12600
10 zdjęć i 10 rozpoznanych pozycji, to dobry znak. Otwierając chmurę punktów lapka_global_out/reconstruction_global/colorized.ply w programie MeshLab zobaczymy coś takiego:

To chmura rzadka, stąd nie przypomina jeszcze naszego fotografowanego obiektu, ale warto zwrócić uwagę na zielone punkty. To pozycje aparatu podczas robienia zdjęć.

Musimy wyeksportować wyniki z openMVG do formatu przyjaznego openMVS (dokumentacja):
../../Linux-x86_64-RELEASE/openMVG_main_openMVG2openMVS -i \
lapka_global_out/reconstruction_global/sfm_data.bin -d lapka_out -o lapka_out/scene.mvs
Teraz, już używając openMVS, zagęszczamy chmurę:
../../../openMVS_build/bin/DensifyPointCloud lapka_out/scene.mvs
Tak wygląda chmura gęsta otwarta w MeshLab (plik lapka_out/scene_dense.ply):

Pozostało tylko wygenerować siatkę wielokątów i oteksturować ją:
../../../openMVS_build/bin/ReconstructMesh lapka_out/scene_dense.mvs
../../../openMVS_build/bin/TextureMesh lapka_out/scene_dense_mesh.mvs
Efekt końcowy powinien być taki jak na początku wpisu.

 

Q&A:

Q: Ile trwa cały proces?
A: Kilkanaście sekund do kilku minut, dla zadań jak w powyższym opisie.

Q: Nie masz ładniejszego przykładu niż zmiętoszona rękawiczka?
A: 30-letni misiu może być?

 
Q: W czym jest to lepsze od popularnych rozwiązań "w chmurze"?
A: Nie ma ograniczenia co do liczby zdjęć (np. Autodesk 123D Catch pozwala tylko na 70 zdjęć), nie trzeba czekać na wysłanie zdjęć na serwer, łatwiej zdiagnozować problem w razie niepowodzenie i nie trzeba zaptaszać żadnych cyrografów podczas instalacji. Bardziej zaawansowani użytkownicy mogą usprawnić proces pisząc skrypty czy przyjazne GUI (pewnie już ktoś to zrobił).

Q: Czy opisywane oprogramowanie działa na Windows?
A: Oczywiście, że tak.

Q: Coś jeszcze?
A: Obracaj modelem rękawiczki lub misia od patrząc na niego od wewnątrz a ulegniesz złudzeniu optycznemu.

środa, 14 stycznia 2015

Port równoległy jako szybki rejestrator stanów logicznych

Trochę starego, pozornie niepotrzebnego sprzętu, Linux i (odrobinka) wiedzy z programowania może się przydać. Opiszę tutaj moją przygodę z LPT, czyli portem równoległym używanych w przeszłości głównie do łączenia z drukarkami. Chciałem tylko podejrzeć co się dzieje na wyjściach moich poczciwych Atmeg (mikrokontrolerów), kupno oscyloskopu wydawało się nadmierną rozrzutnością a wykorzystanie układów rejestrujących na USB nie miało w sobie znamion przygody...

Cel

Postawiłem sobie za cel możliwie szybki odczyt 4 kanałów, gdzie 0 V odpowiadał stanowi niskiemu, a 5 V stanowi wysokiemu.

Ryzyko

Należy pamiętać, że LPT jest zwykłym portem transmisji danych, którego prawdopodobnie można łatwo uszkodzić podając napięcia poza dopuszczalnym zakresem 0 - 5 V. Przy braku pewności co do jakości sygnału wejściowego warto pomyśleć o jakimś sprzętowym buforowaniu (zabezpieczeniu).

Charakterystyka LPT

Wszystko jest ładnie opisane na Wikipedii. Widać, że piny podzielone są na trzy rejestry: Data, Status i Control. Postanowiłem wykorzystać bity 6:3 rejestru "Status", które zawsze są traktowane jako wejście. Najbardziej znaczący bit 7 też jest wejściem, ale jego stan jest odwracany sprzętowo (podając 0 V widzimy "1"). Oczywiście nie ma problemu, by odwrócić odczyt z powrotem jak i wykorzystać niektóre z pozostałych pinów, no ale to wykracza nieco poza zakres początkowych eksperymentów.

Reasumując zostaną wykorzystane 4 piny (pierwsza kolumna to numeracja w 25-pinowej wtyczce LPT):
10     Ack     Status-6
12     Paper-Out     Status-5
13     Select     Status-4    
15     Error     Status-3

 Shooke CC-SA

Adresy rejestrów mojego LPT zaczynają się od 0x378, co jest wartością typową dla wbudowanego portu. To jest rejest "Data", "Status" będzie miał adres 0x379, a "Control" 0x37A. Zamiast szesnastkowego 0x379 zobaczysz w kodzie BASEPORT=0x378 i dalej "Status" jako BASEPORT+1 - dla wygody i uniknięcia głupich błędów. 

Komputer z odzysku



Z racji, że większość współczesnych komputerów nie ma już LPT lub istnieje ono tylko na płycie i wymaga dokupienia śledzia, postanowiłem wykorzystać stary, niepotrzebny już nikomu sprzęt - malutki komputer, którego przeszłości nie znam (przysięgam, że go nie ukradłem). Założenie było takie, że komputer będzie pracował bez monitora, nadzorowany z zewnątrz przez SSH.

Ten miniaturowy pecet jest oparty o procesor VIA Esther 1 GHz (vendor_id: CentaurHauls, wnuczek znanego swego czasu IDT WinChip?) z chłodzeniem pasywnym, ma 128 MiB RAM-u i pamięć stałą 32 MiB wciśniętą bezpośrednio w gniazdo IDE i widoczną jako zwykły dysk twardy. W sam raz do zainstalowania Tiny Core Linux.



Nie będę tutaj opisywał procesu instalacji Tiny Core Linux i konfiguracji SSH - bardzo dobra instrukcja znajduje się tutaj.

Za tester rejestratora posłużył układ Atmega 8, którego port PC 0:3 (4 nóżki) zostały połączone z pinami "Status" 3:6 (piny 10, 12, 13, 15) Oczywiście połączyć trzeba było też masę (piny 18-25) LPT z masą mikrokontrolera.

Notka nadprogramowa: płytki testowe dla Atmeg 8/48/168/328 leżały u mnie bezużytecznie 4 lata. Po studiach (mechanicznych, więc nie dziwcie się jeśli zobaczycie tu jakieś herezje w temacie elektroniki lub programowania) kupiłem sobie je pod choinkę, na długie zimowe wieczory. Dopiero teraz, widząc na pudełku rok 2010 uznałem, tak dalej nie może być.
Jakież było moje zdziwienie, jak okazało się że mikrokontrolery po przełączeniu na zewnętrzny kwarc nie dają znaku życia. "Ok, pomyliłem się w fuse-bitach, zablokowałem układ", pomyślałem. Ale po trzecim układzie, gdy moja samoocena zaczęła spadać, zacząłem też szukać błędu gdzie indziej. Złożyłem gołą Atmegę z dwoma kondensatorami i kwarcem... działa! Jak później się okazało, "fabryczne" kondensatory SMD na płytkach testowych były wadliwe - przylutowałem chamsko w ich miejsce kondensatory przewlekane, okazało się, że ani jedna Atmega nie była zablokowana.

Pierwsza próba - test szybkości odczytu

Spróbujmy sprawdzić jak szybko można odczytywać rejestry portu LPT. Przyda się tu mały program w C, nazwijmy go bench_lpt:


Całość została skompilowana na lokalnym komputerze, przesłana na zdalny przez SSH i tam uruchomiona z prawami roota (Linux domyślnie nie pozwala każdemu użytkownikowi grzebać w portach):

gcc -m32 -o bench_lpt bench_lpt.c
scp bench_lpt tc@ip_lub_nazwa_zdalnego_hosta:/home/tc/bench_lpt
ssh tc@ip_lub_nazwa_zdalnego_hosta
time sudo ./bench_lpt 
real    0m 12.92s
user    0m 12.92s
sys    0m 0.00s 

Zwróć uwagę na argument -m32, który sprawia, że program jest kompilowany skrośnie na 64-bitowym systemie na system 32-bitowy. 

Wykonanie 10 milionów odczytów rejestru "Status" zajęło niecałe 13 sekund, co daje ponad 770 000 odczytów na sekundę (Hz), każdy po bajcie (rejestr Status jest ośmiobitowy). Dla porównania USB potrafi osiągnąć tylko 1000 Hz (dlaczego nikt nie produkuje myszek dla graczy na LPT?).

W kodzie warto zwrócić uwagę na ioperm, dzięki któremu można uzyskać dostęp do rejestrów portu i inb które umożliwia odczyt ich stanu.

Próba druga - rejestrowanie odczyt po odczycie

Drugą próbą, a zarazem pierwszym pomysłem na rejestrowanie danych było odczyt 4 bitów (Status 6:3), łączenie dwóch takich odczytów w jeden znak (char), który ma przecież 8 bitów i wypisanie go na standardowym wyjściu. Jeśli przekazać standardowe wyjście do pliku, którym znajduje się na tzw. ramdysku, to i tu osiągi były całkiem dobre.

Dalej prędk... eee, szybkość (jestem mechanikiem, nie powinienem robić takich błędów) odczytu przekraczała 700 kHz:

time sudo ./lptr_bin10m > /mnt/ramdisk/testbin.dat
real    0m 13.69s
user    0m 13.66s
sys    0m 0.02s

Oczywiście nowoutworzony plik zajmował dokładnie 5 MB (10 milionów odczytów, każdy po pół bajta).

Część właściwa

Trzecia próba - odczyt z pomiarem czasu

Poniższy kod możecie też znaleźć na GitHubie.
Cała operacja będzie składała się z dwóch etapów:
- rejestracji sygnałów przez prosty, szybki, napisany w C program na zdalnym komputerze z portem LPT
- podglądu (rysowania przebiegu) na lokalnym komputerze i generowania pliku z danymi w postaci czystego tekstu - za to ma odpowiadać wolniejszy, ale łatwo modyfikowalny skrypt w Pythonie.

Nim przejdziemy do tego, chcę zwrócić uwagę na problem szybkiego zapisu danych. Zapis na dysk twardy mógłby okazać się niewystarczająco szybki, lub niestabilny. Najprostszym sposobem jest utworzenie tzw. ramdysku. Mój zdalny komputer ma 128 MiB RAM, co jest wartością zdecydowanie wystarczającą - utworzyłem "ramdysk" 32 MiB:

mkdir /mnt/ramdisk
sudo mount -t tmpfs -o size=32m tmpfs /mnt/ramdisk

Zauważ, że użyłem systemu plików tmpfs. Więcej o różnicach między tmpfs a ramfs można przeczytać tutaj.

Taki ramdysk zniknie oczywiście po restarcie. By temu zapobiec trzeba w /etc/fstab dodać linię:

tmpfs       /mnt/ramdisk tmpfs   nodev,nosuid,noexec,nodiratime,size=32M   0 0

Z racji użycia Tiny Core Linux musiałem w /opt/bootlocal.sh dodać:

/bin/mkdir /mnt/ramdisk
/bin/mount /mnt/ramdisk

w /opt/.filetool.lst dodać:

/etc/fstab

Oraz zapisać dane poleceniem:

sudo filetool.sh -b

Notka poboczna: jedynym domyślnie dostępnym edytorem tekstu w Tiny Core Linux jest vi. Nie jest on tak straszny jak początkowo się wydaje, jest tylko inny niż pozostałe (windowsowate) edytory. Mi wystarczyła wiedza, że "i" włącza tryb wstawiania, "Esc" wraca do trybu komend, komenda :q to wyjście a :wq to wyjście z zapisem.

Do rejestrowania stanu portu LPT posłużył mi tym razem nieco bardziej skomplikowany program, którego w listing w C widzisz poniżej. Zapisuje on tylko stan rejestru (a właściwie 4 interesujących nas bitów - zauważ r = r & 0b01111000;) gdy ulega on zmianie, do tego zapisując czas w którym ta zmiana nastąpiła. Jeśli port nie otrzymuje zmiennych danych, to program kończy działanie po wykonaniu określonej liczby pętli. 


Kompilacja programu i uruchomienie może wyglądać następująco:

gcc -m32 -static lpr_timed.c -o lpr_timed-static
sudo ./lpr_timed-static -o /mnt/ramdisk/test.bin
Loops: 7610340


Argument -static jest używany do statycznego linkowania - plik wykonywalny jest znacznie większy, ale mogłem dzięki temu uniknąć problemów ze zbyt starym glibc na zdalnym komputerze. Jak widać, w ciągu 10 sekund, program wykonał ponad 7 milionów pętli odczytu, przy czym plik wyjściowy miał zaledwie ok. 100 KiB. Czas został zapisany w postaci "float", stan rejestru jako "int" zajmujących po 4 bajty.

Czas trwania odczytu i dopuszczalną liczbę pętli można zmienić np. na 5 sekund i 5000000 pętli:
sudo ./lpr_timed-static -t 5 -l 5000000 -o /mnt/ramdisk/test.bin
 
Dlaczego nie ograniczam czasu w przypadku braku zmian stanu portu? Bo to wymagałoby odczytu zegara (clock_gettime(CLOCK_MONOTONIC, &end);) wewnątrz szybkiej pętli próbkowania portu LPT i mogłoby je spowolnić.


Na zrzucie powyżej widoczny podgląd zdekodowanych danych, oraz dodatkowo wartości zaimportowane do LibreOffice. Do wykonania podglądu, oraz wygenerowania logu w postaci czystego tekstu wykorzystałem poniższy skrypt w Pythonie oraz bardzo wygodny moduł Pythona pyqtgraph.


Przesłanie danych z zdalnego komputera oraz wygenerowanie podglądu:
gcc -m32 -o bench_lpt bench_lpt.c
scp tc@ip_lub_nazwa_zdalnego_hosta:/mnt/ramdisk/test.bin test.bin
python pybingraph.py


Skrypt otwiera plik binarny test.bin, wyświetla podgląd i zapisuje dane w pliku tekstowym test.csv, który można otworzyć w arkuszu kalkulacyjnym. Krotka bits = (0b00001000, 0b00010000, 0b00100000, 0b01000000) definiuje które bity i w jakiej kolejności mają być wyświetlone.

Kilka obserwacji

Przeanalizujmy wynik działania poniższego programu na Atmedze 8 taktowanej kwarcem 8 MHz. Aplikacja ta jest niczym innym jak programowym, czterokanałowym generatorem PWM.


Spójrzmy w fragment zanotowanych wyników (czas, bity PC3:PC0):

9.952934, 1, 1, 1, 1
9.952948, 1, 1, 1, 0
9.953118, 1, 1, 0, 0
9.953954, 1, 0, 0, 0
9.957483, 0, 0, 0, 0

Widać, że wyłączenie PC0 zajęło 14 mikrosekund. W programie odpowiada za to jedna pętla w przerwaniu po 50 cyklach procesora. Czy wykonując 5 pętli, każda po 10 cyklach otrzymamy to samo? Zamieńmy:

uint8_t pwmarray[4] = {1, 10, 50, 200};
OCR2 = 50;

na

uint8_t pwmarray[4] = {5, 50, 120, 200};
OCR2 = 10;


Wyniki to:

9.990124, 1, 1, 1, 1
9.990205, 1, 1, 1, 0
9.991048, 1, 1, 0, 0
9.992504, 1, 0, 0, 0
9.994389, 0, 0, 0, 0

PC0 przeszedł na stan niski dopiero po 81 mikrosekundach. Oczywiście samo 50 cykli procesora trwa tylko 6,25 µs, reszta to narzut pętli i instrukcji warunkowych. To mi uświadomiło jak beznadziejnym pomysłem może być wywoływanie przerwań co kilka cykli procesora (niski preskaler, niska wartość rejestru porównawczego OCR).

Dokonajmy jeszcze jednego testu, zamiast włączać wszystkie bity od PC0 do PC4 jednocześnie, włączajmy je sekwencyjnie modyfikując pętlę wewnątrz przerwania:

    for (i = 0; i < 4; ++i)
    {
        if (count >= pwmarray[i]) 
        {
            if (count < 255) //jesli licznik mniejszy niz 255 i wiekszy od wartosci w tablicy to porty są na stan niski
            {                
                PORTC &= ~(_BV(i)); //porty startuja od PC0
            }
            else //jeśli równy 255 to porty sa na stan wysoki wlaczane
            {
                PORTC |= _BV(i);
            }   
        }    
    }  


Fragment zanotowanych danych:
9.993909, 0, 0, 0, 1
9.993913, 0, 0, 1, 1
9.993917, 0, 1, 1, 1
9.993921, 1, 1, 1, 1
9.993936, 1, 1, 1, 0
9.994109, 1, 1, 0, 0
9.994965, 1, 0, 0, 0
9.998606, 0, 0, 0, 0

Widać, że zmiana każdego z kolejnych bitów na stan wysoki (PORTC |= _BV(i);) zajmuje 4 µs. Inaczej mówiąc powyższa pętla for musi potrzebować około 30 cykli mojej Atmegi. Szczerze mówiąc, nie sądziłem, że rejestrowanie stanów logicznych przez LPT będzie wystarczająco szybka by obserwować pojedyncze przejścia takiej pętli, nie wspominając o pomiarze czasu ich trwania.

Dosyć na dziś. Do tematu pewnie jeszcze kiedyś powrócę, gdy znów postanowię się oderwać od szarej rzeczywistości, chwycić lutownicę i popatrzeć na czarny ekran z białymi literkami. To naprawdę potrafi być relaksujące. Nie, nie mam kucyka.

PS Z LPT i drabinki R2R można zrobić też kartę dźwiękową, mono, 8 bitów rozdzielczości. Nawet Pulse Audio potrafi transmitować dźwięk jako ciąg 8-bitowych liczb całkowitych bez znaku... Ale to projekt na inny wieczór.