[BASCOM] Uśrednianie wyników ADC
: 20 lut 2018, 9:18
O uśrednianiu słyszał każdy, ale nie każdy do końca rozumie sposoby i ich konsekwencje. Postaram się przedstawić trzy przykłady.
Zwykła średnia
Najprostszym sposobem jest zebranie kilku próbek i podzielenie ich. Na przykład, wynik z ADC ma maksymalnie 1023 a Word pomieści 65536. Można więc spokojnie w jednym takim Word dodać do siebie 64 pomiary i potem szybko podzielić przez 64.
Wadą tego rozwiązania jest to, że program się tu zatrzymuje na czas pomiarów. Czyli musi 64 razy zmierzyć...
Można zbierać mniej próbek... W zależności od programu nie musi to wcale przeszkadzać. Kod takiego rozwiązania, gotowy do przetestowania w Bascomowym symulatorze, poniżej. Zwróć uwagę że Bascom pod suwakiem ADC pokazuje co ustawiłeś a na wyświetlaczu masz wynik
Średnia ciągniona
Innym sposobem jest przechowywanie większej ilości poprzednich wyników a nowe odczyty pomału wpływają na na całość średniej.
W tym celu musimy mieć tablicę Wordów(n) i to jest po części wadą tego rozwiązania jeśli mamy uC z małą ilością SRAM.
Sposób działania jest prosty. Mamy zmienną która pokazuje do której komórki tablicy zapiszemy sobie nowy wynik (tutaj Idx).
Zawsze odczytujemy nowy wynik i zawsze sumujemy wszystkie próbki a potem dzielimy przez ilość próbek. To jakby drugi minus tego rozwiązania.
Inne rozwiązanie
Kolejne rozwiązanie nie potrzebuje ani czekać na dużo pomiarów, ale też nie potrzebuje tablicy. Poprzednia suma jest przechowywana w zmiennej typu DWORD.
Wystarczy popatrzeć żeby zobaczyć jak to działa Jeśli masz więcej kanałów ADC do uśrednienia to dla każdego potrzebujesz osobną Sumę DWORD (cztery bajty).
Dla dwóch ostatnich kodów nagrałem film z symulacji Bascomowej. Sam oceń co działa/reaguje szybciej.
Oczywiście metodą średniej ciągnionej przyspieszymy kod zmniejszając ilość próbek (wielkość tablicy).
Można też użyć ilośc próbek która będzie potęgą dwójki czyli 16,32,64 i skorzystać z szybkiego dzielenia przesunięciem bitowym Shift.
[media]https://drive.google.com/file/d/1-R9P4I ... sp=sharing[/media]
Mediana
Mediana - odsyłam do Wikipedii jeśli nie uda mi się tego przedstawić jaśniej - to "zebranie kilku próbek, posortowanie ich w kolejności rosnącej/malejącej i wybranie środkowej"
Najprościej pisząc. Jeśli mamy zbiór pięciu odczytów z ADC (do tego musimy je przechowywać w tablicy) to na końcu sortujemy je.. i tak powiedzmy, że odczyty były 512, 511, 120, 513,511 to widać że odczyty głównie mają 512, ale jeden odczyt 120 zaburza cała sprawę i dla średniej arytmetycznej miałby duży wpływ..
Natomiast mediana to posortowanie tych wyników od najmniejszego do największego i wybranie środkowego. To odrzuca anomalie.
Sortujmy 120,511,511,512,513 i wynikiem tej mediany będzie 511 kiedy średnia arytmetyczna wyniosłaby...433! (dodaj wszystkie do siebie i podziel/5)
Najprostszy kod mediany poniżej. Sort to funkcja Bascoma
Zwykła średnia
Najprostszym sposobem jest zebranie kilku próbek i podzielenie ich. Na przykład, wynik z ADC ma maksymalnie 1023 a Word pomieści 65536. Można więc spokojnie w jednym takim Word dodać do siebie 64 pomiary i potem szybko podzielić przez 64.
Wadą tego rozwiązania jest to, że program się tu zatrzymuje na czas pomiarów. Czyli musi 64 razy zmierzyć...
Można zbierać mniej próbek... W zależności od programu nie musi to wcale przeszkadzać. Kod takiego rozwiązania, gotowy do przetestowania w Bascomowym symulatorze, poniżej. Zwróć uwagę że Bascom pod suwakiem ADC pokazuje co ustawiłeś a na wyświetlaczu masz wynik
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- $regfile = "m8def.dat"
- $crystal = 8000000
- $hwstack = 40
- $swstack = 16
- $framesize = 32
- $sim
- 'OZNACZA PRZYGOTOWANIE KODU DO SYMULACJI - USUNĄĆ DLA PRAWDZIWEGO MIKROKONTROLERA
- Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
- Config Lcd = 16x2
- Deflcdchar 0 , 32 , 32 , 14 , 17 , 31 , 16 , 14 , 1
- Deflcdchar 2 , 32 , 32 , 14 , 1 , 15 , 17 , 15 , 1
- Cursor Off
- Cls
- Config Adc = Single , Prescaler = Auto , Reference = Avcc
- Const Ilosc_probek = 50
- Dim Adc_read As Word , Idx As Byte
- Do
- Adc_read = 0
- For Idx = 1 To 64
- Adc_read = Adc_read + Getadc(0)
- Next
- Shift Adc_read , Right , 6
- Locate 1 , 1 : Lcd Adc_read ; " "
- Loop
Średnia ciągniona
Innym sposobem jest przechowywanie większej ilości poprzednich wyników a nowe odczyty pomału wpływają na na całość średniej.
W tym celu musimy mieć tablicę Wordów(n) i to jest po części wadą tego rozwiązania jeśli mamy uC z małą ilością SRAM.
Sposób działania jest prosty. Mamy zmienną która pokazuje do której komórki tablicy zapiszemy sobie nowy wynik (tutaj Idx).
Zawsze odczytujemy nowy wynik i zawsze sumujemy wszystkie próbki a potem dzielimy przez ilość próbek. To jakby drugi minus tego rozwiązania.
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- $regfile = "m8def.dat"
- $crystal = 8000000
- $hwstack = 40
- $swstack = 16
- $framesize = 32
- $sim
- 'OZNACZA PRZYGOTOWANIE KODU DO SYMULACJI - USUNĄĆ DLA PRAWDZIWEGO MIKROKONTROLERA
- Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
- Config Lcd = 16x2
- Deflcdchar 0 , 32 , 32 , 14 , 17 , 31 , 16 , 14 , 1
- Deflcdchar 2 , 32 , 32 , 14 , 1 , 15 , 17 , 15 , 1
- Cursor Off
- Cls
- Config Adc = Single , Prescaler = Auto , Reference = Avcc
- Const Ilosc_probek = 50
- Dim Tablica(ilosc_probek) As Word
- Dim Idx As Byte , Iteration As Byte
- Dim Sum As Dword , Wynik As Word
- Do
- Incr Idx : If Idx > Ilosc_probek Then Idx = 1
- Tablica(idx) = Getadc(0)
- Sum = 0
- For Iteration = 1 To Ilosc_probek
- Sum = Sum + Tablica(iteration)
- Next
- Sum = Sum / Ilosc_probek
- Wynik = Sum
- Locate 1 , 1 : Lcd Wynik ; " "
- Loop
Inne rozwiązanie
Kolejne rozwiązanie nie potrzebuje ani czekać na dużo pomiarów, ale też nie potrzebuje tablicy. Poprzednia suma jest przechowywana w zmiennej typu DWORD.
Wystarczy popatrzeć żeby zobaczyć jak to działa Jeśli masz więcej kanałów ADC do uśrednienia to dla każdego potrzebujesz osobną Sumę DWORD (cztery bajty).
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- $regfile = "m8def.dat"
- $crystal = 8000000
- $hwstack = 40
- $swstack = 16
- $framesize = 32
- $sim
- 'OZNACZA PRZYGOTOWANIE KODU DO SYMULACJI - USUNĄĆ DLA PRAWDZIWEGO MIKROKONTROLERA
- Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.5 , Db6 = Portd.6 , Db7 = Portd.7 , E = Portd.3 , Rs = Portd.2
- Config Lcd = 16x2
- Deflcdchar 0 , 32 , 32 , 14 , 17 , 31 , 16 , 14 , 1
- Deflcdchar 2 , 32 , 32 , 14 , 1 , 15 , 17 , 15 , 1
- Cursor Off
- Cls
- Config Adc = Single , Prescaler = Auto , Reference = Avcc
- Dim Adc_read As Word
- Dim Suma As Dword
- Dim Wynik As Word
- Dim Help As Dword
- Do
- Adc_read = Getadc(0)
- Help = Suma
- Shift Help , Right , 3
- Suma = Suma - Help
- Suma = Suma + Adc_read
- Help = Suma
- Shift Help , Right , 3
- Wynik = Help
- Locate 1 , 1 : Lcd Wynik ; " "
- Loop
- End
Oczywiście metodą średniej ciągnionej przyspieszymy kod zmniejszając ilość próbek (wielkość tablicy).
Można też użyć ilośc próbek która będzie potęgą dwójki czyli 16,32,64 i skorzystać z szybkiego dzielenia przesunięciem bitowym Shift.
[media]https://drive.google.com/file/d/1-R9P4I ... sp=sharing[/media]
Mediana
Mediana - odsyłam do Wikipedii jeśli nie uda mi się tego przedstawić jaśniej - to "zebranie kilku próbek, posortowanie ich w kolejności rosnącej/malejącej i wybranie środkowej"
Najprościej pisząc. Jeśli mamy zbiór pięciu odczytów z ADC (do tego musimy je przechowywać w tablicy) to na końcu sortujemy je.. i tak powiedzmy, że odczyty były 512, 511, 120, 513,511 to widać że odczyty głównie mają 512, ale jeden odczyt 120 zaburza cała sprawę i dla średniej arytmetycznej miałby duży wpływ..
Natomiast mediana to posortowanie tych wyników od najmniejszego do największego i wybranie środkowego. To odrzuca anomalie.
Sortujmy 120,511,511,512,513 i wynikiem tej mediany będzie 511 kiedy średnia arytmetyczna wyniosłaby...433! (dodaj wszystkie do siebie i podziel/5)
Najprostszy kod mediany poniżej. Sort to funkcja Bascoma
Code: [Zaznacz cały] [Rozwiń/Zwiń]
- Const Ilosc_probek_adc = 5
- Const Mediana_adc =(ilosc_probek_adc + 1) / 2
- Dim Adcy(ilosc_probek_adc) As Word
- N = Ilosc_probek_adc : Adc_sum = 0
- Do
- Adcy(n) = Getadc(adca , 3 )
- Decr N
- Loop Until N = 0
- Sort Adcy(1)
- Adc_result = Adcy(mediana_adc)