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

Uśrednianie pomiarów z ADC- z podejrzeniem paranoi.

janbernat 16 Paź 2010 19:37 6693 28
  • #1 8627592
    janbernat
    Poziom 38  
    Zrobiłem sobie takie uśrednienie:
    
    volatile int ADC_sr;
    //*******************
    ISR(ADC_vect)
    {            
    int ADC_nowe;
    ADC_nowe=ADC;
    ADC_sr=(ADC_sr+ADC_nowe)/2;
    }
    

    No i skacze- co 2/100V.
    Tu cały kod- jakby się komuś chciało czytać:
    
    #include <stdint.h>
    #include <avr/io.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    #include "HD44780.h"	//to jest radzia
    #include "TWI.h"		//i to też jest radzia
    
    #define LICZBA_KANALOW      4 //cztery kanały- tu można zwiększyć ilość kanałów 
    #define PCF8574ADDR			0x20	// to też teraz chwilowo nieważne
    
    volatile uint8_t flaga_pomiaru;
    volatile uint8_t flaga_silnika;
    volatile int ADC_sr;
    unsigned int napiecie[LICZBA_KANALOW];
    
    //prototypy funkcji
    unsigned int debounce(void);
    void SetAdcKanal(void);	
    uint8_t advance_step(int8_t nsteps);
    
    
    int main(void)	// tu jest początek main() i tu ustawiamy konfigurację rejestrów
    {   
    	char buffer_nap[LICZBA_KANALOW];
    	char *text="Kolejne";
        char *text1="testy";
    	uint8_t i=0;	
    //	uint8_t stany;
    
        sei();
        DDRB=255;
        PORTB=255;
    	DDRC=0;
    	PORTC=0xf0; 
     	TCCR0=_BV(CS00)|_BV(CS02);// TIMER0 przepełnia się co 16ms
        ADMUX = _BV(REFS0)|_BV(REFS1) ;//Wewnętrzne źródło napięcia odniesienia-ok.2.56V      
        ADCSRA = _BV(ADEN)|_BV(ADIE)|_BV(ADATE)|_BV(ADPS0)|_BV(ADPS1)|_BV(ADPS2)|_BV(ADSC);//
    
    	TIMSK=_BV(TOIE0);
    
    	TWSR = 0; //******** ustawianie I2C                       
    	TWBR=72;
    
        LCD_Initalize();
        LCD_WriteText(text);
        LCD_GoTo(0,1);
        LCD_WriteText(text1);      
        _delay_ms(300);
        LCD_GoTo(0,0);
        LCD_WriteText("                ");
    	LCD_GoTo(0,1);
    	LCD_WriteText("                ");
    	LCD_GoTo(5,0);											
    	LCD_WriteText("V");
    	LCD_GoTo(5,1);
    	LCD_WriteText("V");
    	LCD_GoTo(12,0);
    	LCD_WriteText("A aku");
    	LCD_GoTo(12,1);
    	LCD_WriteText("V foto");
    
    
    	while(1)			//A tu zaczyna się nieskończona pętla
    	{
    		if(flaga_pomiaru>=8)//***************Tu sprawdzamy flagę i dlatego ten fragment wykonuje się co 1/4s		
    		{			
    			for(i = 0; i < LICZBA_KANALOW; i++)//pętla pomysłu gaskoina- trochę niewygodna- ale co zrobić jak dobra
    			{
    			    if(i<2)
    				{
    			        LCD_GoTo(0,i);
    			        LCD_GoTo(0,i);
    		        } 
    				else
    				{
    				    LCD_GoTo(7,i-2);
    			        LCD_GoTo(7,i-2);
    			    }
    			    LCD_WriteText(dtostrf(napiecie[i]/50.0,5,2,buffer_nap));
    
    				
    			}
    	    flaga_pomiaru=0;	// A tu flagę kasujemy- po 1/4s znowu się ustawi
    		SetAdcKanal();
    		}		//Tu się kończy ten fragment co się wykonuje co 1/4s
    
    			
    //************* a tu silniczek się kręci w stronę słońca- sprawdzając napięcie na fotoopornikach
    
    		if(flaga_silnika)// co 16ms/2=8ms
    		{
    			if(napiecie[1]+5<napiecie[3])	
    			{
    			twistart();
    			twiwrite(0x40 | (PCF8574ADDR << 1));
    			twiwrite(advance_step(1)|0x0f);
    			twistop();
    			}
    			if(napiecie[3]+5<=napiecie[1])	
    			{
    			twistart();
    			twiwrite(0x40 | (PCF8574ADDR << 1));
    			twiwrite(advance_step(-1)|0x0f); 
    			twistop();
    			}
    		flaga_silnika=0;
    		} 
    	}
    }
    
    ISR(ADC_vect)
    {            
    int ADC_nowe;
    ADC_nowe=ADC;
    ADC_sr=(ADC_sr+ADC_nowe)/2;
    }						
    
    ISR(TIMER0_OVF_vect)		// no ale przerwanie od TIMER0 też wykorzystamy- do pomiaru i silnika
    {
    	TCNT0=127;
    	flaga_silnika++;
    	flaga_pomiaru++;
    }
    
    //***********************Program Dr_Vee do sterowania silnika- nieco zmieniony
    enum { NUM_STEPS = 8 }; 
    enum { STEP_MASK = 0xf0 };
    // Układ I2C: A B C D x x x x
    static const uint8_t steps[NUM_STEPS] =
    {
    	0b10000000, 0b11000000, 0b01000000, 0b01100000, 0b00100000, 0b00110000, 0b00010000, 0b10010000 
    };
    
    // obrót o 1/2 kroku w prawo: advance_step(1) 
    // obrót o 1/2 kroku w lewo:  advance_step(-1) 
    uint8_t advance_step(int8_t nsteps)
    {
        static uint8_t stepNum = 0;
    	static uint8_t kroczek;
        kroczek = (kroczek & ~STEP_MASK) | steps[stepNum];
        stepNum += nsteps;
        stepNum %= NUM_STEPS;
    	return kroczek;
    }
    //*****************************a tu funkcja zmieniająca kanały- definicja	- to chyba też gaskoina
    void SetAdcKanal(void) 
    {
    	static uint8_t kanal;
    	ADMUX = ((ADMUX&0xE0)+ kanal);//zmienić kanał	
    
    	napiecie[kanal++]=ADC_sr;		// zapisać pomiar w tablicę
    
        if(kanal>=LICZBA_KANALOW)		
    	kanal=0;  
    } 
    /*
    void inline ADCSetChannel(uint8_t channel)
    {
        ADMUX = ((ADMUX&0xE0)+ channel);
    }
    
    void inline ADCMeasure()
    {
        ADCSRA |=(1 << ADSC) ;                //zacznij pomiar	
    }
    */
    

    A chciałbym zeby pomiar nie skakał.
    Może to przez to dzielenie przez dwa.
    A może to wcale nie jest uśrednienie- tylko tak mi się wydaje.
  • Pomocny post
    #2 8627674
    RitterX
    Poziom 39  
    Możesz zmniejszyć częstość próbkowania, zastosować filtr całkujący (RC) na wejściu A/C albo/i zastosować filtr medianowy. Wystarczy mediana z pięciu pomiarów. Najprościej działa to tak, że bierzesz 5 ostatnich pomiarów, sortujesz od najmniejszego do największego i wybierasz środkowy, trzeci element. Jak przychodzi nowy pomiar to usuwasz najstarszy w puli i po dodaniu nowego znowu sortujesz i wyłaniasz "zwycięzcę".
  • Pomocny post
    #3 8628891
    gaskoin
    Poziom 38  
    nie do końca to jest uśrednianie (chyba, że tylko dwóch pomiarów)
    Załóżmy, że masz idealne napięcie 4V na przetworniku:

    adc_sr na początku = 0
    adc_nowe = 4
    adc_sr = (0 + 4)/2 = 2 (srednia jak ja Cie krece :D)


    adc_nowe = 4
    adc_sr = (2 + 4)/2 = 3

    adc_nowe = 4
    adc_sr = (3 + 4)/2 = 3.5

    adc_nowe = 4
    adc_sr = (3.5 + 4)/2 = 3.75

    generalnie dąży to do 4, ale żadną średnią absolutnie nie jest.

    Jakbyś chciał robić średnią wszystkich dotychczasowych pomiarów musiałoby to wyglądać tak:

    adc_nowe = ADC; //(btw nie wiem po co Ci ta zmienna, procesor tylko marnuje pamięć i czas)
    
    ADC_sr = (ADC_sr*liczba_dotychczasowych_pomiarow + adc_nowe)/(++liczba_dotychczasowych pomiarów);



    Dzięki czemu masz tak:

    adc_sr na początku = 0
    adc_nowe = 4
    adc_sr = (0*0 + 4)/1 = 4


    adc_nowe = 4
    adc_sr = (4*1 + 4)/2 = 4

    adc_nowe = 4
    adc_sr = (4*2 + 4)/3 = 4

    adc_nowe = 4
    adc_sr = (4*3 + 4)/4 = 4

    itd, i zawsze będzie to średnia arytmetyczna
  • Pomocny post
    #4 8629334
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Zamiast tak karkołomnych metod proponuję poczytać informacje o średniej kroczącej - http://pl.wikipedia.org/wiki/Średnia_krocząca , http://en.wikipedia.org/wiki/Moving_average - w skrócie jest to średnia z ostatnich X pomiarów - wg mnie najlepiej nadaje się do uśredniania pomiarów z ADC.

    4\/3!!
  • Pomocny post
    #5 8629876
    ktrot
    Poziom 20  
    Po co w ogóle coś liczyć - dodajemy kolejno przychodzące 8 lub 16 pomiarów a na koniec przesuwamy wynik o 3 lub 4 miejsca w prawo i mamy średnią. Ciekawą opcją jest średnia z 256 pomiarów dla przetwornika w trybie 8 bitowym: dodajemy kolejne 256 pomiarów a uśredniony arytmetycznie wynik sam ląduje w starszym bajcie zmiennej typu unsigned int.
  • #6 8629991
    janbernat
    Poziom 38  
    gaskoin jak dam:
    
    ISR(ADC_vect)
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    ADC_sr = (ADC_sr *(liczba_dotychczasowych_pomiarow) + ADC)/(++liczba_dotychczasowych_pomiarow);
    }
    

    to kompilator ostrzega że operacja może być niezdefiniowana.
    I rzeczywiście- same zera.
    A jak zwiększę poza działaniem:
    
    ISR(ADC_vect)
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    ++liczba_dotychczasowych_pomiarow;
    ADC_sr = (ADC_sr *(liczba_dotychczasowych_pomiarow) + ADC)/(liczba_dotychczasowych_pomiarow+1);
    
    }		
    

    To nie ma ostrzeżeń- ale też same zera.
    Pozaostałe sposoby sprawdzę.
  • Pomocny post
    #7 8630068
    gaskoin
    Poziom 38  
    To zrób to w dwóch operacjach:

    ISR(ADC_vect){
        static unsigned int liczba_dotychczasowych_pomiarow;
        ADC_sr = (ADC_sr *(liczba_dotychczasowych_pomiarow) + ADC)/(liczba_dotychczasowych_pomiarow + 1);
        liczba_dotychczasowych_pomiarow++;
    }


    Zobacz, czy tak działa.

    Najmądrzej jednak będzie mimo wszystko zrobić średnią kroczącą tak jak napisał Freddie Chopin. Bo w tym sposobie bierzesz wszystkie dotychczasowe próbki jakie były. Jeżeli napięcie będzie długo stałe (w średniej jaką liczysz) to na taki oto prosty przykład:

    masz 10 000 próbek z 4 V a 100001 próbka nagle ma 10kV (czysto teoretycznie), wtedy (4*10000 + 10000)/10001 = około 5V. Z takiej średniej mało co da się wyciągnąć. Z resztą poczytaj sobie.
  • Pomocny post
    #8 8630211
    sulfur
    Poziom 24  
    uint16_t pomiary_adc[8];
    
    ISR(ADC_vect){ 
        static uint8_t pomiar;
        static uint16_t ADC_srp; 
        ADC_srp -= pomiary_adc[pomiar];
        pomiary_adc[pomiar] = ADC;
        ADC_srp += pomiary_adc[pomiar];
        pomiar = (++pomiar) & 7; 
        //ADC_sr = ADC_srp>>3; // srednia arytmetyczna z 8 pomiarow wg ktrot
        ADC_sr = ADC_srp / 8;
    }


    Pisane na kolanie. Ciekawe rozwiązanie średniej arytmetycznej dla 8 pomiarów podał kolega ktrot. Jeśli twierdzenie kolegi to prawda, to usunąć ostatnią linijkę i usunąć komentarz z przedostatniej linijki.

    Wadą tego rozwiązania jest to, że prawidłowy wynik dostaniemy dopiero po 8 pomiarach ADC. To, co będzie wcześniej to nieprawda.
  • #9 8630225
    janbernat
    Poziom 38  
    To już sprawdzałem- same zera.
    Chyba zrobię metodą Freddie'go Chopina.
    Bo zrobiłem metodą ktrota- jakieś straszne wyniki wychodzą.
    Z tego co wyczytałem o średniej kroczącej- to chyba trzeba zrobić tablicę.
    Ale co mnie męczy- bo jak coś ruszę w C to zaraz wpadam w jakieś kolczaste krzaki- dlaczego kompilator ostrzega w Twoim rozwiązaniu że tego nie policzy:
    
    ADC_sr = (ADC_sr *(liczba_dotychczasowych_pomiarow) + ADC)/(++liczba_dotychczasowych_pomiarow);

    I faktycznie nie liczy.
    Przecież ta zmienna jest zwiększana tylko wewnątrz tego działania.
    [/code]
    P.S.
    I zwróćcie uwagę że to wszysto odbywa się w przerwaniu- czyli o ile pamiętam co kilkaset us.
    A odczyt ADC_sr co kilkaset ms.
    Potem to muszę jakoś "uładzić" po to aby obługa tego przerwania nie zabierała co chwilę czasu procesora.
    A tak zastosowałem pomysł ktrota:
    
    ISR(ADC_vect)
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    static unsigned int ADC_temp;
    ADC_temp =ADC_temp + ADC;
    ++liczba_dotychczasowych_pomiarow;
    if(liczba_dotychczasowych_pomiarow==7)
    	{ADC_sr=(ADC_temp/8);// podobno kompilator sam przesuwa o 8
    	liczba_dotychczasowych_pomiarow=0;
    	}						
    }
    

    Ale sprawdzę rozwiązanie sulfura.
    Ale za jakiś czas- mam zrobić szybko obiad dla dzieci.
  • Pomocny post
    #10 8630391
    ktrot
    Poziom 20  
    Nie ==7 bo odliczasz tylko 7 pomiarów (od 0 do 6) ale ważniejsze jest to, że oprócz liczby pomiarów musisz w bloku if zerować ADC_temp.
  • #11 8632163
    janbernat
    Poziom 38  
    Teraz tak wygląda pomysł ktrota:
    
    ISR(ADC_vect)
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    static unsigned int ADC_temp;
    ADC_temp+=ADC;
    ++liczba_dotychczasowych_pomiarow;
    if(liczba_dotychczasowych_pomiarow==16)
    	{ADC_sr=(ADC_temp>>4);
    	liczba_dotychczasowych_pomiarow=0;
    	ADC_temp=0;
    	}						
    }
    

    Dwa pomiary z potencjometrów zmieniają się co kilkadziesiąt sekund o 0.02V
    Pozostałe dwa pomiary są z fotooporników oświetlanych świetlówką- więc trochę skaczą.
    Ale po zamianie foto na rezystory są też całkiem stabilne.
    Tak że sposób działa.
    Nie wiem czy to co wpisałem jest optymalne- czy nie wymaga zmian.
    Proszę o uwagi.
    Druga sprawa- wyniki nawet jak się zmieniają z rzadka- to zawsze o 0.02V- nigdy o 0.01V.
    Nie wiem gdzie jest haczyk.
    Trzecia- ale nie ostatnia- dalej nie rozumiem dlaczego w programie gaskoina wpisanie (++liczba_dotychczasowych_pomiarow) generuje ostrzeżenie a wpisanie
    (liczba_dotychczasowych_pomiarow+1) nie.
    To że działa bardzo cieszy- ale dlaczego i czy nie ma błędów- jeszcze bardziej.
  • Pomocny post
    #12 8632252
    sulfur
    Poziom 24  
    1. Wygląda dobrze.
    2. Nie zamieniać na V tylko podglądać wartość przed przeliczeniem <0, 1023>
    3. ++ modyfikuje zmienną, +1 nie. Wchodzi w grę kolejność obliczeń. A zmienna występuje w "równaniu" dwa razy. Sugestia kompilatora prawidłowa.
  • #13 8632458
    janbernat
    Poziom 38  
    ad.3- chyba załapałem.
    W jednym równaniu mamy dwie różne wartości "liczba_dotychczasowych_pomiarow".
    ad.2- nie załapałem.
    Ostatecznie jest to "obrabiane" przez:(dtostrf(napiecie[i]/50.0,5,2,buffer_nap));
    Moze to jest spowodowane przez przesunięcie powodujące dzielenie przez dwa.
    Dalej nie kojarzę.
    ad.1- dzięki.
  • Pomocny post
    #14 8632966
    Freddie Chopin
    Specjalista - Mikrokontrolery
    janbernat napisał:
    napiecie[i]/50.0

    Podziel sobie 1 (najmniejsza zmiana Twojej zmiennej) na 50 i zobacz ile wyjdzie, a potem zastanów się jakim cudem miałoby się zmieniać o 0.01.

    No i daj sobie spokój z tym przesuwaniem zamiast dzielenia - jak dzielisz przez potęgi dwójki, to kompilator sam zadba o to, żeby było wydajne przesunięcie, a nie mało optymalne dzielenie.

    4\/3!!
  • #15 8633047
    hotdog
    Poziom 26  
    janbernat napisał:
    ad.3- chyba załapałem.
    W jednym równaniu mamy dwie różne wartości "liczba_dotychczasowych_pomiarow".


    Kompilator w sumie nie wie, czy pierwsza wartości ma być zwiększona czy nie (czyli zakłada że nie wiesz co robisz :P). Podejrzewam że to nie jest zestandaryzowane, dlatego kompilator pluje. Chce Ciebie uchronić przed błędem. Ciesz się, bo jak by to przeszło bez niczego, to mógłbyś jeszcze błędu szukać.

    Dzielenie było by wykonane ostatnie, więc logicznie pierwsze działanie powinno być wykonane ze starą wartością zmiennej (przynajmniej tak każe mówić wizja skompilowanego programu w asemblerze)

    Normalnie taka notacja ++/-- jest równoznaczna z np:
    x = (abc++)/5;
    na:
    x = abc/5;
    abc++
    a np:
    x = (--abc) /5;
    na:
    --abc;
    x = abc/5;

    a coś takiego
    x = abc*(++abc) / 5
    już nie bardzo wiadomo czemu jest równoważne. Z resztą podobnie np
    x = (++abc)*(++abc) / 5
    logika programowania każe takich notacji/sytuacji unikać

    Standard za to np opisuje jak ma się program zachować w takich sytuacjach jak

    if ((x == 1) && (++abc == 5))

    W tym przypadku abc zwiększy się, tylko w przypadku kiedy x będzie równe 1.

    Podobnie tutaj
    if ((x ==1) || (++abc == 5))

    W tym przypadku abc zwiększy się tylko jeżeli x będzie różne od 1.

    Te 2 przytoczone przykłady z if'em to celowa optymalizacja kompilatora (po co sprawdzać drugi warunek jeżeli pierwszy od razu daje pewność że całe wyrażenie to prawda lub fałsz).

    Jest to bardzo ważna zasada, której często ludzie uczący się z kursów, lub przykładów nie znają. Trochę zszedłem z tematu...
  • #16 8633232
    Konto nie istnieje
    Konto nie istnieje  
  • #18 8634086
    Konto nie istnieje
    Konto nie istnieje  
  • Pomocny post
    #19 8634365
    ktrot
    Poziom 20  
    Cytat:

    Racja !
    powinno być
    
    for(i=0;i<8;i++) bufor[i] = ADC; 
     

    Bynajmniej. Ta procedura przerwania zawiera jeszcze cztery inne błędy, z których najpoważniejszy jest właśnie w tej linijce. Przerwanie od A/D jest zgłaszane wtedy gdy zakończona jest konwersja i rejestr ADC zawiera wynik tej konwersji. Jego odczyt rozpoczyna następną konwersję ale taki szybki odczyt rejestru ADC w pętli for bez czekania na koniec konwersji powoduje, że 7 następnych odczytów nie ma żadnej wartości.

    @janbernat
    Możesz uzyskać rozdzielczość 0.01V zwiększając rozdzielczość przetwornika do 11 bitów. W tym celu wykonaj np 16 pomiarów (tak jak masz w tej chwili) ale przesuń wprawo nie o 4 ale o 3bity, następnie podziel ten wynik nie przez 50 ale przez 100 - zakres się nie zmieni. Jest to opisane w jakiejś nocie Atmela o oversamplingu (może ktoś zna jej numer - ja nie pamiętam).
  • #21 8635880
    janbernat
    Poziom 38  
    Dzięki- ale oversampling może będę przerabiał w nastęnym semestrze.
    Pomysł ktrota oraz uwaga Freddie'go Chopina są trafne.
    Już nie mówiąc o tym że jest to model- to co pokazuje ADC jest regulowane potencjometrami.
    To co mnie męczy to jest ta pętla:
    
    	while(1)			//A tu zaczyna się nieskończona pętla
    	{
    		if(flaga_pomiaru>=8)//***************Tu sprawdzamy flagę i dlatego ten fragment wykonuje się co 1/4s		
    		{			
    
    			for(i = 0; i < LICZBA_KANALOW; i++)//pętla pomysłu gaskoina- trochę niewygodna- ale co zrobić jak dobra
    			{
    			    if(i<2)
    				{
    			        LCD_GoTo(0,i);
    			        LCD_GoTo(0,i);
    		        } 
    				else
    				{
    				    LCD_GoTo(7,i-2);
    			        LCD_GoTo(7,i-2);
    			    }
    			    LCD_WriteText(dtostrf(napiecie[i]/100.0,5,2,buffer_nap));
    
    				
    			}
    
    	    flaga_pomiaru=0;	// A tu flagę kasujemy- po 1/4s znowu się ustawi
    		SetAdcKanal();
    		}		//Tu się kończy ten fragment co się wykonuje co 1/4s
    

    z tym przerwaniem:
    
    ISR(ADC_vect)
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    static unsigned int ADC_temp;
    ADC_temp+=ADC;
    ++liczba_dotychczasowych_pomiarow;
    if(liczba_dotychczasowych_pomiarow==16)
    	{ADC_sr=(ADC_temp/16);
    	liczba_dotychczasowych_pomiarow=0;
    	ADC_temp=0;
    	}						
    }
    

    Przerwanie jak to przerwanie- przychodzi jak ADC skończy przetwarzanie.
    Po 16 przerwaniach zapisuje ADC_sr
    Co jakiś czas- miało być co 1/4s ale tak przerabiałem że muszę sprawdzić- no ale co jakiś czas następuje w pętli głównej odczyt wszystkich kanałów.
    Po tym odczycie (w pętli for) następuje zmiana kanałów.
    Są to zdarzenia asynchroniczne- przerwanie od ADC i sprawdzanie.
    Na pewno jeden z kanałów jest w trakcie przetwarzania.
    No ale z drugiej strony nowa wartość ADC_sr jest zapisywana do tablicy dopiero po zakończeniu przetwarzania- po 16 pomiarach.
    Może ktoś widzi błąd w tym rozumowaniu.
  • #22 8636438
    Konto nie istnieje
    Konto nie istnieje  
  • #23 8636702
    gaskoin
    Poziom 38  
    Jeszcze jedna uwaga:

    Po co marnować pamięć na zmienną która marnuje dodatkowo czas w przerwaniu?

    Masz operacje:

    ADC_temp += ADC;


    masz odczyt wartości zmiennej ADC_temp, odczyt ADC, dodanie do wartości ADC_temp wartości spod ADC

    ADC_sr = ADC_temp/16;


    odczyt wartości ADC_temp

    ADC_temp = 0;


    wyzerowanie zmiennej ADC_temp

    przy kodzie:

    ISR(ADC_vect)
    {           
    static unsigned int liczba_dotychczasowych_pomiarow;
    ++liczba_dotychczasowych_pomiarow;
    if(liczba_dotychczasowych_pomiarow==16)
       {ADC_sr=(ADC/16);
       liczba_dotychczasowych_pomiarow=0;
       }                  
    } 


    działało by to deczko szybciej, bo masz tylko odczyt ADC. BTW dlaczego niewygodna ?
  • #24 8636762
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Przecież w tym Twoim kodzie nie ma żadnego uśredniania... Idąc tym tropem ten kod będzie najbardziej optymalny:

    ISR(ADC_vect) 
    { }


    <;

    No i weźcie przestańcie już z tym "marnowaniem pamięci i czasu" - kompilator zastosuje tyle zmiennych ile naprawdę jest potrzebnych (przy włączonej optymalizacji od course), więc po co chcecie być sprytniejsi od niego? Jest takie przysłowie- "premature optimization is the root of all evil" - polecam je autorowi wątku, aby na początku zajął się jednak ideą programowania w C, a nie bezsensownym i niepotrzebnym optymalizowaniem na siłę, zakręconymi konstrukcjami i niejasnymi algorytmami. Wszystko przyjdzie potem samo - w odpowiednim czasie.

    4\/3!!
  • #25 8636798
    janbernat
    Poziom 38  
    _marek- tak właściwie to działa.
    Bo jest jeszcze funkcja:
    
    //*****************************a tu funkcja zmieniająca kanały- definicja	- to chyba też gaskoina
    void SetAdcKanal(void) 
    {
    	static uint8_t kanal;
    	ADMUX = ((ADMUX&0xE0)+ kanal);//zmienić kanał	
    	napiecie[kanal++]=ADC_sr;			// zapisać pomiar w tablicę
        if(kanal>=LICZBA_KANALOW)		
    	kanal=0;  
    } 
    

    która jest wywoływana- teraz w przerwaniu a przedtem po for w pętli głównej- w której zapisany zostaje ADC_sr po 16 przetwarzaniach.
    Róznica jest niewielka- działa dobrze.
    Z jednej strony- skracam czas obsługi przerwania.
    Z drugiej- jakby logiczniej- gdy po 16 kolejnych przerwaniach jest wynik dla kanału 0 to przechodzi do następnego.
    P.S.
    Teraz mam dylemat- co dalej.
    Mam PCF8583 zamontowany- można by się nauczyć struktur i w końcu zrobić jakieś menu- nigdy tego nie udało mi się zrobić.
    Albo spróbować zrobić rampę do startowania i hamowania silnika krokowego.
    Ten silnik też jest zamontowany.
    Co myślicie zrobić najpierw?
  • #27 8636886
    janbernat
    Poziom 38  
    Freddie Chopin- nic samo nie przyjdzie.
    Trzeba się narobić.
    Poza tym-straciłem wątek kolejności odpowiedzi.
    Czy to jest- czy nie jest usrednianie:
    
    ISR(ADC_vect)//a tu przerwanie od ADC
    {            
    static unsigned int liczba_dotychczasowych_pomiarow;
    static unsigned int ADC_temp;
    ADC_temp+=ADC;
    ++liczba_dotychczasowych_pomiarow;
    if(liczba_dotychczasowych_pomiarow==16)
    	{ADC_sr=(ADC_temp/16);
    	liczba_dotychczasowych_pomiarow=0;
    	ADC_temp=0;
    	SetAdcKanal();
    	}
    }		
    
  • #29 8818531
    dzarek1
    Poziom 14  
    A co powiecie na takie rozwiązanie ?
    void PomiarSr(){
    	int i=0;
    	nap_odn = ADCH;
    	while(i&=100){
    		nap_odn = nap_odn + ADCH;
    		i++;
    	}
    	nap_odn = nap_odn / 100;
    }
    

    U mnie średnia wartość musi być ustalona tylko raz, kilka sec po starcie układu, który jest mierzony.
    Kolejny pomiar wykonywany jest po odczytaniu wartości ADCH, więc przetwornik nie działa cały czas, tak jak by to było, gdyby obsługa sczytywania była zawarta w przerwaniu.
    W przerwaniu natomiast mam tylko zawarte ustawienie flagi, która będzie wykorzystana w dalszej części programu, do sterowania sprzężeniem zwrotnym i utrzymywaniu napięcia na poziomie nap_odn.
REKLAMA