Forum: Mikrocontroller und Digitale Elektronik MCP2515 - Nachricht empfangen - und dann? :)


von Danny P. (Gast)


Lesenswert?

Hi zusammen !

Ich quäle mich gerade mit dem MCP2515 herum.
Ich habe kein Hard- oder Softwareproblem... eher ein 
"glaubenstechnisches" und hoffe auf ein Paar Anregungen ;-)

Es geht um die Realisierung eines Hausbusses, nur damit ihr das 
Datenaufkommen abschätzen könnt. Vorkommen sowohl Tastendrücke, 
regelmässige Broadcasts an alle, flashen per Botloader über CAN.
Programmiert wird in Assembler. CAN Baudrate: 125k
Ich schwanke total in der Bearbeitung empfangener Nachrichten:

- Puffer für empfangene Nachrichten im µC SRAM einrichten ja/nein (der 
MCP kann ja schon drei Nachrichten halten... andererseits wär ein Puffer 
sicher nicht schlecht falls doch mal zufällig was gleichzeitig geschiet

- empfangene Nachricht im Interrupt des µC sofort auslesen und in SRAM 
puffern (FIFO,FILO) damit das Empfangsfach des MCP schnell wieder frei 
ist

- empfangene Nachricht im Interrupt des µC "abchecken" ob von Bedeutung, 
erst dann in SRAM übernehmen

- oder nur im Interrupt schauen welcher Rx-Puffer eie Nachricht hat und 
später abfragen

- viele, oder gar jeder, nutzen einen Sheduler um da ein bisschen 
Ordnung reinzubekommen und nicht nur ereignisorientiert zu arbeiten. Den 
in Assembler zu stricken ist wohl nich so einfach, daher habe ich auch 
kaum Code-Beispiele dafür gefunden und somit immernoch nich richtig 
geschnallt was mir das Teil bringt.
Ob ich nun eine immer wiederkehrende Schleife habe oder in definierten 
Zeitabständen etwas "organisiere" ist doch relativ gleich, oder?

Ich finde überhaupt keinen vernünftigen Gedankenansatz. Bei 125kBaud und 
einer Nachrichtenlänge von 128Bit können also theoretisch 1000 
Nachrichten pro Sekunde anliegen (unrealistisch, aber es können ja mal 
drei oder vier Ereignisse gleichzeitig eintreten).

Baudrate runter, ok. Aber ich will ja gleichzeitige Ereignisse auch 
schnell wegkriegen.
Ist das denn ok wenn ich bei der Sache die "Gleichzeitigkeit" heranziehe 
und einfachmal annehme das nicht mehr als z.B. fünf Dinge direkt 
nacheinander auf dem Bus liegen?
So würde ich natürlich auch programmieren und nicht alle Broadcasts auf 
einmal...

Wie geht ihr sowas an? Könnt ihr mich mal ein bissl anschubsen? :-)

thx & greetz
Danny

von A.K. (Gast)


Lesenswert?

Vielleicht bringt es dir was, mal unter dem Stichwort "CANopen" zu 
suchen.

von Michael U. (Gast)


Lesenswert?

Hallo,

für mich stellt sich da meist die Frage: was ist "schnell".

In welchem Zeitraum muß ich maximal auf ein Ereignis reagieren können?

Ich weiß nicht was, Dein Hausbus alles handeln soll, aber als Beipiele 
vielleicht: Temperatur messen muß ich nicht alle 10ms, so schnell ändert 
die sich nicht, ob die Anzeige dann nochmal 1s hinterher hängt, merkt 
keiner.

Licht schalten/dimmen per Fernbedienung sollte dagegen nie eine größere 
Verzögerung von ca. 0,3s haben, sonst hat man das Gefühl, nicht richtig 
auf die Taste gedrückt zu haben.

Ein Einbruchsalarm muß auch nicht innerhalb 10ms gemeldet werden usw.

Ich habe manchmal das Gefühl, es werden Datenmengen erzeugt, 
Genauigkeiten gefordert, Reaktionszeiten eingebaut, nur weil es 
technisch eben geht.

Letztlich sollte der gewünschte tatsächliche Nutzen entscheiden und der 
dazu nötige Aufwand getriben werden, nicht der maximal technisch 
mögliche.

Sind aber nur meine Gedanken dazu...

Gruß aus Berlin
Michael

von A.K. (Gast)


Lesenswert?

Mit CANopen wird eine Abbildung zwischen CAN-Messages und 
SRAM-Speicherobjekten realisiert, d.h. im Speicher steht der Zustand 
eines Schalters/Sensors/Aktuators, und CAN-Messages aktualisieren diesen 
(rein wie raus). Das korrespondiert recht gut mit den FullCAN 
Message-Objekten grösser entwickelter CAN Controller wie im AT90CAN128 
oder LPC2000 (hier mal ohne Bugs betrachtet), zumindest solange der 
genug Message-Objekte vorgesehen hat.

Mit dem MCP2515 muss man sowas dann im MCP-Treiber erledigen. Generell 
gilt aber für fast FIFO-freie Controller die den MCP: Sofort raus aus 
dem Buffer. Ob du das dann bereits im Interrupt vgl. CANopen 
entsprechend eintütest, oder erst einmal in einen FIFO-Buffer im 
Speicher, das hängt nun stark davon ab, ob damit eher 
Zustands/Aktivitätsinformationen übertragen werden, oder ob du den CAN 
eher als Ethernet für Arme siehst und auch mal grösseren Datenmengen 
sequentiellen Charakters dabei sind (z.B. Logfiles auslesen).

von Danny P. (Gast)


Lesenswert?

