• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
UART, komunikacja pomiędzy dwoma Arduino.
#11
@Scislov bo to działa teraz tak, że gdy w miejscu ifa w buforze znajdują się 2 znaki, w czasie ich odczytu może doleci jeszcze jeden, odczytujesz te 3 i od razu drukujesz. Powinieneś zbierać te znaki aż uzbierasz tyle ile potrzebujesz i wydrukować je np. raz na 100ms lub po spełnieniu warunku ilości i zacząć zbierać od nowa.
Tu jest przykład jak używam SerialEvent: https://forum.arduinopolska.pl/watek-ser...nie-danych, kluczowy jest znak '\n' nowej linii, który oznacza zakończenie odbioru linii tekstu, po tym zabawa zaczyna się od nowa, wiadomo, że po tym będzie nowa linia, można zacząć zbierać od nowa.
@Agregacik raczej robiłem to na Serial1 Arduino Micro i Leonardo, być może na UART udawanym z USB to nie działa, ale zawsze można zostawić sobie taką funkcję i po prostu wrzucić ją do loop. Tak zrobiłem w ESP8266, który też nie wywołuje sam SerialEvent. Można to oczywiście zrobić inaczej, ale skoro już miałem gotową i była to kwestia dodania jednej linijki do loop to poszedłem na łatwiznę.
Dla pewności sprawdziłem, na Serial1 działa, na Serial (USB) musi być w loop:
Kod:
String inputString = "";         // a String to hold incoming data
bool stringComplete = false;  // whether the string is complete
String inputString1 = "";         // a String to hold incoming data
bool stringComplete1 = false;  // whether the string is complete

void setup() {
  // initialize serial:
  Serial.begin(115200);
  Serial1.begin(115200);
  // reserve 200 bytes for the inputString:
  inputString.reserve(200);
  inputString1.reserve(200);
}

