Skocz do zawartości

[C] Problem ze sprzętowym USART


Laszlo900

Pomocna odpowiedź

Witam, mam problem z obsługą sprzetowego USART.

Komunikacja następuje pomiędzy dwiema atmegami: 8L (lub 8A) oraz 128A.

Chcę odczytywać dane z Atmegi8 (nadajnika) na 128mce (odbiorniku), ale program obsługi (napisany na podstawie przykładowych kodów z datasheetów tych kontrolerów) nie zapewnia odpowiedniego działania. W celu przetestowania działania (tłumaczę kod), nadajnik po wysłaniu znaku '1' świeci diodami, czeka, wysyła znak '0', czeka; natomiast odbiornik w tym czasie ma w zależności co dostanie zapalać odpowiednie diody na pinach swojego portu D.

Problem w tym, że stan na odbiorniku się nie zmienia i cały czas świecą się trzy diody (PORTD=0b00000111, czyli czyta cały czas jakby dostawał z Atmegi8 znak '0').

Dodam, że wybrałem port E (TxD0, RxD0) na Atmedze 128A jako wykorzystywane do usartu. Tak, podłączyłem Tx Atmegi8 do Rx 128 i Rx Atm8 do Tx 128.

Byłbym wdzięczny za sugestie. Zamieszczam kody:

NADAWANIE (ATMEGA8):

#define FOSC 1843200// Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1
#include <avr/io.h> 
#include <util/delay.h>

void main( void )
{
DDRC=0xFF;
USART_Init ( MYUBRR );
while(1)
{
USART_Transmit(1);
PORTC=0xFF;
       _delay_ms(200);
USART_Transmit(0);
PORTC=0x00;
       _delay_ms(200);
}

}

void USART_Init( unsigned int ubrr)
{
/* Set baud rate */
UBRRH = (unsigned char)(ubrr>>8);
UBRRL = (unsigned char)ubrr;
/* Enable receiver and transmitter */
UCSRB = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
}

void USART_Transmit( unsigned char data )
{
/* Wait for empty transmit buffer */
while ( !( UCSRA & (1<<UDRE)) )
;
/* Put data into buffer, sends the data */
UDR = data;
}

ODBIÓR (ATMEGA 128A):

#define FOSC 1843200// Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1
#include <avr/io.h>
#include <util/delay.h>

#define FOSC 1843200// Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1

void USART_Init( unsigned int ubrr )
{
/* Set baud rate */
UBRR0H = (unsigned char)(ubrr>>8);
UBRR0L = (unsigned char)ubrr;
/* Enable receiver and transmitter */
UCSR0B = (1<<RXEN)|(1<<TXEN);
/* Set frame format: 8data, 2stop bit */
UCSR0C = (1<<USBS)|(3<<UCSZ0);
}

unsigned char USART_Receive( void )
{
/* Wait for data to be received */
while ( !(UCSR0A & (1<<RXC)) )
;
/* Get and return received data from buffer */
return UDR0;
}

void main( void )
{
DDRD=0xFF;
unsigned char a;
USART_Init ( MYUBRR );
while(1)
{
a = USART_Receive();
if (a=='1')
{
	PORTD=0b00001111;

}
else if (a=='0')
{
	PORTD=0b00000111;
}
else
{
	PORTD=0b00000011;
}
}
}
Link do komentarza
Share on other sites

Moim zdaniem to w ogóle dziwny objaw, bo nadajesz binarne 0 i 1 (0x00 i 0x01) a oczekujesz znaków '0' i '1' (0x30 i 0x31).

Poza tym dziwnie programujesz kierunki portów, bo jednak UART ma jedno wejście i jedno wyjście a tu widzę cały port w tę albo w drugą.

Nie pamiętam (a teraz nie sprawdzę), ale sama inicjalizacja i włączenie UARTa chyba nie gwarantuje przełączenia linii portów w odpowiednią stronę (jak np. w SPI).

EDIT: Sprawdziłem: w ATmega8 odblokowanie nadajnika (TXEN) ustawia pin TXD na wyjście a odbiornika (RXEN) robi z RXD wejście, także ten fragment kodu OK.

