Arduino Due Interrupt wenn 8Bit am Serial1 empfangen wurde

Hallo zusammen

ich bin gerade mit der Realisierung einer Steuerung über einen RS485 Bus beschäftigt.
Um Daten welche an der seriellen Schnittstelle 1 (Serial1) des Arduino Due abzufangen auch während mein Programm irgend etwas anderes macht, würde ich gerne mit einem Interrupt arbeiten.
Derzeit arbeite ich mit dem Serial1.Event() aber hier habe ich das Problem, dass erst nach 3x 16Bytes der Event einmal abgearbeitet wird.

Hat jemand ein gutes Tutorial für solche Serialinterrupts mit dem Arduino Due oder kann mir gerade hier weiterhelfen?

Hi

Das klingt danach, daß Du einfach zu viel in loop() rumgammelst.
Erhöhe die Geschwindigkeit, verkleinere die Einzelhappen, Die abgearbeitet werden müssen.
Dann bist Du schnell genug wieder Da, um auf neue Daten zu prüfen.

Die Serielle selber läuft per Interrupt - Den wirst Du aber kaum 'abgreifen' können - bzw. wenn, dann echt 'abgreifen' - den RX-Pin auf einen anderen Pin brücken und darauf einen Interrupt laufen lassen.

Die ist bekannt, daß so ein Zeichen auf mehr als 8 Bit besteht?
Mit Start- und Stop-Bit sind's 10, was auch die 1000 Zeichen die Sekunde bei 9600 Baud erklärt (also wohl eher 960).

Mein Vorschlag: Räum loop() auf, dann klappt Das auch.
Ok, der Arduino nutzt selber einen Interrupt, um auf die ankommenden Bits zu reagieren.
Aber Du willst 'ganze Zeichen' - da Diese fast getrommelt werden, ist hier ien Interrupt - meiner Meinung nach - ein unnötiger und vor Allem unnötig komplizierter Ansatz.

MfG

Ist mir bewusst. Wenn irgendwie möglich würde ich gerne auf Interrupts verzichten, allerdings habe ich schon jezt eine Art Bufferproblematik. Evtl kennst du ja gerade die Lösung.

#include <Arduino.h>
/***********************************************************************************
 * Definitionen:
 * OwnAdress: Eigene Adresse im Bus-System
 * MaxBadges: Anzahl möglicher RFID-Badges um den Mainloop kurz zu halten
 * Sensors: Anzah der Sensoren im Bus-System
 * Effektors: Anzahl der Effektoren im Bussystem
 * BusDelay: Zeit zwischen den Abfragen die ninimal eingehalten werden soll
 *            um das Programm zu veranschaulichen
 **********************************************************************************/
#define OwnAdress 0x01
#define MaxBadges 10
#define Sensors 2
#define Effektors 2
#define BusDelay 3000
/***********************************************************************************
 * Globale Variabeln welche auch für den Interrupt gebraucht werden
 **********************************************************************************/
volatile unsigned char ucSerialFlag = false;
volatile unsigned char ucReceivedeventFlag = false;
volatile unsigned char ucIndex = 0;
volatile unsigned char ucReceivedmessage[16];               //16 Byte Empfangstabelle
volatile unsigned char ucSendmessage[16];                   //Max 16 Byte für Sendetabelle
volatile unsigned char ucPCRecievedmessage[16];             //16 Byte Empfangstabelle der PC-Messages
volatile unsigned char ucSenderByte = 0;
volatile unsigned char ucReceiverByte = 0;
/***********************************************************************************
 * Testvariablen
 **********************************************************************************/
volatile unsigned int uiAvailableChars = 0;                 //Restliche Bytes im UART-Buffer
/***********************************************************************************
 * Mainloop variabeln
 **********************************************************************************/
unsigned int uiZwischenspeicher = 0;
unsigned char ucLEDSpotWert = false;
unsigned char ucSensors [Sensors] = {0x02, 0x04};
unsigned char ucEffektors [Effektors] = {0x03, 0x05};
unsigned int uiTrueBadges [MaxBadges] = {0xBB01, 0xBC01};   //!! Erlaubte Badges hier eintragen!!
unsigned char ucSensorCounter = 0;
/***********************************************************************************
 * Hilfsvariabeln für den Mainloop
 **********************************************************************************/
