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.
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
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.
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.