Link do komentarza
Share on other sites

Niestety dalej nie odbierałem danych na atmedze128A pomimo zmiany danej nadawanej przez atmege8 na '1' i '0'. Ciekawą rzeczą było jeszcze kompilowanie kodu dla odbiornika w WinAVR, podczas, gdy nie kompilował się w AtmelStudio6 (musiałem ze zrozumiałych względów dodać do RXEN, TXEN, USBS, UCSZ0, RXC zero na końcu (RXEN0 itd.)). Mała korekta: odbiornik czyta cały czas warunek else, czyli wykrywa coś, co nie jest '1' i nie jest '0' (już po poprawieniu 1 i 0 na chary '1' i '0' w nadajniku).

Link do komentarza
Share on other sites

No dobrze, a w ogóle coś odbiera? To znaczy, czy przeprowadziłeś test w którym odbiornik zapala LED tylko na chwilę (np. 10ms) po odebraniu każdego (jakiegokolwiek) znaku? To by pokazało, czy prawidłowo synchronizuje się przynajmniej do bitu startu. Pojedyncze znaki nadawane np. co 1s powinny powodować błyski diody odbiorczej w tym samym czasie.

Jeżeli tak się stanie, to już tylko trzeba uzgodnić format danych 🙂 Nie mając oscyloskopu musisz go sobie "zrobić": możesz np. pokazywać odebrany znak na diodach (na 8 równolegle lub mrugając jakąś jedną zgodnie z odebranymi bitami, no wiesz). Wtedy wyjdzie czy złe są prędkości transmisji czy format (parzystość, stop itd). Mając odpowiednie narzędzia takie problemy rozwiązujesz w 30 sekund pierwszym pomiarem. Szkoda czasu na zastanawianie się już drugi dzień i wgapianie się w kod.

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Rzeczywiście - nie pokazuje, że cokolwiek odebrał. Zauważyłem też, że zegary, pomimo ustawienia takiego samego FOSC dają nieco inny czas w delayach. W ogóle przestawianie FOSC nic nie zmienia z punktu widzenia delayów. Na tę chwilę jestem zmuszony zostawić USART. Wrócę jeszcze do problemu, dzięki za sugestie.

Link do komentarza
Share on other sites

UART działa, ale co dziwne atmega128 odbiera (na wyswietlaczu wyswietlane są) tylko parzyste liczby. W przypadku nieparzystych, przez chwilę wysyła je poprawnie (5 razy), a potem zmienia wskazanie na liczbe parzystą. Każdej wybieranej przeze mnie nieparzystej jest wtedy przypisywana parzysta, np. liczbie 137 -> 162; 77, zmienił na 211 (z jedno wskazanie) i w stanie ustalonym pozostało 186; 207 -> 190. Nie wiem zupełnie, co może być przyczyną. Dodam, że tym razem zastosowany jest w obu procesorach kwarc 16MHz, fusy oczywiscie wczesniej zmienione, czestotliwosc przetestowana na delayach. Testowane przy różnych BAUD rate'ach na podstawie karty katalogowej. Również przy różnych wariantach badania parzystości (even/odd). Co ciekawe, przy ramce z jednym bitem stopu, nawet parzyste były źle odczytywane (stan się nie ustalał - skakał ppomiędzy średnio trzema różnymi wskazaniami).

Zamiszczam kody:

Nadajnik (Atmega8A):
#define F_CPU 16000000
#define USART_BAUDRATE 250000
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#include <avr/io.h> 
#include <util/delay.h> 
#include <HD44780.h>

int main(void)
{
char ByteToSend = 207;
UCSRB = (1<<RXEN) | (1<<TXEN); 
//UCSRC = (1<<URSEL) | (1<<UCSZ1) | (1<<UCSZ0); //8-bitowe znaki danych
UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0) | (1<<UPM0) | (1<<UPM1);
//UCSRA = (1<<U2X);
UBRRH = (BAUD_PRESCALE >>8);
UBRRL = BAUD_PRESCALE;

while (1)
{
	while(!(UCSRA & (1<<UDRE))) {};
	UDR=ByteToSend;
}
}

Odbiornik (atmega128A):

#define F_CPU 16000000
#include <avr/io.h>
#include <avr/HD44780.h>
#define USART_BAUDRATE 250000
#define BAUD_PRESCALE ((( F_CPU / ( USART_BAUDRATE * 16))) - 1)

int main ( void )
{
   DDRD = 0b11111111; // Set LED as output

LCD_Initalize();
LCD_Clear();
LCD_GoTo(0,0);
LCD_WriteText("no kurwa jazda");
_delay_ms(1000);
LCD_Clear();




char ReceivedByte, ByteToSend;
char ReceivedByte_table[15];

UCSR0B = (1 << RXEN0 ) | (1 << TXEN0 ); // Turn on the transmission and reception circuitry
//UCSR0C = (1 << UCSZ00 ) | (1 << UCSZ01 ); // Use 8- bit character sizes - URSEL bit set to select the UCRSC register
UCSR0C = (1<<USBS0)|(3<<UCSZ00)|(0<<UPM00)|(1<<UPM01);
//UCSR0A=1<<U2X0;



UBRR0H = (BAUD_PRESCALE>>8); // Load upper 8- bits of the baud rate value into the high byte of the UBRR register
UBRR0L = BAUD_PRESCALE ; // Load lower 8- bits of the baud rate value into the low byte of the UBRR register	

/*
/////////////////sending data
while (( UCSR0A & (1 << UDRE )) == 0) {}; // Do nothing until UDR is ready for more data to be written to it
UDR = ByteToSend ; // Send out the byte value in the variable " ByteToSend "
*/


while(1){
	////////////////recieving data
	while (( UCSR0A & (1 << RXC0 )) == 0) {
		//LCD_Clear();
	//	LCD_GoTo(0,0);
		//LCD_WriteText("odbieram");
	}; // Do nothing until data have been received and is ready to be read from UDR

	ReceivedByte = UDR0 ; // Fetch the received byte value into the variable " ReceivedByte "
	//LCD_WriteText("recieved");
	//UDR0_temp=

	itoa(ReceivedByte,ReceivedByte_table,10);
	LCD_Clear();
	LCD_GoTo(0,0); 
	LCD_WriteText(ReceivedByte_table);

	for(int i=0;i<15;i++){
		ReceivedByte_table[i]=0;
	}
	_delay_ms(100);


}

}

Układ miał przekazywać zliczane dane. Jeśli nie uda się szybko rozwiązać problemu, to będę zliczał co 2, wysyłał i dzielił w odbiorniku :v

Link do komentarza
Share on other sites

Mała podpowiedź: zamiast pracować na liczbach w postaci dziesiętnej przestaw się na szesnastkowe a najlepiej binarne i takie wypisuj na wyświetlaczach nadajnika i odbiornika. Dopiero w takiej postaci porównuj wyniki - od razu zauważysz pewne zależności. Jeżeli oba układy ruszyły, ale masz problemy ze spójnością transmisji to zacznij to analizować przykładając binarny wzorzec nadawany do tego odbieranego. Podstawowym problemem w takich przypadkach jest oczywiście różnica prędkości transmisji choć - jak piszesz - u Ciebie to raczej nie wchodzi w grę. W takim razie - jeżeli jesteś absolutnie pewien co do:

- równego zegara obu procesorów,

- równego taktowania obu UARTów,
- identycznych formatów nadawanego i odbieranego znaku,
- prawidłowego połączenia mas obu układów

to możesz mieć kłopoty z samą linią transmisyjną. Opowiedz coś o tym. Jak daleko są te procesory od siebie, co jest między nimi (jakie kable, jakie nadajniki/odbiorniki linii itp). Zanim jednak zaczniemy grzebać w takich szczegółach zrób proszę jakiś prosty eksperyment - taki, żeby wyniki były bardziej jednoznaczne. Proponuję:

1. Zacznij od testów statycznych:

a. Wysyłaj stały stan wysoki z nadajnika i zmierz napięcie na odbiorniku (minus woltomierza do masy procesora odbierającego, plus do jego nóżki RXD)

