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

licznik róznicowy atmega c++

qutrit 01 Lip 2010 09:57 2756 20
REKLAMA
  • #1 8249871
    qutrit
    Poziom 20  
    Witam.
    Otóż mam taki problem. Mianowicie chciałem zbudować licznik różnicowy na mikroprocesorze, działającego w ten sposób aby na wyświetlaczu pokazywał różnicę impulsów podawanych do uC.
    Mam do dyspozycji ATMEGE32 z trzema timerami T0,T1,T2. T1 wykorzystałem na przerwania do obsługi wyświetlacza. Pozostał T0,T2. T0 podłączyłem do kontaktronu. Natomiast nie ma wejścia T2 :( Stykając ze sobą blaszki kontaktronu (wejście T0) stan licznika na wyświetlaczu się zwiększa, więc do tego momentu jest OK.
    Mój pomysł polega na tym, aby w tym samym przedziale czasu (powiedzmy przez 10sek) zliczać impulsy z kontaktronów zapisywać je, a następnie odejmować i tak w kółko. Chciałbym aby układ oparty był o ilość impulsów a nie o czas pomiędzy nimi.
    W tym momencie mam problem, jak zrobić aby impulsy zliczane były dokładnie przez 10s z obu kontaktronów?
    Gdzie podłączyć drugi kontaktron skoro T1 wykorzystałem na wyświetlacze ?
    Nie chodzi mi oto żeby ktoś mi podał "gotowca" ale o naprowadzenie na właściwy trop. Dałbym sobie z tym radę gdybym miał do dyspozycji 5 timerów :)
    Dodam, ze przeczytałem sporą ilość postów, jednak większość z nich dotyczy zliczania z jednego kanału i pisana jest w bascomie (którego nie znam).
    Pozdrawiam.
  • REKLAMA
  • Pomocny post
    #2 8249899
    Mat_91
    Poziom 25  
    Przecież masz jeszcze piny przerwań Int0 int1 int2. Drugi kontaktron podłącz do jednego z tych pinów i zwiększaj stan jakiejś zmiennej w przerwaniu.
  • REKLAMA
  • #3 8250203
    qutrit
    Poziom 20  
    INTx słuzą do zewnętrznych przerwań w których mozna zwiększać jakąś zmienną. Tylko jak zrobić aby w tego typu przerwaniu zliczanie trwało przez 10sek.
    Powiedzmy, ze jakiś proces trwa 2h. Nie chodzi mi oto, aby przez 2h zliczać impulsy z obu kontaktronów a na końcu procesu je odjąć, chciałbym sprawdzać różnicę w impulsach co 10sek tzn. aby pomiar był odświeżany co 10sek i wyświetlany na wyświetlaczu.
  • REKLAMA
  • Pomocny post
    #4 8250230
    Mat_91
    Poziom 25  
    No to obydwa kontaktrony podepnij do pinów INTx, a później rób takie coś:

    1)wyzeruj zmienną x i y odpowiedzialną za aktualny stan zliczonych impulsów z wejśc INT0 i INT1 (obydwa kontaktrony), wyzeruj timer0
    2)załącz timer 0 aby odmierzał czas 10 sekund
    3)poczekaj aż timer odmierzy czas, w międzyczasie są zliczane impulsy z wejść INTx
    4)po odmierzeniu 10 sekund wykonaj sobie te działania na zmiennych x i y, wyświetl wynik na lcd i wróć do punktu 1
  • Pomocny post
    #5 8251348
    archanoid
    Poziom 26  
    Witam . Zadanie jest trywialne . Potrzebujesz 3 zmiennych i dwóch przerwań zewnętrznych plus przerwanie do wyświetlacza. Po starcie procka ustawiasz wartość jednej z zmiennych która będzie równa ilości przerwań wyświetlacza w czasie 10 sekund . Podczas aktywacji przerwania od T1 aktywujesz przerwania od INT0 i INT1 . INT0 zwiększa jedną zmienną a INT1 drugą. W przerwaniu T1 zmienna o której mowa (10sekund) jest dekrementowana o 1 , i tak co przerwanie. Jak dojdzie do zera będzie oznaczało że upłynęło 10 sekund. zatrzymujesz przerwania INT0 i 1 i wykonujesz obliczenia. Prościej się nie da i wystarczy tylko jeden timer.
  • Pomocny post
    #6 8252263
    rpal
    Poziom 27  
    Kolega niech raczy zauważyć że kontraktron ma coś takiego jak drżenie styków. Może być tak że przerwanie jedno twoje załaczenie potraktuje jak wiele zboczy je wzywalających i cały pomiar weźmie w łeb bo się wykona po prostu wielokrotnie. Musisz użyć więc kolejnego sprawdzania stanu styku juz po wykonaniu przerwania tak aby się upewnić ze jest to "trzymanie" styków a nie przypadkowe drźenie które musisz sam określic sprawdzając jaka jest zwłoka czasowa na ustabilizowanie poziomu na danym kontraktronie. Albo też użyć np. przerzutnika typu D + zewnętrzne impulsy zegarowe (choćby z samego atmega) po to aby jednoznacznie wyznaczyć moment wystąpienia załączenia styków. lub chocby użyć układu RC który przy odpowiednio dużej pojemności kondensatora i opornika będzie miał sporą stałą czasową niwelującą w.w. drżeie styków tak samo jak się to dzieje w obsłudze klawiatury matrycowej gdzie drżenie styków jest sprawą którą należy brac pod uwagę.
  • #7 8252918
    gaskoin
    Poziom 38  
    po co to R ?
  • Pomocny post
    #8 8252963
    rpal
    Poziom 27  
    gaskoin napisał:
    po co to R ?

    Po to aby podczas ponownego rozwarcia styków na skutek drgań mimo kondensator ładując się przez opornik w dalszym ciągu utrzymywał 0 logiczne na pinie od przerwania. Kondensator musi się jakiś czas ładować. PRzez czasy pojmuję ms.
  • REKLAMA
  • #9 8253214
    qutrit
    Poziom 20  
    Więc tak. Na T0 zrobiłem przerwanie do wyświetlaczy-działa OK. Na T1 odmierzam 10sek. Na INT1 zrobiłem przerwanie zew. w którym umieściłem zmienną powiedzmy z++; W momencie kiedy stykam INT1 z plusem zmienna zwiększa się o 1 co ładnie pokazuje mi wyświetlacz. Ale w którym momencie (gdzie) mam umieścić czas 10 sekund aby przerwanie INT1 działało przez 10sek ?
    Jak dobrze rozumuję to przerwanie INT działa na zbocze, robi to co ma w instrukcji i "wyskakuje", następne zbocze itd... Problem jest chyba w tym jak zrobić, aby program pozostał w tym przerwaniu przez owe 10sek. zwiększając tylko zmienną z++; Jeśli coś źle rozumuję, to proszę mnie poprawić :)
  • #10 8253253
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Odpowiedź na pytanie:

    Mat_91 napisał:
    No to obydwa kontaktrony podepnij do pinów INTx, a później rób takie coś:

    1)wyzeruj zmienną x i y odpowiedzialną za aktualny stan zliczonych impulsów z wejśc INT0 i INT1 (obydwa kontaktrony), wyzeruj timer0
    2)załącz timer 0 aby odmierzał czas 10 sekund
    3)poczekaj aż timer odmierzy czas, w międzyczasie są zliczane impulsy z wejść INTx
    4)po odmierzeniu 10 sekund wykonaj sobie te działania na zmiennych x i y, wyświetl wynik na lcd i wróć do punktu 1


    4\/3!!
  • Pomocny post
    #11 8254021
    Konto nie istnieje
    Poziom 1  
  • #12 8254927
    qutrit
    Poziom 20  
    Wiem ze mam załączyć Timer1, tylko w którym miejscu to zrobić, tak aby to było zsynchronizowane z liczeniem ? Jakby ktoś narysował mi strzałkę w poniższym kodzie byłbym wdzięczny :)
    
    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <util/delay.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <wyswietlacze.h>
    
    volatile uint8_t (wysw);
    volatile uint8_t d=0,s=0,j=0, liczba=0;
    
    int main (void)
    {
    DDRC=(1<<0)|(0<<1)|(0<<6);
    DDRA = 0xFF;
    DDRD = 0x0f;
    TCCR0 |= (1<<WGM01)|(0<<WGM00);
    OCR0 =255;
    TCCR0 |= ((0<<CS02)|(1<<CS01)|(0<<CS00));
    TIMSK |= (1<<TOIE0);//przerwanie wyswietlacze
    
    GICR = _BV(INT1);//przerwanie zew 
    MCUCR = (1<<ISC11)|(1<<ISC10);//przerwanie zew 
    
    TCCR1B |= ((1<<CS12)|(0<<CS11)|(1<<CS10)); //timer1 z preskalerem 1024
    TCNT1 = 60653; //wartosvc poczatkowa do przepelnienia uplynie 5sek
    
    sei();
    
    while(1)
    {	
    		_delay_ms(50);	
    		j = liczba % 10;
    		_delay_ms(50);	
    		d =(liczba / 10);  
            _delay_ms(50);
    }		
    }
    
    SIGNAL(SIG_INTERRUPT1)
    {
    liczba++;
    }
    
    SIGNAL(SIG_OVERFLOW0)
    {
    	switch(wysw)
    	{
       	case 0:
    	PORTD |= ~(1<<0);
    	PORTD &= (0<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5);
    	PORTA = cyfra(s);
    	wysw++;
    	break;
    	case 1:
    	PORTD |= ~(1<<1);
    	PORTD &= (1<<0)|(0<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5);
    	PORTA = cyfra(d);
    	wysw++;
    	break;
    	case 2:
    	PORTD |= ~(1<<2);
    	PORTD &= (1<<0)|(1<<1)|(0<<2)|(1<<3)|(1<<4)|(1<<5);
    	PORTA = cyfra(j);
    	wysw=0;
    	break;
    }}
    
  • Pomocny post
    #13 8256779
    Mat_91
    Poziom 25  
    Tyle rad i nadal żadnego pomysłu nie masz?;p Nie ważne w którym miejscu będziesz włączał timer, możesz to zrobić chociażby na początku funkcji main. Ważne jest żeby PRZERWANIE od tego twojego timera zgłaszało się tak często jak jak chcesz odświeżać wynik na wyświetlaczu (czyli te twoje 10 sekund) i w tym przerwaniu musisz wykonać odejmowanie na zmiennych X i Y i wynik wyświetlić na lcd, ewentualnie wykonać działanie i ustawić jakąś flagę, a wynik wyświetlić w pętli głównej programu (lepsze rozwiązanie).

    To kiedy załączasz timer powinno mieć znaczenie jeżeli chcesz synchronizować liczenie wraz ze startem danego procesu, wtedy sygnał startu procesu powinien załączyć timer oraz włączyć przerwania zewnętrzne. Jak to zrobisz to już zależy tylko od Ciebie:)
  • Pomocny post
    #14 8257512
    janbernat
    Poziom 38  
    Nie masz nigdzie obsługi przerwania:
    
    TIMER1_OVF_vect
    .........
    INT0_vect
    liczba-- 
    ......
    

    W TIMER1_OVF_vect możesz choćby ustawić flagę.
    P.S.
    Drgania kontaktronu wygaszają się w czasie 100us.
    Przynajmniej tego który sprawdzam właśnie.
  • #15 8286801
    qutrit
    Poziom 20  
    Witam
    Dziękuję za rady, są bardzo przydatne. Otóż napisałem program który działa poprawnie, ale ma pewien mankament z którym nie wiem jak sobie poradzić. Podczas wyświetlania wyniku w postaci różnicy impulsów imp0-imp1 dzieje się coś takiego; np. jeżeli na wyświetlaczach jest wynik 054,2 to co jakiś czas ( na ~0,3 sek) wskakuje wynik 0000 po czym znowu powraca na 054,2. Zera wskakują w przypadkowych chwilach, czasami 5 razy w ciągu sek, czasami raz. Dodam jeszcze ze wpisując jakąś wartość na stałe do programu (np imp0=3.3, imp1=1,2) występuje ta sama sytuacja. Moze ktoś z kolegów wie jak sobie z tym poradzić ?

    Cytat:

    #define F_CPU 1000000L
    #include <avr/io.h>
    #include <util/delay.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    #include <inttypes.h>
    #include <wyswietlacze.h>

    volatile uint8_t (wysw);
    float r,sum1=0,d1,imp0=0,imp1=0,sum0=0;
    float r1=0, d,s,j,s1,j1=0;
    float wynik=0,a=0,w=0;
    //volatile signed ;
    int main (void)
    {

    DDRC=(1<<0);//przycisk
    PORTC=(0<<0);//przycisk

    DDRA = 0xFF;//wyswietlacze
    DDRB = 0xFF;//wyswietlacze

    DDRD=(1<<6)|(1<<7); //dioda sygnalizujaca imp1 oraz znak minus

    TCCR0 |= (1<<WGM01)|(0<<WGM00);
    OCR0 =255;
    TCCR0 |= ((0<<CS02)|(1<<CS01)|(0<<CS00));
    TIMSK |= (1<<TOIE0);//przerwanie wyswietlacze

    GICR = ((1<<INT1)|(1<<INT0));//przerwanie zew
    MCUCR = (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00);//przerwanie zew

    TCCR1B |= ((1<<CS12)|(0<<CS11)|(1<<CS10)); //timer1 z preskalerem 1024
    TCNT1 |= 60653; //wartosc poczatkowa do przepelnienia uplynie 5sek
    TIMSK |= (1<<TOIE1);

    sei();

    while(1)
    {

    if(!(PINC & (1<<0)))//wcisniecie przycisku pokazuje PINC0=sum1
    {
    r=r1;
    j=j1;
    d=d1;
    s=s1;
    a=wynik;
    }
    else
    {
    r=j1;
    j=d1;
    d=s1;
    a=sum1;
    }

    _delay_ms(10);
    r1=fmod(a,1.0); // reszta
    r1=(r1*10.0);
    _delay_ms(10);
    j1 = (int)a % 10; //jednosci
    _delay_ms(10);
    d1 = (a/10);//dziesiatki
    s1=(a/ 100); // setki
    _delay_ms(10);
    }
    }


    SIGNAL(SIG_INTERRUPT0)
    {
    //_delay_ms(70);
    imp0++;//pomiar chwilowy kasowany w przerwaniu od wyswietlaczy
    //_delay_ms(70);

    }

    SIGNAL(SIG_INTERRUPT1)
    {
    _delay_ms(1);
    imp1++;//pomiar chwilowy kasowany w przerwaniu od wyswietlaczy
    PORTD ^= (1<<6);//dioda sygnalizujaca impulsy
    PORTD ^= (0<<6);

    }

    ISR(TIMER1_OVF_vect)
    {
    wynik=(imp0-imp1)/39;//wynik w postaci roznicy
    sum1=sum1+(imp1)/39;//pomiar całkowity sum1
    sum0=sum0+(imp0)/39;//pomiar całkowity sum0
    //_delay_ms(10);
    imp0=0;
    imp1=0;
    TCNT1 |= 60653;
    }

    SIGNAL(SIG_OVERFLOW0)//przerwanie wyswietlacze
    {
    switch(wysw)
    {
    case 0:
    PORTB |= ~(1<<0);
    PORTB &= (0<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5);
    PORTA = cyfra(d);
    wysw++;
    break;

    case 1:
    PORTB |= ~(1<<1);
    PORTB &= (1<<0)|(0<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5);
    PORTA = cyfra(j);
    wysw++;
    break;

    case 2:
    PORTB |= ~(1<<2);
    PORTB &= (1<<0)|(1<<1)|(0<<2)|(1<<3)|(1<<4)|(1<<5);
    PORTA = cyfra(r);
    wysw++;
    break;

    case 3:
    PORTB |= ~(1<<3);
    PORTB &= (1<<0)|(1<<1)|(1<<2)|(0<<3)|(1<<4)|(1<<5);
    PORTA = cyfra(r);
    wysw=0;
    break;

    }
    }

  • Pomocny post
    #16 8286926
    piti___
    Poziom 23  
    Oznacz zmienne występujące w przerwaniach jako volatile.
  • #17 8289050
    qutrit
    Poziom 20  
    Próbowałem volatile ale nic nie pomaga, nadal co jakiś czas "wskakują" zera :(
  • #18 8303332
    qutrit
    Poziom 20  
    Witam,
    sytuacja uległa poprawie po tym jak zmieniłem czas odświeżania wyświetlaczy. Natomiast zauważyłem kolejny o wiele poważniejszy problem. Otóż, jeżeli wynik=0 0.4 lub wynik=04,(obojętnie jaka cyfra) to blokuje ona wyświetlacz w ten sposób, ze gasną całkowicie cyfry dziesiątek jedności i świeci się tylko 4 na polu dziesiątych części po przecinku, lub jedności. Sytuacja jest taka sama, gdy na stałe w programie wpiszę wynik=0.4 lub wynik=4 :( Procesor pracuje normalnie, bo dioda sygnalizująca przychodzące impulsy mruga. Szukałem, ale nic nie znalazłem na temat takiego zachowania :( Czym to może być spowodowane ?
    Pozdrawiam
  • Pomocny post
    #19 8307931
    rpal
    Poziom 27  
    kolega ma kompletnie skopane procedury obslugi przerwań, nie wiem czemu nikt dotychczas tobie nie napisał że obsługa przerwania to nie jest miejsce na stosowanie procedury delay(). To także nie jest miejsce na to aby wstawiać tam instrukcje case-select choć to jeszcze od biedy przejdzie. Przerwanie musi byc maksymalnie zwięzłe. Dla wyświetlacza musisz zmieniać jedynie warość licznika aktualnie wyświetlanej cyfry (od 0 do 3) i na podstawie tego indeksu przepisywać wartość z bufora do portu wyświetlacza, nic poza tym. Przerwanie powinno mieć co koło 4-5 linii kodu.
  • #20 8309502
    qutrit
    Poziom 20  
    Witam
    co do delay() to się zorientowałem i już pousuwałem je z przerwań.
    Cytat:
    To także nie jest miejsce na to aby wstawiać tam instrukcje case-select choć to jeszcze od biedy przejdzie.
    To jak w takim razie jest lepiej multipleksować wyświetlacze ?
    W jaki sposób najprościej zrealizować bufor ? Jestem przekonany, ze taki bufor rozwiązał by problem. Przeczytałem wiele postów typu "bufor atmega c" ale wszystkie dotyczą komunikacji atmega-komputer i te kody są tak rozległe, ze nie mogę się w nich odnaleźć :( Oprócz moich początków z atmegami są to również moje początki z c.
    pozdr.
  • #21 8309775
    rpal
    Poziom 27  
    poszukaj dobrze to jeszcze znajdziesz delay-a który nie ma komentarza :) Bufor to tablica umieszczona w pamięci RAM. w zależności jak sobie to wymyslisz to będzie to kod znaku albo gotowy obraz segmentów który ma zostac wysłany do portu. W przerwaniu cyklicznie powiększasz indeks od 0-3 a dalej przepisujesz zawartość bofora na pozycji indeksu do portu i to tyle. Głowny program zajmuje się konwersją danych i zapisem do bufora tak aby dane tam wpisane odwzaorowały odpowiednie segmenty LED-a.
REKLAMA