logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Pomiar napięcia w zakresie 2-16V używając jak najmniej elementów

eiliat 19 Lut 2013 23:26 2295 16
  • #1 11959193
    eiliat
    Poziom 15  
    Witam,

    Przy użyciu Atmega32 chcę zmierzyć napięcie. Odczyt nie musi być dokładny, dokładność do 0.4V przy zakresie 2-16V. Rozumiem, że mierzone napięcie podłączam w taki sposób:
    Pomiar napięcia w zakresie 2-16V używając jak najmniej elementów

    Tak jak na obrazku, procek będzie napędzany 16 MHz przy zasilaniu 3.3V
    Jak dobrać parametry rejestrów ADC i wartości rezystorów, tak aby uzyskać prawidłowe wyniki? Programuje w C.
  • #2 11959497
    medicb
    Poziom 28  
    Zakładając, że użyjesz super dokładnego stabilizatora napięcia 3,3V a tym samym będzie to Twoje napięcie odniesienia w układzie pomiarowym to 3,3V/1024 = 0,00322265625 na jedną działkę ADC. Dzielnik powinien dzielić w skali 1:5 czyli 16/5 = 3,2V. Dalej już tylko programowe liczenie. Jeżeli Twój ADC dla przykładu odczyta wartość 456 to mnożysz razy 0,00322265625 i jeszcze razy 5 z dzielnika rezystorowego, czyli wychodzi 7,34765625V. W dzielniku możesz wykorzystać oporniki 1k i 4k. Napięcie odniesienia powinno być dokładnie zmierzone w chwili uruchomienia i pisania programu oraz uwzględnione w programie.
  • #3 11959695
    BlueDraco
    Specjalista - Mikrokontrolery
    A jak nie chcesz zużyć całej pamięci programu i całego czasu procesora na obliczenie napięcia, to piszesz:

    unisigned int mV = ((unsigned long int)ADC * 3300 * 5) >> 10;

    czyli prościej
    unsigned int mV = ((unsigned long)ADC * 825 * 5) >> 8;

    I masz to samo, co opisał kol. medich, ale 200 razy szybciej.

    To "5" - to współczynnik podziału dzielnika. Dla dowolnych wartości napięcia odniesienia w mV i podziału dzielnika da się ten wzór zapisać w postaci mnożenia i przesuwania w prawo liczb całkowitych, a to jest najprostsze dla procesora.
  • #4 11959815
    Konto nie istnieje
    Konto nie istnieje  
  • #5 11959866
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Widzę że wszyscy ostro zabrali się za "premature optimization" - pamiętajcie więc, że "premature optimization is the root of all evil". Dokładność oczekiwana 0.4V, a Albert proponuje taki dzielnik, aby otrzymać jakiś superprecyzyjny stopień podziału, więc rezystory 0.1% i do tego pewnie jeszcze jakieś kosmiczne wartości (lub kilka różnych). A jak autor tematu chce zmierzyć wolnozmienne napięcie i przedstawić je użytkownikowi na LCD raz na sekundę? Niektóry chyba mają alergię jak widzą "float" (;

    4\/3!!
  • #6 11959994
    BlueDraco
    Specjalista - Mikrokontrolery
    Spoko, jak wyjdzie ATmega z FPU to odszczekam te optymalizacje. ;)
  • #7 11960032
    Freddie Chopin
    Specjalista - Mikrokontrolery
    No dobra, tylko po co te przesunięcia bitowe skoro chodzi Ci o dzielenie? Czy istnieje na świecie kompilator tak głupi, żeby faktycznie dzielenie przez potęgę dwójki realizować "wprost" zamiast przez przesunięcia (na architekturze dla której ma to sens)? Czy zamiast tego:
    unisigned int mV = ((unsigned long int)ADC * 3300 * 5) >> 10;
    to nie jest bardziej czytelne:
    unisigned int mV = ((unsigned long int)ADC * 3300 * 5) / 1024;

    Co do braku FPU, ponownie pytam - jaki ma sens taka kombinacja przy założonej BARDZO MAŁEJ dokładności i gdyby np. celem pomiaru było pokazanie wartości jeden raz na sekundę na LCD, przy okazji w ramach redukcji do absurdu (nie do końca), załóżmy że te pomiary to JEDYNA funkcja urządzenia - czemu wtedy float jest zły. Jak optymalizować, to czemu całości nie napisać od razu w assemblerze, żeby było jeszcze lepiej? (;

    4\/3!!
  • #8 11960133
    Konto nie istnieje
    Konto nie istnieje  
  • #9 11960201
    BlueDraco
    Specjalista - Mikrokontrolery
    Odpowiadam:
    1. Kombinacja ma sens taki, żeby programista pamiętał, że dzielnik ma być potęgą dwójki. Oczywiście kompilatorowi wszystko jedno, czy napiszemy >> 10, czy / 1024, ale nie wszystko jedno, czy napiszemy /1024 czy /1000. Dzielenie zabija małe procesory niewiele mniej skutecznie od zmiennego przecinka. Cortexy zresztą też za nim nie przepadają, zwłaszcza M0.

    2. Drugi sens "kombinacji" - to ograniczenie długości stałych, a w konsekwencji również zmiennych, do niezbędnego minimum. Dlatego lepiej jest napisać (x * 825) >> 8 niż (x * 3300) >> 10. Zobacz, co się stanie, jeśli x będzie miał 20 bitów znaczących - całkiem realny przypadek dla ADC. Wydaje mi się, że kompilatory jeszcze nie potrafią wykonywać takich redukcji (zresztą chyba byłoby to nielegalne), ale może nie jestem na bieżąco - popraw mnie, jeśli się mylę.

    Oczywiście - najpierw dobry algorytm, choćby i z FP, potem "kombinacje". Niemniej póki kompilator ma IQ niższe od mojego, lepiej jest, gdy wspomagam go w czymś, w czym ja jestem lepszy. ;)
  • #10 11962491
    hans512
    Poziom 15  
    BlueDraco napisał:

    unisigned int mV = ((unsigned long int)ADC * 3300 * 5) >> 10;

    czyli prościej
    unsigned int mV = ((unsigned long)ADC * 825 * 5) >> 8;

    Tak z ciekawosci. Moglbys wytlumaczyc czemu akura 825 a nie powiedzmy 892? Co reprezentuja te wartosci?
  • #11 11962504
    BlueDraco
    Specjalista - Mikrokontrolery
    3300 to Vref

    3300 / 4 = 825

    Zamiast mnożyć przez 3300 i dzielić przez 1024 można mnożyć przez 825 i dzielić przez 256.
  • #12 11965297
    rrytel
    Poziom 14  
    może ja się nie znam ale jak chcecie optymalizować to wg. mnie najlepiej będzie dorzucić stałą (np. o nazwie ADC_const) i wtedy obliczanie napięcia wyglądało by w bardzo prosty sposób:
    result_in_mV = (unsigned long)ADC * ADC_const;

    dla procesora wychodzi tylko jedno mnożenie (ATmegi mają taką instrukcję), reszta zajmuje się kompilator. szybciej prościej czytelniej.
    Edit:
    obliczanie zmiennej można dać na początku i obliczać z innych stałych reprezentujących rezystancje dzielnika i napięcie odniesienia. (łatwiej wtedy edytować w przypadku zmian)
  • #14 11965864
    eiliat
    Poziom 15  
    Ależ rozpoczęły się emocje :) Napięcie będzie sprawdzane raz na 3-5 sekundy w przerwaniu. Czyli niezbyt często ale jednak obok tego procesu będzie wykonywać się kilka innych, dlatego potrzebuje sposobu, który nie obciąży mi reszty :)
  • #15 11966792
    rrytel
    Poziom 14  
    [quote="Freddie Chopin"]
    rrytel napisał:
    Twoja stała to 16,11328125 - jeśli nie widzisz problemu, to trudno...

    chodziło mi o to że stała obliczana na postawie wzorów np:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    stałą obliczy kompilator więc dla mikrokontrolera zostaje wyłącznie jedno mnożenie.
  • #16 11966915
    Freddie Chopin
    Specjalista - Mikrokontrolery
    No dobrze, tym razem Twoja stała to 24,4140625 i widzę że dalej nie zauważyłeś problemu... Podpowiem więc - jaką wg Ciebie wartość wstawi kompilator w miejsce Twojej stałej? Innymi słowy - jak po takim podstawieniu będzie wyglądało to wyrażenie "wynik_w_mV=ADC*ADC_const"? Ja twierdzę z całą pewnością, że kompilator wstawi tam 20, więc wytłumacz gdzie odleciała ta super dokładność wynikająca z Twojego wzoru?

    4\/3!!
  • #17 11969294
    BlueDraco
    Specjalista - Mikrokontrolery
    Chciałbym również zauważyć, że AVR nie ma instrukcji mnożenia dannych 32-bitowych, więc nawet z tym to jednym mnożeniem też będzie musiał się trochę pomęczyć. Naprawdę warto ten wzór sprowadzić do mnożenia przez stałą i przesuwania w prawo. Nawet jeśli stopień podziału dzielnika jest taki, że mianownik nie jest potęgą dwójki - wtedy trzeba przeskalować mnożnik tak, żeby można było dzielić przez potęgę dwójki przez przesuwanie. Błąd wynikający z zaokrąglenia mnożnika i tak będzie mniejszy od wagi LSB ADC.
REKLAMA