unsigned char i = 0;
/***********************************************************************************
 * Funktionsdefinitionen
 **********************************************************************************/
void ClearPayload();
void ClearReceivemessage();
void sendData();
void requestData(unsigned char ucTeilnehmer);
void TasterPotiEvent();
void InitRGB();
void RFIDEvent();

void setup() 
{
  Serial.begin(9600);
  Serial1.begin(9600);
  ucSendmessage[0] = 0xAA;
  ucSendmessage[3] = OwnAdress;
  while(!Serial1)
  {
    ;
  }
  Serial.println("Init fertig!");
}

void loop() 
{
  Serial.print("Zeuchen im Buffer: ");
  Serial.println(uiAvailableChars);
  delay(BusDelay);
  if(ucReceivedeventFlag == true)
  {
    Serial.print("Wert empfangen von: ");
    Serial.println(ucReceivedmessage[3], HEX);
    for(i = 0; i <= 15; i++)                                              //Gibt ucReceivedmessage auf die Kosole aus
    {
      Serial.print(ucReceivedmessage[i], HEX);
      Serial.print(" ");
    }
    Serial1.println(" ");
  }
  requestData(0x04);
}

/***********************************************************************************
 * Die Funktion "ClearPayload()" slöscht den Payload der sendebytes der Sendetabelle.
 * 
 * 
 **********************************************************************************/
void ClearPayload()
{
  unsigned char i = 4;
  for(i = 4; i <= 13; i++)
  {
    ucSendmessage[i] = 0x00;
  }
}

/***********************************************************************************
 * Die Funktion "ClearReceivemessage()" slöscht den Payload der empfangenen Daten.
 * 
 * 
 **********************************************************************************/
void ClearReceivemessage()
{
  unsigned char i = 0;
  for(i = 0; i <= 15; i++)
  {
    ucReceivedmessage[i] = 0x00;
  }
}

/***********************************************************************************
 * Die Funktion "sendData()"" sendet die Sendetabelle (ucSendmessage) über beide 
 * seriellen Schnittstellen. Da niemals der Wert 0xAA gesendet werden darf ausser bei
 * der Synchonisierung (ucSendmessage[0]) wird im Payload dieser Wert abgefangen und
 * um eins erhöht.
 * 
 **********************************************************************************/
void sendData()
{
  unsigned char i = 0;
  for(i=0; i <= 15; i++)
  {
    if((ucSendmessage[i] == 0xAA) && (i > 0))
    {
      ucSendmessage[i] = 0xAB;
    }
    Serial1.write(ucSendmessage[i]);
  }
}

/***********************************************************************************
 * Die Funktion "requestData()" sendet einen Request an einen Sensor.
 * Der Sensor wird über die Variable ucTeilnehmer übergeben.
 * 
 * !!Check dass kein Request an Effektor gesendet werden kann fehlt!!
 * 
 **********************************************************************************/
void requestData(unsigned char ucTeilnehmer)
{
  ucSendmessage[1] = 0x01;
  ucSendmessage[2] = (ucTeilnehmer);
  sendData();
  ClearPayload();
}
/***********************************************************************************
 * Der Interrupt "serialEvent1()" wird aufgerufen sobald Daten am seriellen Input
 * des uC anliegen. Zuerst prüft man ob der buffer voll ist, anschliessend wird der
 * Buffer ausgelesen. Nach dem auslesen, wird der Wert fortlaufend in die Empfangs-
 * tabelle geschrieben. 
 * 
 **********************************************************************************/
