Co właściwie mierzy analogRead() ?
Widzicie to kłębowisko? To się oficjalnie nazywa stanowisko testowe LCD kitów. Po prostu – po zlutowaniu partii, każdy trzeba podłączyć aby sprawdzić czy świeci i wyświetla jak należy. Mógłby to być jakiś głupawy tekst, ale to byłoby zbyt proste.
Dlatego wśród kłębu kabli jest ukryty czujnik temperatury MPC9700 (a raczej dwa) i na LCD wyświetla się po prostu odczyt z niego. Wczoraj w nocy jakoś mnie zaintrygowało temperatura jaką wyświetlało Arduino. Była za wysoka, zdecydowanie. Różnicę między 21 a 24 każdy z nas dobrze zauważy (zdjęcie zrobiono w innych warunkach termicznych :) ). A zrzucić tego na niedokładność pomiaru trudno, bo czujnik ma przecież dokładność +/- 2 st.
Ponadto, raz czy dwa w rozmowie z klientami słyszałem takie opinie, że pokazuje za dużo (czujnik, czujnik :) ). Dlatego zacząłem grzebać i próbować różnych rzeczy.
Jak działa przetwornik napięcia?
Przetwornik analogowo cyfrowy ADC jest dokładnie opisany w nocie katalogowej ATmega328 strona 250. W dużym skrócie: napięcie AREF jest napięciem zasilającym przetwornik DAC (podzielnik napięcia sterowany cyfrowo) którego wyjście jest podłączone do komparatora porównywającego je z napięciem wejściowym na danym wejściu analogowym. Napięcie z przetwornika DAC 'startuje’ od zera i jest zwiększany aż osiągnie maksimum lub komparator wykaże przekroczenie poziomu sygnału badanego. Dzięki temu wiadomo jaką wartość ma badany sygnał.
Od razu rzuca się w oczy co wpływa na dokładność takiego pomiaru. Pierwsza rzecz to rozdzielczość (w bitach) przetwornika DAC – inaczej z jakim krokiem zwiększane jest napięcie do porównywania. ATmega328 ma 10-cio bitowy.
Druga wartość to napięcie AREF. Jeżeli liczba kroków jest określona (1023 bo 10 bitów) to wiadomo jakie napięcie przypada na krok. Jest to AREF/rozdzielczość. ATmega może korzystać z 3 różnych źródeł napięcia AREF. Może to być wewnętrzne źródło 1.1V (nie wiem jakiej dokładności), napięcie zasilania (około 5V) lub wartość podana na pin AREF.
Znajomość tej wartości jest konieczna tak aby móc przeliczyć odczyt cyfrowy z przetwornika na rzeczywistą wartość napięcia. Pomińmy na razie opcje korzystania z wewnętrznego źródła 1.1V oraz z podawania AREF własnoręcznie. Skupmy się na domyślnej wartość AREF – 5V.
Teraz kłania się nam podstawowa informacja o czujniki MCP9700 – czyli czułość. Wynosi ona 10mV/st C – dlatego jeżeli chcemy odczytywać dokładnie to co podaje czujnik to musimy znać dokładną wartość AREF.
Może na przykładzie
Odczyt z analogRead() dał nam 150. Myślimy że napięcie odczytane to 5/1023*150 = 0.7331 V. Jest to prawda, jeżeli AREF faktycznie wynosi 5V. A jeżeli ma naprawdę 4.9V? To wówczas prawdziwa wartość jaką oznacza odczyt 150 to: 0.7178V. Różnica – 14.6 mV. Dla czułości 10mV/C – prawie 1.5 stopnia!
A ile wynosi dokładnie ten AREF w domyślnym ustawieniu? Niby 5V, ale tak naprawdę Vcc (napięcie zasilania) podane na ATmega328.
Zaraz, powie ktoś, przecież Arduino ma na sobie regulator napięcia na 5V, więc taka duża różnica nie powinna występować. Prawda. Ale tylko jeżeli zasilamy Arduino przez gniazdo zasilania, a nie przez USB!
Jak wiadomo, standard USB mówi, że napięcie zasilania wynosi 5V, więc regulator napięcia nie działa, napięcie z kabla USB idzie prosto do ATmega. A regulator napięcia nie działa, bo żeby dostarczyć regulowane 5V to musi dostać więcej aby mieć z czego regulować, więc nie ma technicznej (prostej) możliwości regulacji zapewniającej 5V jeśli napięcie zasilania to też 5V.
Inaczej mówiąc jesteśmy zdani na to co komputer podaje na USB. I tutaj właśnie zostałem zaskoczony, bo mój notebook na porcie USB najwyraźniej podaje 4.9 V zamiast równych 5V.
Finalnie – przez to odczyt w moim przypadku był zawyżony o te 1.5 stopnia.
Ale, jeżeli skorzystać z analogReference(INTERNAL); to rezygnujemy z górnych zakresów odczytu (bo maksymalny odczyt to 1.1V czyli po przeliczeniu na temperature (1.1-0.5)/0.01 = 60 stopni) na rzecz zwiększenia dokładności odczytu (1.1/1023 = ok 1mV, tj 0.1 C) . Jeszcze raz uwaga – zwiększenie dokładności odczytu nie pomiaru :)
To tyle w dużym skrócie jeżeli chodzi o to co naprawdę mierzy analogRead. Ażeby wyjaśnić czemu na zdjęciu jeden odczyt to 18 stopni a drugi 20 – nie jest to jakaś magia związana z analogRead – jeden z czujników MCP to wersja A a drugi bez A w numerku. Ten z A (dołączony do Starter Kitu jak i sprzedawany samodzielnie) ma dokładność odczytu +/i 2 a bez A +/- 4 stopnie).
Ja już zauważyłem to wcześniej – u mnie na laptopie, też wartość napięcia jest 4,9. Czasem nawet spada niżej, jak porusza się wtyczką.. ja mam dość luźne gniazdo w komputerze, i często np. lcd backlight przygasa w wyswietlaczu podpiętym do arduino.
Ja jeszcze dodam jedną rzecz, którą sobie przypomniałem później – jeśli używacie zewnętrznego zasilania, warto dobrze uziemić Arduino. Ja miewałem problemy jeśli nie było uziemione – odczyty z ADC był mocno zmienny.
Cześć,
jeśli mam podłączone arduino do komputera, i dodatkowo do zasilania. To z którego źródła jest wtedy zasilane arduino? Czy mogę zasilać arduino 9V i czy takie napięcie będzie wtedy na pinie oznaczonym Vin (arduino Uno) i jednocześnie być podpiętym do komutera po USB.
Można tak podłączyć i na Vin będzie to co z zasilacza przychodzi
czyli, jak mierzę wartości analogowe, na zwykłej atmedze, to wystarczy, że połącze aref do +5V z regulatora i wartości powinny być poprawne, czy muszę jeszcze w kodzie zmienić wartość napięcia referencyjnego?
Domyślnie napięcie referencyjne jest pobierane z zasilania. Nic nie trzeba podłączać. Jak chcesz jakieś AREF to są specjalne układy scalone jako „źródła napięcia odniesienia”. One tworzą bardzo dokładne napięcie odniesienia.
Można też sprawdzać napięcie zasilania względem wewnętrznego źródła i kalibrować tym wynik pomiaru:
https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
Witam
Ja u siebie zastosowałem dodatkowo potencjometr który ma 10 obrotów wraz z rezystorem i po wgraniu kodu skalibrowałem sobie tak aby pokazywał prawdziwą wartość. Dzięki temu nie musiałem rozglądać się za rezystorami 1% nie mówiąc o kalibracji w programie bo u mnie dzielnik jest równy 4. Obecnie testuje a potem docelowo wyląduje w urządzeniu docelowym.
Pierwsza informacja „Druga wartość to napięcie AREF. Jeżeli liczba kroków jest określona (1023 bo 10 bitów) to wiadomo jakie napięcie przypada na krok. Jest to AREF/rozdzielczość.” jest prawdziwa. 10 bitów to 2^10 = 1024 wartości od 0 do 1023, czyli 1023 kroki (przedziały). A dalej brak konsekwencji: „Myślimy że napięcie odczytane to 5/1024*150 = 0.7324 V. „. Powinno być dzielenie przez 1023. Przecież dla wartości maksymalnej wyjdzie 5/1024*1023 = 4,9951171875, a należałoby oczekiwać 5/1023*1023 = 5!!!
Ten błąd jest często powielany w programach. Ponadto, aby wymusić obliczenia zmiennoprzecinkowe należy jedną z liczb zapisać w postaci zmiennoprzecinkowej, np. 5.0/1023*adc lub 5/1023.0*adc, gdzie adc oznacza całkowitą wartość odczytaną z przetwornika.
Panie Wiesławie,
Absolutna racja. Poprawiłem tekst używając wszędzie 1023 zamiast 1024. Co do zapisu zmiennoprzecinkowego – ten zapis to nie fragment kodu, tylko „zwykły” zapis matematyczny więc dlatego brakuje tego .0.
Dodam tylko że błąd wprowadzony przez użycie 1024 zamiast 1023 jest na poziomie jednego promila, więc w zdecydowanej większości przypadków nie ma wpływu na decyzje podejmowane na podstawie pomiaru. W przykładzie użytym w tekście różnica wynosi 0.71 mV. Pewnie dlatego ten błąd jest powielany w wielu miejscach. Niemniej dziękuję za zwrócenie uwagi, teraz sam będę pewnie o tym zawsze pamiętał :)