Forum: Mikrocontroller und Digitale Elektronik Erstes größeres Projekt - Multitasking Konzept


von Jan K. (jan_k)


Lesenswert?

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

von Mehmet K. (mkmk)


Lesenswert?

Never touch a running system.

von Jan K. (jan_k)


Lesenswert?

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.

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

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

von Jonas B. (jibi)


Lesenswert?

>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

von Mehmet K. (mkmk)


Lesenswert?

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
von Jonas B. (jibi)


Lesenswert?

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

von Jan K. (jan_k)


Lesenswert?

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
von Falk B. (falk)


Lesenswert?

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

von Jan K. (jan_k)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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

von Gebhard R. (Firma: Raich Gerätebau & Entwicklung) (geb)


Lesenswert?

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

von Mehmet K. (mkmk)


Lesenswert?

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 :)

von Ulrich (Gast)


Lesenswert?

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.

von Stefanus (Gast)


Lesenswert?

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

von Conny G. (conny_g)


Lesenswert?

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
von Jan K. (jan_k)


Lesenswert?

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

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


Lesenswert?

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.

von Wolfgang S. (wsm)


Lesenswert?

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.

von Lobba (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ 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

von Jan K. (jan_k)


Lesenswert?

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.

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


Lesenswert?

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
von MarcVonWindscooting (Gast)


Lesenswert?

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!

von D. V. (mazze69)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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.

von MarcVonWindscooting (Gast)


Lesenswert?

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.

von Stefanus (Gast)


Lesenswert?

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

von Stefanus (Gast)


Lesenswert?

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

von X2 (Gast)


Lesenswert?

MarcVonWindscooting schrieb:
> Und Vorsicht bei C-Bitfeldern - besser nicht benutzen im Zusammenhang
> mit Synchronisationsmechanismen.

Kannst du das mal erleutern bzw. nen Link dazu posten?

von MarcVonWindscooting (Gast)


Lesenswert?

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

von X2 (Gast)


Lesenswert?

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