INFO: Dieses Forum nutzt Cookies...
Cookies sind für den Betrieb des Forums unverzichtbar. Mit der Nutzung des Forums erklärst Du dich damit einverstanden, dass wir Cookies verwenden.

Es wird in jedem Fall ein Cookie gesetzt um diesen Hinweis nicht mehr zu erhalten. Desweiteren setzen wir Google Adsense und Google Analytics ein.

Antwort schreiben 
 
Themabewertung:
  • 0 Bewertungen - 0 im Durchschnitt
  • 1
  • 2
  • 3
  • 4
  • 5
Esp32 FIFO-Problem
20.08.2020, 20:29
Beitrag #1
Esp32 FIFO-Problem
Ich habe da mal wieder ein Problem...

Ich benutze eine "while(serial.available())... serial.read()"-Schleife, um aus einer HardwareSerial-Schnittstelle zu lesen. Das klappt auch bis zu Datentelegrammen von 112 Byte, größere werden aber bei genau 112 Byte abgeschnitten. Die Übertragung findet mit 19200 Baud statt, einigermaßen gemächlich also.

In der ESP32-Implementierung sind die 112 Byte exakt die Hochwassermarke, bei der das Auslesen des FIFO-Buffers in der Hardware ausgelöst wird und die Daten in den RX-Puffer der Serial übertragen werden. Meine Datenpakete sind aber z. B. 135 Byte groß, so dass kein zweiter 112-Byte-Interrupt stattfindet,

Hier soll dann ein zweiter Mechanismus zuschlagen, der den FIFO nach einer Pause von zwei Zeichenlängen ohne neues Zeichen ebenfalls leert. Das scheint aber nicht zu geschehen, wodurch die restlichen Bytes im FIFO versauern.

Ist das einem von Euch mal untergekommen?
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
21.08.2020, 08:11 (Dieser Beitrag wurde zuletzt bearbeitet: 21.08.2020 08:12 von biologist.)
Beitrag #2
RE: Esp32 FIFO-Problem
Ich habe mal einen Sketch geschrieben, bei dem ich einen ESP8266 seriell an einen ESP32 gehängt habe (zum Debugging). Den seriellen Output vom ESP32 habe ich dann vom ESP8266 aus per Syslog zu einem Raspi geschickt. Weiß nicht, ob dir das was hilft, poste das einfach mal ausschnittsweise hier rein. Probleme mit abgeschnittenen Zeilen hatte ich auf jeden Fall nie.
In meinem Falle wars so, dass die Zeilen nie länger als 200 Zeichen waren, insofern habe ich die Buffer nur entsprechend lang bemessen.

Code:
char strBuf[200];
char newLine[200];
unsigned int lastPointer = 0;
char lastChar;

void loop() {
  if (Serial.available() > 0) {
    lastChar = Serial.read();
    Serial.print(lastChar);

    if (lastChar != '\n' && lastChar != '\r') {
      if (lastPointer < (sizeof(strBuf) / sizeof(strBuf[0]) - 1)) {
        *(strBuf + lastPointer++) = lastChar;
      }
    } else if (lastChar == '\n') {
      *(strBuf + lastPointer) = '\0';
      lastPointer = 0;
      strcpy(newLine, strBuf);
      pushStringToSyslog(newLine);
    }
  }
}
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
21.08.2020, 09:39 (Dieser Beitrag wurde zuletzt bearbeitet: 21.08.2020 09:58 von miq19.)
Beitrag #3
RE: Esp32 FIFO-Problem
(21.08.2020 08:11)biologist schrieb:  Ich habe mal einen Sketch geschrieben, bei dem ich einen ESP8266 seriell an einen ESP32 gehängt habe (zum Debugging). Den seriellen Output vom ESP32 habe ich dann vom ESP8266 aus per Syslog zu einem Raspi geschickt. Weiß nicht, ob dir das was hilft, poste das einfach mal ausschnittsweise hier rein. Probleme mit abgeschnittenen Zeilen hatte ich auf jeden Fall nie.
In meinem Falle wars so, dass die Zeilen nie länger als 200 Zeichen waren, insofern habe ich die Buffer nur entsprechend lang bemessen.

