Skocz do zawartości

UART Arduino...pomoc w zrozumieniu


farmaceuta

Pomocna odpowiedź

Witam kolegow..

Hmmm..nawet nie wiem od czego zaczac..chcialbym ogolnie zrozumiec zasade dzialania UART. O ile sa mi wiadome funkcje Serial jak np:

read()

write()

readbytes()

available()    (oczywiscie w sensie podstawowym potrafie sie nimi obslugiwac ) to nie rozumien np:

1. Czy sprzetowy UART dziala w tle? Tzn. Czy podczas odczytu/wpisania do bufora program czeka az zostanie wykonane polecenie i dopiero rusza dalej czy tylko "inicjuje" polecenie i reszta juz w tle sie odbywa..

2. A jak sprawa sie ma z biblioteka SoftwareSerial?

3.czy arduino jednoczesnie odbiera i wysyla bajty?

4.czy jest jakis PROSTY sposob na synchronizacje transmisji? Mianowicie o ile przesylanie znaku/bajtu/tablicy czy struktury powiedzmy raz na sekunde na drugie arduino tylko w jedna strone nie stanowi problemu to juz w dwie strony i z duza czestotliwoscia jest problem...dane otrzymuje prawidlowe lecz widac ze arduino sie "dlawi" i transmisja jest nie systematyczna..

5.a jak poradzic sobie z rozszyfrowaniem odebranych pakietow kiedy nie wiemy jaka dlugosc beda mialy? Dodam ze w przypadku komunikacji miedzy dwoma arduino moglbym dodac znak zakonczenia lini itp a pozniej szukac tego znaku i zaczynac wczytywac bajty do wykrycia nastepnego znaku (nie zbyt doskonale ale napewno lepsze niz czekac az "utrafie" z delay() zeby odebrac prawidlowej dlugosci pakiet)..lecz dane bede otrzymywal z pc i nie ma mozliwosci "oddzielenia" pakietow..

Z gory przepraszam za pytania ktore dla wielu sa smieszne, ale jestem poczatkujacy i nie wszystko jest dla mnie jasne..😕

Bede wdzieczny za kazda pomoc..

Pozdrawiam Andrzej

 

Link do komentarza
Share on other sites

(Nie wiem czemu nie moglem edytowac pierwszego postu w celu jego usuniecia, bo wszystko powyzsze nie wazne juz..)

Mam takie pytanie...przesylam jakies informacje w postaci bajtow..np.

struct txbuf {
  char znak1 = '<';
  uint8_t dane[5] = {1,2,3,4,5};
  char znak2 = '>';
  }

kopiuje strukture do tablicy i wysylam...po stronie odbiorczej odbieram bajty..

char rx = Serial.read();

szukam pierwszego "znak1" ktory informuje o poczatku informacji i wpisuje wszystko co przyjdzie do tablicy do czasu az wykryje "znak2".

No i to dziala elegancko...tylko ze "znak1" i "znak2" to odpowiednio 60/62 w postaci bajta...no i napewno dojdzie do takiej sytuacji ze wkoncu bede mial jakis bajt wysylany ktory taka wartosc bedzie mial i po konwersji na char podczas odbioru bede mial bledny poczatek lub koniec transmisji...

Moje pytanie brzi czym w miare pewnym moge oznaczac poczatek i koniec przesylania danych??

Link do komentarza
Share on other sites

30 minut temu, farmaceuta napisał:

Moje pytanie brzi czym w miare pewnym moge oznaczac poczatek i koniec przesylania danych??

To ciężko stwierdzić - zależy co przesyłasz jako dane. Zwykle w takich przypadkach stosuje się pakietowanie, by uniknąć problemów - tak działa ETH(ernet) - używa ramek, I2C - tutaj akurat używa sygnału start/stop, USB - SYNC/EOP i wiele zaawansowanych protokołów. 

Tobie również polecam to pakietować - patrząc na ilość danych masz jeszcze sporo miejsca w przepustowości magistrali, więc overhead nie będzie mocno uciążliwy. Ja zwykle stosuję najprostszy pakiet:

image.thumb.png.d22c3eb8ac03fd277c088c5dc2cd74ed.png
Format najprostszego pakietu z CRC

Jak chcesz CRC możesz sobie podarować, ale zwykle wolę je stosować, wtedy przynajmniej odbiorca wie czy dane są poprawne 😉 

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

6 minut temu, H1M4W4R1 napisał:

To ciężko stwierdzić - zależy co przesyłasz jako dane. 

Jako dane to tak jak w przykladzie czyli strukture ktora zawsze moge rozbudowac o inne typy i latwo skopiowac do tablicy (typu char na chwile obecna) no i wyslac..

 