@Michael: Und ich versteh immer nich warum man sofort den "Datenhighway" 
anprangert obwohl ich nur mal die Gleichzeitigkeit betrachte. :-)
Ich will nicht auf Dauer einen an der oberen Grenze befindlichen 
Datendurchsatz erreichen weils technisch möglich ist. Aber der "normale 
Zustand" sollte nicht schon in der Planung in einer 
"arme-Schwein-Schleife" enden.

Aber: Einen µC über den BUS per Bootloader flashen, in der Zeit bewegt 
sich jemand im Flur (Bewegungsmelder) oder es wir eine Temperatur 
gebroadcastet und jemand zweites drückt im Keller nen Lichtschalter - 
das kann wohl jeden Tag passieren.

Ich will also nicht dauerhaft viel - sondern nur einen Ansturm gut 
bewältigen können ohne das mein Licht-Ein-Telegramm verloren geht.
Und in dem einen Moment ist es nun mal ein großer Unterschied ob ich 
"nur" die zwei bzw. drei MCP-Speicher nutze oder sofort ins SRAM 
kopiere/puffer.


@A.K.: Danke für dein Tip, dann werde ich mich mal ein bissl durch 
CANOpen graben ;-)
Datenmengen sind nicht der Normalzustand, aber wenn Sie anfallen 
(flashen per Bus oder wie du sagtest LogFiles) will ich keine spürbare 
veränderung des Busses sehen (ich hab immer die sich ständig ändernden 
Blinktakte einer S5 bei großer Auslastung vor meinem Inneren Auge :-) )


greetz
Danny

von Stefan K. (_sk_)


Lesenswert?

Hallo Danny,

>Datenmengen sind nicht der Normalzustand, aber wenn Sie anfallen
>(flashen per Bus oder wie du sagtest LogFiles) will ich keine spürbare
>veränderung des Busses sehen (ich hab immer die sich ständig ändernden
>Blinktakte einer S5 bei großer Auslastung vor meinem Inneren Auge :-) )

das sollte kein Problem darstellen, wenn Du die Prioritäten der CAN-msgs 
vernünftig planst. Ungefährer Anhaltspunkt:

Prio 1: sehr wichtige msgs
Prio 2: msgs mit geringem Datenaufkommen
Prio 3: msgs mit mittelgroßem Datenaufkommen
Prio 4: msgs mit sehr großem Datenaufkommen

In Prio 2 fallen alle "normalen" msgs, z.B. Tastendrücke, 
Temperaturdaten, etc.

In Prio 4 fallen z.B. Log-Daten-Übermittlung, Bootload-Daten.

So kommen Tastendrücke auch noch an, wenn z.B. gerade größere Bootmengen 
über den Bus gehen.

Ich habe bei mir Eingangs- und Ausgangs-Fifos im RAM angelegt. Warum 
darauf verzichten? Ansonsten musst Du bei jeder Aktion, die Dein mc 
macht, aufs Timing schielen.

Beispiel 2:
Ich habe Graphikdisplays mit 128*64 Pixel verwendet. Wenn ich das 
komplette Display update, dann sind das > 1024 Schreibzugriffe über SPI. 
Ich habe 4Mhz Taktfrequenz, also brauche ich Minimum 2ms für diese 
Aktion.

>- empfangene Nachricht im Interrupt des µC "abchecken" ob von Bedeutung,
>erst dann in SRAM übernehmen

Dann ist es sinnvoller, die Masken des MCP2515 zu benutzen. Dann 
bekommst Du erst gar keine Interrups.

>- oder nur im Interrupt schauen welcher Rx-Puffer eie Nachricht hat und
>später abfragen

Das bringt gar nichts. Was machst Du später mit der Info, dass da mal 
eine CAN-msg war, aber leider Dein main zu spät gekommen ist und die msg 
bereits überschrieben wurde?


Zur Geschwindigkeit:
Je langsamer die Datenübertragung, desto langsamer kann auch der mc 
getaktet sein, ohne dass er Timingprobleme bekommt. Und je weniger Strom 
verbraucht er dann. Das war für mich u.a. ein Grund, eine sehr langsame 
Übertragungsrate zu wählen.

Warum willst Du alles in Assembler machen?
Meine Erfahrung: das Programm selbst bekommst Du locker in einen 
ATmega16 rein - auch in C ist das kein Problem. Selbst ein ATmega8 wird 
wohl gehen (den kann man nicht jtag-debuggen - deshalb kommt der für 
mich nicht in Frage). Vom Flash-Bedarf wird es erst problematisch, wenn 
Du texte und Graphiken benötigst. Und die brauchen in Assembler und C 
denselben Platz.
Versteh mich nicht falsch: ich habe früher sehr viel in Assembler 
gemacht. Irgendwann habe ich dann gemerkt, dass > 70% der Bugs typische 
Assembler-Bugs waren. In C programmierst Du 1. schneller und 2. 
wesentlich bugfreier.

Viele Grüße, Stefan


von Philipp C. (ba4_philipp)


Lesenswert?

Ich betreibe so einen Bus in meinem Auto (komplett eigenständig, 
Fahrzeug hat nichtmal CAN). Dabei haben fast alle Module einen Mega8 in 
C programmiert, der Flash reicht da mehr als genug, wenn nicht gerade 
ein LCD mit Menüführung dranhängt (wie Stefan ja auch schon sagte)