Danke schön, das ist im Prinzip genau das, was ich mache. Ich glaube allerdings, dass das Problem nicht an der Serial-Schicht liegt, sondern darunter. Beim ESP32 hat die Serial einen Puffer von 256 Byte in Empfangsrichtung, deswegen kann da nichts stocken. Ich vermute im Moment, dass ich irgendwie den FIFO 2-Zeichen-Timeout verpasse, der nach dem Eintreffen des letzten Zeichens zuschlagen müsste.

Das ganze passiert auf einem RS485-Modbus, wo das Bustiming Lücken von >3,5 Zeichenlängen als Ende der Nachricht interpretiert. Das 2-Zeichen-Timeout liegt deutlich darunter, und ich habe schon mal probeweise das Bustimeout auf 4ms hochgesetzt (8-9 Zeichenlängen bei 19200 Baud) und der Effekt ist genauso da.

Ich habe nirgends (bewusst) Interrupts blockiert oder runterpriorisiert. Wenn ich vor der while-Schleife zu lange bummeln würde, müsste eher der Serial-Buffer überlaufen, denn die Übertragung aus dem FIFO-Buffer erfolgt ja per Interrupt, unabhängig davon, was das Programm zu der Zeit treibt.

Ach ja: Pakete kürzer als 112 Zeichen werden seltsamerweise problemlos verarbeitet, unter den ominösen 112 muss der 2-Zeichenlängen-Timeout also noch funktionieren.
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
21.08.2020, 16:04
Beitrag #4
RE: Esp32 FIFO-Problem
Falls Euch die Funktion komplett interessiert oder dem Verständnis hilft:
Code:
ModbusResponse* esp32ModbusRTU::_receive(ModbusRequest* request) {
  // Allocate initial buffer size
  const uint16_t BUFBLOCKSIZE(128);
  uint8_t *buffer = new uint8_t[BUFBLOCKSIZE];
  uint8_t bufferBlocks = 1;

  // Index into buffer
  register uint16_t bufferPtr = 0;

  // State machine states
  enum STATES : uint8_t { WAIT_INTERVAL=0, WAIT_DATA, IN_PACKET, DATA_READ, ERROR_EXIT, FINISHED };
  register STATES state = WAIT_INTERVAL;

  // Timeout tracker
  uint32_t TimeOut = millis();

  // Error code
  esp32Modbus::Error errorCode = esp32Modbus::SUCCES;

  // Return data object
  ModbusResponse* response = nullptr;

  while(state != FINISHED)
  {
    switch(state)
    {
    // WAIT_INTERVAL: spend the remainder of the bus quiet time waiting
    case WAIT_INTERVAL:
      // Time passed?
      if(micros() - _lastMicros >= _interval) {
        // Yes, proceed to reading data
        state = WAIT_DATA;
      }
      else {
        // No, wait a little longer
        delayMicroseconds(1);
      }
      break;
    // WAIT_DATA: await first data byte, but watch timeout
    case WAIT_DATA:
      if(_serial->available()) {
        state = IN_PACKET;
        _lastMicros = micros();
      }
      else if(millis() - TimeOut >= TimeOutValue) {
        errorCode = esp32Modbus::TIMEOUT;
        state = ERROR_EXIT;
      }
      break;
    // IN_PACKET: read data until a gap of at least _interval time passed without another byte arriving
    case IN_PACKET:
      // Data waiting and space left in buffer?
      while (_serial->available()) {
        // Yes. Catch the byte
        buffer[bufferPtr++] = _serial->read();
        // Buffer full?
        if(bufferPtr >= bufferBlocks * BUFBLOCKSIZE) {
          // Yes. Extend it by another block
          bufferBlocks++;
          uint8_t *temp = new uint8_t[bufferBlocks * BUFBLOCKSIZE];
          memcpy(temp, buffer, (bufferBlocks - 1) * BUFBLOCKSIZE);
          delete buffer;
          buffer = temp;
        }
        // Rewind timer
        _lastMicros = micros();
      }
      // Gap of at least _interval micro seconds passed without data?
      if(micros() - _lastMicros >= _interval) {
        state = DATA_READ;
      }
      break;
    // DATA_READ: successfully gathered some data. Prepare return object.
    case DATA_READ:
      // Allocate response object
      response = new ModbusResponse(bufferPtr, request);
      // Move gathered data into it
      response->setData(bufferPtr, buffer);
      state = FINISHED;
      break;
    // ERROR_EXIT: We had a timeout. Prepare error return object
    case ERROR_EXIT:
      response = new ModbusResponse(5, request);
      response->setErrorResponse(errorCode);
      state = FINISHED;
      break;
    // FINISHED: we are done, keep the compiler happy by pseudo-treating it.
    case FINISHED:
      break;
    }
  }

  // Deallocate buffer
  delete buffer;
  _lastMicros = micros();

  return response;
}
Detailerklärungen gerne - einfach fragen!Wink
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
21.08.2020, 17:27
Beitrag #5
RE: Esp32 FIFO-Problem
Ich glaube, ich habe es dank einer Diskussion im Espressif ESP32-Forum verstanden. Es sieht so aus, als dauere der Kopiervorgang zum Verschieben der 112 Bytes vom FIFO-Puffer in den seriellen Puffer länger als die MODBUS-Ruhezeit dauert, so dass mein Code
Code:
if(micros() - _lastMicros >= _interval) {
        state = DATA_READ;
      }
eine Paketende zu erkennen glaubt.

Die im Thread gezeigte Lösung bestand darin, einen kleineren Schwellenwert für das FIFO festzulegen, damit weniger Bytes in einem Rutsch kopiert werden. Da dies jedoch im ESP32-arduino-Kern (esp32-hal-uart.c) geschieht, ist es schwierig, den Wert in der Benutzerdomäne zu ändern.

Meine Abhilfe besteht darin, das Lückenerkennungsintervall auf eine wirklich hohe Zahl zu erhöhen (ich verwende im Moment 30000, also 30ms). Das funktioniert aber nur, weil wir der Master sind, der den Bus kontrolliert. Ein Slave wird mit diesem Verhalten unmöglich zu implementieren sein, weil es da auf das genaue Bustiming ankommt. Mit höheren Busgeschwindigkeiten wird es schlimmer...

Komisch, dass die ESP32-Arduino-Implementierung in diesem Detail viel schwächer ist als der originale Arduino-Kern auf den ATMEL-CPUs - mein Slave-Gerät, das genau wie beabsichtigt nach dem gleichen Prinzip arbeitet, läuft auf einem mageren Nano...
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
21.08.2020, 23:19
Beitrag #6
RE: Esp32 FIFO-Problem
Über diese Eigenart des ESP32 bin ich auch schon mal gestolpert.
Ich hatte mich gewundert dass Telegramme mit gleicher Länge oft nach unterschiedlichen Zeiten im meinem Empfangspuffer ankamen. Manchmal auch verstümmelt.

Daraufhin habe ich einen Testaufbau gemacht bei dem ein Arduino jede Sekunde mit 9600 Baud die Zeichen 0x00 bis 0xFF seriell zu einem ESP32 gesendet hat. Beim ESP32 wurde die Hardware Serielle 2 (Pin 16 und 17) verwendet.

In den Oszillogrammen sieht man deutlich das ungewöhnliche Verhalten des ESP32.
   
Kanal 1 zeigt das Telegramm das vom Arduino gesendet wurde.
Kanal 2 zeigt wann Daten vom Empfangs-FIFO in meinen Empfangspuffer geschrieben wurden. Man sieht auch deutlich dass die 256 empfangenen Bytes in drei Etappen zerteilt in den Empfangspuffer übertragen wurden.
Beim ersten Telegramm kam die dritte Etappe gut 300ms verzögert im Empfangspuffer an.

Hier noch ein Beispiel von einem Fehlverhalten des ESP32.
   
Beim zweiten Telegramm wurde eine Übertragung zum Empfangspuffer komplett ausgelassen und der dritte Teil wieder gut 300ms verzögert übertragen.

Zum Vergleich dazu hier der Vorgang wenn der Empfänger ein MEGA2560 (Serial1) ist.
   
Dort wird bei 9600 Baud wohl nicht (oder nicht viel) im FIFO abgelegt sondern gleich jedes Byte in den benutzerdefinierten Empfangspuffer durchgereicht.