b. To samo w stanie niskim - wyłącz UART i wyślij na pin TXD stan 0. Zmierz ile dochodzi do przeciwległego RXD.

2. Test prostych wzorców. Ustaw dokładnie te same, proste formaty znaku w obu procesorach, np. 8 bitów danych, 1 bit stopu, bez parzystości oraz niezbyt duże prędkości transmisji, coś w okolicach 10000bps.

a. Wysyłaj wzorzec 0b11111111 i sprawdź co się odbiera, potem kolejno: 0b11111110, 0b11111101, 0b11111011 i td aż do 0b01111111

b. To samo dla trochę innych: 0b11111110, 0b11111100, 0b11111000, 0b11110000 itd aż do 0b00000000

Nawet jeśli wyniki nie będą pewne i ustalone, zapamiętuj które bity mały kłopoty. Potem rozpisz sobie to wszystko na kartce i przemyśl. Możesz też wysłać to tutaj. Pamiętaj, że znaki wysyłane są przez UART "od tyłu", tj. najmłodszy bit idzie przodem a tuż przed nim jest bit startu o wartości zawsze = 0. Możesz następnie zwiększyć prędkość transmisji np. 10-krotnie i powtórzyć testy. Czy widzisz jakieś regularności, czy zachodzą jakieś "przesłuchy" między bitami", jakieś krytyczne długości np. łańcuchów zer po których jest źle ? itp.

Jak widzisz wszystkie te działania sprowadzają się do próby analizy przychodzących danych za pomocą narzędzia którym dysponujesz, czyli UARTa odbiorczego. Mając oscyloskop problemy takich prostych interfejsów rozwiązujesz w minutę. Powodzenia.

----------------

EDIT: I jeszcze jedno: Twój nadajnik wysyła w ciemno znaki z prędkością 250000bd czyli ok. 25000 znaków/s czyli 40us/znak. W pętli odbiorczej, po każdym znaku robisz konwersję bin->string a potem kasujesz cały LCD i wypisujesz tam sporo rzeczy. Ile to trwa? I przy takiej prędkości to kabelki, nie mówiąc już o tych najprostszych układach interfejsów RS232, mogą się pogubić. Opisz fizyczną konfigurację tego systemu i przemyśl organizację transmisji. Może jakiś prosty protokół typu: wysłanie wyniku, oczekiwanie na potwierdzenie, wysłanie następnego?

----------------

EDIT2: Dziwne przekłamania nie mające nic wspólnego ze zmianami na pojedynczych bitach mogą mieć związek z wysyłaniem w ciemno strumienia znaków i przepełnianiem się odbiornika. Jeżeli tak zatkany UART, po "odkorkowaniu" zacznie obserwować linię RXD w przypadkowym czasie, to pierwsze zero jakie zobaczy (np. w środku jakiegoś znaku) będzie dla niego bitem startu no a potem wszystko już będzie źle.

Zobacz czy się coś poprawi gdy między nadawane znaki wstawisz choćby 10ms opóźnienia.

Możesz też sprawdzać w odbiorniku bit błędu ramki (Frame Error) mówiący o nieodebraniu prawidłowego bitu stopu - to ewidentnie świadczy o rozsynchronizowaniu się UARTów.

Nadajnik musi wysłać co najwyżej tak szybko jak odbiornik jest stanie znaki odbierać i przetwarzać. Docelowo najlepiej gdybyś wysyłał jakieś krótkie komunikaty (nawet z pełną prędkością) a odbierał je do bufora w przerwaniu. Po zgromadzeniu całego komunikatu możesz opróżnić bufor odbiorczy przez przepisanie całej informacji w inne miejsce i wysłanie informacji zwrotnej "jestem gotów na przyjęcie nowych danych". Teraz, już na spokojnie jakaś funkcja może zająć się analizą i obróbką otrzymanych danych. Bufor odbiornika musi być oczywiście na tyle długi, by mieścił nawet najdłuższy komunikat.

  • Pomogłeś! 1
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.