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

[ATMEGA16][C] Problem z ustawieniem bitów

GStrzelec 26 Mar 2010 01:11 1197 2
REKLAMA
  • #1 7880918
    GStrzelec
    Poziom 10  
    Witam. Zwracam się z prośbą o przeanalizowanie fragmentu tego kodu. Nie mogę sobie poradzić z 4 bitem port B. Zmieniam jego wartość w przerwaniu na przeciwną, w pętli for nie zmieniam już jego wartości przy operacjach ustawiania i zerowania bitów - ruszam tylko bity 0-3 - pomimo to nie zawiera on wartości takiej jakiej bym się spodziewał (powinien co 1 sek. zmienić stan na przeciwny a zachowuje się całkiem przypadkowo - przynajmniej tak mi się wydaje).


    
    
    #include <avr/io.h>                
    #include <avr/interrupt.h>        
    
    
    volatile unsigned int sec_l=0x00;
    
    int digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90};
    int i;
    
    ISR (SIG_OVERFLOW1)
    {
    	PORTB ^=_BV(4);        
    	sec_l++;
    	TCNT1 = 0x85EE;        
    }
    
    int main(void)
    {
    
    	DDRB = 0xFF;
    	PORTB = 0xFE;
    	
    	DDRC = 0x00;
    	PORTC = 0x03;
    
    	DDRD = 0xFF;
    
    	TIMSK = _BV(TOIE1);       
    	TCNT1 = 0x85EE;        
    	TCCR1A = 0x00;        
    	TCCR1B = _BV(CS12);
                            
    	sei();                
    
    	while(1){
    
    		for(i=0; i<4; i++){
    			PORTB |= 0x0F;
    			PORTD = digit[sec_l];
    	 		PORTB &= ~_BV(i);
    		}
      	};
    }
    


    Obsługa przerwania działa prawidłowo - jeżeli ustawię port A jako wyjście i w obsłudze przerwania dodam
     PORTA ^=_BV(4);  
    Wszystko działa prawidłowo.
  • REKLAMA
  • #2 7880959
    lelekx
    Poziom 30  
    PORTB |= 0x0F;

    w assemblerze wygląda to mniej więcej tak:
    in r16, PORTB
    ori r16, 0x0F
    out PORTB, r16


    podobnie
    PORTB &= ~_BV(i);

    przy czym to drugie polecenie zajmuje kilka instrukcji i trwa zdecydowanie dłużej.

    Przerwanie może wystąpić w dowolnym momencie, nawet pomiędzy instrukcjami in i out. Jeżeli to się stanie, stan portu ustawiony w przerwaniu zostanie zamazany podczas wykonania instrukcji out.
    Rozwiązania masz 3:
    - wyłączyć przerwanie na czas zmiany na porcie B w pętli
    - manipulować pojedynczymi bitami portu makrem _BV
    - przerwaniu tylko ustawiać znacznik zmiany stanu bitu 4 portu B, a w pętli sprawdzać znacznik i ustawiać stosownie stan portu

    Chyba najbardziej eleganckie jest trzecie rozwiązanie. Może to wyglądać tak:
    #include <avr/io.h>                
    #include <avr/interrupt.h>        
    
    
    volatile unsigned int sec_l=0x00; 
    
    int digit[10] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90}; 
    int i; 
    unsigned short sec_flag = 0;
    
    ISR (SIG_OVERFLOW1) 
    { 
       sec_flag ^= 1 << 4;
       sec_l++; 
       TCNT1 = 0x85EE;        
    } 
    
    int main(void) 
    { 
    
       DDRB = 0xFF; 
       PORTB = 0xFE; 
        
       DDRC = 0x00; 
       PORTC = 0x03; 
    
       DDRD = 0xFF; 
    
       TIMSK = _BV(TOIE1);        
       TCNT1 = 0x85EE;        
       TCCR1A = 0x00;        
       TCCR1B = _BV(CS12); 
                            
       sei();                
    
       while(1){ 
    
          for(i=0; i<4; i++){ 
             PORTD = digit[sec_l]; 
             PORTB = (PORTB & 0x10) | sec_flag | (0x0F & ~(1 << i));
          } 
         }; 
    }
  • #3 7884694
    GStrzelec
    Poziom 10  
    Dziękuję za pomoc. Przykład co prawda nie do końca działał ale bardzo pomógł. Myślałem też właśnie o takim rozwiązaniu.

    Poniżej analiza zapisu dla "krytycznego" przypadku - wtedy bit nie zostaje zamieniony.


    PORTB = (PORTB & 0x10) | sec_flag | (0x0F & ~(1 << i));


    xxx1xxxx
    & 00010000 - 0x10
    = ------------
    00010000
    | 00000000
    = ------------
    00010000
    | 00000000 - (0x0F & ~(1 << i))
    = ----------
    00010000
REKLAMA