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
Vielleicht bringt es dir was, mal unter dem Stichwort "CANopen" zu suchen.
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
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).
@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
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
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
@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
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.
PS: Filter im CAN-Controller habe ich auch noch nie benutzt.
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
> 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...
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
@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
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... :)
@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
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.
@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. ;-)
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?
@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
> 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.
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?
@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 :-)
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
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
@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
@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
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
@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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.