Ich habe den MCP2515 zwar an einem IRQ Pin, aber das benutze ich nur um 
den Controller aufzuwecken. Die Nachrichten hole ich alle in der Main() 
ab. Wenn man sie in einer ISR abholt, dann muss man meiner Meinung nach 
auch einen sehr großer Buffer anlegen in die einige Nachrichten passen, 
wenn man nur eine einzige Nachricht dort speichern kann bringt es IMHO 
keinen Vorteil gegenüber dem holen aus der Main. Im Gegenteil, evtl. 
leert man den Buffer aus dem MCP und wenn dann vor dem erneuten 
durchlaufen der Main() eine neue Nachricht kommt ist die alte weg.
Ich würde mir einfach mal den Worstcast Fall durchrechnen. Also wielange 
habe ich vom abholen der Nachrichten Zeit bis wieder 3 Nachrichten im 
Buffer liegen können. Wenn es sein kann, dass die main() Schleife nicht 
in dieser Zeit mindestens einmal wieder durchlaufen wurde, dann muss man 
was am Timing ändern.
Ich weiß nicht, was du bei dir alles in den Knoten machst, aber bei mir 
sind die Zeitaufwendigsten Dinge eigentlich AD Wandlungen, alles andere 
wird ja eh nur angestossen und irgendwann wieder abgestellt unterbricht 
die Main Schleife aber nicht wirklich.

Ich komme damit bisher auf jedenfall sehr gut klar. Auch ohne gleich so 
große Dinge wie CANopen oder so zu implementieren.

Gruß Philipp

von Danny P. (Gast)


Lesenswert?

@Stefan: Danke für Deine ausführliche Antwort :-)

zur Priorisierung: Klar, auf CAN-Ebene problemlos durch den MCP 
erledigt. Aber anschliessend habe ich immernoch 3 direkt aufeinander 
folgende Nachrichten im µC. Oder hast du bei -jeder- deiner 
µC-Aktionen,Berechnungen oder was auch immer sichergestellt das es auf 
Grund der niedrigen Baudrate bis zum möglichen Eintreffen des nächsten 
Datenpaketes abgearbeitet ist?

Wenn ich nun noch im eigentlich Programm Prioritäten unterscheiden 
soll... mh ich weiss nich...


Die Messages per MCP zu filtern habt ihr mir vor nem Jahr ausgeredet ;-)
fand ich seit dem auch nich mehr so dolle, denn der kleine UP-Controller 
soll evt doch mehr mitbekommen als nur Broadcast und eine Lampe.
Somit ist es glaub ich gradlinieger (vom Konzept her) alles einzulesen 
als bei jedem Knoten andere Filter und Masken zu haben. (ja gefühlssache 
;-) )

Zum Assembler: Du kennst den Aufwand den so ein Hausbus mit sich bringt 
:-) würdest du den Weg gemeinsam mit einer dir fremden 
Programmiersprache beschreiten wollen? Ich bin mir sicher das ich mehr 
C-Anfängerfehler mache als deine "typischer Assemblerfehler" denn wenn 
ich fertige C-Programme sehe streiche ich zur Zeit echt die Segel :)
Bin noch in der Umbauphase des Hauses und möchte wenn ich ma Zeit und 
Lust hab nun nebenbei programmieren. Und ich möchte als erstes das 
Flashes über CAN realisieren um schon von beginn an hier beim testen 
nich immer wegen des umsteckens verrückt zu werden :-) und das trau ich 
mir in C nicht zu momentan, bzw hab einfach keine Lust da auch noch mit 
anzufangen


greetz
Danny

von crazy horse (Gast)


Lesenswert?

so viel Text zu einem so einfachen Thema :-)
CAN-Bus mit 125k - hihi, da lacht doch jeder Controller nur drüber.
Überlegt mal, eine Standardbotschaft dauert da ungefähr 1,2 ms (grob 
gerechnet 150Bit). Eher kommt auch keine neue. Was bitte soll den mit 
den Daten geschehen, dass die in 1ms nicht weggeschaufelt/verarbeitet 
sind?
Einen  struct anlegen (aus ID, data_length, remote-bit und 8 
Datenbytes), in der ISR per aus dem CAN-Controller in den struct 
schreiben, ein Bit setzen, dass der main mitteilt, dass neue Daten da 
sind. Die kann dann ganz in Ruhe damit was anstellen, vielleicht sogar 
erst mal in eine andere Variable kopieren, damit der neue Empfang nicht 
die alten Daten überschreibt. Und wenn man dann noch die Filter im MCP 
entsprechend setzt, hat der MC noch viel weniger zu tun.
Das ganze Programm besteht fast nur aus Langeweile/im Kreis hopsen oder 
sleep des MC.

von crazy horse (Gast)


Lesenswert?

PS: Filter im CAN-Controller habe ich auch noch nie benutzt.

von Philipp C. (ba4_philipp)


Lesenswert?

Genauso seh ich es auch crazy horse. Allerdings finde ich nicht, dass 
man die Nachricht in der ISR abholen muss und dann ein Bit setzen. Was 
bringt das?
Das Bit setzt der MCP eh indem er solange die IRQ Leitung auf low hält 
bis die Nachricht abgeholt wurde, dass kann ich genauso in der Main 
prüfen und da dann die Nachricht holen und gleich verarbeiten. Es bringt 
ja keinen Vorteil die Nachricht in der ISR zu holen, wenn man eh nur 
eine Puffert und diese erst in der Main verarbeitet.

Gruß Philipp

von and_ref (Gast)


Lesenswert?

> das sollte kein Problem darstellen, wenn Du die Prioritäten der CAN-msgs
> vernünftig planst.
Das hat meiner Meinung nach nichts mit den CanId-Prioritäten zu tun.

Dannys Problem war ja, dass innerhalb einer gewissen Zeit mehr 
Telegramme kommen können, als er gerade_ _jetzt verarbeiten kann. (Im 
Mittel müssen die aufgelaufenen Telegramme natürlich abgearbeitet sein, 
sonst ist da konzeptionell was schief).
Wenn man von einer Hauptprogrammschleife von 10ms ausgeht, d.h. nur alle 
10ms nachsieht, ob und welche Telegramme ankamen und diese auf einen 
Schlag verarbeitet, und in der Zwischenzeit z.B. das LCD aktualisiert, 
dann können ~16 CanMsgs eingehen, bevor erneut nachgesehen wird. 
(Annahmen: 125kBit/s; kurze Botschaften: ~80Bit; -> ~16CanMsg pro 10ms). 
Diese 16 müssen dann natürlich zwischengepuffert werden.