9 minut temu, H1M4W4R1 napisał:

 Ja zwykle stosuję najprostszy pakiet:

image.thumb.png.d22c3eb8ac03fd277c088c5dc2cd74ed.png
Format najprostszego pakietu z CRC

Moglbys to troszke rozwinac?? O co chodzi z tym pakietowaniem...no i z tym CRC...jak to ugryzdz tak ogolnie...moge sie mylic ale chyba metoda na takie "CRC" jest XOR'owanie wszystkich bajtow i po odbiorze powtorne w celu sprawdzenia czy oba wyniki sa prawidlowe..tak?

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

/*Begining of Auto generated code by Atmel studio */
// Dołączenia dla Atmel Studio - nie powinny być wymagane w Arduino IDE
#include <Arduino.h>
#include <inttypes.h>

// Opis pakietu danych - Twojej struktury
class Packet
{
	// Długość pakietu (max 255B, pamiętaj, że pakiet przechowujesz w RAM'ie ;))
	uint8_t length; //0 - 255
	
	
	public: 
	uint8_t* data; // Dane pakietu
	Packet(uint8_t len); // ..ctor (konstruktor)
	void transmit(); // Wyślij pakiet po UART
};

// Opis strumienia pakietów - podobnie do UART ;)
class PacketStream{
	uint8_t bIndex = 0; // Indeks odczytu 0 - długość; n gdzie n > 0 - (n-1)'ty bajt pakietu
	uint8_t cPacketLength; // Odczytana długość pakietu
	uint8_t* cPacketData = nullptr; // Dane pakietu
	Packet* packet; // Wskaźnik do pakietu
		
	public:
	void(*onPacket)(Packet* packet); // Funkcja wykonywana po odbiorze pakietu - handler
	void onData(uint8_t recv) // Po uzyskaniu danych na UART
	{
		if(bIndex == 0) // Jeżeli indeks = 0
		{
			cPacketLength = recv; // Ustal długość
			cPacketData = (uint8_t*) calloc(cPacketLength, sizeof(uint8_t)); // Przygotuj tablicę na dane dla pakietu
		}
		else
		{
			cPacketData[bIndex - 1] = recv; // Przepisz dane na odpowiednie miejsce tablicy
		}			
	
		
		bIndex++; // Zwiększ indeks
		if(bIndex > cPacketLength) // Jeżeli odczytałeś cały pakiet
		{
			bIndex = 0; // Zresetuj indeks
			Packet* p = new Packet(cPacketLength); // Stwórz obiekt pakietu
			p->data = cPacketData; // Przypisz mu dane
			onPacket(p); // Wykonaj callback
		}
	}
};



// Konstruktor
Packet::Packet(uint8_t len){
	length = len; // Ustal długość pakietu
	data = (uint8_t*) calloc(len, sizeof(uint8_t)); // Przypisz dane
}

void Packet::transmit() // Wyślij pakiet
{
	Serial.write(length); // Wyślij długość na UART
	for(uint16_t l = 0; l < length; l++) // Dla całej długości pakietu
	{
		Serial.write(data[l]); // Wyślij określony bajt danych
	}
	free(this); // Wyczyść pakiet z pamięci ;) - po wykonaniu już nie będziesz mógł go użyć
}


/* Arduino Sketch */

// Przykładowy callback pakietu
void afterPacketReceived(Packet* p){
	// PAPUGA ;)
	p->transmit();
}

// Strmień pakietów - klasa opisująca zachowanie po uzyskaniu pakietu
PacketStream stream;

void setup() {
	Serial.begin(115200); // Inicjacja UART
	stream.onPacket = afterPacketReceived; // Dodaj zdarzenie odczytu pakietu (handler)
}

void loop() {
	// Gdy otrzymasz dane
	if(Serial.available())
	{		
		stream.onData(Serial.read()); // Przy otrzymaniu danych odczytaj je i przekaż do strumienia pakietów ;) 
	}
	
}

Chodzi m/w o coś takiego 😉 Jak coś to jest sketch z Microchipa, więc dyrektyw #include raczej nie będziesz potrzebować. Wprawdzie to "wyższy poziom" programowania, aczkolwiek jest bardzo wygodny w obsłudze 😉 

Wadą jest głównie to, że do czasu otrzymania pakietu przetrzymuje całe jego dane w RAM'ie 😞 

Pakietowanie to w skrócie paczkowanie danych do pakietów - każdy pakiet ma określoną długość (często przesyłaną razem z danymi) oraz dane. Oprócz tego może dodatkowo zawierać nagłówki, flagi, adresy etc., aczkolwiek najprostszy pakiet to np. liczba całkowita - 4 bajty danych. Jeżeli przesyłasz po UART int32_t to przesyłasz tak naprawdę najprostszy pakiet 😉 