void serialEvent1()
{
  unsigned char ucReadByte = 0;                             //ReadByte initialisieren
  while(Serial1.available())
  {   
    Serial.print(Serial1.available()); 
    Serial.print(" ");    
    ucReadByte = Serial1.read();                            //Empfangsregister (1 Byte) auslesen
  if (ucReadByte == 0xAA)                                 //Erkennen ob das Startbyte (0xAA) ist --> zum Synchronisieren
  { 
  ucIndex = 0;                                          // Pointer auf den ersten Datenwert im Receivedmessage-buffer stellen
      ucReceivedmessage[ucIndex] = ucReadByte;              //Zeichen in Empfangstabelle an erste Stelle schreiben
  } 
  else
  {
  ucIndex++;                                            // nächstes Zeichen (nicht Synchronisierungsbyte)
  if (ucIndex <= 15) 
  {
  ucReceivedmessage[ucIndex] = ucReadByte;            //Zeichen in Empfangstabelle an nächste Stelle schreiben
  if (ucIndex == 15)                                  //Letztes Zeichen der Nachricht angekommen
  {
  if ((ucReceivedmessage[2] == OwnAdress))          //Bin ich gemeint
  {
  ucSenderByte = ucReceivedmessage[3];            //Byte No. 5 ist die Senderadresse
  ucReceiverByte = ucReceivedmessage[2];          // Byte No. 3 ist die Empfängeradresse
  ucReceivedeventFlag = true;                     //Flag heben, da ich gemeint bin und eine Gültige Message erhalten habe
  } 
  }
  }
    }
  }
  uiAvailableChars = Serial1.available();
  Serial.println(" ");
}

Da dies ein Testaufbau ist werden nicht alle Routinen benötigt. Ich will derzeit einfach einen Sensor abfragen, und die Antwort auf den seriellen Monitor der IDE geben. Leider dauert es 3 Antworten des Sensors (mit dem KO gemessen) bis die Daten auf dem Monitor erscheinen. Danach habe ich immer einen Buffer von 3 Abfragen.
Im angefügten Bild siehst du noch wie die Kommunikation erfolgen soll.

Hi

Die Serial.print bremsen Dir den Sketch zusätzlich aus - erhöhe die Baud-Rate Mal drastisch.
Dann das delay in loop ... Du willst also warten - dann brauchst Du Dich nicht wundern, daß Dir die gesamte Kommunikation flöten geht.

Schmeiß ALLES, was Bremst, raus!
Debug-Ausgaben klar, bleiben drin, aber mit schnellerer Baud-Rate.

Schaue, wie schnell Du loop() bekommst - schaue, wo Du am meisten Zeit vertrödelst - DORT müssen wir anfassen!

loop() muß so schnell werden, daß immer früh genug das nächste Zeichen von der Seriellen abgeholt werden kann.

Vll. sind die Task-Makros von combie hier einen Blick wert - man könnte Funktionen, Die momentan in loop() 'ewig' brauchen, damit unterbrechen - also gestückelt ausführen.
Dieses 'Raus springen' macht man alle x Millisekunden, dann prüft loop() auf neue Zeichen (wird zumeist Nichts sein) und macht mit dem Trott von vorhin weiter.

mfG

Das Delay benötige ich nur, damit ich nicht zu schnell die einzelnen Sensoren abfrage....
Ich könnte dies natürlich auch über einen Timer lösen. Gibt es hier eine einfach Implementierung?

Ja, mit millis(). Schau Dir BlinkWithoutDelay an und verstehe es.

Gruß Tommy

Alles klar, danke vielmals.
Ich werde die Implementierung morgen vornehmen.

habe aber noch ne Frage zum Buffer. Der sollte mir ja die Zeichen bis 64 Bit Buffern. heisst auch wenn ich zu langsam wäre im Main-Loop könnte ich den Buffer auslesen und hätte trotzdem alle meine Zeichen die in der Zwischenzeit empfangen wurden? ICh war bis anhin der Meinung das mache ich mit dem SerialEvent1() und dem darin geschriebenen Code?

ICh war bis anhin der Meinung das mache ich mit dem SerialEvent1() und dem darin geschriebenen Code?

Serial Event wird nach jedem loop() Aufruf aufgerufen.
Es ist keine ISR !

Siehe einen Ausschnitt aus DUE Core main.cpp:

int main( void )
{
  init();

  __libc_init_array();

  initVariant();

  delay(1);
#if defined(USBCON)
  USBDevice.init();
  USBDevice.attach();
#endif

  setup();

  for (;;)
  {
    loop();
    if (serialEventRun) serialEventRun();
  }

  return 0;
}

Genau, aber wenn ich das richtig verstanden habe, sollte mir der DUE während der Zeit die ich im loop() bin, die Zeichen welche er über die serielle Schnittstelle empfängt buffern. Diese könnte ich ja nach jedem loop() in der Funktion SerialEvent1() auslesen?

Ja!

#define SERIAL_BUFFER_SIZE 128