Próbuję znaleźć skuteczny sposób obliczenia odwrotności na AVR (lub przybliżenia go).
Próbuję obliczyć okres impulsu dla silnika krokowego, aby móc liniowo zmieniać prędkość. Okres jest proporcjonalny do odwrotności prędkości ( p = K/v
), ale nie mogę wymyślić dobrego sposobu na obliczenie tego w locie.
Moja formuła to
p = 202/v + 298; // p in us; v varies from 1->100
Testując na Arduino, podział wydaje się całkowicie ignorowany, pozostawiając p
ustalony na 298
(chociaż być może byłoby inaczej w avr-gcc). Próbowałem także sumować v
w pętli, aż przekroczy 202
, i licząc pętle, ale jest to dość powolne.
Mogłem wygenerować tabelę odnośników i zapisać ją w pamięci flash, ale zastanawiałem się, czy istnieje inny sposób.
Edycja : Może tytuł powinien być „efektywnym podziałem” ...
Aktualizacja : Jak wskazuje pingwin, moja formuła mapowania okresu na prędkość jest nieprawidłowa. Ale głównym problemem jest operacja dzielenia.
Edycja 2 : Podczas dalszych badań podział działa na arduino, problem był spowodowany zarówno nieprawidłową formułą powyżej, jak i przepełnieniem int w innym miejscu.
źródło
Odpowiedzi:
Jedną fajną rzeczą w podziale jest to, że mniej więcej wszyscy to robią. Jest to dość podstawowa funkcja języka C, a kompilatory takie jak AVR-GCC (wywoływane przez Arduino IDE) wybiorą najlepszy dostępny algorytm podziału, nawet jeśli mikrokontroler nie posiada instrukcji podziału sprzętu.
Innymi słowy, nie musisz martwić się o sposób podziału, chyba że masz bardzo dziwny przypadek szczególny.
Jeśli się martwisz, możesz przeczytać oficjalne algorytmy podziału Atmela (jeden zoptymalizowany pod kątem rozmiaru kodu, a drugi zoptymalizowany pod kątem szybkości wykonywania; żadne z nich nie zajmuje pamięci). Są w:
http://www.atmel.com/dyn/resources/prod_documents/doc0936.pdf
czyli nota aplikacyjna „AVR200: Mnożenie i dzielenie procedur” wymieniona na stronie Atmel ze względu na (dość duże) procesory Atmega, takie jak Atmega 168 i Atmega 328, stosowane w standardowych Arduinos. Lista kart danych i notatek aplikacyjnych znajduje się w:
http://www.atmel.com/dyn/products/product_card.asp?part_id=4720
źródło
wygląda na to, że wszystko, czego potrzebujesz, to 100-wejściowa tabela odnośników. Nie robi się dużo szybciej.
EDYTUJ w rzeczywistości tylko tabelę wyszukiwania wartości 68, ponieważ wartości v większe niż 67 zawsze dają wynik 300.
źródło
Jest kilka bardzo dobrych technik wymienionych w książce „Hackers Delight” Henry'ego Warrena i na jego stronie internetowej hackersdelight.org . Aby zapoznać się z techniką, która działa dobrze z mniejszymi mikrokontrolerami podczas dzielenia przez stałe, zapoznaj się z tym plikiem .
źródło
Nie wydaje się, aby twoja funkcja dawała pożądany efekt. Na przykład wartość 50 zwraca około 302, a 100 zwraca około 300. Te dwa wyniki nie spowodują prawie żadnej zmiany prędkości silnika.
Jeśli dobrze cię rozumiem, naprawdę szukasz szybkiego sposobu na zmapowanie liczb od 1 do 100 w zakresie od 300 do 500 (w przybliżeniu), na przykład od 1 mapy do 500 i 100 map do 300.
Być może spróbuj: p = 500 - (2 * v)
Ale mogę się nieporozumieć - czy próbujesz obliczyć czas fali prostokątnej o stałej częstotliwości? Co to jest 298?
źródło
Skutecznym sposobem przybliżenia podziałów jest zmiana. np. jeśli x = y / 103; dzielenie przez 103 jest równoznaczne z pomnożeniem przez 0,0097087, dlatego w celu przybliżenia tego pierwszego wybierz „dobrą” liczbę przesunięcia (tj. liczbę podstawową 2, 2, 4, 8, 16, 32 itd.)
W tym przykładzie 1024 jest dobrym dopasowaniem, ponieważ możemy powiedzieć, że 10/1024 = 0,009765 Można wtedy kodować:
x = (y * 10) >> 10;
Pamiętając oczywiście, aby upewnić się, że zmienna y nie przepełnia swojego typu po pomnożeniu. To nie jest dokładne, ale szybkie.
źródło
Z drugiej strony, jeśli próbujesz dokonać podziału na procesorze, który nie obsługuje dzielenia, w tym artykule na Wiki jest naprawdę fajny sposób.
http://en.wikipedia.org/wiki/Multiplicative_inverse
źródło
Ten proces tutaj wygląda na przyjazny dla mcu, choć może wymagać trochę przeniesienia.
Choć wydaje się, że LUT byłoby łatwiejsze. Potrzebowałbyś tylko 100 bajtów, mniej, gdybyś użył interpolacji, a ponieważ LUT jest wypełniona stałymi, kompilator może nawet zlokalizować ją w obszarze kodu zamiast w obszarze danych.
źródło
Sprawdź, aby upewnić się, że podział jest wykonywany jako zmiennoprzecinkowy. Używam Microchipa nie AVR, ale używając C18 musisz wymusić, by twoje literały były traktowane jako zmiennoprzecinkowe. Na przykład. Spróbuj zmienić formułę na:
p = 202.0/v + 298.0;
źródło
Chcesz szybko, więc oto idzie ..... Ponieważ AVR nie może efektywnie normalizować (przesuwając w lewo, aż nie można już przesunąć), zignoruj wszelkie pseudo algorytmy zmiennoprzecinkowe. Najprostszym sposobem na bardzo dokładny i najszybszy podział liczb całkowitych w AVR jest zastosowanie wzajemnej tabeli przeglądowej. Tabela będzie przechowywać odwrotności skalowane dużą liczbą (powiedzmy 2 ^ 32). Następnie implementujesz mnożenie unsigned32 x unsigned32 = niepodpisane 64 w asemblerze, więc odpowiedz = (licznik * inverseQ32 [mianownik]) >> 32.
Zaimplementowałem funkcję mnożenia za pomocą wbudowanego asemblera, (owiniętego funkcją ac). GCC obsługuje 64-bitowe „długie długie”, jednak aby uzyskać wynik, należy pomnożyć 64 bity przez 64 bity, a nie 32 x 32 = 64 z powodu ograniczeń języka C w architekturze 8-bitowej ......
Minusem tej metody jest użycie 4K x 4 = 16 K pamięci flash, jeśli chcesz podzielić przez liczby całkowite od 1 do 4096 ......
Bardzo dokładny podział bez znaku osiąga się teraz w około 300 cyklach w C.
Można rozważyć użycie liczb całkowitych skalowanych w 24 lub 16 bitach dla większej prędkości, mniejszej dokładności.
źródło
p=298
Zwrócona wartość twojego równania już jest, ponieważ kompilator najpierw dzieli, a następnie dodaje, użyj liczb całkowitych muldiv, czyli:Używanie tego jest takie samo mnożenie
a*f
, z ułamkiem = liczba całkowita f =.Ta wydajność,
r=a*f
alef=b/c
potem,r=a*b/c
ale jeszcze nie działa, ponieważ pozycja operatorów, funkcja końcowar=(a*b)/c
lub funkcja muldiv, sposób obliczania liczb ułamkowych przy użyciu tylko liczby całkowitej.źródło