Hallo, Mein Name ist Walter und ich arbeite mit mikrocontrollern bisher auf UART Basis. Habe aber mittlerweile auch den Can Bus für mich entdeckt. Da ich gerne große Datenmengen sicher übertragen will habe ich das Phanomen entdeckt das mein Can nicht mehr schritt halten kann. Mein Testaufbau sind zwei Peakcan Adapter und eben mein STM Board, natürlich mit Widerständen. Nachdem man z.B mit 1Mbit und 1ms Abstand die Nachrichten zwischen den beiden Peakcan hin und herschicken kann, fehlen bei meinem STM dann ca. 2 bis 10 Nachrichten, immer unterschiedlich. Mit etwas mehr Programm scheinen es dann mehr zu werden. Habe ich quasi nur Can am laufen, schaffe ich aber auch nie alle 1000. Dann habe ich gedacht, das es vieleicht auch an den 1Mbit liegt und habe mal auf 125kBit gedrosselt, aber eben genau das gleiche Problem. Die Auslaszung des Busses liegt zwichen 5 und 40%, also auch kein Beinbruch. Jetzt habe ich gedacht den DMA für Can zu benutzen, habe aber ein grundsätzliches Problem. Wie kann ich den DMA sozusagen als Ringpuffer einrichten, da ich ja grundsätzlich nie weiß wieviel Nachrichten kommen werden. Auch verstehe ich nicht so ganz warum mein STM das nicht schafft mit Interrupt, muss man den Interrupt vieleicht noch beenden. Ich arbeite mit den alten Libs, mehr als "can_receive (fifo0, %structur)" und ein zähler steht da nicht drin.
Nach wälzen des Datenblattes habe ich einen möglichen Fehler entdeckt. Der Can besitzt ja 3 Puffer für Nachrichten. Mit der oben genannten aktion wird aber nur maximal 1 Nachricht pro Interrupt geholt. So steht in den Beispielen von ST. Ich denke das ich das Pending benutzen muss im Interrupt um den Fifo jedesmal geleert zu haben bevor ich den Interrupt verlasse. Mit dem DMA steht die Frage aber immer noch, ich suche ja eine Lösung um den ständigen Interrupt noch zu vermeiden.
Abeitest Du mir STM32F103 oder STM32F105/7. Falls Du auch USB beim 103 benutzt, könnte der gemeinsam genutzte Speicher ein Problem sein.
Nochmals die Frage F103 oder F105/7? Siehe RM0008 10.2, da gibt es unterschiede!
Genaugenommen benutze ich einen 103 als Slave und einen 105 als Master gleichzeitig. Das ganze ist ja eine Multibusanwendung als eine eigene Hausbusangelegenheit. Ist auf beiden auch das gleiche Problem. Da sollen bei mir ca. 20 Teilnehmer rankommen. Ich werde erstmal die Fifo Geschichte genau prüfen, wenn ich wieder Zeit habe. DMA habe ich bis jetzt noch nicht benutzt, kenne das aber von anderen Steuerungen.
Ich nutze den F103 und DMA bei der AD-Wandlung. Ich wandle 50 Werte von 5 AD-Eingängen immer im Kreis. Somit sind von jeder Messung immer 10 neue im Buffer (die ich jede Sekunde einfach laufen lasse). Das klappt prima. Beim CAN hat man das Problem, dass es für eine Message immer mindestens 3 Worte (32-Bit) von der Pheriperie benötigt. Ich habe in der Doku RM0008 jetzt nichts gefunden, wie ich jetzt 3 Worte von der Pheriperie nach x * 3 Worte in das RAM bekomme. Ich kann mir nur vorstellen, dass man 3 Worte von der Pheriperie kopiert und dann bei einem DMA Interrupt die RAM-Adresse auf den nächsten Block setzt. Somit braucht es nur relativ wenig Rechenaufwand im DMA Interrupt. Eigentlich es es auch Quatsch, genauso könnte man sich die CAN Message selbst im CAN Interrupt abholen und in einen Ring-Buffer erst mal ablegen und das erst im Main-Zyklus berechnen. CAN mit DMA auslesen geht nur bei 105/107er.
mmh, ich habe das ganze jetzt mal mit Pending probiert, naja, besser ist es nicht geworden. Der Interrupt wird wohl solange immer wieder angesprungen bis der Fifo leer ist. Meine Variante beschleunigt das ganze somit nur um die Zeit des Interrupts selber und hilft natürlich unnötige unterbrechungen zu vermeiden. Hab mal mit Debugger nachgesehen. Bei 8Byte Can Nachrichten und 16ms dazwischen kann es vorkommen, dass der Puffer überläuft. Verstehe ich nicht so ganz. Was macht der zum Teufel die ganze Zeit??
Also mein Interrup sieht so aus, mit Software Ringpuffer. Natürlich habe ich den auch mal Testweise abgeschaltet. Mit ab zähle ich halt die Anzahl der messages. Da ein Peakcan sendet und der andere alles empfängt gehe ich davon aus, das Hardwaremäßig alles stimmt. void USB_LP_CAN_RX0_IRQHandler(void) { CanRxMsg canmsg; while (CAN_MessagePending(CAN_FilterFIFO0)!=0) { CAN_Receive(CAN_FilterFIFO0, &canmsg); //can_send_rxring(canmsg); ab++; } }
So, habe es mal nachgemessen. Der stm benötigt ca 14Mikrosekunden um die Interruptschleife abzuarbeiten. Den Rest hat er Zeit (while (1);) Keiner eine Idee?
Hallo Walter, da ich den STM32 nicht kenne (arbeite mit Renesas) hier nur algemeine Fehlerquellen: Treten Error-Frames auf ? Haben die Frames unterschiedliche IDs? Was sagen die die Fehlerflags (overrun) und die Errorcounter vom CAN-Controller? ciao Volker
Mit welchem Takt läuft der STM32? Mit welchem Takt läuft der entsprechende Peipheriebus? Mit welchem Compiler und welcher Optimierungsstufe wurde übersetzt? Sind die assert_param() Makros aktiv?
Der ISR Code sollte so gehen. Poste mal den Teil der Initialisierung. Man könnte das noch vereinfachen: void USB_LP_CAN_RX0_IRQHandler(void) { CanRxMsg canmsg; CAN_Receive(CAN_FilterFIFO0, &canmsg); ab++; }
Die Library von ST ist nicht unbedingt ein Beispiel für effiziente Programmierung. Wenn man auf Effizienz Wert legt, dann sollte man selber Hand anlegen. Beispielsweise verwendet CAN_Receive
1 | RxMessage->Data[0] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDLR; |
2 | RxMessage->Data[1] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 8); |
3 | RxMessage->Data[2] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 16); |
4 | RxMessage->Data[3] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDLR >> 24); |
5 | RxMessage->Data[4] = (uint8_t)0xFF & CANx->sFIFOMailBox[FIFONumber].RDHR; |
6 | RxMessage->Data[5] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 8); |
7 | RxMessage->Data[6] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 16); |
8 | RxMessage->Data[7] = (uint8_t)0xFF & (CANx->sFIFOMailBox[FIFONumber].RDHR >> 24); |
was zwar korrekt und elegant aussieht, aber unterhalb einer Schleife so ungefähr der aufwendigste Weg ist, wie man so etwas lösen kann. Der Rest sieht ähnlich aus. Was kein Problem ist, wenn das wie oft keine Rolle spielt. Aber hier vielleicht doch (ganz besonders ohne Optimierung). Generell gilt wenn man es eilig hat: Die Konvertierung der CAN Mailboxes gehört nicht in den Interrupt. Der sollte so simpel wie möglich sein, d.h. die Datenstruktur in den Queues sollte derjenigen der Mailbox entsprechen, um eine schnelle 1:1 Kopie zu ermöglichen. Auseinander nehmen kann man das woanders.
Danke für die Antworten, hier mal ein paar Tests. @Markus Genauso hatte ich die Schleife ja, aber es wird dann nur eine einzige Can Nachricht abgerufen pro Interruptaufruf. Durch die Abfrage des Fifo Anzeigers kann ich ja eine gerade hinzugekommende auch gleich mit abfragen. @beide Ich bin in der glücklichen Lage in der Firma einen Hardware Debugger zu haben und mit dem Trace konnte ich genau sehen das ein kompletter Interrupt Aufruf mit Abspeichern in meinen Ringpuffer 14us vergehen. Sonst läuft gar nichts mehr. In der main steht nur noch while(1); Entlosschleife. Ein Versuch, einfach den Systick zu nutzen und alle ms eine Abfrage des Can Busses zu machen schlägt ebenso fehl. Scheint so als wenn der Can Bus für ne Zeit gesperrt ist. Bei der Überwachung der Can Register zeigt sich das 1 bis 2 Nachrichten in den Fifo gehen bei 20ms Nachrichtenabstand. Bei 15ms Abstand sind es drei und werden ab und an dann eben vier was einen Überlauf erzeugt und die Nachricht futsch ist. Da ja nach 14us eigendlich alles erledigt ist finde ich keine Erklärung. Das die Libs nicht optimal programmiert sind ist klar. aber da ich ja nichts anderes mache zur Zeit "egal". Der Prozessor läuft auf 72Mhz und der ABB auf 32. Sonst würde auch keine Nachricht gesendet werden wenn diese Einstellung nicht stimmen würde. Die Initialisierung ist nach Beispielen der Libs wieder zurückentwickelt um Fehler auszuschließen und entspricht momentan genau dem Beispiel für 1Mbit. Wie gesagt verhält es sich auch bei 20kBit genauso. Das ganze Programm hat also nur die Initialisierung, Filter offen und natürlich an, sowie die gpios, Freigabe des Interrups und die entsprechende Nvic Programmierung. Prioritäten habe ich auch mal auf null geändert ohne Wirkung. Da der 103 keinen DMA für Can hat stehe ich auch etwas aufn Schlauch. Ich benötige das schnelle senden zunächst nur für einen bootloader, aber es nervt mich irgendwie Daten verlieren zu können. Es würde mich natürlich mal interessieren wer seinen Can ähnlich bombadiert, ansonsten merkt man den Fehler ja nicht.
Ergänzung: Ich kopiere die originale Can Nachricht Struktur in ein Array und setze nur einen Zähler, mehr nicht. Auseinander genommen wird sie dann in der main. Ich muss aber immer wieder sagen das alles in 14uS abgeschlossen ist. Demnach hat er eigendlich genügend zeit. Ich habe das mal so gerrechnet: 130bits dauert eine Nachricht mit 8 Datenbyte und allem Gedönse. Stand irgendwo im Netz. 1000000/130=7692 mögliche theoretische Nachrichten wenn kein Abstand besteht. Also 1/7692=130uS pro Nachricht. Das wiederum bedeutet 130-14= 116 uS Zeit die die Cpu verwenden kann. Da der Fehler aber auch bei niedrigen Taktraten auftaucht und da ms zwischen den "möglichen" Interrups liegen muss (meiner Meihnung nach) bei einem dreifachen FIFO in Hardware genug zeit sein. Bitte korrigiert mich wenn ich da einen Denkfehler habe.
Ich nutze auch CAN im Hausbus, allerdings nur 20KBaud und es kommen nur wenige Telegramme/s. Hast du/kannst du mal testen: - Ohne Interrupt - In der Main-While(1) Schleife pollen und CAN_Receive() und ab++ zählen? Ist zwar nicht Praxistauglich, zumindest hätte man da herausgefunden ob der Interrupt-Controller hinkt oder tatsächlich das CAN-Modul.
Irgentwie erkennst du den Füllstand des (Hardware-)FIFOs nicht. Was passiert wenn du ihn abschaltest? Zeit genug hast du ja, um jeden Frame einzelnd zu bearbeiten. ciao Volker
"Irgentwie erkennst du den Füllstand des (Hardware-)FIFOs nicht. Na ja, irgendwie schon, ich sehe mit dem Debugger ja in Echtzeit wie er voll wird. Mir kommt es nur komisch vor das er so lange intern braucht bis er wieder bereit ist neue Telegramme aufzunehmen. Es muss sich ja um Millisekunden handeln wenn ich schon beim senden immer 1ms zwischen den Telegrammen habe. Wie kann man den abschalten? @Markus Habe zwar nicht in der while gepollt, aber in der Systick, werde es aber auch mal versuchen.
Solange der Fifo nicht voll ist, muß er jederzeit Frames aufnehmen. Sonst ist kein Fifo. Schalte ihn ab . Volker
Es ist mir etwas peinlich, aber auch das muss gesagt werden: Ich habe mich sozusagen selbst verarscht. Der Debugger hat einen Echtzeit und einen Monitor Modus, Ich hatte zusätzlich den MonitorModus eingestellt wo der Debugger 500us zugriff hat und alles unterbricht bis dahin. Klasse wenn man sich mit seinen Tools auskennt! Danke für die Hilfe, es hat wie immer etwas gebracht, auch wenn es schmerzt.
Ich habe für meine "Zeitkritischen" Debug-Sachen immer einen UART mit 115KBaud am laufen, Dabei wird die Info in einen 10KB großen Buffer gespeichert und der wird per Interrupt automatisch ausgegeben. Sollten die 10K "überlaufen", dann fehlen diese in der Debug-Ausgabe, macht aber auch nichts. Da haben die Debug-Tools des kleinen Mannes doch ein paar Vorteile, denn die benötigen sehr wenig µs Rechenzeit (Irgend was in den Buffer schreiben). Und wenn's wirklich kritisch ist, dann eben nur ein ASCII Character ausgeben.
Walter schrieb: > void USB_LP_CAN_RX0_IRQHandler(void) > { > CanRxMsg canmsg; > while (CAN_MessagePending(CAN_FilterFIFO0)!=0) > { > CAN_Receive(CAN_FilterFIFO0, &canmsg); > //can_send_rxring(canmsg); > ab++; > } > > } Hallo Walter, es ist zwar schon ein Weilchen her bei Dir, aber ich hab zu Deiner ISR noch ne Frage. Ich versuche mich gerade ebenso an einer CAN_Receive-ISR und frage mich, ob die 'while' in der ISR nicht zu Problemen führt, wenn z.B. sehr viele CAN-Nachrichten rein kommen.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.