> In C programmierst Du 1. schneller und 2. wesentlich bugfreier.
Volle Zustimmung. Wenn du C erstmal etwas begriffen hast, dann wirst du 
dich fragen, warum du dich solange mit Asm abgekämpft hast. :-)


> Die Messages per MCP zu filtern habt ihr mir vor nem Jahr ausgeredet ;-)
> Somit ist es glaub ich gradlinieger (vom Konzept her) alles einzulesen
> als bei jedem Knoten andere Filter und Masken zu haben. (ja gefühlssache
> ;-) )
War damals nicht die Begründung, dass die Rechenzeit durch 
Softwarefilter eher vernachlässigbar ist? :-) Ich überschlage nochmal 
kurz:
CanInterrupt: 20µs (oder weniger; um zu erkennen, ob relevant und in den 
Puffer holen) * 16Interrupts pro 10ms. => 320µs/10ms => 3.2% 
Prozessorauslastung (das ist absolute "worst case"-Betrachtung. Bei 
kürzeren Interruptlaufzeiten (z.B. weil das Telegramm nicht für den 
eigenen Knoten bestimmt ist) entsprechend (deutlich) weniger; bei 
kürzeren Taskzyklen entsprechend mehr...
Aber das hat ja crazy horse schon ausgeführt...

von Philipp C. (ba4_philipp)


Lesenswert?

Naja 16 Nachrichten halte ich für ziemlich großzügig. 10ms sind ja 
selbst bei mur 1MHz 10000 Takte. Ich denke man muss einfach schauen was 
der Knoten macht und wielange die main benötigt. Schaltet er einfach nur 
eine Pin auf High, wenn eine bestimmte Nachricht kommt (sowas wie 
Schalter an -> Lampe an) und bei einer anderen wieder aus, dann braucht 
man bestimmt keinen Puffer. Wenn er aber was kompliziertes macht und 
wirklich 10ms für einen main() Zyklus braucht sieht das komplett anders 
aus. Ich denke eine generelle perfekte Lösung gibt es da nicht.

Gruß Philipp

von Danny P. (Gast)


Lesenswert?

@and_ref: Nachdem ich so viel Anderes im Kopf hatte die letzte Zeit muß 
man ersma wieder reinkommen ins Thema :-) Ich sag ja, ich brauch nun n 
Anstoß ;-)

Ich hatte das folgendermaßen überschlagen vorhin: 1000Msg/Sek möglich. 
Wie ich ja schon schrieb nicht auf Dauer aber das mal drei bis fünf 
direkt aufeinanderfolgen sollte man denk ich einplanen.
Ihr rechnet in Zeit, ich in Takten. Das Abrufen einer kompletten Msg per 
SPI inkl. schreiben ins SRAM, dauert ~250Takte.

Nun muß das ganze gelesen und geprüft werden. Sagen wir mal wieder 
~250Takte. Dann haben wir bei einem Controller mit 1MHz bereits 50% 
Auslastung (weil ja nur 1000Takte in 1ms). Wenn nun noch ne CAN-Antwort 
generiert, über den SRAM und anschliessend per SPI raus muß liegen wir 
bei geschätzten 100%.
Bei 2Mhz sind 50%, bei 4Mhz 25%... aber auf die berechneten 3% komm ich 
nur schwer und mit einem zugedrückten Auge.

Das wäre bei direkter Abarbeitung vor der nächsten Nachricht so. Daher 
denke ich das ein puffern (Ringpuffer ~8Nachrichten) nötig ist im 
Gegensatz zu Phillips Auffassung. Oder hab ich mich irgendwie 
verrechnet?

Klar, dannach habe ich Zeit den Stapel abzuarbeiten.


zum Thema C: Würdet ihr mir tatsächich raten jetzt bei diesem Projekt 
mit C zu beginnen? Ihr könnts sicher beurteilen wie schnell ihr 
reingekommen seid. Ich hab ein wenig Angst nach 2 Wochen son bissl die 
Lust zu verlieren weil ich weiterkommen möchte aber mit der Sprache 
nicht klar komm. Und denn mach ich nämlich alles nochmal in asm...


greetz
Danny

von Philipp C. (ba4_philipp)


Lesenswert?

Hmm, das klingt doch plausibel was du sagst, wenn du wirklich 500Takte 
benötigst um eine Nachricht zu verarbeiten, dann gehts aber nur (wie du 
ja auch schon gesagt hast) mit Ringpuffer bzw. mit einem Puffer der mehr 
als eine Nachricht speichern kann. Naja und 100% sind ja nicht verboten, 
also wenn du deine 1000 Msg/s noch schaffst ist doch alles ok, auch wenn 
der Controller dazu voll ausgelastet ist.
Ich wohl muss wohl dringend mal bei mir nachzählen... :)

von Danny P. (Gast)


Lesenswert?

@Philipp: wie gesgat is ja kein Dauerzustand, aber möchte ich 
berücksichtigen. Denn irgendwann kommen mal n paar auf einmal und denn 
möchte ich nich das die Außenbeleuchtung die ganze Nacht unbemerkt 
brennt weil was verloren ging....

denn krempelt man doch wieder alles um :)


greetz
Danny



ps: nun habt ihr mich soweit... ich schnüffel wieder mal im C-Tutorial 
:D

von crazy horse (Gast)


Lesenswert?

gut, wenn man die Taktfrequenz beliebig klein wählt, wird irgendwann die 
einfache Addition zweier 8bit-Zahlen zu einem Zeitproblem...:-). Wer 
schreibt dir denn 1MHz vor??
Ich habe mal eine CAN-Bridge gebaut, da hängen 2 MCP2515 an einer SPI, 
Mega16.
Eine ziemliche Anzahl von Botschaften von beiden Seiten, viele davon im 
10ms-Raster. Bus mit 500kHz. Es mussten ein Auto und ein 
Motorsteuergerät miteinander verheiratet werden, die eignetlich gar 
nicht zueiander passen. Also dem Motorsteuergerät das dazugehörige Auto, 
dem Auto das entsprechende Steuergerät vorspielen. Konkret gelöst wurde 
das so:
-Empfang beider Seiten per Interrupt, je ein 1-msg-Buffer
-alle für beide Seiten zu sendenden Botschaften wurden als RAM-Abbild 
gehalten (ID und data-length fest)
-einen Sendetimer, der entsprechend der erforderlichen Intervalle die 
Botschaften nach beiden Seiten verschickt, ganz stur.
-bei Empfang Daten nach diversen Umrechnungen und Bitschubsereien 
passend in dei Matrix der Gegenseite eintütet
-nebenbei ein paar zus. Sensoren auswerten und dummy-Daten generieren

Da es ein einmaliges Projekt war und nicht allzuviel kosten sollte (und 
auch nicht für den öffentlichen Strassenverkehr war), wurde nie eine 
echte worst-case-Betrachtung gemacht. Der MC lief mit 16MHz und hatte 
noch jede Menge Luft. Ausfälle sind mir nie berichtet worden.

von and_ref (Gast)


Lesenswert?

@Philipp
> schaltet er einfach nur eine Pin auf High, wenn eine bestimmte Nachricht
> kommt (sowas wie Schalter an -> Lampe an) und bei einer anderen wieder
> aus, dann braucht man bestimmt keinen Puffer.
Die Notwendigkeit eines Puffers ergibt sich nicht daraus, was ein 
empfangenes Telegramm im Knoten auslöst, sondern wie lange die 
Abarbeitung der empfangenen Telegramme maximal verzögert werden kann.
Und darauf hat der Scheduler (Hauptprogrammschleife) einen großen 
Einfluss.

> man muss einfach schauen was der Knoten macht und wielange die main
> benötigt.
Ich würde unbedingt davon abraten die Hauptprogrammschleife sofort 
wieder starten zu lassen, wenn der letzte Durchgang gerade_ _eben 
beendet war. Damit jittert die Laufzeit des Programms und es ergeben 
sich nichtdeterministische lustige Effekte. Das ist der Horror bei der 
Fehlersuche und bei Erweiterungen.
Also: Ein zyklischer Task mit festen Aufrufabständen muss her! Bleibt 
Laufzeit am Ende übrig, so wird dieser eben aktiv "verwartet".

In deinem Konzept (ohne Puffer) muss der Programmfluss mindestens alle 
1ms bei der Can-Abfrage vorbeikommen, sonst könntest du Telegramme 
verpassen. Wenn du jetzt deine Software erweiterst musst du das immer im 
Hinterkopf behalten - und schonmal garnix mit LCD-Ansteuerung, die mal 
eben 2ms braucht.

> Wenn nun noch ne CAN-Antwort generiert, über den SRAM und anschliessend
> per SPI raus muß liegen wir bei geschätzten 100%.
Oh, stimmt. Hatte vergessen, dass die Kommunikation mit dem externen 
CAN-Controller auch noch Zeit braucht. Deshalb verwende ich integrierte 
CAN-Controller. :-)

> Das Abrufen einer kompletten Msg per SPI inkl. schreiben ins SRAM,
> dauert ~250Takte.
Bei 16MHz bist du dann aber wieder im grünen Bereich... Oder du machst 
für "Lichtschalterknoten" eine strengere CanMsg-Filterung und senkst 
dadurch die Prozessorlast. Den Knoten für die PC-Anbindung kannst du 
dann auch mit 16MHz laufen lassen.

> Würdet ihr mir tatsächich raten jetzt bei diesem Projekt mit C zu
> beginnen?
Wenn nicht jetzt, wann dann? ;-)
Natürlich reisst du dir damit eine weitere große Baustelle auf. Zumal 
zur C-Programmierung noch der formale Kram kommt, wie z.B. Compiler 
einrichten, Makefile, usw.. Das sollte man auch nicht unterschätzen. 
Irgendwann wird sich der zusätzliche Zeitaufwand amortisieren - das wann 
hängt davon ab, wie schnell du in C reinkommst. ;-)

von Philipp C. (ba4_philipp)


Lesenswert?

Ich meinte das mit dem nur Pin setzen ja bezogen auf die main() und 
diese würde dann besonders kurz und auch schnell sein.
Wenn er einfach bei eintreffen der Nachricht "Schalter xy gedrückt" die 
Lampe einschalten möchte (mal ganz vereinfacht jetzt alles). Dann wird 
das sicher nicht mehr als 1ms in Anspruch nehmen.
Mit LCDs habe ich zB beim Einschalten usw nicht gewartet bis das Display 
soweit ist sondern habe dann einfach x main durchläufe gewartet. 
Dadurch, dass ich die main immer sofort wieder starte ist diese Zeit 
zwar nicht exakt gleich, aber meist reicht es ja, wenn min. zB 2ms 
eingehalten werden. Und das kann ich garantieren.

Wobei ergeben sich denn noch Probleme durch eine einfach endlosschleife 
statt timergestütztem Neustart der main? Und hast du ein Konzept wie man 
soetwas am besten angeht?

von Danny P. (Gast)


Lesenswert?

@and_ref: durch unsere Diskussion vor nem Jahr bin ich deinen Gedanken 
gar nicht sooo fern ;-)

mh... aber mal ne Frage die ich beim letzten Mal schon nich ganz 
verstanden hab. Du sprichst nun ja auch wieder von nem Sheduler.

Versteh ich das richtig: Das is im Grunde nur eine per Timerinterrupt 
aufgerufene Schleife die in definierten Zeitabständen alles überprüft?

also-> SRAM auf neue Nachrichten, wenn vorhanden eine davon abarbeiten
    -> Tasten einlesen und gemäß Entprellung weiterverarbeiten
    -> Zeit hochzählen für Zeitrelaisfunktionen
    -> nochwas machen

WARTEN und nach definierter Zeit von vorn.



Wo ist darin der Sinn? Ich lege mir zeitrelevante Dinge in ein TimerISR 
(Tastenabfrage, Zeit zählen) und den Rest mache ich in einer 
Hauptschleife (evt. sag ich ja auch bald main dazu :D ) im Grunde 
ununterbochen. Wo genau ist nun der Vorteil ALLES streng rhytmisch zu 
machen? Hast du ein greifbares beispiel für "nichtdeterministische 
lustige Effekte"?


greetz
Danny

von and_ref (Gast)


Lesenswert?

> Wobei ergeben sich denn noch Probleme durch eine einfach endlosschleife
> statt timergestütztem Neustart der main?
Ein Beispiel gibst du ja selbst:
> Mit LCDs habe ich zB beim Einschalten usw nicht gewartet bis das Display
> soweit ist sondern habe dann einfach x main durchläufe gewartet.
Dein x ist davon abhängig was noch so alles in main gemacht wird.
 Irgendwann entfällt ein Programmteil oder läuft z.B. wegen 
Compileroptimierung oder sonstigen Änderungen schneller als bisher und 
schon verlierst du jedes 100.000ste CanTelegramm. Das merkst du erst, 
wenn das Aussenlicht wieder die ganze Nacht gebrannt hat. :-) Und den 
Fehler wirst du bestimmt nicht auf die Änderung von vor 2Monaten in 
einem völlig unbeteiligt scheindenen Programmteil schieben...


> Und hast du ein Konzept wie man soetwas am besten angeht?
Ich leite alle Zeiten (z.B. Entprellzeiten für Taster; Timeoutzeiten; 
Sekundentakt für Uhr...) von den 10ms ab indem ich in der entsprechenden 
Routine einfach zähle wie oft diese durchlaufen wird. Ich brauche keine 
weiteren Timerinterrupts mehr einzustellen.
Die 10ms erzeuge ich aus einem 1ms-Timer, der im Timerinterrupt eine 
Variable hochzählt, die von Main dann laufend gepollt wird und bei 
">=10" den Task10ms() aufruft und die Variable um 10 dekrementiert.

@Danny:
> Versteh ich das richtig: Das is im Grunde nur eine per Timerinterrupt
> aufgerufene Schleife die in definierten Zeitabständen alles überprüft?
nicht im Interrupt, sondern in der Hauptschleife, damit Interrupts 
dazwischen kommen können. (s.o.)

> Wo genau ist nun der Vorteil ALLES streng rhytmisch zu machen?
Es ist besser planbar. Wird die Laufzeit durch einen Programmteil für 
einen Durchgang länger (z.B. komplexe Berechnung nur wenn "Uhrzeit < 
7.00Uhr" ist oder bei bestimmtem eingegangenen CanTelegramm o.ä.), so 
wird diese Zeit von der Reservezeit abgezogen, die am Ende sowieso 
gewartet wird, bevor der nächste Durchgang beginnt.
Somit hat jeder Durchgang (in etwa) den gleichen zeitlichen Abstand zu 
seinem Vorgängerdurchgang.
Beispiel: Schalten der Rolladenrelais; Vorgabe: zwischen dem Schalten 
zweier Relais müssen z.B. 400ms vergehen.
Im 1. Durchlauf: Abschalten des ersten Relais
Im 40.Durchlauf: Einschalten des zweiten Relais
Dazwischen liegen genau 400ms.
 In der "schnellen Hauptschleife" schwankt die Zeit sehr stark in 
Abhängigkeit der Uhrzeit (siehe Beispiel oben; <7.00Uhr). Es ist 
überhaupt nicht vorhersagbar, wie lange das wirklich dauern kann. 100ms 
würden die Relaiskontakte stark belasten (Funkenbildung...) und 800ms 
wird der Benutzer deutlich merken...

> Hast du ein greifbares beispiel für "nichtdeterministische lustige
> Effekte"?
Ich konstruiere mal: Dein Programm entprellt einen Taster über 100x 
"schnelle" Hauptschleifen. (angenommen das heisst, du musst für die 
Dauer von 100x Hauptschleife die Taste gedrückt halten). Das 
funktioniert auf dem Schreibtisch immer. Im Verbund mit anderen Knoten 
steigt die CanInterruptLast und die Laufzeit von 100x Schleifen 
verdoppelt sich. Du drückst die Taste aber gleichlang und wunderst dich, 
dass der Tastendruck nicht erkannt wird bzw. die Lampe nicht angeht - 
aber nicht jedesmal, sondern nur sporadisch. Falls dir das zu abstrakt 
ist, dann nimm die 100x Schleifen zur Erkennung eines "langen" 
Tastendrucks.
 Oder andersrum: Der Tastendruck verschickt seit der letzten 
Umkonfiguration nicht mehr die Information "Licht an", sondern "Licht 
toggeln". Da deine Entprellung seit jeher nicht stabil funktioniert (da 
laufzeitabhängig; hast du ja garnicht gemerkt) fällt dieses Problem erst 
jetzt auf... und bevor du auf die Idee kommst, das alles mit 
Hardwaretimern erschlagen zu wollen, soviele Timer gibts nicht. Und über 
Softwaretimer ist es eigentlich genau das was ich schreibe.


Übrigens: In der Wartezeit bis zum nächsten Durchgang musst du natürlich 
nicht ausschliesslich auf das "jetzt geht's los"-Flag pollen, sondern 
kannst noch andere kurze Dinge tun (Zufallszahlen berechnen; naja 
eigentlich fällt mir nichts sinnvolles ein :-). Nennt sich dann 
Idle-Task und die läuft dann wieder quasi wie deine jetzige 
Hauptprogrammschleife. Auch hier kann man wieder das beste aus beiden 
Konzepten verwenden.

von Philipp C. (ba4_philipp)


Lesenswert?

hmm, das klingt wirklich überzeugend. logische Verknüpfungen kann man 
dann zB in dem Idle Task lassen.
So entfällt auch das genaue Abschätzen der Taktzyklen die benötigt 
werden usw. man muss nur sichergehen das alles geschafft werden kann.
Ich bin überzeugt :)

Ich habe bei mir aber jetzt eine CanDispatch Funktion die eine CAN 
Nachricht verarbeitet. Wenn ich diese Funktion jetzt aber nur noch 
zyklisch aufrufe, dann gewinne ich ja durch den Buffer keine Zeit. Oder 
sollte man diese Funktion dann so gestalten dass sie erst wiederkehrt, 
wenn sie den ganzen Buffer abgearbeitet hat? (Momentan bekommt die immer 
eine Nachricht und im nächsten main die zweite) Dann hängt der 
Controller aber ggf. für eine ganze Zeit dortdrin und versäumt evtl. 
etwas an den IO Pins, was vorher problemlos mit Polling abzufragen war. 
(So ein Pin Polling würde ich auch in diesen Idle Task legen, oder 
sollte man dann nur noch Dinge pollen die zB länger als 10ms anliegen 
damit man sie mit dem Sheduler problemlos erkennn kann?)

Wie gestaltet man denn das abarbeiten des Puffers am geschicktesten?

PS: Was mir gerade noch einfällt. Man könnte sogar eine Überlast LED 
ranmachen. Wenn alles normale läuft geht der ms Zähler ja nie über 10, 
weil er bei 10 wieder die main anstösst und diese ihn zurücksetzt. Läuft 
die main aber mal länger als 10ms ist der Zählerstand auch größer. So 
hat man eine einfache Kontrolle, falls mal was nicht richtig klappt

edit2:
oder setzt du den Zähler gar nicht zurück und machst nur sowas wie:
if (!counter%10)  task_10ms;
if (!counter%100) task_100ms;

usw?

von Danny P. (Gast)


Lesenswert?

@and_ref: es beruhigt mich das meine Art zu programmieren deiner nicht 
so fern ist wie ich zunächst dachte.
Alles was zeitabhängig gezählt wird habe ich auch per TimerISR 
hoch/runtergezählt und in einer Schleife ich sag mal wie viele einzelne 
Statemachines abgefahren.

Zusammengefasst wird dein main regelmässig durchlaufen, springt so die 
routinen regelmässig an in denen du zählst.
Und ich habe bisher immer im TimerISR gezählt und in ner Endlosschleife 
ausgewertet.
Ich denke mal die Funktion ist gleich sicher, auch ohne das das 
Aussenlicht ne Nacht durchbrennt ;-) aber deines scheint eleganter, 
gerade was Übersicht und andere Interrupts angeht.


nun nochmal ne dusselige C-mal-sehn-Anfängerfrage ;-)

Du sagst du durchläufst die main regelmässig.
Wenn ich was versuche zu programmieren wird immer bei main gestartet, 
das heisst ich muss in main mit den ganzen initialisierungen beginnen. 
dann kommt ein label ab dem meine ganzen funktionsaufrufe stehen, 
anschliessend nehme ich ein while oder was auch immer und bilde 
innerhalb der main eine schleife die nur schaut ob die regelmässige zeit 
um ist. wenn ja springt er wieder zum label.
Sieht so im Kern das aus was wir besprochen haben?
Denn wird ja im Grunde nicht die main regelmässig durchlaufen, sondern 
nur sagen wir mal der letzte Teil der main, seh ich das richtig?

thx & greetz
Danny

ps: ich find es echt klasse sich hier so mit Gleichgesinnten zu 
unterhalten. Auch wenns, man möge mir verzeihn, nicht mehr nur um die 
eigentliche Frage geht. Macht echt Spaß mit Euch :-)

von Stefan K. (_sk_)


Lesenswert?

wow, jetzt ist es mal wieder soweit, dass man kaum mit dem Lesen 
nachkommt. Hatten wir das nicht schonmal vor 2 Jahren ? :-))

Zu den Puffern:
Ich finde sie deshalb wichtig, weil ich mir mit ihnen gerade NICHT 
ständig Gedanken um das Timing machen muss. Ob eine Aktion 10us dauert, 
weil nur ein Port zu setzen ist oder mehrere ms, weil eine 
Graphikausgabe stattfindet, ist damit letztendlich egal.
Wichtig finde ich übrigens nicht nur den Eingangspuffer. Der 
Ausgangspuffer ist genausowichtig: wenn mhrere msgs gleichzeitig 
verschickt werden, soll main() nicht warten müssen, bis die letzte msg 
verschickt ist, sondern gleich nach dem Kopieren der msgs in den Puffer 
weiterarbeiten können.

Zu den CAN-Mask-Registern:
Ich selbst benutze diese auch nicht. Alle msgs landen bei mir erstmal im 
Puffer, überflüssige werden dann einfach weggeschmissen. Das fand ich 
sicherer, weil dann keine msgs "irgendwie" verschwinden können (wenn man 
bei der Programmierung der Mask-Register was falsch gemacht hat).
Ich finde sie nur dann eine Option, wenn man ansonsten Timingprobleme 
befürchtet (sollte man "eigendlich" bei einem Hausbus aber nicht 
bekommen).

Zum Scheduler:
Auf meiner Hardware laufen mehrere virtuelle "Module", die für einzelne 
Funktionen zuständig sind, z.B. das Einlesen eines Tasters oder das 
Schalten eines Relais. Jedes dieser Module besitzt 3 Funktionen:
on_init()
on_timer()
on_msg(msg)
Wenn eine msg eintrifft, prüft der Scheduler, für welches Modul diese 
ist und leitet sie SOFORT an das Modul weiter - an die on_msg(msg) 
Funktion.
Zusätzlich prüft der Scheduler im x-ms-Raster, ob der Timer abgelaufen 
ist und ruft dann die zyklische Funktion on_timer() der Module auf.

Das hat folgenden Vorteil: Jede msg wird sofort verarbeitet. 
Gleichzeitig gibt es für jedes Modul ein festes Zeitraster.
Die Programme für die Einzelfunktionen werden dadurch sehr einfach, und 
können ziemlich unabhängig vom Restprogramm geschrieben werden.

Viele Grüße, Stefan

von Stefan K. (_sk_)


Lesenswert?

Admin:
Liest hier einer der Admins mit, der diesen Thread nach Hausbus 
verschieben könnte?
Da ist er deutlich besser aufgehoben

Viele Grüße, Stefan

von Danny P. (Gast)


Lesenswert?

@Stefan: ja sorry, ich wollte es ja eigentlich allgemein halten weils 
nur um die Bearbeitug des MCP2515 gehen sollte... und wieder 
abgeschweift :-D

> Wenn eine msg eintrifft, prüft der Scheduler, für welches Modul diese
> ist und leitet sie SOFORT an das Modul weiter - an die on_msg(msg)
> Funktion.

mit "msg eintrifft" meinst du das sie im selbst gebastelten SRAM-Puffer 
liegt? also das Auslesen des MCP und übertragen in den SRAM-Puffer ist 
in diesem Beispiel schon geschehen?

würde bedeuten:
- main brummt vor sich hin
- Interrupt vom MCP wird erzeugt, kurz per ISR abgefragt und  z.B. ein 
Rx0-Flag in einer Globalvariable des ATMega gesetzt
- diese wird im main laufend gepollt und wenns da is wird das 
MCP-Rx0-Register ausgelesen, in den ATMega-Puffer geschrieben und beide 
(MCP- und ATMega-) Flags rückgesetzt
- nun merkt die main durch n anderes Flag das in den Puffern ne Msg. 
liegt und überprüft diese, sollte sie von Belang sein wird die 
zuständige Funktion aufgerufen

Hab ich das so richtig verstande? Ich hab schon so oft versucht dein 
C-Code zu verstehen :-) (danke nochmal für die Veröffetlichung) immer 
gescheitert - aber nicht an Dir sondern wegen fehlenden C-Kenntnissen...

thx & greetz
Danny

von Stefan K. (_sk_)


Lesenswert?

@Danny:
Nein:
Wenn ein IR vom MCP2515 kommt, lese ich die CAN-msg sofort (im IR) in 
einen msg-Puffer aus. Der wird wie ein Fifo verwaltet.

In main() frage ich dann nur noch, ob im Puffer eine msg vorhanden ist.

Nur das Flag <CAN-msg received> aus dem MCP2515 zu holen, bringt Dir 
nicht viel. Dann kannst Du den MCP2515 auch fast komplett ohne IR 
pollen.

Wenn Du dagegen das msg-holen komplett in den MCP2515-Interrupttreiber 
verlegst, dann wird der Zugriff auf die msgs aus dem Hauptprogramm 
einfacher und timing-unkritischer.

>Hab ich das so richtig verstande? Ich hab schon so oft versucht dein
>C-Code zu verstehen :-) (danke nochmal für die Veröffetlichung) immer
>gescheitert - aber nicht an Dir sondern wegen fehlenden C-Kenntnissen...

Sehr gerne :-)
Ich bin leider immer noch nicht ganz fertig, im Moment schreibe ich 
gerade die letzten Änderungen am Bootloader. Sobal alles einigermassen 
vollständig und aufgeräumt ist, will ich es nochmal veröffentlichen 
(gpl). Naja, zumindest der Bootloader wird nicht mehr lange dauern ...

Viele Grüße, Stefan

von Philipp C. (ba4_philipp)


Lesenswert?

Wie arbeitet ihr den Puffer eigentlich ab?

Wenn man bei jedem Aufruf der main(9 nur eine Msg holt, dann wächst 
evtl. der Puffer bis zum Überlauf und der Controller langweilt sich 
trotzdem.

Wenn man den kompletten Puffer jedesmal leer arbeitet, dann kann es doch 
wieder passieren, dass man die main() nicht in dem Zeitschlitz zuende 
bekommt?

Oder seh ich da noch etwas völlig falsch?

Vielen Dank
Gruß Philipp

von Danny P. (Gast)


Lesenswert?

@Philipp: nein vom Grundsatz siehst Du es richtig.
Aber genau das ist ja der Punkt den ich schon mehrfach versucht habe zu 
verdeutlichen:

Es geht mir nicht darum dauerhaft dieses hohe Message-Aufkommen auf 
dem Bus bewältigen zu können. Dies Aufkommen wird schon auf Grund der 
Anwendung nicht erreicht und wenns n Dauerzustand wäre hast du Recht muß 
alles im Zeitraum bis zur nächsten CAN-Message abgefrühstückt sein.

Es geht mir lediglich darum einen Moment mit hohen Aufkommen ohne 
Probleme verarbeiten zu können.
Nur um für den Fall gerüstet zu sein das mal drei bis fünf CAN_Messages 
direkt aufeinander folgen. Kommt vermutlich sehr selten vor, ist aber 
nicht auszuschliessen... (zum Beispiel parametrieren oder flashen per 
CAN, laden von Logfiles und gleichzeitig drückt jemand nen Schalter)

greetz
Danny

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.