Gruß Arne
Mit zunehmender Anzahl qualifizierter Informationen bei einer Fragestellung, erhöht sich zwangsläufig die Gefahr auf eine zielführende Antwort.
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
22.08.2020, 04:55
Beitrag #7
RE: Esp32 FIFO-Problem
Ich habe beim ESP32-Arduino-Projekt auf Github mal einen Issue aufgemacht, allerdings ohne viel Hoffnung, dass die Maintainer darauf reagieren werden. Wenn man weiß, wonach man sucht, findet man im Netz so einige, denen genau das passiert und aufgefallen ist, ohne dass es im Core Änderungen geben würde.
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
22.08.2020, 14:33
Beitrag #8
RE: Esp32 FIFO-Problem
Wenn jemand den ESP32-Arduino-Core verfügbar hat, löst folgende Änderung das Problem:

In der Datei esp32-hal-uart.c gibt es diese Funktion
Code:
void uartEnableInterrupt(uart_t* uart)
{
    UART_MUTEX_LOCK();
    uart->dev->conf1.rxfifo_full_thrhd = 1; // 112;
    uart->dev->conf1.rx_tout_thrhd = 2;
    uart->dev->conf1.rx_tout_en = 1;
    uart->dev->int_ena.rxfifo_full = 1;
    uart->dev->int_ena.frm_err = 1;
    uart->dev->int_ena.rxfifo_tout = 1;
    uart->dev->int_clr.val = 0xffffffff;

    esp_intr_alloc(UART_INTR_SOURCE(uart->num), (int)ESP_INTR_FLAG_IRAM, _uart_isr, NULL, &uart->intr_handle);
    UART_MUTEX_UNLOCK();
}
Die Zeile
Code:
uart->dev->conf1.rxfifo_full_thrhd = 1; // 112;
ist die entscheidende - hier steht normalerweise der Wert 112. Ändert man ihn auf 1, wird für jedes im FIFO eintreffende Zeichen ein Interrupt ausgelöst und das Zeichen in den Serial-Buffer übertragen. Damit sind die Pausen unter 1ms, womit zumindest mein Code oben wie geplant läuft.
Das erzeugt natürlich eine Menge mehr Interrupts - müsst Ihr entscheiden, was Euch an der Stelle wichtiger ist Wink
Alle Beiträge dieses Benutzers finden
Diese Nachricht in einer Antwort zitieren
Antwort schreiben 


Möglicherweise verwandte Themen...
Thema: Verfasser Antworten: Ansichten: Letzter Beitrag
  ESP32 Schieberegler Woolli 13 241 Gestern 13:37
Letzter Beitrag: Tommy56
  Max. Eingangsspannung ESP32 Devkit V1 Gerdchen03 4 128 Gestern 00:45
Letzter Beitrag: Bitklopfer
  ESP32 BLE - Wie kann ich die UUID aus INI-WiFiManger übernehmen ? Stargazer 0 96 18.10.2020 16:26
Letzter Beitrag: Stargazer
  AZ-Touch ESP32 Grafiktest ckuehnel 0 154 13.10.2020 17:34
Letzter Beitrag: ckuehnel
  ESP32: Filesystem uploader tool findet mklittlefs.exe nicht ultralex 6 101 12.10.2020 22:07
Letzter Beitrag: hotsystems
  ESP32+SIM808 Gpsdaten auf Display und SMS reinhard-adam 0 172 06.10.2020 16:49
Letzter Beitrag: reinhard-adam
  7 Segmentanzeige Problem ESP8266 dani 43 1.395 28.09.2020 19:12
Letzter Beitrag: hotsystems
  ESP32+SIM808 GPS Daten auslesen und auf LCD reinhard-adam 2 337 21.09.2020 18:25
Letzter Beitrag: georg01
  Fehler bei Board AI Thinker ESP32-CAM Manny 4 722 30.08.2020 16:37
Letzter Beitrag: Manny
  ESP32 Ethernet shrimps 33 24.185 27.08.2020 15:05
Letzter Beitrag: biologist

Gehe zu:


Benutzer, die gerade dieses Thema anschauen: 1 Gast/Gäste