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][C] Przerwanie a długość impulsu

mrrudzin 14 Mar 2009 22:26 2451 8
REKLAMA
  • #1 6282657
    mrrudzin
    Poziom 39  
    Mam problem z napisaniem programu rozpoznającego stany w sieci.
    Na wejście INT0 procesora podpiąłem sygnał z sieci 1Wire. Chciałbym rozpoznać trzy stany w sieci:
    - reset (czyli ściągnięcie linii danych do 0 na 480us)
    - nadanie 0 - (czyli 10us stan niski, 80us stan wysoki)
    - nadanie 1 - (czyli 80us stan niski, 10us stan wysoki)
    0 i 1 następują po sobie, więc z nimi można sobie poradzić bez przerwań (gdy wystąpi stan niski - odczytać stan po 50us), problem jest tylko z stanem reset. Trzebaby zerować timer przy każdym opadającym zboczu, a przy narastającym sprawdzać czy czas stanu niskiego mieści się w granicach 450-550us. Można to jakoś zrealizować programowo (tak by jednocześnie czytać / nadawać do sieci - czyli nie zjadając cennych mikrosekund pracy procesora)? A może da się to jakoś prościej rozwiązać?

    Procesor (ATMEGA8) z zewnętrznym rezonatorem 8MHz.
  • REKLAMA
  • #2 6282887
    _Robak_
    Poziom 33  
    A nie lepiej po prostu wszystko na timerach i przerwaniu? Tak co mi na szybko przychodzi do glowy tolinie podpiac pod przerwanie, ustawic na detekcje zmiany zbocza. Zrobic jakas zmienna ktora mowi czy jest to przerwanie startujace czy konczace. Jesli startujace to zczytujesz czy jest to 0 czy 1 i start timera. Jesli przerwanie konczace to stop timer i zapisujesz gdzies wynik. Troche juz pozno wiec nie wiem czy zrozumiale napisalem :)
  • REKLAMA
  • #3 6283273
    mrrudzin
    Poziom 39  
    Twój pomysł wygląda ok, ale trzebaby jakoś wyeliminować ryzyko zgubienia jednego zbocza, bo wtedy komunikacja padnie.

    Próbowałem przy opadającym zboczu wejść do przerwania, uruchomić timer i wykonywać przerwanie aż do momentu gdy na linii będzie stan wysoki (za pomocą loop_until_bit_is_clear, a następnie przepisując stan licznika do nowej zmiennej), albo konstrukcji
    
    do 
    {
    
    }
    while(bit_is_clear);
    LICZNIK = TCNT1;
    


    gdzie sam licznik to
    volatile double LICZNIK;
    TCCR1A = 0;
    TCCR1B |= _BV(CS10);

    Niestety bez rezultatów. Może mam gdzieś byka w kodzie?
  • REKLAMA
  • #4 6283570
    skynet_2
    Poziom 26  
    jak kolega _Robak_ napisał czyli
    datasheet -> rozdział External Interrupts -> Table 31 -> wiersz 2/4
    "ISC11=0 ISC10=1 Any logical change on INT1 generates an interrupt request."

    Natomiast w samym przerwaniu dajesz
    zmienna globalna = timer
    timer = 0
    if który sprawdza poziom na pinie od przerwania jeżeli 1 to było zbocze rosnące else zbocze opadające... .

    Ja zrobiłem tak detekcję RC5 i trzeba się mocno namęczyć żeby odbiornik coś zgubił.

    Przy 8MHz masz dla 10us 800 cykli zegarowych, czyli co byś nie napisał to procek i tak się wyrobi.

    Pozdrawiam
  • REKLAMA
  • #5 6283590
    _Robak_
    Poziom 33  
    W przerwaniu wystartuj moze tylko samego timera. A w glownym while`u zczytaj w dobrym momencie stan licznika ;) Ale i tak zrobilbym tak jak napisalem :P
  • #6 6283996
    mrrudzin
    Poziom 39  
    Napisałem coś takiego (kod poniżej) i prawie działa.
    Problem był w deklaracji:
    volatile int LICZNIK (miałem double zamiast int i nie chciało działać).
    Jak przepisać cały licznik do zmiennej (jaką zmienną zadeklarować)?

    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/signal.h>
    
    //Definicje i markodeklaracje
    
    //Definicje do delay-a 
    
    #define F_CPU 8000000 // 8MHz zegar procesora
    #define CYCLES_PER_US ((F_CPU+500000)/1000000) // cpu cycles per microsecond
    
    //F-cje odpowiedzialne za delay
    	void delay(unsigned int us)
    	
    	{
    	do{asm("nop");}while(--us);
    	}
    	
    	void delayms(unsigned int ms)
    	{
    	unsigned int i;
    	for (i=0;i<ms;i++)
    	 {
    	delay(999);
    	asm volatile (
    	"WDR"::);
    	}
    	
    	}
    
    //Definicje diody pomocnicze
    
    #define LEDP1ON sbi(PORTD,PD0) 
    #define LEDP2ON sbi(PORTD,PD1)
    
    #define LEDP1OFF cbi(PORTD,PD0)
    #define LEDP2OFF cbi(PORTD,PD1)
    
    #define LEDP1 PD0 
    #define LEDP2 PD1
    
    
    uint16_t czas1w;
    
    int i;
    volatile int FLAGA;
    volatile int INIT;
    volatile int INICJALIZACJA;
    volatile int LICZNIK;
    
    
    SIGNAL (SIG_INTERRUPT0) // przerwanie od 1-wire SLAVE
    {
    
    if(bit_is_clear(PIND,PD2))
    {
    TCNT1 = 0; 
    FLAGA=0;
    }
    
    if(bit_is_set(PIND,PD2))
    {
    LICZNIK=TCNT1L; 
    FLAGA=1;
    }
    
    }
    
    
    int main()
    {
    //Diody statusu
    DDRD |= _BV(LEDP1)|_BV(LEDP2);
     
    //PRZERWANIE
    	GIMSK = _BV(INT0); //włącz obsługę przerwań Int0
    	MCUCR |=_BV(ISC00);//Kazda zmiana stanu wywoluje przerwanie
    	sei();       // włącz obsługę przerwań	
    	
    //LICZNIK 1
    TCNT1 = 0;        
    TCCR1A = 0;
    TCCR1B |= _BV(CS11)|_BV(CS10);//preskaler CLK/64
    INIT=1;
    FLAGA=0;
    
    
    do
    {
    	
    	if(FLAGA==1)
    	{
    		cli();
    		if(LICZNIK>150) LEDP1ON;
    		else LEDP1OFF;
    		FLAGA=0;
    		sei();
    	}
    
    
    	
    }
    while(1);
    
    return 0;
    }
    
  • #7 6284056
    _Robak_
    Poziom 33  
    Zmienna to uint16_t :) Polecam w avr studio sprawdzac zachowanie zmiennych bo roznie z nimi bywa (tutaj raczej blad programisty:P ), czasami zachowuja sie calkowicie nie tak jak bysmy oczekiwali ;)
  • #9 6284168
    _Robak_
    Poziom 33  
    W AVRach nie da sie, priorytety sa ustawione na sztywno, ale uwzglednia sie je tylko wtedy, kiedy przerwania maja zostac wywolane w tym samym czasie. Dlatego sama obsluga musi byc jak najkrotsza.
REKLAMA