void loop() {
   serialEvent();
  // print the string when a newline arrives:
  if (stringComplete) {
    Serial1.println(inputString);
    // clear the string:
    inputString = "";
    stringComplete = false;
  }

    if (stringComplete1) {
    Serial.println(inputString1);
    // clear the string:
    inputString1 = "";
    stringComplete1 = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent1() {
  while (Serial1.available()) {
    // get the new byte:
    char inChar = (char)Serial1.read();
    // add it to the inputString:
    inputString1 += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete1 = true;
    }
  }
}

void serialEvent() {
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#12
Na ten temat już wszystko jest dla mnie jasne.

Teraz pojawiły mi się nowe pytania. Czy "void serialEvent()" jest przerwaniem od transmisji i czy będzie działać, gdy wykorzystuję przerwania na pinach 2 i 3 (arduino nano), oraz, czy odczytywanie lub nadawanie przez uart nie będzie zakłócane przez przerwania, ani tych przerwań nie będzie zakłócać? Na przykład gdy podczas transmisji pojawi się przerwanie od pinu 2 lub 3.

Dlaczego skoro wykorzystuję HARDWARE uart, gdy zmierzyłem czas wykonania pętli z itoa i SerialWrite wysyłając 10-cio bajtową liczbę, wynosił on aż 10ms? Docelowo chcę wysyłać w obie strony ok. 20 zmiennych, więc byłoby już spore opóźnienie. Arduino nr 1 ma zbierać informacje z czujników, sterować kilkoma elementami i wykonywać obliczenia, więc jeżeli uart działa bez problemów z przerwaniami, to opóźnienie nie przeszkadza. Drugie arduino ma wyświetlać te dane i obsługiwać przyciski i trymer.
 
Odpowiedź
#13
serialEvent() jest funkcją obsługi przerwania od UART. To przerwanie raczej nie zakłóci przerwań od INT0 i INT1, ale przerwania od tych pinów jak najbardziej przeszkodzą w odczycie danych z seriala. Chodzi o to, że w Atmega jest możliwe przerwanie w trakcie wykonywania innego przerwania. Dla tego funkcja jego obsługi powinna być jak najszybsza (np. zmiana flagi i wyjście) Jeśli chcesz aby inne przerwania nie przerywały funkcji serialEvent(), to na jej początku wpisz cli(); a przed wyjściem sei();. Wyłączy to obsługę przerwań na czas odczytu z UART, więc lepiej aby to trwało jak najszybciej. Prędkość UART masz ustawioną na 9600, może powinieneś ją zwiększyć. Arduino mówi, że można 115200, ale w manualu do 328p stoi, że dla 16MHz pociągnie 1000000.
Eksperymentuj.
Jeśli masz problem z kodem lub sprzętem, zadaj pytanie na forum. Nie odpowiadam na PW, jeśli nie dotyczą one spraw forum lub innych tematów prywatnych.

[Obrazek: SsIndaG.jpg]
 
Odpowiedź
#14
W Arduino AVR jest w danej chwili obsługiwane 1 przerwanie w kolejności wywołania, a przynajmniej mniej więcej.
Jeśli na pinie INT pojawi się przerwanie to ono nie przepadnie, co najwyżej jego obsługa zostanie wykonana po zakończeniu aktualnie trwającej obsługi innego przerwania. To nawet czasami przeszkadza, np. wciskasz przycisk do INT0 i jest wywołane przerwanie, a że są drgania styków to od razu jest wywołane kolejne i kolejne, zaraz jak wyjdziesz z obsługi przerwania to flaga jest już ustawiona i znowu następuje obsługa przerwania. By to działało tak jak chciałem to wyłączam to przerwanie w obsłudze przerwania, ustawiam odliczenie 100ms w pętli głównej, potem kasuję flagę i znowu aktywuję przerwanie. Ewentualnie pomaga dodanie trochę sprzętu, tak by jedno wciśnięcie przycisku dawało dokładnie jeden impuls na pin INT0.
Dlatego tak istotne jest by nie robić w przerwaniu nic co trwa długo, obsługa przerwania powinna trwać co najwyżej u_sekundy.
Jak chcesz szybciej i używasz hardware serial to ustaw większą szybkość, jak widzisz w przykładach mam ciągle 115200, można ustawić 500k, 9600 to strasznie wolno. A jest buforowany i sprzętowy co łatwo możesz sprawdzić ustawiając prędkość raz na 115k, raz na 9k i wysłanie linijki tekstu 10 znaków trwa ~76us. Może to zależy od tego czy wysyłasz 10 x bajt czy 1x 10bajtów?
Kod:
uint32_t czasTeraz,czasPoprzedni,tik=10, czas1,czas2,czas3,czas4,roznica,maximum,srednia;
uint8_t n10,fn10,sekundy,minuty,godziny,dni,nic;

bool fsekundy,fminuty,fgodziny,fdni;
void setup() {
//  sysClock(INT_OSC32);
  // put your setup code here, to run once:
Serial.begin(115200);

}

void loop() {
  czas();
  // put your main code here, to run repeatedly:

if(fsekundy) 
{
 
czas1=micros();
Serial.print("Minimum=  ");   

czas2=micros();
roznica=czas2-czas1;
Serial.println((uint32_t)roznica); 
}

}


void czas()
{
  czasTeraz=millis();
fn10=fsekundy=fminuty=fgodziny=fdni=0;
if((uint32_t)(czasTeraz-czasPoprzedni)>=tik)
{
  czasPoprzedni=czasTeraz;
  fn10=1;
  n10++;
  if(n10>=100)
  {
    n10=0;
    sekundy++;
    fsekundy=1;
     if (sekundy>=60)
    {
      sekundy=0;
      minuty++;
      fminuty=1;
      if (minuty>=60)
      {
        minuty=0;
        godziny++;
        fgodziny=1;
        if (godziny>=24)
        {
          godziny=0;
          fdni=1;
          dni++;
   
        }
      }
    }
  }
}
}
Co ciekawe ustawienie prędkości na 1M wydłuża czas przepisania zmiennych do bufora, 20 znaków to już jest 140us przy 115200 i 208us przy 1M (mój CH340 nie działa na 500k).
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#15
Racja, całkowicie zapomniałem o zmianie prędkości transmisji. Pinu od przerwań są podłączone do kontraktów mierzących prędkość obrotową. Stykają one na bardzo krótko, więc zastosowałem przerwania, aby nie pominąć pomiaru. Rozumiem również, że gdy nastąpi przerwanie podczas transmisji, to pomiar zostanie wykonany, ale później? Co w przypadku, gdy wyłączę przerwania w SerialEvent?

Dziękuję za pomoc. Tematu nie zamykam, gdy będę miał jeszcze jakieś pytania to tu będę pisał.
 
Odpowiedź
#16
Długo pisałem tamtego posta, widzę, że @Robson Kerman odpowiedział. Ale wg mnie SerialEvent nie jest tak ściśle powiązany z przerwaniami, to bardziej element loop. Dlatego go można dodać do loop jak w przykładzie z Leonardo i działa tak samo.
Funkcję do przerwań UART musiałbyś szukać dużo głębiej w core Arduino. Przerwanie od UART RX ma zwykle do zrobienia tylko przeniesienie 1 znaku z FIFO do bufora kołowego, przestawienie wskaźnika. TX przepisanie jednego znaku do FIFO i przestawienie wskaźnika w buforze nadawczym. Wg mnie nie ma potrzeby w tym mieszać.
Ile trwa impuls jest nieistotne, jeśli jest ładne zbocze to po prostu ustawiasz na rosnące lub opadające i przerwanie wywoływane jest dokładnie raz, nie na czas trwania impulsu tylko na czas zrobienia "zmienna++". Problemem mogłoby być to, że wchodzisz w obsługę innego przerwania i w tym czasie stuka 2 razy impuls (czy tam więcej). O tym informacja zostanie utracona. Jeśli coś takiego możesz mieć to stosujesz wejście licznika i ono sobie zliczy niezależnie od kodu programu wszystkie impulsy. Wg mnie przy 16MHz można zliczyć do paru tysięcy impulsów przerwaniem INT, im więcej masz przerwań w programie tym większe prawdopodobieństwo, że coś umknie, więc mają być krótkie i do najważniejszych rzeczy.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości