Hi Leute, ich muss mich mal wieder an euch wenden :) Vorab: den Artikel Multitasking kenne ich, ebenfalls die Timer und Interrupt Artikel. Ich verwende aber einen stm32 und muss daher die Routinen etwas anpassen (ich verwende die aber sowieso nicht komplett, sondern versuche mir etwas daraus zusammenzubauen). Folgende Situation: Ein stm32f103 @ 56Mhz hat einige Aufgaben zu erfüllen: - UART bedienen, read+write, unter Umständen nahezu kontinuierlich - jede Millisekunde soll der ADC 5 Channels sampeln, das mehrmals und dann mitteln, um kurzfristige jitter auszugleichen - alle 10 Millisekunden sollen die dann 10 Messwerte pro Kanal gefiltert und dezimiert werden (1kHz -> 50Hz digitaler Tiefpass -> 100 Hz sampeln) - das selbe soll für einen per SPI angebundenen Sensor passieren - ebenfalls nach 10ms muss eine relativ komplexe Berechnung ausgeführt werden. Natürlich innerhalb dieser 10ms, inklusive Filterung + Dezimation So, das sind die Anforderungen. Das Programm läuft bereits, ich möchte es aber wartungsfreundlicher und besser erweiterbar machen, deswegen soll es angepasst werden. Ich darf leider den kompletten Quelltext nicht zeigen, ich glaube auch, dass dieser momentan viel zu unübersichtlich ist, den guckt sich eh niemand an;) Das Programm ist nämlich komplett aus verschiedenen Teilprogrammen zusammengewürfelt. Folgende Überlegungen habe ich und wurden teils bereits umgesetzt: Der mainloop ruft im Round Robin Verfahren einzelne Tasks auf, die nicht extrem zeitkritisch sind. Also z.B. werden alle 10ms tasks (filtern, dezimieren, Berechnungen) hier angeworfen. Dafür habe ich die großen Aufgaben wie in http://www.mikrocontroller.net/attachment/111803/MdkM.c vorgeschlagen in einzelne Tasks mit jeweils einer State Machine pro task unterteilt. Standardmässig laufen diese im Zustand 'idle', der prüft, ob der dazugehörige Timer abgelaufen ist und wechselt dann in den Zustand 'do_it'. Höherpriorisierte Tasks werden direkt in der ISR des timers angeworfen. Darunter fällt das Abfragen des Sensors über SPI jede Millisekunde (875 kHz SPI Takt, 4*16 Bit werden geholt). Dieser Prozess wird blockierend betrieben, ich warte also bis die paar Bytes da sind. Ich dachte mir, dass auf Grund des hohen SPI Taktes dies kein besonderes Problem sein würde (habe ich hier schon häufiger gesehen), ist diese Annahme falsch und ich sollte das ändern? Ebenfalls in der ISR wird der ADC eingeschaltet und die 5 Kanäle N-Mal gesampelt. Das mache ich per DMA, indem ich die Anzahl der zu übertragenden Bytes einstelle und dann den ADC anschalte. In der DMA ISR wird der ADC und der DMA Kanal deaktiviert und der (DMA-)Puffer auf den Anfang zurück gesetzt. Gleichzeitig werden die 5*N gepufferten Werte gemittelt und in einen anderen Puffer geschrieben. Die Uart läuft ebenfalls über DMA und wird momentan per main loop auf eine flag gepollt, die in der DMA ISR für die UART gesetzt wird. Dann wird geparst. Das funktioniert auch im Moment einwandfrei, vermutlich integriere ich in diesem task aber ebenfalls eine kleine state machine, um alles einheitlich zu haben. Soweit die Ideen, die größtenteils umgesetzt, aber noch nicht getestet worden sind. Seht ihr bis hier hin bereits größere Probleme? z.B. die vielen verschiedenen Interrupts, gepaart mit dem Mini-Scheduler? Meine Fragen sind aber nun: Ich muss warten, bis der ADC und SPI 10 Mal gesampelt haben, bis ich die weiterverarbeitenden Tasks anschmeissen kann. Angenommen ich habe einen "Filter-Task". Dieser wird alle 10ms getriggert und muss warten, bis die Daten ebenfalls 10 Mal gesampelt wurden. Je nachdem, wie lange nämlich der DMA Transfer des ADC dauert, kann sich das überschneiden. Die SPI macht kein Problem, da die (noch?) blockierend direkt in der ISR läuft. Dafür habe ich einen 'WAIT' state eingeführt, der anspringt, wenn der task eigentlich dran sein soll, aber eben noch warten muss. Jetzt die große Frage: Wie finde ich heraus, ob er noch warten muss? Ich möchte eigentlich ungerne mehrere globale status-Flags einführen, die gesetzt werden, wenn z.B. der DMA fertig ist. Oder ich lasse halt mitzählen, wie oft der DMA Puffer ausgelesen worden ist. Das müsste ich aber auch per globaler Variable oder per get-Funktion machen (damit die Anzahl-Variable lokal bleibt). Wie würdet ihr das machen? Ich habe insbesondere Bedenken in Hinsicht atomic read/write, kann durch die status flags was zerschossen werden? Wie macht ihr das denn, wenn verschiedene Tasks aufeinander warten müssen, macht ihr das über einen zusätzlichen Zustand in der state-machine, z.B. "TASK_READY", der dann in dem anderen Task abgefragt wird? Ich weiß, es ist viel Text, bei Bedarf erkläre ich natürlich alles entweder detaillierter oder etwas oberflächiger, je nachdem ;) Vielen Dank!!
Ich möchte aber etwas dabei lernen, um weitere Projekte vernünftig aufziehen zu können, außerdem muss dieses Projekt unter Umständen auch noch etwas länger leben. Daher jetzt die Arbeit, in einem Jahr blickt keiner mehr durch.
Hmmm... Deine Beschreibung ist jetzt etwas dürftig. Ich nehme an, dass deine Multitasking-SW nicht preemtiv ist. Das macht Probleme, wenn ein "eiliger" Task auf das Ende eines anderen warten muss. Die Synchronisation erfolgt im allgemeinen immer über Semaphoren, was ja auch naheliegend ist. Mir stellt sich die Frage, warum du nicht ein richtiges RTOS wie RTX von Keil oder free RTOS einsetzt. Dort gibt es alle möglichen Hilfen zur Synchronisation, und die Dinger sind alle preemtiv. Grüsse
>Je nachdem, wie lange nämlich der DMA Transfer des ADC dauert, >kann sich das überschneiden. Double-Buffering ist dein Freund. Ein Buffer wird dabei gefüllt, aus einem anderen werden die Daten gefiltert, berechnet etc. Dann tauschen beide... Um es mal einfach zu erklären. Das entspannt das Timing. Gruß Jonas
Jan K. schrieb: > Ich möchte aber etwas dabei lernen Wenn Du bis anhin noch nie mit RTOS gearbeitet hast und der Bedarf dafür nicht zwingend notwendig ist: Schau dir mal Protothreads an: http://dunkels.com/adam/pt Benutze ich sehr oft, wenn ein RTOS nicht zwingend ist. Ansonsten verwende ich scmRtos. Weil es sehr einfach gehalten ist und ohne Klimmzüge mit C++ programmiert werden kann. http://scmrtos.sourceforge.net/ScmRTOS
:
Bearbeitet durch User
Und ich würde nur eine ISR benutzen, und lieber in dieser einen Zähler inkrementieren und wenn der 10 erreicht hat dann führst du den Teil der normalerweise im 10ms takt läuft aus. Dann können die beiden auch nicht durcheinander kommen... Gruß Jonas & Sorry Doppelpost
Hi, erstmal vielen Dank für eure Antworten! Ich versuche mal zu antworten: - Nein, das System ist nicht preemtive, sondern kooperativ, denke das ist einfacher für den Anfang und das Problem noch nicht komplex genug oder? - die "eiligen" tasks werden direkt in der ISR bearbeitet, die anderen können also unterbrochen werden. Diese eiligen Tasks haben keine state machine und sind kurz gehalten. - ich benutze nur eine timer ISR. Diese enhält die kleinste Zeiteinheit, hier 1ms. - aber es gibt halt noch ISRs für den DMA (ADC + UART) - ein "echtes" rtos scheint mir etwas heftig, zumal dort auch Einarbeitung nötig ist. - ich möchte warten, bis die 1ms tasks abgelaufen sind, damit ich (genau) 10 samples erhalte, ein Puffer Problem gibt es nicht, wenn man so will benutze ich einen Doppelpuffer. Der Inhalt des DMA Puffers wird am Ende der DMA ISR in einen weiteren Puffer kopiert, der kann dann weiter verwendet werden. - wie sieht eine vernünftige implementation eines semaphore in c aus? edit: bei der letzten Frage ziele ich konkret auf das atomare schreiben/lesen hinaus, z.B. per bit-banding beim stm32 Schöne Grüße und Danke!
:
Bearbeitet durch User
@Jan K. (jan_k) >- Nein, das System ist nicht preemtive, sondern kooperativ, denke das >ist einfacher für den Anfang und das Problem noch nicht komplex genug Ja. >- die "eiligen" tasks werden direkt in der ISR bearbeitet, die anderen >können also unterbrochen werden. Diese eiligen Tasks haben keine state >machine und sind kurz gehalten. OK. >- ich benutze nur eine timer ISR. Diese enhält die kleinste Zeiteinheit, >hier 1ms. Klingt OK. >- aber es gibt halt noch ISRs für den DMA (ADC + UART) Muss man auspassen. Wenn die ISR aber garantiert sehr kurz ist, wird es gehen. >- ein "echtes" rtos scheint mir etwas heftig, zumal dort auch >Einarbeitung nötig ist. Sehe ich auch so. >- ich möchte warten, bis die 1ms tasks abgelaufen sind, Wie warten? Ein Multitasking wartet nicht. Siehe Artikel Multitasking.
Danke für deine Antwort. Ich meine Warten nicht im sinne von 'delay(...)', sondern dann bleibt eben der task im zustand 'WAITING', wie oben beschrieben. Nicht ok?
@ Jan K. (jan_k) >Darunter fällt das Abfragen des Sensors über SPI jede Millisekunde (875 >kHz SPI Takt, 4*16 Bit werden geholt). Dieser Prozess wird blockierend >betrieben, ich warte also bis die paar Bytes da sind. Ich dachte mir, >dass auf Grund des hohen SPI Taktes 875 kHz hoher SPI Takt? ;-) Da fehlt wohl ne Null an Ende. egal, für die 36us ist das voll OK. > dies kein besonderes Problem sein Nein. >würde (habe ich hier schon häufiger gesehen), ist diese Annahme falsch >und ich sollte das ändern? Nein. >Ebenfalls in der ISR wird der ADC eingeschaltet und die 5 Kanäle N-Mal >gesampelt. Das mache ich per DMA, indem ich die Anzahl der zu >übertragenden Bytes einstelle und dann den ADC anschalte. In der DMA ISR >wird der ADC und der DMA Kanal deaktiviert und der (DMA-)Puffer auf den >Anfang zurück gesetzt. Gleichzeitig werden die 5*N gepufferten Werte >gemittelt und in einen anderen Puffer geschrieben. Kann man machen, damit sollte die ISR der DMA dem 1ms Timer auch nicht in die Quere kommen. >Die Uart läuft ebenfalls über DMA und wird momentan per main loop auf >eine flag gepollt, die in der DMA ISR für die UART gesetzt wird. Dann >wird geparst. Klingt nicht so optimal, wenn das alles zeichenbasiert vom der ISR in die main loop tröpfet. Besser ist das Ganze Befehls/Paketorientiert + FIFO. >Das funktioniert auch im Moment einwandfrei, vermutlich >integriere ich in diesem task aber ebenfalls eine kleine state machine, >um alles einheitlich zu haben. Jo. >Wie macht ihr das denn, wenn verschiedene Tasks aufeinander warten >müssen, macht ihr das über einen zusätzlichen Zustand in der >state-machine, z.B. "TASK_READY", der dann in dem anderen Task abgefragt >wird? Globale Flags oder mutexte/semaphoren.
@Jan K. (jan_k) >Ich meine Warten nicht im sinne von 'delay(...)', sondern dann bleibt >eben der task im zustand 'WAITING', wie oben beschrieben. Nicht ok? Das ist OK.
>- ein "echtes" rtos scheint mir etwas heftig, zumal dort auch >Einarbeitung nötig ist. Ich hab ein ähnliches Projekt, vielleicht noch um einiges komplexer, durchgeführt, und das erstemal auf ein Keil RTX gesetzt. Muß sagen,es sind fast alle Dinge auf Anhieb geglückt, und ich hab mir viel, viel Arbeit erspart. Sicher, unbedingt braucht man das nicht, aber es macht halt vieles einfacher und übersichtlicher. Grüsse
Gebhard Raich schrieb: > (...) und ich hab mir viel, viel > Arbeit erspart. Sicher, unbedingt braucht man das nicht, aber es macht > halt vieles einfacher und übersichtlicher. Ich hoffe W.S. kriegt das nicht zu lesen :)
Die Aufgabe klingt nicht so, als ob man da wirklich Multitasking gebrauchen kann - so etwas wie RTOS auch nicht. Das passt mehr zu einer Aufteilung der Aufgaben in mehrere ISRs - wobei die ISR alle 10 ms von anderen ggf. unterbrochen werden muss.
EIn Prozess kann durch ein Flag signalisieren, dass er fertig ist: int datenVerfuegbar=0; Der Prozess setzt das Flag: datenVerfuegbar=1; Der Konsument der Daten fragt das Flag ab: if (datenVerfuegbar) {...} Wenn mehrere Prozesse sich Daten teilen (z.B. einen Buffer) aber nur einer gleichzeitig drauf zugreifen darf, dann geht das so: int pufferInUse=0; Wer den Puffer nutzen will, macht das: Alle Interrupts sperren if (pufferInUse) { puferInUse=1 Interrupts wieder freigeben Puffer benutzen pufferInUse=0 } else { Interrupts wieder freigeben ggf. den Fehler behandeln. }
Nimm einen Parallax Propeller, der hat einfach 8 Cores :-) Nicht ganz ernst gemeint, aber ich finde den grad cool. Der bringt konzeptionell Interrupts und Multithreading auf eine Ebene. Es scheint sich um ein kommerzielles Projekt zu handeln, dafür ist der Propeller evtl. zu sehr Nischenprodukt um den ernsthaft in Erwägung zu ziehen - die Fa. ist wohl Faktor 100 bis 1000 kleiner als die Atmels & Co.
:
Bearbeitet durch User
Ersteinmal frohes neues Jahr :) Ich zitiere mal wieder etwas bunt durch die Gegend: Falk Brunner schrieb: >>- aber es gibt halt noch ISRs für den DMA (ADC + UART) > > Muss man auspassen. Wenn die ISR aber garantiert sehr kurz ist, wird es > gehen. Naja was heißt sehr kurz? Im Prinzip nur der DMA angeworfen, die routine ist wirklich kurz, oder ein Puffer mit max 100 Byte kopiert (unsigned long). Das sollte nicht wirklich aufhalten? Falk Brunner schrieb: >>Die Uart läuft ebenfalls über DMA und wird momentan per main loop auf >>eine flag gepollt, die in der DMA ISR für die UART gesetzt wird. Dann >>wird geparst. > > Klingt nicht so optimal, wenn das alles zeichenbasiert vom der ISR in > die main loop tröpfet. Besser ist das Ganze Befehls/Paketorientiert + > FIFO. Es tröpfelt eigentlich nicht so in die main, sondern die flag signalisiert, dass ein komplettes Paket (10 Byte) empfangen wurde. Falk Brunner schrieb: >>Wie macht ihr das denn, wenn verschiedene Tasks aufeinander warten >>müssen, macht ihr das über einen zusätzlichen Zustand in der >>state-machine, z.B. "TASK_READY", der dann in dem anderen Task abgefragt >>wird? > > Globale Flags oder mutexte/semaphoren. Ich denke hier muss ich leider etwas genauer nachhaken: Globale flags würde ich eigentlich gerne vermeiden oder zumindest einschränken, Zwecks der Übersichtlichkeit. Egal? Wie sähe so ein semaphore aus? @Stefanus: Das läuft ja auch auf flags hinaus. Siehe oben ;) Ist es eigentlich üblich, Interrupts global zu deaktivieren bei einem STM32? Habe das noch nie gesehen, immer nur bei den AVR Beispielen. @ConnyG Ja, dir auch Prost ;) Vielen Dank Leute
Jan K. schrieb: > Ist es eigentlich üblich, Interrupts global zu deaktivieren bei einem > STM32? Habe das noch nie gesehen, immer nur bei den AVR Beispielen. Nein. Beim AVR gibt es das Read-Modify-Write Problem, das der STM32 nicht hat.
Schau mal unter: Beitrag "Zeitscheibe und genaue Sekunde erzeugen" Dort habe ich eine Zeitscheibe erklärt und benutzt. Und schau mal bei: http://avr.myluna.de/doku.php?id=de:lib-taskkernel Ist Multitasking für Atmegas W.
> So, das sind die Anforderungen. Das Programm läuft bereits, ich möchte > es aber wartungsfreundlicher und besser erweiterbar machen, deswegen > soll es angepasst werden. Ich würde ein handelsübliches Betriebssystem wie FREERTOS benutzen und mir das dazugehörige Buch besorgen, wo alles (auch die dahinterliegenden Prinzipien) ganz praktisch und genau erklärt werden. Dann hast du ein System das wartungsfreundlich, sehr gut dokumentiert und erweiterbar ist. Dann hast du auch etwas, was nicht so zusammengefrickelt ist... Das System ist ferner gut getestet, bietet diverse Möglichkeiten der sicheren Kommunikation zwischen Tasks und Vergeben von Prioritäten. Im Buch steht auch genau drinn, wie man mit Interrupts umzugehen hat, so dass alles seine Richtigkeit hat. ka ob dir das jetzt hilft...
@ Markus Müller (mmvisual) >Nein. Beim AVR gibt es das Read-Modify-Write Problem, das der STM32 >nicht hat. Jaja, die selektive Wahrnehmeung mal wieder. JEDER CPU hat nichtatomare Datenzugriffe, ist nur eine Frage der Datenbreite. Dein STM32 kann z.b. auf Arrayss nicht atomar zugeifen.
@ Jan K. (jan_k) >> Muss man auspassen. Wenn die ISR aber garantiert sehr kurz ist, wird es >> gehen. >Naja was heißt sehr kurz? Kurz genaug, um im worst case das Timing des 1ms Interrupts nicht zu sehr zu stören. > Im Prinzip nur der DMA angeworfen, die routine >ist wirklich kurz, oder ein Puffer mit max 100 Byte kopiert (unsigned >long). Das sollte nicht wirklich aufhalten? IM DMA Interupt wird ein 100 Byte Puffer kopiert? Wieso das? Oder kopiert die DMA die 100 Byte? >Es tröpfelt eigentlich nicht so in die main, sondern die flag >signalisiert, dass ein komplettes Paket (10 Byte) empfangen wurde. Das ist OK. >> Globale Flags oder mutexte/semaphoren. >Ich denke hier muss ich leider etwas genauer nachhaken: Globale flags >würde ich eigentlich gerne vermeiden oder zumindest einschränken, Zwecks >der Übersichtlichkeit. Egal? Wie sähe so ein semaphore aus? Keine Ahnung, dazu bin ich zu wenig Softwerker :-0
Falk Brunner schrieb: > @ Markus Müller (mmvisual) > >>Nein. Beim AVR gibt es das Read-Modify-Write Problem, das der STM32 >>nicht hat. > > Jaja, die selektive Wahrnehmeung mal wieder. JEDER CPU hat nichtatomare > Datenzugriffe, ist nur eine Frage der Datenbreite. Dein STM32 kann z.b. > auf Arrayss nicht atomar zugeifen. Ok. Woran liegt das, oder besser: wo kann ich das nachlesen? Für 32 Bit Variablen kann ich von atomarem Schreib- oder Lesezugriff ausgehen. Aber wie ist das bei kleineren Datentypen? Falk Brunner schrieb: >> Im Prinzip nur der DMA angeworfen, die routine >>ist wirklich kurz, oder ein Puffer mit max 100 Byte kopiert (unsigned >>long). Das sollte nicht wirklich aufhalten? > > IM DMA Interupt wird ein 100 Byte Puffer kopiert? Wieso das? > Oder kopiert die DMA die 100 Byte? Naja, die DMA kopiert z.B. 100 Byte (vermutlich etwas überschätzt) von dem ADC, diese werden dann in der ISR in einen zweiten Puffer aufaddiert und gemittelt, damit der ursprüngliche Puffer wieder frei wird. Der zweite Puffer ist deutlich kleiner. Würde ich das per flag oder so erst in der main machen, bräuchte ich tatsächlich zwei komplett identische DMA Puffer, die dann insgesamt deutlich mehr Platz beanspruchen würden. Falk Brunner schrieb: > Keine Ahnung, dazu bin ich zu wenig Softwerker :-0 Echt? Darf ich fragen, was du dann bist? Du gibst hier so viele hilfreiche Antworten :) Habe btw dieses Dokument gefunden: http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf Denke damit kann ich was anfangen, dort werden mutexe bzw semaphore beschrieben, ohne Interrupts zu sperren.
Bei mir tut der DMA 10x5 Wandlungen in ein RAM Bereich kopieren:
1 | void AD_ISR(void) |
2 | {
|
3 | SAVE_PC(); |
4 | if (!bADInBerechnung) |
5 | {
|
6 | iAD_Count_Int++; |
7 | s16 i; |
8 | for (i = 0; i < 50; i += 5) // Alle 5x10 Wandlungen zusammen rechnen |
9 | {
|
10 | iADE_DMA[0] += iAD[i]; |
11 | iADE_DMA[1] += iAD[i + 1]; |
12 | iADE_DMA[2] += iAD[i + 2]; |
13 | iADE_DMA[3] += iAD[i + 3]; |
14 | iADE_DMA[4] += iAD[i + 4]; |
15 | }
|
16 | }
|
17 | DMA_ClearITPendingBit(DMA1_IT_GL1 | DMA1_IT_TC1 | DMA1_IT_HT1 | DMA1_IT_TE1); |
18 | }
|
iAD[] ist der DMA Buffer der AD Wandlungen. Wenn "bADInBerechnung" gesetzt ist, so erfolgt gerade das Auslesen von iADE_DMA[x] und die letzten 50 Wandlungen gehen dann verloren (ist mir aber egal). Alle 10ms macht der Main-Task diese Berechnung. Damit der weiß wie viele Additionen da drin stehen wird der Zähler iAD_Count_Int incrementiert und der wird im Main-Task wieder gelöscht. Somit macht der viele Wandlungen und mittelt das ganze. Das klappt ganz gut und ist Threadsicher (ich nutze kein Betriebssystem).
:
Bearbeitet durch User
Also f"ur mich ist der gr"osste Unterschied zwischen Multitasking und ISR oder Polling die v"ollig andere Denkweise beim Umsetzen von Algorithmen. ISR/Polling: Zustandsmaschine Multitasking: (scheinbar) sequentieller Ablauf. Der sequentielle Ablauf ist (mindestens) f"ur mich einfacher zu verstehen, vor allem wenn man geschachtelte Schleifen hat. Ich hab erst im Herbst zum ersten mal sowas verwendet und kann mich dem Charme der (nicht preemtiven) Threads nicht erwehren :) Aber unter keinen Umst"anden w"urde ich mir dazu ein RTOS antun. Mein simpler Context-Switcher kostet 56Bytes ARM7-Thumb code und ich verwende das nur um Funktionen, die ich periodisch aufrufen und mit (explizitem Zustand versehen) m"usste sch"on sequentiell hinzuschreiben. Ja ich brauch separate Stacks (z.B. ein Array lokal in der main(), wenn man mal paar Leute erschrecken will) und Kooperation (yield() oder sowas). Synchronisieren muss man nicht, wenn nur kooperativ umgeschaltet wird. Nur evtl. gegen Interrupts nat"urlich - was ja vorher nicht anders war. Die Protothreads sind ein Werk das als Verbrechen an der Menschheit gewertet werden muss: da kann man nicht mal lokale Variablen verwenden! Am besten gleich noch symbolische Namen f"ur Variablen und Funktionen abschaffen, oder ?? Mannomann, Herr Dunkels, nur weil etwas machbar ist heisst das noch lange nicht, dass man's auch wirklich realisieren sollte. Das ist mit der "Uberwachung so, mit der Atomenergie, mit vielen Bereichen der Chemie, Gentechnik,..... und das ist AUCH in der Informatik so!! Der Jan wollte sein Programm "ubersichtlicher machen(= leichter zu verstehen/ver"andern). Und dazu gibt es meiner Meinung nach noch jede Menge Abstufungen zwischen einem 'richtigen' System und 'bare metal'. Wobei ich pers"onlich der 'bare metal' Seite n"aher stehe und das obwohl ich eigentlich ein Informatiker bin. Oder gerade deshalb? Ich denke meine Realisierung ist die minimale Abweichung von 'bare metal' ohne wichtige Konzepte zu brechen (siehe Protothreads). Oder zumindest nahe dran. Jan ich finde es klasse, dass Du nicht zufrieden bist mit einem funktionierenden Programm. Bin gespannt was du realisieren wirst. Lass es uns wissen!
Ich finde das gut, was der Jan da macht. Ohne lange rumzusülzen, ich habe damals auch innerhalb eines Programmes unter DOS sowas mit TP und der Unit Graph gemacht. Aber das Multitasking ist schwierig. Man denke nur an die 5 Philosophen am Eßtisch, die sich 5 Gabeln teilen sollen, obwohl man zum Spaghetti-Essen üblicherweise 2 Gabeln benötigt - zumindest in diesem Beispiel :-). Ausserdem kotzt es mich an, dass Hard- und Softwareentwickler auf bestehende "Operating Systems" zurückgreifen. Es gibt genug Beispiele, wo diese Denkweise Meßgeräte langsam machen. Ein hier immer wieder diskutiertes Beispiel sind die Billig-Scopes, die mit Linux laufen (sollen). Danke an Thomas R. aka tinman! Benutzt man ein bestehendes OS(wenn der µC passt), ist es natürlich einfacher, eine Hardware zum Laufen zu bekommen. Selbst hp (heute Agilent) bedient sich Windows für diverse Meßgeräte -> siehe auch Lebenserwartung diverser Windows Versionen. z.B.: Agilent HP 54815A, 500Mhz, 1GSa/s, four channels, type infinium: Simple, analog-like front panel with Windows® GUI Damit machen es sich die Entwickler sehr einfach. Sie brauchen nur das Frontend samt ADC zu entwickeln, eine Software-Schnittstelle via DLL zu basteln (was viele schon hier überfordert) und schwups, kann ein PC in einem Meßgerätegehäuse mind. das 20 bis 30-fache eines PCs bringen. Who really does like Windows? Nichtsdestotrotz werde ich diesen Fred mit Interesse weiterverfolgen.
@ Jan K. (jan_k) >> Datenzugriffe, ist nur eine Frage der Datenbreite. Dein STM32 kann z.b. >> auf Arrayss nicht atomar zugeifen. >Ok. Woran liegt das, oder besser: wo kann ich das nachlesen? Möglicherweise im Prozessordatenblatt oder Compilerhandbuch. > Für 32 Bit >Variablen kann ich von atomarem Schreib- oder Lesezugriff ausgehen. Das sollte man besser schwarz auf weiß haben, auch wenn die Wahrscheinlichkeit hoch ist. > Aber wie ist das bei kleineren Datentypen? Siehe oben. >> IM DMA Interupt wird ein 100 Byte Puffer kopiert? Wieso das? >> Oder kopiert die DMA die 100 Byte? >Naja, die DMA kopiert z.B. 100 Byte (vermutlich etwas überschätzt) von >dem ADC, diese werden dann in der ISR in einen zweiten Puffer aufaddiert >und gemittelt, damit der ursprüngliche Puffer wieder frei wird. Das klingt nicht unbedingt sinnvoll. > Der >zweite Puffer ist deutlich kleiner. Würde ich das per flag oder so erst >in der main machen, bräuchte ich tatsächlich zwei komplett identische >DMA Puffer, die dann insgesamt deutlich mehr Platz beanspruchen würden. Was bei 100 Byte und STM32 wohl kaum ins Gewicht fällt. Überleg doch einfach mal, wann und wie oft diese Addition und Mittelung gemacht werden muss und wie dringend das ist. Ich glaube nicht, dass diese Aktion einer ISR bedarf. >Echt? Darf ich fragen, was du dann bist? Du gibst hier so viele >hilfreiche Antworten :) Ich bin nur Hardwerker mit mittelmäßigen C-Kenntnissen.
Jan K. schrieb: > - jede Millisekunde soll der ADC 5 Channels sampeln, das mehrmals und > dann mitteln, um kurzfristige jitter auszugleichen Ich habe sowas auch schon gemacht, allerdings auf nem ATtiny861. Gefordert war eine ergonomische Anzeige, also nicht schneller als 5 Anzeigen/s. Da ich 7 Eingänge und den Mittelwert über 64 Samples brauchte, ergibt sich daraus ein Timerinterrupt von: 200ms / 64 / 7 = 446µs. Der Timerinterrupt liest dann die letzte Wandlung aus, addiert sie in den Summenspeicher, schaltet den Multiplexer einen Eingang weiter und startet die nächste Wandlung. Somit wird die ADC-Wandlungszeit nicht unnütz im Interrupt verwartet. Sind die 7 * 64 Interrupts um, werden die 7 Summen in ein Ergebnisarray kopiert und ein Flag gesetzt. Das Main kann nun die 7 Werte entsprechend umrechnen und anzeigen. Dann dreht es wieder Däumchen, bis das Flag wieder gesetzt ist, also die nächsten 200ms um sind. Durch die Aufteilung der 200ms in 64 gleiche Zeitabstände zum Samplen ergibt sich automatisch auch eine 50/60Hz Unterdrückung. Ein Multitasking ist also völlig unnötig.
Falk Brunner schrieb: >>> Datenzugriffe, ist nur eine Frage der Datenbreite. Dein STM32 kann z.b. >>> auf Arrayss nicht atomar zugeifen. > >>Ok. Woran liegt das, oder besser: wo kann ich das nachlesen? > > Möglicherweise im Prozessordatenblatt oder Compilerhandbuch. Und im ARM Architecture Reference Manual. Vieles ist schon durch den Core festgelegt. Und Vorsicht bei C-Bitfeldern - besser nicht benutzen im Zusammenhang mit Synchronisationsmechanismen. Und ebenso ist es ganz wichtig "uber das Schl"usselwort 'volatile' Bescheid zu wissen, sonst macht der Optimierer den falschen Code wirklich falsch ;-) @ D.V. : das ist Balsam auf meine Seele, was Du da schreibst! Wie lange braucht dein Handy eigentlich vom ersten Druck auf den Power-Knopf bis es **irgendeine** Best"atigung gibt, tats"achlich gewillt zu sein etwas zu tun? Power-Knopf => Piep, LED-Flash oder Vibration ist aber schon zu kompliziert als dass man es OHNE richtiges System hinbekommen k"onnte. Zumindest f"ur die professionellen Entwickler des 3. Jahrtausends.
@MarcVonWindScooting > Die Protothreads sind ein Werk das als Verbrechen an der Menschheit > gewertet werden muss: da kann man nicht mal lokale Variablen verwenden! Die Protohtreads wurden für den sehr speziellen Fall entwickelt, wo nur ganz wenig RAM in Größenordnung einiger Bytes (nicht Kilobytes) verfügbar ist. Die Nutzung von lokalen Variablen ist in Adams Konzept gar nicht vorgesehen. Wo genug RAM für lokale Variablen verfügbar ist, machen die Protothreads keinen Sinn. meines Wissens nach hat Adam das auch klar dokumentiert (in der alten Doku von µIP 1.0).
Ein schöner Aufsatz zu dem Thema Task-Umschaltung und wie man dabei mit Stack, registern und Variablem umgehen kann: www.controllersandpcs.de/lehrarchiv/pdfs/hs/ISR_01.pdf
MarcVonWindscooting schrieb: > Und Vorsicht bei C-Bitfeldern - besser nicht benutzen im Zusammenhang > mit Synchronisationsmechanismen. Kannst du das mal erleutern bzw. nen Link dazu posten?
X2 schrieb: > Kannst du das mal erleutern bzw. nen Link dazu posten? Das hat seine Ursache nur darin, dass die Sprache C "uber die Umsetzung von Bitfeldern in etwa so pr"azise Festlegungen trifft wie f"ur die Gr"osse der Datentypen int, char, usw. . Also nichts woran man sich festhalten k"onnte. Es ist also nicht klar, wo Wortgrenzen liegen und ob der Compiler nun 8-bit, 16bit oder 32bit Zugriffe auf ein paar bits eines solchen Feldes verwendet. Wer kann da noch sagen ob eine Zuweisung atomar sein wird? > Die Protohtreads wurden für den sehr speziellen Fall entwickelt, wo nur > ganz wenig RAM in Größenordnung einiger Bytes (nicht Kilobytes) Lokale Variable wurden f"ur den allgemeinen Fall entwickelt. Die lohnen sich GERADE eben bei wenig Stack, weil immer wieder der gleiche Bereich genutzt werden kann, im Gegensatz zu static oder globalen Variablen! Nee, beim besten Willen, ich bleib dabei: Protothreads = Blei-Tetraethyl der Informatik. Nur dass letzteres nicht so viele negative Eigenschaften hat...
MarcVonWindscooting schrieb: > X2 schrieb: >> Kannst du das mal erleutern bzw. nen Link dazu posten? > > Das hat seine Ursache nur darin, dass die Sprache C "uber die Umsetzung > von Bitfeldern in etwa so pr"azise Festlegungen trifft wie f"ur die > Gr"osse der Datentypen int, char, usw. . Also nichts woran man sich > festhalten k"onnte. > Es ist also nicht klar, wo Wortgrenzen liegen und ob der Compiler nun > 8-bit, 16bit oder 32bit Zugriffe auf ein paar bits eines solchen Feldes > verwendet. Wer kann da noch sagen ob eine Zuweisung atomar sein wird? Ok danke, hab gerade gesehen das sich deine Aussage allgemein auf C Bitfelder bezog und nichts mit der ARM Architektur zu tun hatte. Dachte du meinst das speziell im zusammenhang mit ARM.
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.