obsługa błędów od odbiornika UARTa

0

Witam

Napisałem właśnie driver do UARTa.

/* interrupt from receive buffer - data in UDR is ready to read */
ISR(USART_RXC_vect)
{
	/* read errors : frame error, data overrun, parity error */
	uint8_t status=UCSRA;

	/* read char from receive data register */
	char data=UDR;

	/* calculate new head index */
	uint8_t temp_head;
	temp_head=(uart_rx_head+1)&UART_RX_BUF_MASK;

	/* check if the receive buffer is full */
	if (temp_head==uart_rx_tail)
	{
		/* will execute if receive buffer overflow */
		LED_RX_BUF_OVF=ON;
	}
	else
	{
		/* check if frame is good - mean has no errors */
		if (!(status&((1<<FE)|(1<<DOR)|(1<<PE))))
		{
			uart_rx_head=temp_head;
			uart_rx_buf[uart_rx_head]=data;
		}
		else
		{
			LED_ERR=ON;
		}
	}
}

Mam zrobioną "pętlę zwrotną" w Atmega16 - tj. dane które wysyłam trafiają mi na Rx i je sobie tym driverem czytam.
Wszystko działa poprawnie ponieważ po "zapchaniu" bufora zaświeca mi się dioda.
To co mnie niepokoi to to że nigdy nie zaświeca mi się dioda LED_ERR i w związku z tym mam pytania:

  1. Czy poprawnie stworzyłem procedurę wykrywania błędów od Frame Error, Parity Error i Data Overrun ?
    W dokumentacji pisało że zanim się zczyta UDR należy zczytać status więc tak też robię...

  2. Czy w przypadku gdy mam pętlę zwrotną a drucik ok 10 cm to nigdy takie błędy nie wystąpią ?
    Moim zdaniem czasem powinny (szczególnie Frame Error) ponieważ nadajnik i odbiornik UART'a to dwa różne niezależne moduły a Atmedze16... Nie wiem jak jest z taktowaniem - być może błąd nie występuje dlatego że oba moduły są ze sobą zsynchronizowane w jakiś sposób i nigdy nie wystąpi przesunięcie modułu próbkującego i nadawczego ? Dodam tylko że pracuję na 8MHz (oscylator wew.) więc w związku z tym zgodnie z tabelą dot. błędów UARTa jakiś tam procent ramek błędnych powinien być.
    U mnie jest tak że nie ma w ogóle. Błędów nie wykrywa nawet jeśli w przerwaniu przestanę odczytywać UDR, to jest linię:

 char data=UDR; 

zastąpię linią:

 char data=0; 

a to już wg mnie definitywnie powinno generować błąd od Data Overrun ponieważ po przyjściu kolejnej danej w UDR jest jeszcze poprzednio nieodczytana.

Ktoś pomoże ?

1

Istnieje możliwość, że masz błąd logiczny w przepływie programu.
Spróbuj wyciągnąć sprawdzanie statusu przed odczyt danych, bo możliwe, że czytasz śmieci:

#define ERR_MASK ((1 << FE) | (1 << DOR) | (1 << PE))

/* interrupt from receive buffer - data in UDR is ready to read */
ISR(USART_RXC_vect)
{
    uint8_t status, temp_head, data_h, data_l;

    /* read errors : frame error, data overrun, parity error */
    status = UCSRA;
    /* read char from receive data register */
    /*
     * Dokumentacja zakłada odczyt z UDR i UCSRB niezależnie od
     * statusu.
     * Uart może mieć 9bitowe ramki, przepisz kod tak, aby używał int16_t
     * albo int32_t.
     */

    /* Edit: data_h ma być czytane _przed_ data_l. */
    data_h = UCSRB;
    data_l = UDR;

    /* check if frame is good - mean has no errors */
    if (status & ERR_MASK != 0)
    {
        LED_ERR = ON;
        /*
         * Wychodzimy z procedury, bo możliwe, że dostaliśmy śmieci.
         * Ponadto zgaduję, że przerwanie może też być wywoływane
         * w przypadkach błędów transmisji. Ale nie jestem tego pewien.
         */
        return;
    }
 
    /* calculate new head index */
    /* 
     * Wygląda na jak zoptymalizowane dzielenie modulo.
     * Robisz jakiś bufor cykliczny?
     */
    temp_head = (uart_rx_head + 1) & UART_RX_BUF_MASK;
 
    /* check if the receive buffer is full */
    /*
     * Co to jest uart_rx_tail? Jaką ma to wartość? Może wygenerowałeś przypadek,
     * który wpadał w to rozgałęzienie kompletnie ignorując status.
     */
    if (temp_head == uart_rx_tail)
    {
        /* will execute if receive buffer overflow */
        LED_RX_BUF_OVF = ON;
    }
    else
    {
        uart_rx_head = temp_head;
        /* uart_rx_buf powinien być typu uint16_t. */
        uart_rx_buf[uart_rx_head] = (uint16_t) (((data_h & 0x01) << 8) | data_l);
    }
}

