Forum: Mikrocontroller und Digitale Elektronik DMA für Can beim STM 32


von Walter (Gast)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

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.

von Uwe Bonnes (Gast)


Lesenswert?

Abeitest Du mir STM32F103 oder STM32F105/7. Falls Du auch USB beim 103 
benutzt, könnte der gemeinsam genutzte Speicher ein Problem sein.

von Walter (Gast)


Lesenswert?

ne,ich benutze nur can

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Nochmals die Frage F103 oder F105/7?

Siehe RM0008 10.2, da gibt es unterschiede!

von Walter (Gast)


Lesenswert?

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.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

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

von Walter (Gast)


Lesenswert?

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++;
  }

}

von Walter (Gast)


Lesenswert?

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?

von Volker Z. (vza)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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?

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

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++;
}

von (prx) A. K. (prx)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

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.

von Walter (Gast)


Lesenswert?

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.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

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.

von Volker Z. (vza)


Lesenswert?

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

von Walter (Gast)


Lesenswert?

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

von Volker Z. (vza)


Lesenswert?

Solange der Fifo nicht voll ist, muß er jederzeit Frames aufnehmen. 
Sonst ist kein Fifo.

Schalte ihn ab .


Volker

von Walter (Gast)


Lesenswert?

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.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

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.

von Frank L. (frank1967)


Lesenswert?

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
Noch kein Account? Hier anmelden.