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

[mega32][C] UART prawie działa

bolek 04 Sie 2010 15:51 1552 9
  • #1 8364456
    bolek
    Poziom 35  
    Cwicze UARTa i natrafiłem na problem. Całość rozgrywa się w przerwaniu:

    SIGNAL (USART_RXC_vect) 
    {
    	UDR_KO=UDR;
    	if (UDR_KO==48) toggle(IO_PORT, BUZ);
    	UDR=UDR_KO;
    	UDR_KO=0;
    }
    
    


    Procesor ma odesłać odebrany znak i przy okazji gdy =0 to negauje port. Echo działa jak należy, jednak zmiana pinu odbywa sie nieregularnie (niekiedy trzeba wysłać znak dwa razy). Czemu?
  • #2 8364534
    rpal
    Poziom 27  
    Czy dobrze określiłeś prędkość transmisji jednakową dla odbiornika i nadajnika ? Jaka ma być i pokaż co masz w rejestrach za nie odpowiedzialnych i jaka jest czestotliwośc kwarca ?
  • #3 8364572
    bolek
    Poziom 35  
    Tu masz wszystko, kwarc 11059200. Do terminala wraca mi się dokładnie to co z niego wysłałem więc predkości sa ok. Jak się przyglądnie człowiek to widać że led mignie zamiast się zaświecić, tak jak by to przerwanie zrobił dwa razy (ale znak wysyła się tylko jeden)

    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    #define UART_BAUD 19200
    #define UART_SPD (F_CPU/(16ul*UART_BAUD))-1
    
    #define clr(PORT, BIT) (_SFR_BYTE(PORT) &= ~_BV(BIT))
    #define set(PORT, BIT) (_SFR_BYTE(PORT) |= _BV(BIT))
    #define toggle(PORT, BIT) (_SFR_BYTE(PORT) ^= _BV(BIT)) 
    
    
    
    #define IO_DDR DDRC
    #define IO_PORT PORTC
    #define IO_PIN PINC
    #define LED1 0
    #define LED2 1
    #define BUZ 7
    
    char UDR_KO=1;
    
    
    
    int main()
    {	
    
    	IO_PORT=255;
    	IO_DDR= 1<<LED1|1<<LED2|1<<BUZ;
    
    //=======================================================
    	UBRRL=UART_SPD; //wpisanie predkosci uarta
    	UCSRB= 1<<RXCIE|1<<RXEN|1<<TXEN;		// przerwania tylko od RX, odb i nad. wlaczone				
    	sei();	
    		UDR=UART_SPD;
    	while(1)
    		{
    		toggle(IO_PORT, LED1);
    		}
    		
    return 0;
    }
    
    
    
    SIGNAL (USART_RXC_vect) 
    {
    	UDR_KO=UDR;
    	if (UDR_KO==48) toggle(IO_PORT, BUZ);
    	UDR=UDR_KO;
    	UDR_KO=0;
    }
    
    
    SIGNAL (USART_TXC_vect) 
    {
    	UDR=UDR_KO;
    
    }
    
    


    Dodano po 1 [godziny] 54 [minuty]:

    Nikt nie ma pomysłu?
    ---------------edit
    Jeśli do while dodam jakiekolwiek polecenie to dioda miga. Symulacja także zaczyna działać prawidłowo (przedtem zwieszała się za toggle w while)
  • Pomocny post
    #4 8365712
    BoskiDialer
    Poziom 34  
    Zastanów się, czy makro toggle jest operacją atomową. W tym kodzie można tą operację rozbić na 3 etapy: pobranie IO_PORT, zanegowanie bitu oraz zapisanie IO_PORT. Jeśli przerwanie wystąpi pomiędzy pobraniem a zapisaniem IO_PORT, przerwanie spowoduje zmianę stanu pinu, ale zaraz po powrocie nastąpi wpisanie nowej wartości z IO_PORT, z którą wiąże się wpisanie starej wartości (odczytanej przed przerwaniem) na bicie BUZ. Możesz makro toggle w pętli głównej zmienić na operację atomową wrzucając ją pomiędzy cli/sei lub rozbić makro na warunek testujący dany bit oraz realizujący ustawienie bitu lub skasowanie bitu jako osobne przypadki z nadzieją, że kompilator przekształci te operacje w instrukcje sbi/cbi które nie spowodują utraty wartości na bicie BUZ.
  • #5 8365840
    bolek
    Poziom 35  
    Wydaje się być oczywiste... Chodzi może nie tyle o atomową prędkość, co bezpośredni dostęp do każdego bitu. To makro w takim razie nie ma prawa występować w programach gdzie dostęp do portów odbywa się z kilku poziomów

    set i clr działają na podobnej zasadzie jak toggle czy właśnie bezpośrednio pracują na konkretnym bicie?
  • #6 8365870
    BoskiDialer
    Poziom 34  
    Ustawienie i skasowanie bitu może zostać przez kompilator zrealizowane jako operacja nie atomowa w ten sam sposób co toggle. Wyjątkiem jest przypadek, kiedy kompilator ma włączoną optymalizację oraz operacja jest dokonywana nad rejestrem IO pod adresem z pewnej małej puli - wtedy kompilator może zrealizować ustawienie lub skasowanie bitu za pomocą instrukcji sbi lub cbi które to są atomowe. Najłatwiejsze i najbezpieczniejsze jest rozwiązanie postaci:
      cli();
      toggle(IO_PORT, LED1);
      sei();

    lub co bardziej preferuję:
      uint8_t tmp_sreg;
    
      tmp_sreg = SREG;
      cli();
      toggle(IO_PORT, LED1);
      SREG = tmp_sreg;
  • #7 8366003
    mirekk36
    Poziom 42  
    Panowie, ale tak naprawdę co tu ma atomowość do rzeczy skoro dioda ma być toglowana w przerwaniu gdy inne przerwania są przecież wyłączone. To wtedy nie jest istotne czy będzie przestawiana przez sbi, cbi czy też poprzez ładowanie rejestru i maskowanie któregoś tam bitu. To jedno.

    A drugie:

    Ponieważ mam pod ręką identyczną płytę testową co autor postu i jestem (chociaż na urlopie) to przy kompie - więc zapodałem ten BARDZO DZIWNY kod do procka:

    
    ISR (USART_RXC_vect)
    {
    	UDR_KO=UDR;
    	if (UDR_KO==48) toggle(IO_PORT, BUZ);
    	UDR=UDR_KO;
    
    }


    Wszystko działa IDEALNIE tak jak zamierzał i wymyślił sobie autor. Dioda się togluje tylko i wyłącznie gdy zostanie naciśnięte zero w terminalu. Wszystko działa w pełni poprawnie.

    Ale już wiem co może być przyczyną tego dziwnego zachowania u autora, że dioda LED dziwnie miga, albo czasem dwa razy mignie albo w ogóle nie mignie ;)

    ŹLE DOPASOWANA PRĘDKOŚĆ baudrate w mikrokontrolerze i terminalu. Mnie udaje się ten efekt uzyskać dokładnie taki sam czyli dziwny - gdy w procku mam ustawione (jak w listingu programu) baudrate 19200 natomiast w terminalu gdy ustawiam 9600. To dioda zachowuje się dokładnie tak jak opisuje autor (aż sam się dziwię, że echo przylatuje prawidłowe - tak jakby nie było niedopasowania prędkości ;)

    Ale gdy w terminalu ustawię 19200 to wtedy wszystko działa jak brzytwa czyli po naciśnięciu ZERO w terminalu dioda LED się zapala, gdy kolejny raz nacisnę ZERO to gaśnie no i tak cyklicznie.

    zatem żadne wyłączanie przerwań w przerwaniu nie jest potrzebne.
  • #8 8366040
    BoskiDialer
    Poziom 34  
    Przerwanie samo z siebie jest nieprzerywalne więc tutaj nie trzeba nic grzebać. Co innego z pętlą główną, w której również występuje toggle, na innym bicie ale dalej w tym samym rejestrze, co powoduje problemy przy braku atomowości.

    fragment kodu o którym mowa:
     while(1) 
          { 
          toggle(IO_PORT, LED1); 
          }
  • #9 8366071
    mirekk36
    Poziom 42  
    BoskiDialer ---> aaaa no tak, ja się skupiłem tylko na tym toglowaniu w przerwaniu bo myślałem że tylko o to chodzi. Chociaż w przerwaniu ta dioda LED1 nie jest ruszana tylko BUZ więc tu też by się nic nie stało. Jednak gdyby była toglowana i w przerwaniu i w pętli głównej to jak najbardziej trza by było zwrócić uwagę na atomowość .... albo raczej w takim przypadku inaczej przemyśleć program.
  • #10 8366230
    bolek
    Poziom 35  
    Przy 9600, a zatem 2x mniejszej prędkości PC można sądzić że procek odbierze dwa bajty, nie ma żadnej kontroli gotowości rejestru ani poprawności danej więc ma szanse działać jak chce.

    Wracając do tematu, ktoś może by rozkręcił podane makra set clr w disassemblerze przy różnych optymalizajach? (nie mam już tutaj takiego narzędzia, i nigdy tego nie robiłem :) ).
    Pamiętam że wyczytałem iż to co bazuje na portach i "niby" przesuwaniu bądź operacjach logicznych na bitach jest przez kompilator zamieniane na bezpośredni rozkaz. Czyli:
    
    PORTA |1<<1 

    da to samo co
     sbi PORTA, 1

    Ciekawe czy większość softów w których miesza się w portach zarówno w pętli głównej jak i przerwaniu jest "felerna" :)
REKLAMA