Zastrzeżenia: nie testowałem, bo nie za bardzo mam na czym. a dokumentacja pokazywała tylko przykłady aktywnego pollingu.

Co do fizycznych możliwości wystąpienia błędów - na Twoim miejscu nie liczyłbym długości drucików, tylko raczej założył, że błędy tak czy siak wystąpią.

P.s. dobrą praktyką są spacje wokół operatorów - readability matters.

Ukłony.

1

Dzięki za odp. Mam pewne "ale" :

  1. Odczyt statusu jest przed odczytem danych więc nie bardzo rozumiem po co to piszesz. Jak widać to :
status = UCSRA;

znajduje się przed odczytem danych z UDR'a. Sprawdziłem to w wygenerowanym pliku assemblera, jest dokładnie tak jak mówię : najpierw odczyt statusu a bezpośrednio za tym z UDR'a dopiero.

  1. Wszystko mam skonfigurowane pod parametry transmisji zawierającej 8 bitów danych więc kodu nie bardzo chce mi się przepisywać i dlatego nie stosuję odczytu z UCSRB bo nie ma po co.

  2. Masz rację warunek zapełnienia bufora czyli rx_tail == rx_head pojawiał się i to po nieznacznym czasie od momentu startu ale po wywaleniu tego warunku i wstawieniu sugerowanego sprawdzania i wychodzenia returnem jeśli śmieci , nic to nie zmieniło.

  3. Jako że ramka jest 8 bitowa bufora na 16 bitów zmieniał nie będę bo nie ma to wpływu na to czy ramka ma błąd czy nie.
    Przecież ramka 8 bitowa też może jakieś tam błędy mieć.

Nasuwa mi się taka myśl że nie wyłapuję ani jednego błędu ponieważ nadajnik nadaje na tej samym zegarze co odbiornik odbiera.

Hurrrra... mogę potwierdzić własne przypuszczenia ponieważ jeśli na chwilkę wyciągnąłem drucik z odbioru (linii Rx) to od razu zapala się dioda która własnie sygnalizuje "jakiś tam" błąd komunikacji (jeden z trzech sprawdzanych).

Mamy więc wniosek dla zainteresowanych :
W przypadku pętli zwrotnej zorganizowanej za pośrednictwem jednego mikrokontrolera bardzo ciężko jest wygenerować błąd jeśli długość drucika łączącego jest mała oraz jeśli w pobliżu brak jest zakłóceń.
Jest to spowodowane prawdopodobnie (to tylko moje przypuszczenie jednak) tym że nadajnik i odbiornik są w tym jednym mikrokontrolerze zsynchronizowane - to z kolei może wynikać z tego że sygnał taktujący te moduły idzie z tego samego głównego zegara taktującego (to zdanie proszę traktować czysto intuicyjnie - nie potrafię tego logicznie wytłumaczyć ale tak czuję...)

I nigdy prosze nie robić "bez nawiasu" takiego czegoś :

if (status & ERR_MASK != 0)

ponieważ operator & ma mniejszy priorytet niż != tak więc najpierw sprawdzi się ERR_MASK != 0 a potem wynik tego sprawdzenia będzie "and'owany".

Dołączając informację dla przedmówcy , wniosek jest też taki że przerwanie występuje również wówczas gdy mamy "błąd ramki" bo to przerwanie od tego że dana gotowa do odebrania w UDR.

Pozdrawiam ciepło
Adam

0

Dzięki za wyłapanie tego ifa. Pisałem z głowy i przeoczyłem to.

1 użytkowników online, w tym zalogowanych: 0, gości: 1