Ich habe einen Mikrocontroller TM4C123GXL Launchpad mit 8 seriellen UART-Ports. Ich verwende Keil uVision5 mit Tiva Ware zum Programmieren.
Ich wollte 3 UART-Geräte ohne Byteverlust mit dem Mikrocontroller verbinden. Die Baudrate wurde auf 115200 Bit/s ohne Parität eingestellt, wie in der Abbildung unten gezeigt. Alle Geräte senden regelmäßig alle 5 ms einen Datenrahmen .
TX- und RX-Zeit werden mit No_0f_bytes*10/BaudRate berechnet
Ich habe die Geräte erfolgreich mit dem UART von µC verbunden. Das Problem bei der Kommunikation ist, dass ich die Bytes verliere. Wenn ich eine Kommunikation mit einem einzelnen Gerät (Gerät 2) mache, verliere ich immer noch die Bytes aus dem gesamten Frame (20 Bytes).
Liegt es an der 16-Byte-FIFO-Beschränkung von Tm4c123 oder an etwas anderem?
Ich habe auch die µDMA-Funktion von TM4C123 implementiert. Aber trotzdem gehen Bytes verloren. Was kann ich tun, um das System für die verlustfreie Übertragung und den Empfang von Datenrahmen zu verbessern?
Bearbeiten:
Hier ist die Softwarearchitektur:
Ich verwende Periodic Timer Interrupt von 5 ms, um den Frame zu empfangen und zu senden. Alle Frames haben zuerst 2 Byte Header und ein Zählerbyte als letztes Byte.
void Timer1IntHandler(void) // Periodic Service Routine every 5ms
{
DisableIntrupts();
bool Correct=ReadJoystick(); //10 bytes Device 1
if(Correct)
{
GenerateServoCardsFrame();
SendServo1Frame(); //20 bytes Device 2
SendServo2Frame(); //17 bytes Device 3
ReadServo1Frame(); //15 bytes Device 2
ReadServo2Frame(); //20 bytes Device 3
GenerateJoystickFrame();
SendJoystickFrame(); //10 bytes Device 1
EnableIntrupts();
}
}
main()
{
SetupClock() ; //Setup 16 MHz Clock
SetupJoystick(); //Initalize uart1 port for Device1
SetupServoCard1(); //Initalize uart2 port for Device2
SetupServoCard2(); //Initalize uart3 port for Device3
InitalizePeriodicTimerHandler(5); //Periodic Service Routine every 5ms (Timer1IntHandler)
while(1)
{
}
}
bool ReadJoystick(void)
{
int BytePos=0;
int CountInvalid=0;
int LoopoutTime=0;
while(1)
{
if (ROM_UARTCharsAvail(UART1))
{
ByteRX = ROM_UARTCharGetNonBlocking(UART1);
if (BytePos==0)
{
if (ByteRX== 0xA1) //Header1 found
{
KArray[0] = Bytebuf ;
BytePos ++;
}
else
{
CountInvalid++;
if (CountInvalid>5)
return 0;
}
}
else if (BytePos ==1)
{
if (ByteRX == 0x66) //Header2 found
{
KArray[1] = ByteRX;
BytePos ++;
}
else
BytePos=0;
}
else
{
KArray[BytePos++] = ByteRX;
if (BytePos==10)
return 1; //Frame Recived
}
}
else
{
SysCtlDelay(0.25*SysCtlClockGet()/3 / 1000); // 0.25ms delay
LoopoutTime++;
if (LoopoutTime > 10)
return 0;
}
}
}
Nach meiner Berechnung benötigt 1 Byte 10/115200 = 0,08680 ms und das Lesen eines FIFO von 16 Bytes erfordert 1,38 ms. Die Tabelle in der Abbildung zeigt eine Gesamt-Tx-Zeit von 4,08 ms und eine Rx-Zeit von 3,91 ms, was insgesamt 8 ms ergibt. Dies ist größer als meine Periodic Interrupt Service Routine.
Muss ich das Periodic Interrupt Timing erhöhen?
Ihr Softwaredesign ist nicht gut und ist wahrscheinlich der Grund dafür, dass eingehende Bytes verloren gehen.
Erstens bin ich mir nicht sicher, ob dies ein Fehler oder ein Tippfehler ist. Sie deaktivieren Interrupts zu Beginn von, Timer1IntHandler()
aber Sie aktivieren Interrupts nur dann wieder, wenn Correct
wahr ist. Möchten Sie Interrupts nicht erneut aktivieren, bevor Sie unabhängig von der Bedingung zurückkehren? Es scheint seltsam, dass Interrupts deaktiviert bleiben könnten, wenn die Funktion zurückkehrt.
Es scheint, dass Ihr Code Zeichen von UART1 nur innerhalb der Funktion liest ReadJoystick()
. Und ich vermute, dass UART1 nicht gelesen wird, während all diese Funktionen von GenerateServoCardsFrame()
bis SendJoystickFrame()
aufgerufen werden. Wie lange dauert es, bis diese Funktionen ausgeführt werden? Könnten diese Funktionen lange genug dauern, bis sich der UART1-FIFO füllt und überläuft? Dies kann daran liegen, dass eingehende Bytes verworfen werden.
Wenn ich diese Software entwerfen würde, würde ich sie ganz anders implementieren als Sie es getan haben. Ich würde die UART-Interrupt-Anforderung aktivieren und eine schnelle UART-Interrupt-Handler-Routine erstellen. Das einzige, was der UART ISR tun würde, ist, Bytes zu/von den UART TX/RX-Registern zu kopieren. Ich würde zwei kreisförmige (auch bekannt als Ring-) Puffer erstellen, um die Bytes zu halten. Der UART ISR würde ein empfangenes Byte aus dem UART-RX-Register in den RX-Ringpuffer kopieren. Und die UART ISR würde ein zu übertragendes Byte aus dem TX-Ringpuffer in das UART-TX-Register kopieren. Der UART ISR würde nicht versuchen, die Bedeutung irgendeines der Bytes zu interpretieren. Es verschiebt lediglich Bytes zwischen den RAM-Pufferspeichern und dem UART-Peripheriegerät. Dadurch bleibt die UART ISR kurz, wodurch das Gesamtprogramm besser auf andere Interrupts reagieren kann.
Dann würde ich eine Funktion mit einer Endlosschleife erstellen main()
und innerhalb der Endlosschleife würde ich eine Funktion aufrufen, die aufgerufen wird, SerialReceive()
um Nachrichten aus dem RX-Puffer zu lesen. SerialReceive()
würde als Zustandsmaschine implementiert werden. Wenn irgendwelche Bytes im RX-Puffer verfügbar sind, verarbeitet er eine endliche Anzahl von Bytes durch die Zustandsmaschine. Die Zustandsmaschine hätte Zustände für den Frame-Header, -Body und -Trailer, ähnlich wie Sie es getan haben. SerialReceive()
kehrt sofort zurück, wenn entweder eine Nachricht abgeschlossen ist oder keine Bytes mehr verfügbar sind. Wenn eine Nachricht unvollständig ist, weil keine weiteren Bytes sofort verfügbar sind, SerialReceive()
wartet sie nicht auf sie, sondern merkt sich den aktuellen Zustand und die Nachricht, sodass sie mit derselben Nachricht fortfahren kann, wenn sie erneut von aufgerufen wird main()
.
Wenn Sie regelmäßig etwas tun müssen, richten Sie einen Timer ein, wie Sie es getan haben, aber anstatt die ganze Arbeit innerhalb der Timer-ISR zu erledigen, setzen Sie stattdessen einfach ein Flag. Die Haupt-Endlosschleife sollte das Flag wiederholt überprüfen und alles tun, was angemessen ist, wenn das Flag durch den Zeitgeber ISR gesetzt wurde. Das Erledigen der Arbeit aus dem Kontext von main()
bedeutet, dass das System auf andere Interrupts reagieren kann, während es die Arbeit verrichtet.
Das Kurzhalten von ISRs ermöglicht es dem Gesamtsystem, besser auf andere Interrupt-Anforderungen zu reagieren. Wenn Sie zu viel Zeit in einer ISR verbringen, wie ich glaube, dass Sie es in Ihrer Timer-ISR tun, reagiert das System nicht.
Update: In Ihrem Kommentar sagen Sie, dass diese Funktionen eine Schleife durchlaufen, bis die Übertragung abgeschlossen ist, und dass sie über 7 Millisekunden dauern. Das ist genug Zeit, damit 80 Bytes am UART ankommen, und Ihr Code liest diese Bytes während dieser Zeit nicht, sodass natürlich Bytes verloren gehen.
Ihre Sendefunktionen sollten Bytes in den TX-Puffer kopieren und zurückkehren, ohne auf die Übertragung der gesamten Nachricht zu warten. Der UART ISR sollte bei jedem Aufruf ein Byte übertragen, während der TX-Puffer Bytes enthält.
Der RX- und TX-Puffer sollte größer sein als jede Nachricht. Typischerweise haben die Puffer eine Zweierpotenz in der Größe, da dies es einfach macht, die Pufferzeiger zurück zum Anfang zu kreisen. Machen Sie sie also zu 256 Bytes (oder 128 oder 64, aber warum nicht größer?).
Sie sollten für jeden UART einen unabhängigen Satz RX/TX-Puffer haben.
Das Ändern der Periode Ihres periodischen Timers ISR wird das Problem mit Ihrem ursprünglichen Code nicht beeinflussen. Innerhalb Ihrer periodischen ISR verbringt Ihr Code 7 Millisekunden damit, den UART NICHT zu lesen. Ihr Code wird unabhängig von der Timer-Periode Bytes verlieren.
Mitu Raj
Masood Salik
Mitu Raj
Masood Salik
Mitu Raj
krambo
Mitu Raj
jonk
jonk
jonk
Masood Salik
jonk
Masood Salik
jonk
jonk
jonk
Lundin
volatile
überall fehlende Fehler haben. Die verwendete Formmain
stammt aus der Dinosaurierzeit, was darauf hindeutet, dass ein schlechter Compiler verwendet wird. Usw.Masood Salik
jonk
Masood Salik
jonk
jonk