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

[AVR-GCC][Attiny2313] Problem z wysyłaniem rc5

Brutus_gsm 06 Cze 2009 13:35 4478 16
REKLAMA
  • #1 6622708
    Brutus_gsm
    Poziom 25  
    Witam mam następujący problem. Chciałem zrobić pilot rc5. Sam napisałem wszystko praktycznie od początku i funkcja wysyłająca kod rc5 działa wyśmienicie.

    Problem pojawia się przy obsłudze przycisków w przerwaniu int0. Dwa microswitch'e podłączone mam tak: Jedna strona razem do PD2, czyli wejścia INT0 i z drugiej strony do PB1 i PB0 (każdego oddzielnie).

    Robię tak, gdyż chcę usypiać procesor w docelowym układzie i ma on być budzony właśnie przerwaniem INT0 tylko na czas wysłania kodu rc5.

    Chcę wykorzystać sztuczkę, której użył mirekk36 w swoim uniwersalnym pilocie. Mianowicie w obsłudze przerwania zmieniam PD2 na wyjście i ustawiam stan wysoki, następnie microswitch'e jako wejścia ze stanem niskim i sprawdzam na którym występuje stan wysoki (podciągnięty z PD2). Robię to, aby rozpoznać który przycisk został wciśnięty.

    Tak wygląda kod, który działa w miarę dobrze (bez sprawdzania przycisków).
    
    int main (void)
    {	
    	toggle=0;
    	
    	DDRD &= ~(1<<SW_INT0);		// PD2 wejscie button do INT0
    	PORTD |= (1<<SW_INT0);		// podciagniecie buttona do 1
    	
    	DDRB |= ( (1<<SW1) | (1<<SW2) | (1<<IR_LED) ); // guziki i dioda jako wyjscie	
    	PORTB &= ~( (1<<SW1) | (1<<SW2) ); // stan niski na guzikach
    	PORTB |= 1<<IR_LED; // dioda nie swieci
    	
    	MCUCR |= (1<<ISC01); 	// INT0 falling
    	GIMSK |= 1<<INT0;		// INT0
    	
    	sei();	 // wlaczenie przerwan
    	
    	while(1);
    
    	return 0;
    }
    
    SIGNAL(SIG_INTERRUPT0)
    {	
    
    	cli();
    	
    	_delay_ms(50);
    		
    	rc5_send(toggle,0,13);
    	
    	_delay_ms(200);
    	
    	sei();
    }
    


    Natomiast już tutaj układ czasami wysyła śmieci. Nie mogę dojść dlaczego...
    int main (void)
    {	
    	toggle=0;
    	
    	DDRD &= ~(1<<SW_INT0);		// PD2 wejscie button do INT0
    	PORTD |= (1<<SW_INT0);		// podciagniecie buttona do 1
    	
    	DDRB |= ( (1<<SW1) | (1<<SW2) | (1<<IR_LED) ); // guziki i dioda jako wyjscie	
    	PORTB &= ~( (1<<SW1) | (1<<SW2) ); // stan niski na guzikach
    	PORTB |= 1<<IR_LED; // dioda nie swieci
    	
    	//MCUCR &= ~(1<<ISC01 | 1<<ISC00); 	// INT0 low level
    	MCUCR |= (1<<ISC01); 	// INT0 falling
    	GIMSK |= 1<<INT0;					// INT0
    	
    	sei();	// wlaczenie przerwan
    
    	while(1);
    
    	return 0;
    }
    
    SIGNAL(SIG_INTERRUPT0)
    {	
    	cli();
    	
    	_delay_ms(100);
    	DDRB &= ~( (1<<SW1) | (1<<SW2) );	// guziki jako wejscie
    	PORTB &= ~( (1<<SW1) | (1<<SW2) ); // stan niski na guziki
    	
    	DDRD |= 1<<SW_INT0; // pd2 jako wyjscie
    	PORTD |= 1<<SW_INT0; // stan wysoki
    	
    	PORTB |= 1<<IR_LED; // dioda nie swieci
    	
    	
    	if( PINB & (1<<SW1) )
    	{
    		_delay_ms(5);		
    		if( PINB & (1<<SW1) )
    			rc5_send(toggle,0,13);
    	}
    	
    	if( PINB & (1<<SW2) )
    	{
    		_delay_ms(5);
    		if( PINB & (1<<SW2) )
    			rc5_send(toggle,0,12);
    	}
    	
    	_delay_ms(200);
    	
    	DDRD &= ~(1<<SW_INT0);		// PD2 wejscie button do INT0
    	PORTD |= (1<<SW_INT0);		// podciagniecie buttona do 1
    	
    	DDRB |= ( (1<<SW1) | (1<<SW2) | (1<<IR_LED) ); // guziki i dioda jako wyjscie	
    	PORTB &= ~( (1<<SW1) | (1<<SW2) ); // stan niski na guzikach
    	PORTB |= 1<<IR_LED; // dioda nie swieci
    	
    	sei();
    }


    Może mi ktoś pomóc? W przerwaniu używam cli(); i sei(); aby podczas wysyłania rc5 nie wyskoczyło mi następne przerwanie. Kody sprawdzam w programie Girder z IgorPlug'iem na USB. Powinienem otrzymywać 300D i 300C a czasami dostaję jakieś śmieci typu 5804 albo 4FF2 :/


    Attiny2313 działa na wewnętrznym rezonatorze 8MHz. Generowanie nośnej 36kHz na timer0, odliczanie czasu potrzebnego do kodu rc5 na timer1.
  • REKLAMA
  • #2 6624650
    mirekk36
    Poziom 42  
    Witam,

    może sprecyzuj problemy jakie masz po kolei - bo teraz to już nie wiem za bardzo czy nie działa ci "sztuczka" z usypianiem i budzeniem procka? czy nie za bardzo działa ci nadajnik RC5 ?

    nie pokazałeś wprawdzie kodu swojej procedury nadawczej RC5 ale coś czuję przez skórę, jeśli chodzi o błędy w nadawaniu u ciebie, że to właśnie ona jest temu winna. Generalnie masz swoje przerwanie INT0 - potem wyłączasz przerwania cli(); a potem w tej procedurze nadajesz RC5. Rozumiem, że w samej procedurze załączasz przerwania od Timer1 i Timer0. Tak na gorąco u ciebie prawie nie ma obsługi drgań styków klawisza, tylko maleńki delams(5); . A skoro wchodzisz w nadawanie i musisz włączyć przerwania sei() to włącza się także INT0 - bo nie zablokowałeś samego INT0 przecież tylko globalne przerwania - więc już choćby z powodu drgań styków - może nastąpić kilka- kilkadziesiąt razy wejście w twoją procedurę nadawania RC5

    Ale powiem też tak - jeśli coś źle nadaje ci kody RC5 - to na 99% winna może być sama procedura a nie to co się dzieje na zewnątrz niej czyli w twoim przypadku szczególnie nie ma co zakłócać jej pracy

    to tyle tak na razie ;)
  • #3 6624758
    Brutus_gsm
    Poziom 25  
    No wieć tak. Dziękuję za odpowiedź ;) Oto procedura nadawania rc5.

    Może nie jest napisana jakoś cudownie, ale wydaje mi się, że poprawnie. Timer 0 ustawiony w CTC z opcją toggle OC1A generuje nośną 36kHz a timer 1 odlicza potrzebny czas w µsekundach.

    Chodzi ogólnie o to, że przy tym drugim kodzie urządzenie wysyła czasami jakieś śmieci :/ I nie wiem, czym może być to spowodowane...

    Usypiania procesora jeszce nie implementowalem, gdyż nie mogę sobie poradzić z poprawną obsługą klawiszy i wysyłaniem kodu.

    Jak może wyglądać taka procedura programowej delecji drgań styków? Bo inaczej niż delay bez używania timerów nie umiem sobie tego wyobrazić... Timery są już zajęte do rc5.
  • REKLAMA
  • #4 6624991
    mirekk36
    Poziom 42  
    nieee - no procedurka do nadawania jest OK ;)

    w związku z tym pozostaje problem chyba tych drgań, które mogą powodować kilkakrotne wejście do tej samej procedury w ciągu bardzo krótkiego okresu czasu (co 5ms) co daje śmieci w nadawaniu. A drgania mogą trwać dłużej niż 5ms.

    hmm teraz patrzę, że jednak też dobrze masz zrobioną obsługę przycisków - ale może dużo za małe dajesz opóźnienie - tylko 5ms. Ja zwykle daję minimum 40-50ms w takim przypadku. A przyciski (choć to może nie ma takiego znaczenia) zawsze robię tak, że zwierają do GND a u ciebie jest odwrotnie. Czasem aby jeszcze sprzętowo poprawić obsługę drgań wystarczy dać kondensator 100nF równolegle do przycisku - też to często stosuję - ale w pilocie chyba pominąłem aby było jak najmniej części. A zajrzałem teraz do kodu i opóźnienie mam na 40ms

    popróbuj z tym
  • #5 6625031
    Brutus_gsm
    Poziom 25  
    No tak, zazwyczaj też robię tak, że zwierają do masy, ale w tym przypadku chciałem zrobić taki myk jak u Ciebie w pilocie uniwersalnym... Zeby jednoczeście wywoływać przerwanie int0 i móc określić który klawisz jest wciśnięty...
  • #6 6625057
    mirekk36
    Poziom 42  
    no tak , masz rację - też tak zrobiłem w pilocie - sorry - ale dawno to już robiłem i słabo pamiętam szczegóły tamtego projektu

    no ale teraz jak zwiększysz to opóźnienie to powinno już chyba ruszyć z kopyta i bez błędów działać - co? ;)

    daj znać jak ci wyjdzie
  • #7 6625116
    Brutus_gsm
    Poziom 25  
    No więc kombinowałem już wczesniej z tymi różnymi opóźnieniami. Wstawiałem je w kilka miejsc na wszelki wypadek i zmieniałem na bardzo duże. Niestety poległem ;) Często klawisze powtarzały się same, nawet bez wciskania przycisku :/ Nie wiem do tej pory co to było.

    Ale rozwiązałem już problem. Przynajmniej na razie wszystko działa jak powinno. ;)

    Zmieniłem kilka rzeczy ;) W Przerwaniu int0 wyłączam tylko to jedno przerwanie ;) A nie wszystkie. No i ogólna procedura obsługi klawiatury wygląda tak:

    guziki jako wyjście
    guziki stan niski
    int0 wejście
    int0 stan wysoki

    po zwarciu pd2 do masy przerwanie int0 i sprawdzanie klawiszy:

    guziki jako wejście
    guziki stan wysoki
    int0 wyjście
    int0 stan niski

    I sprawdzam, czy na jakimś występuje stan niski ;) To rozwiązało problem ;) Niby nie ma różnicy... a jednak teraz działa poprawnie. Bez żadnych błędów. Przypadkowych wysyłaniach kodu (bez wciskania klawisza)... Ważne, że działa ;)

    No i jeszcze pytanie... W którym miejscu usypiać procesor ;) Nigdy nie używałem trybu power down...


    EDIT:
    Już sobie poradziłem ;) chyba wszystko już działa tak jak powinno, gdyż mój miernik na zakresie 2mA pokazuje 0 ;) No i procesor wybudza się na czas przerwania i wysłania danych ;)
  • Pomocny post
    #8 6625686
    mirekk36
    Poziom 42  
    jeśli pokazuje ci 2mA to jeszcze nie jest niestety w najpełniejszym trybie PowerDown. W tym najgłębszym trybie miernik powinien ci pkazywać 0,6uA

    a skoro są mili ampery to znaczy, że bardzo częto się wybudza ze snu

    Dodano po 3 [minuty]:

    w związku z tym, że wykonałeś kawał dobrej roboty to tutaj pokażę ci jak ja zrobiłem usypianie procka, jak widać jest to robione tylko w pętli głównej i w niej w pętli while . A przed tą pętelką stosowne ustawienia:

                    // Wyłączenie komparatora analogowego
    	ACSR SET (1<<ACD);
    
    	// WYŁĄCZENIE WATCHDOG'a 
    	WDTCSR |= (1<<WDCE) | (1<<WDE);
    	// Turn off WDT
    	WDTCSR = 0x00;
    
    
    	// ustawienie trybu POWER-DOWN
    	MCUCR SET ((1<<SM0)|(1<<SM1));
    
    
    
    	// ustawienie pinu PORTD.2 (INT0) jako wejście
    	DDRD RESET (1<<2);
    	// podciągnięcie do 1
    	PORTD SET (1<<2);
    
    	// zezwolenie na przerwania INT0
    	GIMSK SET (1<<INT0);
    	// skasowanie flagi wystąpienia przerwania INT0
    	EIFR SET (1<<INTF0);
    
    
    	while (1)
    	{
    		// globalne zezwolenie na przerwania
    		sei();
    
    		// wprowadzenie procesora w tryb POWER-DOWN
    		sleep_mode();
    	}


    powodzenia
  • REKLAMA
  • #9 6626188
    Brutus_gsm
    Poziom 25  
    Dzięki ;) Raczej szybko się uczę ;)

    Źle mnie zrozumiałeś: na zakresie 2mA pokazuje 0, czyli prąd jest bardzo mały poniżej 10uA. A tego mój miernik już nie pokazuje niestety a szkoda, bo chciałbym wiedzieć.

    A usypianie już sam zrobiłem ;) Trochę inaczej, gdyż pętlę while(1) pozostawiam pustą a przed nią w funkcji main ustawiam globalne zezwolenie na przerwania i usypiam i na końcu obsługi przerwania robię to samo. Efekt jest identyczny. ;)

    int main (void)
    {	
    	/* funkcja main */
    	   
    	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    
    	sei();	// wlaczenie przerwan
    	sleep_mode();
    	while(1){};
    
    	return 0;
    }
    
    SIGNAL(SIG_INTERRUPT0)
    {
    	/* obsługa przerwania */
    
    	sei();
    	sleep_mode();
    	
    }


    Dzięki za pomoc :)
  • #11 6626424
    Brutus_gsm
    Poziom 25  
    No tak :) Pełen sukces :) Pilot może pracować teraz pod baterią cały czas bez wyłączania i o to mi chodziło :D Teraz tylko płytka, obudowa i wsio :D

    Takie małe a cieszy ;)

    Jeszcze raz dziękuję za zainteresowanie. Pozdrawiam.!
  • Pomocny post
    #12 6626761
    BoskiDialer
    Poziom 34  
    Brutus_gsm: Radził bym Ci przeprojektować trochę ten kod: ustawianie flagi I (sei()) w przerwaniu jest największym błędem (chyba, że wiesz co robisz oraz to kontrolujesz). Przed wyjściem z funkcji przerwania ustawiasz flagę I (jeśli przyjdą kolejne przerwania, pojawią się kolejne wywołania i przepełni się stos - błąd), po czym usypiasz procesor (zakładając optymistycznie że po uśpieniu wykonały by się jeszcze 4 instrukcje, to i tak procesor nie ukończył by wykonywania funkcji i na stosie zostawały by dane po kolejnych wywołaniach). Po pewnej liczbie wywołań przerwania wskaźnik stosu zacznie wchodzić w obszar rejestrów IO co spowoduje różne rzeczy - od dziwnego zachowania (nadpisywanie rejestrów port/ddr) po zawieszenie się (wyłączenie przerwań). Najprościej rozwiązać to w prosty sposób: usypianie przenieść do pętli głównej, a ustawianie flagi I w przerwniu usunąć. Po zakończeniu przerwania pojawi się jeden przebieg pętli głównej po czym procesor zostanie uśpiony, jednak stos będzie już czysty, gdyż funkcja przerwania się zakończyła.
  • REKLAMA
  • #14 6634449
    Brutus_gsm
    Poziom 25  
    Dzięki panowie za pomoc :) Przeniosłem to do pętli głównej.

    Zdrowia :)
  • #15 9160665
    Karol966
    Poziom 31  
    Trochę dawno no ale spróbuję tu napisać by nie tworzyć nowego tematu ;) Bawiłem się również z usypianiem tego procka, również dla układu pilota. Usypianie działa bo tak samo mam na zakresie 2mA mam wynik 0.000
    Problem jest taki, że jak nie dam zewnętrznego rezystora pull-up (20k) do INT0 to układ ciągle się wybudza. Połączenia mam analogicznie ;)

    Mój pilocik wygląda tak: [AVR-GCC][Attiny2313] Problem z wysyłaniem rc5
  • #16 9160775
    asembler
    Poziom 32  
    No to może masz nie ustawiony wewnętrzny pull-up na INT0.
  • #17 9160971
    Karol966
    Poziom 31  
    Po wykonaniu transmisji RC5 wywołuję tą funkcję:

    void sleep()
    {
    	_delay_ms(200);
    	
    	ACSR |=(1<<ACD);					// Wyłączenie komparatora analogowego
    	WDTCSR |= (1<<WDCE) | (1<<WDE);	// WYŁĄCZENIE WATCHDOG'a
    	WDTCSR = 0x00;						// Turn off WDT
    	
    	MCUCR |= ((1<<SM0)|(1<<SM1));// ustawienie trybu POWER-DOWN
    	//set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    	
    	DDRD &=~(1<<2);		// ustawienie pinu PORTD.2 (INT0) jako wejście
    	PORTD |= (1<<2);		// podciągnięcie do 1
    	
    	GIMSK |= (1<<INT0);	// zezwolenie na przerwania INT0
    	EIFR |= (1<<INTF0); 	// skasowanie flagi wystąpienia przerwania INT0
    	
    	DDRB  |= 0b11111011; 	// wyjścia oraz jedno wejście
    	PORTB &= 0b00000100;	// wyjścia do masy
    	
    	DDRD |=_BV(PD6);		// jako wyjście
    	PORTD &=~_BV(PD6);		// zwarte do masy
    	sei();   				// wlaczenie przerwan
    	
    	MCUCR |= (1<<SE);		// sleep enable (?)
    	sleep_mode();
    }


    Dodałem wartości elementów do projektu płytki ;)

    [AVR-GCC][Attiny2313] Problem z wysyłaniem rc5
REKLAMA