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

[ATMEGA8][C] Reset zamiast przerwania

mkkomp 27 Gru 2010 18:29 5907 54
  • #1 8919912
    mkkomp
    Poziom 10  
    Witam! Napotkałem problem przy obsłudze przerwań zewnętrznych (jestem początkujący). Oto mój programik:

    volatile int przerwanie=0;
    
    ISR(INT1_vect) 
    {
    	przerwanie=1;
    }
    
    int main()
    {
    	LCD_Initalize();
    	LCD_WriteText("START");
    	_delay_ms(1500);
    	DDRD &= ~(1<<DDD3); //pin PD3 jako wejście
    	PORTD |= (1<<PORTD3); //wewnętrzny pull-up
    	MCUCR |= (1<<ISC11)|(0<<ISC10);      // Wyzwolenie przerwania zboczem opadającym 
    	GICR |= (1<<INT1); //Załączenie przerwania na INT1
    	sei();
    	
    	while(1)
    	{
    		LCD_Clear();
    		LCD_WriteText("Hello!");
    		_delay_ms(1500);
    		LCD_Clear();
    		LCD_WriteText("Witaj!");
    		_delay_ms(1500);
    		if(przerwanie==1)
    		{
    			LCD_Clear();
    			LCD_WriteText("Przerwanie");
    			_delay_ms(1500);
    			przerwanie=0;
    		}
    	}
    	return 0;
    }


    Przycisk spina masę z pinem PD3. Kiedy go naciskam, program wykonywany jest od początku, czyli najprawdopodobniej µC się resetuje, w ogóle nie jest wykonywana funkcja obsługi przerwania (testowałem umieszczając w jej wnętrzu wyświetlanie tekstu na lcd z delayem na wychwycenie rezultatu). Dla INT0 jest ta sama historia. Bardzo proszę o wskazanie błędów, bo nie mogę sobie z tym poradzić.
  • Pomocny post
    #2 8920849
    dondu
    Moderator na urlopie...
    Może to zły kierunek podpowiedzi:
    Tak napisany program powoduje przerwanie na każde drganie przycisku podczas jego naciśnięcia. Może w tym jest przyczyna?
    Aby to sprawdzić albo zmień program albo dodaj kondensator do przycisku + zmień wywołanie INT1 na wywołanie poziomem nie zboczem.
  • #3 8921063
    mirekk36
    Poziom 42  
    dondu napisał:
    Może to zły kierunek podpowiedzi:
    Tak napisany program powoduje przerwanie na każde drganie przycisku podczas jego naciśnięcia.


    Racja.

    dondu napisał:
    Może w tym jest przyczyna?

    Drgania wywołają kolejno przerwania i wyjścia z niego więc stos się nie przepełni, zatem resetowanie na moje oko może być powodowane jeszcze innymi częściami kodu.

    dondu napisał:
    Aby to sprawdzić albo zmień program

    To bardzo dobry pomysł a szczególnie to żeby zrezygnować z obsługi klawisza w taki sposób na przerwaniu zewnętrznym bo to najczęściej nieporozumienie. (chociaż można - ale nie w taki okrutny sposób)

    dondu napisał:
    albo dodaj kondensator do przycisku + zmień wywołanie INT1 na wywołanie poziomem nie zboczem.


    O ile kondensator można dodać to warto podpowiedzieć autorowi mniej więcej jaką wartość albo granice wartości ;) bo dodanie np elektrolita 100uF nie dość, że nie pomoże to zaszkodzi totalnie. Taki kondensator to w granicach 100nF (ceramik)

    Jednak można się w ogóle obejść bez kondensatorów byle napisać tak program aby całkowicie programowo eliminować drgania styków.

    To jednak wywoływanie w takich przypadkach przerwania poziomiem to już totalna porażka :( lepiej tego tak nigdy nie robić, a pamiętać by trzeba było albo dodać , że takim przypadku jak ktoś już się uprze to trzeba by ręcznie wyłączać takie przerwanie po wejściu w nie, inaczej będzie masakra i rzeczywiście RESET'owanie przez przepełnianie się stosu.
  • #5 8921167
    mirekk36
    Poziom 42  
    dondu napisał:
    Mam wrażenie, że przycisk jest tylko dla testów.


    Ja też mam takie wrażenie, ale zawsze wolę od razu kogoś nakierować na właściwy tor żeby nie działał po omacku. Dla początkującego taki sposób wydaje się zwykle jedynie słuszny gdy pozna się przerwania w jakiś tam stospniu. Tymczasem to błędne założenie i warto robić debouncing od samego początku bo to podstawa a używać do tego jakiegoś przerwania timera zamiast marnować INTx. Tym bardziej że takich przerwań jest zwykle aż 2 sztuki , a chyba ktoś nie będzie dawał procka typu ATmega128 żeby mieć więcej takich przerwań po to tylko żeby obsłużyć dodatkowe np 2-3 przyciski. ;)
  • #7 8921247
    mkkomp
    Poziom 10  
    Dziękuję za podpowiedzi:) Wstawiłem konda 100nF, próbowałem wyzwalać przerwanie na niski stan jak i na zbocze opadające i dalej to samo.. Czy jeśli napiszę taki kod:

    ISR(INT1_vect) 
    {	
    	_delay_ms(100);
    	while(bit_is_clear(PIND,PD3));
    	LCD_Clear();
    	LCD_WriteText("Co tam!");
    	_delay_ms(1500);
    	przerwanie=1;
    }


    to wpływ drgania przycisku wyeliminuję? Chodzi w tym miejscu tylko o ustalenie przyczyny resetowania się uC. Troszkę irytuje fakt, że innym działa to bez żadnych zabezpieczeń przed tym zjawiskiem (biorąc pod uwagę kody z innych, podobnych wątków).

    Przycisk na razie zastępuje mi wyjście z transoptora, który docelowo ma być zasilany sygnałem sterowania przekaźnika z podajnika węgla w piecu C.O. Chcę zliczać ilość podań w ciągu np 24h, żeby ustawić na podstawie wyników optymalnie parametry sterownika kotła. Problem w tym, że są tam dwie możliwości podawania (dogrzewanie i przepalanie) i chcę, na podstawie czasu trwania tego sygnału(długość podawania węgla), odróżniać te dwie fazy i zliczać je osobno, myślę, że najłatwiej będzie to zrobić na przerwaniach zewnętrznych. Chociaż mogę się mylić:)
  • #8 8921276
    dondu
    Moderator na urlopie...
    Rozumie że ten kod to tylko dla testów, więc Delay w przerwaniu w tym wypadku może być, ale w docelowym programie nie stosuj takiego rozwiązania. Przerwanie powinno się wykonać jak najszybciej i zakończyć.

    Sprawdzasz PD2 w INT1 - to pomyłka.
    PD2 jest w INT0
  • #10 8921329
    mkkomp
    Poziom 10  
    dondu napisał:
    Rozumie że ten kod to tylko dla testów, więc Delay w przerwaniu w tym wypadku może być, ale w docelowym programie nie stosuj takiego rozwiązania. Przerwanie powinno się wykonać jak najszybciej i zakończyć.

    Sprawdzasz PD2 w INT1 - to pomyłka.
    PD2 jest w INT0


    Tak, już wiem, ze delay'a się w przerwaniu nie stosuje, to tylko dla odszukania przyczyny braku poprawnego działania.

    Oczywiście ma być PD3, w międzyczasie próbowałem całości na porcie PD2 i wkleiłem zmieniony kod, już poprawione:)
  • #11 8921334
    mirekk36
    Poziom 42  
    Nawet jeśli TYLKO do testów, to kod w którym w procedurze obsługi przerwania wprowadzasz jakikolwiek DELAY , a nawet wyświetlanie na LCD to NIEPOROZUMIENIE.

    A do tego wprowadzenie pętli oczekującej:

    while(bit_is_clear(PIND,PD3)); 


    to już w ogóle gwódź do trumny programistycznej. Proponuję jednak doczytać lepiej jak działają przerwania, jak się je obsługuje, podpatrzeć kody innych. Janbernat podał ci kilka użytecznych linków.
  • #13 8921368
    mkkomp
    Poziom 10  
    mirekk36 napisał:
    Nawet jeśli TYLKO do testów, to kod w którym w procedurze obsługi przerwania wprowadzasz jakikolwiek DELAY , a nawet wyświetlanie na LCD to NIEPOROZUMIENIE.


    Wiem, wiem:) Napisałem to tylko po to, żeby sprawdzić czy w ogóle wchodzi do funkcji obsługującej przerwanie i czy w ogóle zadziała mi to jak trzeba. Nawet tak nie działa i się resetuje, nie dając oznak wywołania funkcji obsługi przerwania:/ Jutro popróbuję z tym debouncingiem. Dzięki wielkie za pomoc!
  • #14 8921386
    Konto nie istnieje
    Konto nie istnieje  
  • #15 8921436
    dondu
    Moderator na urlopie...
    mkkomp napisał:
    Dzięki wielkie za pomoc!

    Dodaj kliknięcia przycisku POMÓGŁ :) choć na razie efekt nieciekawy.

    _marek napisał:
    No dobrze ale :
       DDRD &= ~(1<<DDD3); //pin PD3 jako wejście
       PORTD |= (1<<PORTD3); //wewnętrzny pull-up 

    co to jest DDD3 i czy nie powinno tam być cyferki? ( jak i zamiast portd3)

    Skoro reaguje na naciśnięcie klawisza to sugeruje, że ta część kodu jest OK.

    A może problem tkwi w BROWN OUT (czyli reset z powodu brudnego zasilania, choć to raczej naciągana próba znalezienia przyczyny)?

    Aby poznać przyczynę restartu, daj na samym początku main():
    //zapamiętaj powód restartu systemu
    unsigned char reset_MCUCSR;
    reset_MCUCSR = MCUCSR;


    I wyświetla zawartość reset_MCUCSR na LCD, będziesz miał info o przyczynach resetu strona 41 dokumentacji ATMEGA8.
  • #16 8923397
    mkkomp
    Poziom 10  
    _marek napisał:
    No dobrze ale :
       DDRD &= ~(1<<DDD3); //pin PD3 jako wejście
       PORTD |= (1<<PORTD3); //wewnętrzny pull-up 

    co to jest DDD3 i czy nie powinno tam być cyferki? ( jak i zamiast portd3)

    Tak jest chyba poprawnie, wg tego pdfa: Link.

    dondu napisał:

    Dodaj kliknięcia przycisku POMÓGŁ :) choć na razie efekt nieciekawy.


    Jeśli czasownik pomagać z niedokonanego zmieni się w dokonany (pomógł), to oczywiście tak zrobie ;)

    dondu napisał:

    Aby poznać przyczynę restartu, daj na samym początku main():
    //zapamiętaj powód restartu systemu
    unsigned char reset_MCUCSR;
    reset_MCUCSR = MCUCSR;

    No więc wklepałem taki kod:

    
    ISR(INT1_vect) 
    {	
       _delay_ms(10);
       LED_ON;      //dioda na PD5
    	przerwanie=1;
    }
    
    int main()
    {
    	LCD_Initalize();
    	LCD_Clear();
    
    	unsigned char reset_MCUCSR;
    	LCD_WriteText("MCUCSR=");
    	reset_MCUCSR = MCUCSR; 
    	LCD_WriteText(itoa(reset_MCUCSR,bufor,3));
    	_delay_ms(1500);
    	
            [...]
    	
    	while(1)
    	{
                    [...]
    
    		MCUCSR=0;
    		if(przerwanie==1)
    		{
    			LCD_Clear();
    			LCD_WriteText("Przerwanie");
    			_delay_ms(1500);
    			przerwanie=0;
    		}
    	}
    	return 0;
    }

    Na starcie (po zaprogramowaniu) MCUCSR jest równe 2 (reset przez LPT). W programie, po pierwszym wyświetleniu MCUCSR, następuje jego wyzerowanie. Po tym wyzerowaniu (w pętli while) wciskam przycisk reset i wartość na starcie znów jest 2. Natomiast w takiej samej sytuacji, ale po wciśnięciu przycisku podłączonego do INT1, wartość MCUCSR jest dalej 0. Czyli wynika z tego, że reset nie następuje... no to dlaczego program: a) nie wchodzi w funkcję obsługi przerwania b) wykonuje się od początku?
  • #17 8924000
    Konto nie istnieje
    Konto nie istnieje  
  • #18 8924307
    mkkomp
    Poziom 10  
    _marek napisał:
    Faktycznie jest takie makro jak DDD3. Z przyzwyczajenia używam wszędzie PDx ponieważ tym makrom przypisane są tylko cyfry o ile ma być przesunięta jedynka w lewo.
    Nie mogę znaleźć takiego makra jak PORTD3, spróbuj PD3 lub po prostu 3.

    dodane;
    Można jeszcze PORT3

    Wklepałem:
    
    DDRD &=~ (1 << PD3);
    PORTD |= (1 << PD3);

    i nie pomogło.
  • #19 8924423
    sulfur
    Poziom 24  
    Proszę zmierzyć różnicę potencjałów pomiędzy pinem uC a masą. Zarówno gdy przycisk jest nienaciśnięty, jak i gdy jest naciśnięty.
  • #20 8924489
    mkkomp
    Poziom 10  
    Pomiędzy pinem PD3 a masą jest 5,06V przy nieprzyciśniętym, 0V przy przyciśniętym, natomiast prąd po zwarciu przycisku ≈ 0,14mA.

    Funkcję obsługi przerwania zrobiłem taką:

    ISR(INT1_vect)
    {   
       LED_ON;      //dioda na PD5
       przerwanie=1;
    } 


    Nawet jeśli wystąpi zjawisko drgania przycisku, to chyba dioda powinna tak czy siak się zaświecić?
  • #21 8924581
    sulfur
    Poziom 24  
    No to zależy, bo wkleja kolega kawałki kodu zamiast całego. Nie wiadomo, czy PD5 ustawia kolega jako wyjście oraz czy dioda się zapala stanem niskim czy wysokim. Na podstawie załączonego programu mogę stwierdzić, że wszystko działa prawidłowo.
  • #22 8924721
    mkkomp
    Poziom 10  
    Aktualnie program wygląda tak (przelutowałem przycisk pod PD2):

    
    #define LED_ON sbi(DDRD,PD5);cbi(PORTD,PD5)
    #define LED_OFF sbi(DDRD,PD5);sbi(PORTD,PD5)
    
    char bufor [17];
    volatile int przerwanie=0;
    
    SIGNAL (SIG_INTERRUPT0) 
    {
    	LED_ON;
    	przerwanie=1;
    }
    
    int main()
    {
    	LCD_Initalize();
    	LCD_Clear();
    	LCD_WriteText("START");
    	_delay_ms(1500);
    	DDRD &=~ (1 << PD2);  // pin PD2 jako wejście
    	PORTD |= (1 << PD2);  // wewnętrzny pull-up
        MCUCR |= (1<<ISC01)|(0<<ISC00);      // Wyzwolenie przerwania zboczem opadającym 
    	GICR |= (1<<INT0); //Załączenie przerwania na INT0
    	_delay_ms(100);
    	sei();
    	
    	while(1)
    	{
    		LCD_Clear();
    		LCD_WriteText("Hello!");
    		_delay_ms(1500);
    		LCD_Clear();
    		LCD_WriteText("Witaj!");
    		_delay_ms(1500);	
    		if(przerwanie==1)
    		{
    			LCD_Clear();
    			LCD_WriteText("Przerwanie");
    			_delay_ms(1500);
    			przerwanie=0;
    		}
    	}
    	return 0;
    }

    Dioda podłączona pod PD5 zasilana jest z zewnątrz, nie włącza się (działa, gdy LED_ON umieszczę w funkcji main, więc podłączona jest dobrze).
  • #24 8925029
    mkkomp
    Poziom 10  
    Kabelki zakończone wtyczką LPT mam przylutowane bezpośrednio do płytki przy nóżkach atmegi. Wtyczkę odłączałem od komputera i nie pomagało. Pół metra kabelków raczej nie powinno sprawiać żadnych problemów? Jest to programator najprostszy z możliwych - 5 przewodów i brak innych dodatkowych elementów.
  • #26 8925108
    mkkomp
    Poziom 10  
    Nie ma rezystorków. Są tylko i wyłącznie kabelki. Wygląda to tak:
    [ATMEGA8][C] Reset zamiast przerwania
    Zasilania podczas programowania nie pobieram z portu LPT, tylko z płytki, jest jeden kabelek mniej niż na rysunku.
  • #27 8925153
    dondu
    Moderator na urlopie...
    1. Programator - nie wypowiem się czy działać powinien (przy zasilaniu 5V raczej tak), ale rezystory dołóż zgodnie z podanym przeze mnie linkiem:

    2. Wstawiłeś czytanie flagi resetu po inicjacji i zerowaniu wyświetlacza, a miałeś wstawić na samym początku main():
    cyt. "To make use of the Reset Flags to identify a reset condition, the user should read and then reset the MCUCSR as early as possible in the program."

    3. Przy tym programatorze radzę jednak stosować gniazdko i wtyczkę by móc go odłączać (tak jak pisałem miałem z nim problemy bo wypływał na pracę układu).


    4. Masz podłączony wyświetlacz, ale nie wiemy, jak bo nie podałeś schematu. Ponieważ przyczyna jest trudna do znalezienia, odłączyłbym wyświetlacz i wywalił z programu obsługę LCD, a wprowadził 2 diody:

    - czerwona - długi błysk przy resecie + krótkie w ilości określonej przez MCUCSR odczytaną na samym początku main() + długi by wiedzieć, to koniec tej części kodu.

    - zielona - krótki błysk w przerwaniu

    To chyba załatwi sprawę testu Twojego rozwiązania.

    5. Gdy to zrobisz wklejaj zawsze cały program.
  • #28 8925323
    mkkomp
    Poziom 10  
    Programator działa w 100% (zrobiłem na nim już kiedyś sonar), te rezystorki to chyba tylko zabezpieczenie przed uszkodzeniem portu LPT w komputerze w razie jakiegoś przypadkowego zwarcia. Mogę go odłączyć, ale raczej same kabelki nie powinny sprawiać problemów. W obecnym układzie, przed chwilą, zrobiłem obsługę wspomnianej już diodki owym przyciskiem podłączonym pod PD2, tylko nie na przerwaniach, a sprawdzając jego stan w pętli (port ustawiony jako wyjście z podciągniętym wewnętrznie VCC) i działa tak jak trzeba - diodka się zapala, wyświetlacz pokazuje ilość naciśnięć. Wyświetlacz jest podłączony dokładnie tak jak na poniższym schemacie:

    [ATMEGA8][C] Reset zamiast przerwania

    Aktualny, pełny program wkleiłem tu Link
  • #29 8925371
    dondu
    Moderator na urlopie...
    Ten link nie zawiera całego programu np. brak deklaracji biblioteki obsługującej LCD (która także może być przyczyną problemów) i może pliku *.h ... poza tym nadal w nim jest obsługa LCD.

    Zrobisz jak będziesz uważał. Ja podałem, co ja bym zrobił, bo błąd jest trudny do zlokalizowania i najprościej pozbyć się zbędnego balastu sprzętowego i programowego.
  • #30 8925588
    mkkomp
    Poziom 10  
    Usunąłem całą obsługę wyświetlacza, tzn. całą bibliotekę, bo fizycznie układ taki jak był, dioda dalej nie reaguje na przerwanie. Biblioteka obsługująca LCD jest w całości ze strony Link. Położę uC na płytce stykowej z jedną diodą i przyciskiem, zobaczę czy będzie działać. Dałem odczyt rejestru MCUCSR na początku maina (w pierwszej linijce) i jest tak samo, jak opisywałem poprzednio, nie ma żadnej różnicy.

    Dodano po 1 [godziny] 52 [minuty]:

    Położyłem uC na płytce stykowej (już zaprogramowany), na PD5 podpiąłem diodę z zewnętrznym zasilaniem, na PD2 przycisk do masy. Pod PD2 i reset podpiąłem kondensatorki 100nF. Dioda mruga 2 razy po włączeniu zasilania i za każdym razem kiedy nacisnę przycisk pod PD2, brak wejścia w warunek z jednym długim mrugnięciem. Program:

    #include <avr/interrupt.h>
    //#include <stdlib.h> 
    //#include <inttypes.h> 
    #include <util/delay.h>
    #define LED_ON sbi(DDRD,PD5);cbi(PORTD,PD5)
    #define LED_OFF sbi(DDRD,PD5);sbi(PORTD,PD5)
    
    
    volatile int przerwanie=0;
    
    SIGNAL(SIG_INTERRUPT0) 
    {
    LED_ON;
    	przerwanie=1;
    }
    
    int main()
    {
        DDRD &=~ (1 << PD2);  // pin PD2 jako wejście
        PORTD |= (1 << PD2);  // wewnętrzny pull-up
        MCUCR |= (1<<ISC01)|(0<<ISC00);      // Wyzwolenie przerwania zboczem opadającym 
    	GICR |= (1<<INT0); //Załączenie przerwania na INT1
    	_delay_ms(100);
    	LED_ON;
    	_delay_ms(100);
    	LED_OFF;
    	_delay_ms(100);
    	LED_ON;
    	_delay_ms(100);
    	LED_OFF;
    	_delay_ms(100);
    	sei();
    
      while(1)
      {
       if(przerwanie==1)
       {
    	LED_ON;
    	_delay_ms(2000);
    	LED_OFF;
    	przerwanie=0;
       }
        asm volatile ("WDR"::);
      } 
      return 0;
    }
    


    Tak więc kwestia błędu w lutowaniu czy też wpływu reszty elementów na płytce odpada. Pozostaje błąd programistyczny. Czy możliwe, że uC jest w jakiś sposób uszkodzony?
REKLAMA