blog.garaż.net

10 luty 2014

Mikrokontolery - pierwsze poważne starcie

Mając do dyspozycji porządny procesor człowiek nie zdaje sobie sprawy ile można zrobić w ciągu sekundy mając prosty mikrokontroler taktowany zegarem 1MHz.

Właśnie skończyłem pierwszą, działającą wersję pary programów do nadawania i odbierania danych wykorzystując podczerwień. Nadajnik banalny, nadaje stałą, zakodowaną wiadomość o rozmiarze 2 bajtów z kilku bajtową przerwą około 100 razy na sekundę. Odbiornik trochę mniej banalny bo oprócz odebrania tego musi sobie poradzić, z wychwytywaniem błędów, dekodowaniem, obsługą kilku kanałów na raz (doszedłem do wniosku, że szkoda marnować kilka układów do obsługi wielu detektorów, skoro jeden ma całkiem sporo wejść i jak mi się wydawało całkiem sporo wolnego czasu procesora) oraz transmisję zdekodowanych danych lub informacji o błędach dalej wykorzystując sprzętowy układ UART.

Wybrane mikrokontrolery do rozwiązania problemu to ATMega8 w odbiorniku i ATTiny13 w nadajnikach. Dodatkowo w nadajniku zwykła dioda emitująca podczerwień, a w odbiorniku scalony demodulator IR 56kHz. O ile nawet ATTiny to za wiele dla nadajnika, to ATMega ma trochę za mało zegarów do moich celów (chciałem mieć 8 kanałów) więc dekodowanie zostało zrealizowane w pełni programowo, a jeden z timerów wykorzystałem tylko do próbkowania w równych odstępach czasu.

Naiwnie myśląc, że proste obliczenia odpowiedzą na pytanie czy się da to zrobić, zabrałem się do zbierania danych. Wg noty katalogowej demodulator potrzebuje minimum 8 taktów nośnej do wykrycia sygnału i prawidłowego działania, przyjąłem 10 dla pewności. Również dobrym pomysłem będzie nadpróbkowanie jego wyjścia, przyjąłem 4 razy na półokres, wyszło więc że próbkowanie powinno się odbywać 22400 razy na sekundę (56000/10*4). Kilka obliczeń na szybko i wychodzi, że rdzeń przy taktowaniu 16MHz powinien sobie spokojnie poradzić - do wykorzystania na jedną próbkę jest ponad 700 instrukcji. Moc nie do przejedzenia, nieprawdaż? :)

I tyle z obliczeń. Po napisaniu aplikacji, która wygląda bardzo niewinnie w C okazało się, że procesor dekodera nie mieści się z końcowym przetwarzaniem nawet dla 4 kanałów i chociaż działa przez większość czasu prawidłowo to jednak najpewniej przepiszę całość w ciut bardziej optymalny sposób, co powinno umożliwić obsługę 6, może nawet zakładanych początkowo 8 kanałów. Bez stresu, że błąd pojawiający się, na którymkolwiek kanale sprowokuje dłuższe obliczenia i rozsypie przetwarzanie pozostałych.

A co zrobiłem nie tak? Bardzo dużo rzeczy. Największym grzechem było uproszczenie programu i wrzucenie niemal całość kodu (wbrew ostrzeżeniom ;)) do funkcji obsługi przerwania. Pomijając fakt, że takie rozwiązanie nieoptymalnie wykorzystuje wolne cykle procesora pomiędzy kolejnymi wywołaniami z timera i może przyczynić się do zgubienia przerwania, to pozostaje jeden bolesny problem jakim jest fakt, że funkcja przerwania jest pełnoprawną funkcją i co za tym idzie dostajemy duży narzut w postaci instrukcji zapisujących i przywracających stan procesora. W tym przypadku duży to mało powiedziane, podgląd wersji zdekompilowanej pokazał, że samych funkcji push i pop procesor musiał wykonać 40... Kolejny problem to specyfika rdzenia. Nie wszystko co wydaje się proste do wykonania w C przełoży się na równie prosty kod w asemblerze, a przynajmniej nie tak prosty do jakiego mogą być przyzwyczajeni ludzie wychowani na x86, czyli przykładowo ja. Często rzeczy, które spodziewałem się, że będą zajmowały góra dwie-trzy instrukcje, w rzeczywistości zajmowały kilka razy więcej. Prosta funkcja zapisująca bajt w tablicy pełniącej rolę bufora:

decodedData[decodedNextToFill++] = fcn_arg;

I po kompilacji:

lds r25,decodedNextToFill # załadowanie indeksu następnego wolnego miejsca
ldi r30,lo8(decodedData)  # załadowanie adresu tablicy - adres 16bitowy
ldi r31,hi8(decodedData)  # jak widać boli bardziej jeśli trzeba policzyć na 8bitowym CPU :)
add r30,r25               # obliczanie offsetu też
adc r31,__zero_reg__      # trzeba pamiętać o nadmiarze
st Z,r24                  # zapis, nareszcie...
subi r25,lo8(-(1))        # rdzeń nie ma instrukcji dodawania stałej - RISC FTW :)
sts decodedNextToFill,r25 # zapis uaktualnionego indeksu

Niby to jest nic wielkiego, ale trzeba o takich rzeczach pamiętać estymując ilość potrzebnego czasu, bo w praktyce jest go niewiele.

Pomimo masy błędów, cieszy mnie ten mały eksperyment. Tak naprawdę to pierwszy poważniejszy projekt, który robię na mikrokontrolerach. I nie lubię dowiadywać się, że czegoś nie wolno robić bo nie, zawsze lepiej samemu zbadać sprawę, dowiedzieć się kilku szczegółów więcej i kiedy przyjdzie pora wykorzystać je. :)

Nie obeszło się także bez problemów sprzętowych.

Skalibrowany fabrycznie zegar w ATTiny13 to dobry żart, z wyliczonymi parametrami dla timera generował nośną 36kHz zamiast 56kHz. Chociaż czuję, że to również moja zasługa. Albo biedaka wygrzałem przy lutowaniu albo przesadziłem z prądem na diodzie IR, która idzie bez tranzystora z pełną dopuszczalną mocą.

I jeszcze jeden ważny wniosek. Czas na poważnie rozejrzeć się za instrumentami pomiarowymi bardziej zaawansowanymi niż multimetr, których da się używać na Linuksie. O ile logikę można sobie przetestować wycinając potrzebne funkcje do prostego programu wypluwającego dane na konsolę lub korzystając z dostępnych emulatorów, to testowanie poprawności przebiegów i poprawnego zmontowania fizycznego układu jest już zadaniem dość irytującym bez oscyloskopu lub jeszcze lepiej analizatora stanów logicznych. W sumie przez to zmarnowałem całkiem sporo czasu domyślając się, zamiast po prostu sprawdzić przebiegi. U mnie zawiódł jeden z demodulatorów, który reagował na nośną ale głupiał w trakcie. Nieuszkodzone też nie działały tak idealne jakbym tego chciał, na szczęście oversampling i dosyć spora tolerancja dekodera zrobiły swoje.

Jeśli dotrwałaś/dotrwałeś do końca tego mało ciekawego tekstu to gratulację i dziękuję za wysłuchanie. ;)

Komentarze

  • Garaż (2014-03-16 14:16:04):

    Mikrokontolery - pierwsze poważne starcie cz. 2

    Jakiś czas temu pisałem o małym projekcie wykorzystującym mikrokontrolery. Przez ten weekend postanowiłem posiedzieć trochę i zgodnie z postanowieniami nieco przepisać całość aby pozbyć się kilku potencjalnych problemów - lepiej zało[...]

Comments !