Bardziej zaawansowaną wersją jest wersja z podaną długością - pozwala klientowi magistrali odczytać ile danych jest przesyłanych i się do tego dostosować - pozwala na pakiety o różnych długościach - podajesz, że przesyłasz 0x05 bajtów po czym przesyłasz te 5 bajtów jeden po drugim.

CRC jest zazwyczaj na końcu pakietu i rodzajów implementacji CRC jest tyle, że ciężko zliczyć - od XORów po najzwyklejsze sumy kolejnych bajtów 😉 

Wracając do wyżej wymienionego przykładu - API pozwala na łatwe odczytywanie pakietów oraz wykonywanie zdarzenia po otrzymaniu poprawnych danych. Możesz to przetestować sobie w realtermie 😉 

image.thumb.png.93bc66fa1c8486d2f59d583a9084531d.png
Przesłane dane są równoznaczne z tymi odebranymi 😉 

Podajesz długość pakietu (np. 0x02) a potem dane np. 0x00 i 0x01 - dopiero po otrzymaniu ostatniego bajtu arduino wyśle odpowiedź w formie pakietu 😉 

image.png.90fb86d961df296e28aec967d5cc9bd5.thumb.png.3031da8ba6623ad035432ec6a7b1b2ce.png
Czerwona ramka to długość pakietu, pomarańczowa to dane 😉 

EDIT

Pamiętaj by po odebraniu pakietu go usunąć 😉 free(p) na koniec afterPacketReceived - inaczej szybko Ci wyparuje RAM 😉 W moim przykładzie tego nie ma, bo transmit ma wbudowane czyszczenie pakietu.

Edytowano przez H1M4W4R1
  • Lubię! 1
  • Pomogłeś! 1
Link do komentarza
Share on other sites

(edytowany)
5 godzin temu, H1M4W4R1 napisał:

Wprawdzie to "wyższy poziom" programowania, aczkolwiek jest bardzo wygodny w obsłudze 😉 

 

Eeee tam wyzszy...😅 ((...zeby tylko sie nie kapnol ze blefuje)) pomalutku na spokojnie przetestuje twoj sposob bo jak dobrze rozumiem to wtedy moge wysylac wszystko bez zadnych "znaczkow". Wystarczy ze podam dlugosc pakietu..

 

Edytowano przez farmaceuta
Link do komentarza
Share on other sites

50 minut temu, farmaceuta napisał:

wtedy moge wysylac wszystko bez zadnych "znaczkow". Wystarczy ze podam dlugosc pakietu..

Dokładnie - tylko ma tą wadę, że przechowuje wszystko w pamięci 😞 Ja ostatnio korzystam głównie z STM32, ESP i Pi Pico, więc RAMu mi nie brakuje,  ale na Arduino to może być problem.

Link do komentarza
Share on other sites

13 minut temu, H1M4W4R1 napisał:

 Ja ostatnio korzystam głównie z STM32, ESP i Pi Pico, więc RAMu mi nie brakuje,  ale na Arduino to może być problem.

Tez mam STM32 w domku "bluepill" czy jakos tak, ale az sie boje go do reki wziasc zeby mnie nie przerosl...niby duzo jest bibliotek etc do niego, ale jednak atmega prostrza chyba no i duzo wiecej wlasnie bibliotek, przykladow etc...

Link do komentarza
Share on other sites

15 minut temu, farmaceuta napisał:

Tez mam STM32 w domku "bluepill"

Blue Pill jest bardzo łatwy jeżeli używasz Arduino IDE / Platform IO, same biblioteki STM też są łatwe, tylko trzeba się do nich przyzwyczaić. Między innymi trzeba poczytać o wskaźnikach 😉 Reszta to tylko kopiuj wklej, jak działa za wolno to pracujesz na rejestrach i działa 😛 

  • Lubię! 1
Link do komentarza
Share on other sites

Witam panowie...takie pytanie mam czy da sie jakos sprytnie uzywac UART'a programowego wykorzystujac tylko jedna linie?? Tzn..powiedzmy ze musze miec dwa UART'y programowe, ale mam tylko dwa wolne piny z czego jeden by pracowal jako TX1 a drugi jako RX2 (poprostu jeden UART tylko odbiera, drugi tylko wysyla) da sie cos takiego zrobic? W przypadku NeoSWSerial..(bo z tego co wyczytalem to SoftwareSerial jest cieniutki i faktycznie mialem z nim problem na dwa porty i i2c jednoczesnie) 

I jeszcze czy funkcja .listen() calkowicie wylacza drugi uart?

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.