Hallo Leute,
ich programmiere schon eine Weile Microkontroller, bin der Meinung, dass
ich jetzt schon ein bisschen was gelernt habe (wenn auch bei leibe nicht
alles).
Während meiner zweiten Ausbildung lernten wir die ganze Grundlagen, also
das 1x1 der Microkontrollerprogrammierung.
Wie etwa dimmen über PWM, mittels ADC Spannungswerte auslesen, auf
verschiedene Art und Weisen Tasten entprellen, UART, mittels UART und
AT-Commands weitere Peripherie ansprechen wie etwa einen
Ethernet-Controller und damit ein kleines Netzwerk aufbauen,
LCD-Displays, usw, usf.
Zum auf die Sahnetorte-Hauen reicht es zwar noch nicht, aber ich habe
immerhin einigen an der Uni jetzt was voraus.
Alles Weitere müsste ja „nur” die Weiterführung bekannter Strategien
sein. Sehr salopp ausgedrückt.
Aber genau daran zweifel ich gerade.
Denn das, was ich oben angesprochen habe, könnte ich alles ohne Probleme
mit einem ATtiny13 ode ATtiny45 realisieren. Zusammen aber, brauche ich
einen größeren µC. Alleine schon wegen der Ports. Alles klar soweit.
Aber wie löst man das ganze, wenn man alles scheinbar gleichzeitig
ablaufen lassen will?
Ich bin gerade dabei das DOGL-Display anzusprechen. (Aktuell bin ich da
noch nicht weit.)
http://www.reichelt.de/DOG-LCD-Module/EA-DOGL128W-6/index.html?;ACTION=3;LA=444;GROUP=A5213;GROUPID=3007;ARTICLE=86713;START=0;SORT=artnr;OFFSET=16;SID=13TgBLLn8AAAIAAApYZKcc7facb4c1c94d9d088dd547b81ff243b
Das finde ich ein gutes Beispiel.
Ich denke, es ist normal, wenn ich folgendes mal ausprobieren möchte.
Ich möchte mit dem ADC analoge Werte einlesen, sie auf dem Display als
Graph darstellen, die LEDs dimmen und vll. später noch zusätzliche
Funktionen implementieren wie etwa Tasten für Settings nutzen.
Also wie man das ganze grundsätzlich umsetzt ist mir klar.
Wenn mein Display denn nun endlich laufen sollte, dann kann alles auch
noch rein sequenziell ablaufen.
Nach dem Init aller Geräte erst den ADC auslesen, das Displaybild
ausrechnen und das Display ansprechen. Dann wieder mit dem ADC beginnen.
Ich denke, das Timing des Displays lässt sich wieder mit „Takte zählen”
realisieren.
Aber was, wenn ich später Interrupts brauche und dieser mir zum Beispiel
dann das Displaytiming oder das Timing für den UART kaputt macht?
Wonach ich also im Grunde frage, gibt es eine Art Design-Pattern um
komplexere µC-Programme (jetzt werden sicher die einen oder anderen
lachen ;)) zu planen und zu programmieren?
Denn, selbst ohne Interrupts kann es vom Timing her schon schwierig
werden.
Ich ziehe mir mal folgendes an den Haaren herbei:
Ich möchte diese Werte nicht nur anzeigen, sondern auch über UART
schicken und Befehle über UART und den zu entprellenden Tasten
erhalten. Zusätzlich habe ich für das Display ja auch noch den
Touch-Screen den ich ja auch noch auslesen möchte.
Jetzt müsste ich mir doch langsam Gedanken machen, wie ich das ganze
ineinander verflechte, damit jedes Programmteil zur rechten Zeit, seinen
Teil der Rechenzeit abbekommt? Oder würde vielleicht mit diesem Vorhaben
der µC auch gar nicht erst an seine Leistungsgrenzen gebracht? Das habe
ich jetzt nicht nachgeprüft. Ich nutze im Moment einen ATmega32, habe
aber auch noch einen mega644 und mega1284 daliegen. Wie sieht es mit
denen aus?
Ab wann, abgesehen, davon, dass der Flash oder RAM nicht reicht, muss
ich auf einen größeren Kontroller wie etwa einen XMega oder gar einen
ARM setzen? Oder auch einen der anderen zahlreichen Familien?
Gibt es da Hausnummern, an die man sich orientieren kann?
Ich meine, oftmals, lässt sich das ausrechen. Wenn man etwa ein großes
Display anzusprechen hat und den Inhalt anzeigen muss, errechnen muss
und diesen vielleicht sogar noch gestreamt bekommt. Dann ist mir klar,
dass ich da schnell an meine Grenzen kommen und wo die liegen. Was aber,
wenn ich einige Aufgaben habe, die ich einzeln mit einem Kontroller
locker erledigen könnte, zusammen aber komplex werden?
Dabei sehe ich aber auch im Internet, wie Leute auf den Megas und diesem
Display Graphiken in Echtzeit(TM) errechnen und darstellen.
http://www.youtube.com/watch?v=kksbmkOKyO0http://www.youtube.com/watch?v=wohy1HPF-Fkhttp://www.youtube.com/watch?v=Ps4HbTnFMCchttp://www.youtube.com/watch?v=QsQdH37aVWUDas mit der Erwähnung der Liveberechnung,
habe ich jetzt nicht mehr gefunden.
____________________
So, jetzt habe ich mich wunderbar verloren.
Gibt es Design-Pattern für komplexere Programme?
(Eine Seite mit Design-Pattern generell wäre auch gut, aber
mikrocontroller.net bietet da schon viel.)
Wie skaliert man die Zielplattform für ein neues Projekt richtig?
Besonders wenn größer nicht unbedingt besser ist, alleine des Geldes
wegen.
Gruß und einen schönen Sonntag noch.
Ich hoffe, ihr hattet auch so einen schönen Sonnenaufgang wie ich. ;)
Fabian
Fabian Hoemcke schrieb:> Nach dem Init aller Geräte erst den ADC auslesen, das Displaybild> ausrechnen und das Display ansprechen. Dann wieder mit dem ADC beginnen.> Ich denke, das Timing des Displays lässt sich wieder mit „Takte zählen”> realisieren.>> Aber was, wenn ich später Interrupts brauche und dieser mir zum Beispiel> dann das Displaytiming oder das Timing für den UART kaputt macht?
Wenn du eine Hardware-UART nutzt, wird dieser sogar einen Interrupt
erzeugen können. ;) Software-Technisch müsste man mit einem
Timer-Interrupt arbeiten, um die im Taktfenster zu bleiben.
Laut Datenblatt hat das Display ein SPI-Interface - auch dabei wird das
Senden von eigener Hardware übernommen, und hinterher kann ein
Interrupts ausgelöst werden.
Hast du diese Dinge komplett in Software gemacht, oder die
Controller-Eigenen Hardware-Schnittstellen genutzt?
Ein Design-Pattern generell habe ich noch nicht gefunden, aber auch
nicht gesucht. Wenn es um ein neues Projekt geht: Stift und Zettel!
Funktionalität beibehalten, aber komprimieren. Welche Aktionen kann ich
mit einem Timer machen (entprellen, spart Hardware), welche MÜSSEN einen
Timer haben? ADC-Werte ermittel' ich immer im gleichen Abstand, bilde
den Mittelwert 64 Werten und setze ein Flag in "meinem" Statusregister.
Der Programmteil, der den Wert braucht, "entnimmt" sich den Wert und
setzt das Flag zurück. - Erst wenn das Flag wieder gesetzt ist, wird der
entprechende Programmteil wieder ausgeführt. (Stichwort: State-Machine
;))
Es ist zwar nicht alles, aber ich hoffe ein Anreiz für dich, zum
nachdenken.
Gruß,
Marcel
Marcel Papst schrieb:> Wenn du eine Hardware-UART nutzt, wird dieser sogar einen Interrupt> erzeugen können. ;) Software-Technisch müsste man mit einem> Timer-Interrupt arbeiten, um die im Taktfenster zu bleiben.> Laut Datenblatt hat das Display ein SPI-Interface - auch dabei wird das> Senden von eigener Hardware übernommen, und hinterher kann ein> Interrupts ausgelöst werden.> Hast du diese Dinge komplett in Software gemacht, oder die> Controller-Eigenen Hardware-Schnittstellen genutzt?
Danke Dir erstmal.
Si ganz genau kann ich Dir jetzt auf die letzte Frage nicht antworten.
Zu mindest nicht auf einmal.
Denn es waren ja meist einzelne Projekte und dass ich das letzte mal
eines davon umgesetzt habe ist schon eine Weile her. Denn in der Uni
hatten wir „nur” FPGAs. Auch was feines. Aber für zu hause eben zu
teuer.
Also den UART habe glaube ich immer in Hardware umgesetzt und dann in
polling- oder Interrupt-Mode (ich weiß es nicht mehr) dann nur noch die
Werte gelesen oder geschrieben.
PWN hatte ich immer in Software mit einen Timer gemacht. Overflow- und
CompareMatch- Interrupt. Aber ich weiß, dass es dafür auch spezielle
Channels gibt.
SPI-als HW-Lösung gibt es auch? Das hatte ich bisher überlesen.
Bisher nie genutzt, daher...
Außerdem scheinen das alle hier im Forum, was ich bis hetzt überflogen
habe, in SW zu lösen.
Wahrscheinlich so lange man experimentier.
Für den Rest vielen Dank.
Klingt sehr nach divide and conquer. ;)
Gruß
Fabian
Fabian Hoemcke schrieb:> SPI-als HW-Lösung gibt es auch? Das hatte ich bisher überlesen.> Bisher nie genutzt, daher...> Außerdem scheinen das alle hier im Forum, was ich bis hetzt überflogen> habe, in SW zu lösen.> Wahrscheinlich so lange man experimentier.
Ausschnitt des Datenblattes für den ATMEGA16, Seite 1
• Peripheral Features
– Two 8-bit Timer/Counters with Separate Prescalers and Compare Modes
– One 16-bit Timer/Counter with Separate Prescaler, Compare Mode, and
Capture
Mode
– Real Time Counter with Separate Oscillator
– Four PWM Channels
– 8-channel, 10-bit ADC
8 Single-ended Channels
7 Differential Channels in TQFP Package Only
2 Differential Channels with Programmable Gain at 1x, 10x, or 200x
– Byte-oriented Two-wire Serial Interface
– Programmable Serial USART
– Master/Slave SPI Serial Interface
– Programmable Watchdog Timer with Separate On-chip Oscillator
– On-chip Analog Comparator
4 PWM-Kanäle
Hardware-TWI (für I2C z.B.)
Hardware-USART (Serielle Schnittstelle für z. B. einen PC)
*Master/Slave SPI Schnittstelle*
Alles da, würde ich mal sagen. ;)
Gruß,
Marcel
> aber ich habe immerhin einigen an der Uni jetzt was voraus.> Aber genau daran zweifel ich gerade.
Eben.
Die an der Uni wissen bei ANDEREN Dingen eben mehr als du,
selbst wenn sie zufälligerweise DEINE Kenntnisse noch nicht
erarbeitet haben.
Das, was du Design Pattern nennst, wird an der Uni durchaus
gelehrt (heisst bloss alle 10 Jahre anders), aber das ist
ja nur der DERZEITIGE Stand der Technik (und auch nur ein
kleiner Ausschnitt davon, den Rest findet man per Google :-).
Was die Uni vor allem lehren sollte, ist DARÜBERHINAUSGEHENDES,
wie man also "die Design Vorlagen der Zukunft" entwickeln kann,
und sie liefert dazu das Handwerkszeug (Komplexitätsberechnung).
Das fehlt dir vollkommen.
> Gibt es Design-Pattern für komplexere Programme?
Es gibt mehrere.
Und man muß das möglichst gut passende auswählen, damit
die Lösung gut wird (wir fordern nicht mal 'bestmöglich',
sondern es reicht schon wenn es funktioniert und in den
uC hineinpasst).
Das allgemeine Design Pattern lautet erst mal: Es gibt
eine im Prinzip endlos rumlaufende Hauptschleife, in der
anstehende Aufgaben in kleinen Häppchen abgearbeitet werden
(Event-Loop).
Es KANN nötig sein, Ereignisse (per Interrupt) schnell zu
registieren, aber wenn deren Bearbeitung länger dauert,
ist nur ein Merker zu setzen, damit die Hauptschleife die
Erledigung der Arbeit übernimmt, wenn Zeit ist (Message
Bearbeitung).
Bei lange abzuarbeitenden Aufgaben, so lange daß die
Reaktionsgeschwindigkeit der Event Loop gestört wird,
kann es nötig sein, diese als Prozess niedriger Priorität
aufzufassen, was kooperativ oder preemptiv verwaltet
werden kann.
Dieses Design Pattern reicht recht weit, vom winzigen
superschnellen Microcontroller bis zum Windows-PC, aber
natürlich ist es auch so schwammig, daß immer Möglichkeit
besteht, es besser oder schlechter umzusetzen.
Daher gibt es eine unverrückbare Messlatte nach der die
Qualität der Gesamtlösung bewertet und gemessen werden
kann:
Alles, was an Prozessorinstruktionen ausgeführt wird,
die weggelassen werden könnten ohne die äussere Funktion
des Programms zu beeinträchtigen, war überflüssig und
damit schlecht.
Optimal ist es, wenn auch mit anderen Algorithmen und
Strukturen nichts mehr weglassbar ist, dann ist es die
vielgesuchte "elegante Lösung".
"Weglassen" bezieht sich dabei nicht unbedingt auf die
Anzahl der Prozessorinstruktionen, 3 Schiebebefehle
können besser sein als 1 Divisionsbefehl wenn der länger
dauert, mehr Strom verbraucht, mehr Resourcen belegt, die
Schiebebefehle bewirken aber weniger Mikrocodeinstruktionen,
so daß trotzdem die Instruktionsanzahl die Messlatte ist.
Timing Sachen können schon problematisch sein.
Man muss da ein wenig unterscheiden.
Ist das Timing ein Mindesttiming und spielt es keine Rolle wenn ein Puls
mal etwas länger wird. Oder muss es ganz exakt eingehalten werden.
Oft ist es so, dass bei synchroner Ansteuerung von externen Komponenten
das Timing nicht so wahsinnig kritisch ist. Die Synchronisierung erfolgt
ja zb durch Pulsflanken und ob der Level ein paar µs länger als
mindestens notwendig ist, ist nicht so kritisch.
In diesem Hintergrund: Kurze Zeiten, ein paar µs, kann man dann durchaus
zb mittels _delay_us machen und wenn sich diese Zeit durch einen
Interrupt etwas verlängert, dann ist das oftmals nicht kritisch. Falls
doch, dann macht man eben eine Interruptsperre rundum.
Längere Wartezeiten mittels _delay_ms sind hingegen oftmals tödlich. Ein
derartiges 'Design' taugt oft nicht viel. Sobald mehrere Komponenten im
Spiel sind, die nach Möglichkeit alle mehr oder weniger gleichzeitig
angesteuert werden sollen, gibt es mit hoher Wahrscheinlichkeit Ärger
und das Design ist nicht zu retten. Hier führt der Ausweg praktisch
immer über Timer. Damit einher geht ein Umdenken. Weg von "Ich benötige
eine Wartezeit" und hin zu "Es gibt einen Basistakt, was ist jetzt, bei
erneutem Auftreten das Basistaktes, zu tun". Im Grunde also schon eine
Form von Event-basiertem Programmieren.
Als Design-Pattern hat sich die State-Maschine bewährt, mit der man eine
große Klasse von Aufgaben relativ problemlos in den Griff kriegt und die
auch das Einbinden von "Events" sehr einfach ermöglicht. Im Einzelfall
kann es natürlich sein, dass eine State-Maschine etwas abgespeckt und
vereinfacht bzw. implizit vorhanden ist und daher als solche nicht
gleich erkennbar ist, sie aber nichts desto trotz vorhanden ist. Auch
ist es oft problemlos möglich, mehrere voneinander unabhängige
State-Maschinen in einem Programm zu betreiben und so das Multitasking
zu erreichen.
Das wäre also das 'Pattern', das ich jedem auf einem µC ans Herz legen
würde:
Es gibt so etwas wie 'Zustände' für einzelne Komponenten und diese
Zustände durchlaufen abhängig von Ereignissen gewisse Werte und lösen
dabei Aktionen aus.
Vielen Dank.
MaWin schrieb:> Die an der Uni wissen bei ANDEREN Dingen eben mehr als du,> selbst wenn sie zufälligerweise DEINE Kenntnisse noch nicht> erarbeitet haben.
Arroganz steht mir nicht gut zu Gesicht.
Es war auch überhaupt nicht so gemeint. Es sollte eher eine Orientierung
sein, wo ich gerade stehe. Klar dass es in anderen Fächern Leute gibt,
die besser sind als ich. Ich wollte noch nicht mal damit gesagt haben,
dass ich im Thema µC der Beste bin. Stimmt meines Wissens auch gar
nicht.
Sondern nur, dass ich etwas mehr drauf habe, als man von uns erwartet.
Was deine anderen Punkte angeht, vielen Dank.
Mit solch einer Antwort habe ich jetzt nicht gerechnet, aber ich denke,
sie bringt mich ein Stück weiter.
Jedoch weiß ich im Moment auch nichts mehr darauf zu antworten.
Wie gesagt:
MaWin schrieb:> (Komplexitätsberechnung).>> Das fehlt dir vollkommen.
Gruß
Fabian
Karl Heinz Buchegger schrieb:> In diesem Hintergrund: Kurze Zeiten, ein paar µs, kann man dann durchaus> zb mittels _delay_us machen und wenn sich diese Zeit durch einen> Interrupt etwas verlängert, dann ist das oftmals nicht kritisch. Falls> doch, dann macht man eben eine Interruptsperre rundum.
Und das macht man wie?
_
Wie betreibt man denn zwei voneinander unabhängige State-Machines in
einem µC die sich nicht gegenseitig beeinflussen?
Fabian Hoemcke schrieb:> Karl Heinz Buchegger schrieb:>> In diesem Hintergrund: Kurze Zeiten, ein paar µs, kann man dann durchaus>> zb mittels _delay_us machen und wenn sich diese Zeit durch einen>> Interrupt etwas verlängert, dann ist das oftmals nicht kritisch. Falls>> doch, dann macht man eben eine Interruptsperre rundum.>> Und das macht man wie?
Interrupts abschalten/disablen
_delay_us
Interrupts wieder einschalten/enablen
> Wie betreibt man denn zwei voneinander unabhängige State-Machines in> einem µC die sich nicht gegenseitig beeinflussen?
jede als eigene Funktion, die die State-Auswertung und damit zugehörigen
Aktionen ausführt
Und die dann wechselseitig aufrufen.
(OK, bei kurzen und einfachen Statemaschines geht das auch direkt in der
Hauptschleife und eigene Funktionen sind nicht notwendig)
zb
1
uint8_t Led1On;
2
uint8_t Led2On;
3
4
...
5
6
7
while( 1 ) {
8
9
if( get_key_press( 1 << TASTE_LINKS ) {
10
if( IsLed1On() ) {
11
Led1_Off();
12
Led1On = 0;
13
}
14
else {
15
Led1_On();
16
Led1On = 1;
17
}
18
}
19
20
if( get_key_press( 1 << TASTE_RECHTS ) {
21
if( IsLed2On() ) {
22
Led2_Off();
23
Led2On = 0;
24
}
25
else {
26
Led2_On();
27
Led2On = 0;
28
}
29
}
30
}
sind im Prinzip schon 2 State-Maschines. Alle Zutaten sind vorhanden:
Jede State-Maschine hat eine Zustandsvariable (die in dem Fall vermerkt,
in welchem Zustand die jeweilige LED ist). Abhängig vom Zustand wird bei
Auftreten eines Erignisses eine Aktion ausgeführt und die jeweilge
Maschine wechselt in einen anderen Zustand.
Die beiden State-Maschines sind nicht in der klassischen Form
ausgeführt, weil in beiden Maschinen der jeweilige Zustandswechsel für
beide Zustände durch gleiche Aktionen eingeleitet wird (Tastendruck),
aber es ist eine relativ triviale Umformung von der klassischen
State-Maschine zu dieser Form hier. Würde zb ein Blinktakt zum Blinken
der LED ins Spiel kommen, dann würde diese Umformung wieder anders
aussehen, programmiert man das alles allerdings als klassische
Statemschine einfach runter, dann kann erst mal nicht viel schief gehen.
1
ZustandA // Zustand der Statemaschine A
2
ZustandB // Zustand der Statemaschine B
3
4
5
while( 1 ) {
6
case (ZustandA) {
7
8
switch Zustand1:
9
mach möglicherweise was
10
if( Ereignis )
11
mach den Übergang - Aktionen ausführen
12
ZustandA = neuer Zustand
13
break;
14
15
switch Zustand2:
16
mach möglicherweise was
17
if( Ereignis )
18
mach den Übergang - Aktionen ausführen
19
ZustandA = neuer Zustand
20
break;
21
22
...
23
}
24
25
case (ZustandB) {
26
....
27
}
> Wie betreibt man denn zwei voneinander unabhängige State-Machines in> einem µC die sich nicht gegenseitig beeinflussen?
Das ist die falsche Fragestellung.
Denn komplett 'gegenseitig nicht beeinflussen' geht nicht. Zumindest ein
bischen Rechenzeit klaut eine Maschine immer der anderen. Die Frage ist
dann eben: Wieviel ist das, und macht das etwas aus?
Fabian Hoemcke schrieb:> Und das macht man wie?
Die Interruptsperre? Ganz einfach (Beispiel in C für AVR):
1
uint8_tSREG_cpy=SREG;
2
cli();
3
4
// ungestörter Code
5
6
SREG=SREG_cpy;
Das man nicht mit sei() arbeitet und statt dessen das SREG sichert, hat
den Grund das sonst unter Umständen das I-Bit durch den sei()-Befehl
setzt, obwohl es zuvor (vor cli()) gar nicht gesetzt war.
LG :)
PS: Interessanter Thread und vielen Dank für deine Fragestellung. Ich
hoffe hier auch noch etwas (mehr) draus lernen zu können.
ist aber überflüssig oder?
Geht es hier mehr ums Prinzip? Also dass der eigene Zustand irgendwo
geschrieben steht?
Karl Heinz Buchegger schrieb:> Das ist die falsche Fragestellung.> Denn komplett 'gegenseitig nicht beeinflussen' geht nicht.
Schade, das hatte ich gemeint. Aber deine Antworten vorher gaben ja
genug auskunft.
J. W. schrieb:> PS: Interessanter Thread und vielen Dank für deine Fragestellung.
Hey, danke!
Ich überlege mir zwar immer zweimal (bis auf wenige Ausnahme) bevor ich
was schreibe, dennoch passiert es mir zu oft, dass ich mir Fragen hätte
sparen können.
Deshalb, danke!
Ich möchte gerne meine Stimme dafür abgeben diesen Thread oder den
Inhalt der Beiträge von MaWin und Karl-Heinz aufgearbeitet in die
Artikelsammlung einzubauen.
In der Hoffnung, dass es da jemand findet.
Nach vielen Jahren Mikrocontrollerprogrammierung meinerseits kann ich
bestätigen, dass das der praxisnahe und auch in der Realität
angetroffene Weg ist.
> ist aber überflüssig oder?> Geht es hier mehr ums Prinzip? Also dass der eigene Zustand irgendwo> geschrieben steht?
richtig.
Ich dachte bei mir: Wenn ich das jetzt auch noch implizit mache, dann
entferne ich mich formal schon so weit von der SM, dass sie eventuell
nicht mehr erkennbar ist.
>> Denn komplett 'gegenseitig nicht beeinflussen' geht nicht.>> Schade, das hatte ich gemeint.
Na ja.
Das ist aber auch logisch. Eine einzige µC kann nun mal nicht x Sachen
absolut gleichzeitig machen, sondern immer nur hintereinander.
Eine weitere Ebene kommt hinzu, wenn es Resourcen gibt, die übergreifend
von allen gemeinsam genutzt werden müssen. Zb LCD, zb UART.
Die müssen dann koordiniert werden und auch das hat Auswirkungen auf die
Einzel-SM
Karl Heinz Buchegger schrieb:>>> Denn komplett 'gegenseitig nicht beeinflussen' geht nicht.>>>> Schade, das hatte ich gemeint.>> Na ja.> Das ist aber auch logisch. Eine einzige µC kann nun mal nicht x Sachen> absolut gleichzeitig machen, sondern immer nur hintereinander.
Ja, aber ich hatte gedacht, oder eher gehofft, dass es irgendwo ein
Feature gibt, dass ich noch nicht kenne. Wie etwa Hardware-Threading
oder so! (Habe ich mir jetzt ausgedacht).
Denn irgendwie muss ja der Kontroller auch die Interruptlösung
implementiert haben. Was zu mindest der erste Schritt hin zur
Nebenläufigkeit ist.
@Simon K. @Oliver J. Danke!
@Karl Heinz Buchegger @All
Unfassbar was Ihr alles wisst!
_
Ohne das Forum hier, wäre Deutschland wohl nicht das Ingenieursland, was
es ist.
^^
Fabian Hoemcke schrieb:> Karl Heinz Buchegger schrieb:>>>> Denn komplett 'gegenseitig nicht beeinflussen' geht nicht.>>>>>> Schade, das hatte ich gemeint.>>>> Na ja.>> Das ist aber auch logisch. Eine einzige µC kann nun mal nicht x Sachen>> absolut gleichzeitig machen, sondern immer nur hintereinander.>> Ja, aber ich hatte gedacht, oder eher gehofft, dass es irgendwo ein> Feature gibt, dass ich noch nicht kenne. Wie etwa Hardware-Threading> oder so! (Habe ich mir jetzt ausgedacht).
Hehe! :-) Das, was du als bereits vorhandenes Feature suchst ist der
Knackpunkt, an dem gute und schlechte Programmierer sich unterscheiden.
Das Schreiben von großer Software, die wartbar bleiben soll und dabei
eine gute Performance erzielt ist eben der Punkt, um dem es bei der
Programmierung geht.
Und wie MaWin schon sagt, gibt es hier kein "globales Konzept", sondern
man muss das Konzept eben passend zur Anwendung wählen.
Herausstellen welche Programmabschnitte eine hohe Priorität haben und
das bei der Programmierung eben so vorsehen.
Simon K. schrieb:> Hehe! :-) Das, was du als bereits vorhandenes Feature suchst ist der> Knackpunkt, an dem gute und schlechte Programmierer sich unterscheiden.> Das Schreiben von großer Software, die wartbar bleiben soll und dabei> eine gute Performance erzielt ist eben der Punkt, um dem es bei der> Programmierung geht.
Das ist klar.
Große Programme in kleine µCs packen, die dann noch schnell und
fehlerfrei funktionieren.
Je enger und kritischer es wird, um so eher wird man gezwungen in ASM zu
schreiben und Clocks zuzählen. ^^
Da denke ich an die sagenumwobene Geeks die bei den Demos das letzte aus
dem C64 und Co rausholen.
Aber soweit bin ich laaaange noch nicht.
> Je enger und kritischer es wird, um so eher wird man gezwungen in> ASM zu schreiben und Clocks zuzählen. ^^
Aber Vorsicht!
Nicht immer ist der Schritt von C nach ASM notwendig, wenn sich in einem
Programm herausstellt, dass es eng wird. Viele "Engstellen" entstehen
nur durch ungeschickte Programmierung.
Auf Anhieb fällt mir jetzt nur das AVR-Basic Projekt aus der
Codesammlung ein, bei dem es nicht anders geht, als einen Teil in ASM zu
formulieren. Und das ist die Generierung des Videosignals. Das ist so
zeitkritisch, dass es tatsächlich auf jeden einzelnen Taktzyklus ankommt
und dann kommt man mit C nicht mehr weiter.
Aber abgesehen davon, fällt mir jetzt nichts weiter ein, was tatsächlich
Asm erzwingen würde.
>> ist aber überflüssig oder?>> Geht es hier mehr ums Prinzip? Also dass der eigene Zustand irgendwo>> geschrieben steht?>> richtig.> Ich dachte bei mir: Wenn ich das jetzt auch noch implizit mache, dann> entferne ich mich formal schon so weit von der SM, dass sie eventuell> nicht mehr erkennbar ist.
Um da noch mal auszuholen.
Normal würde man das wohl so schreiben
1
...
2
3
while(1){
4
5
if(get_key_press(1<<TASTE_LINK)
6
PORTA^=(1<<LED_ROT);
7
8
...
9
}
Und wenn du mir ein bischen Interpretationsspielraum zugestehst, dann
ist auch das eine Statemaschine. Es sind alle Zutaten vorhanden.
Da ist eine "Variable", die sich den Zustand der Maschine "LED" merkt.
Wo steckt sie? Es ist das Portbit selber, welches diese Funktion
erfüllt. Und da die Maschine nur 2 Zustände haben kann (Led ein / Led
aus), reicht es auch aus diesen Zustand zu toggeln und nicht explizit
umzuschalten. Die bei einem Zustandswechsel auszuführende Aktion ist mit
diesem Zustandswechsel gekoppelt, in dem am Portpin, welcher den Zustand
repräsentiert, die LED hängt.
Die Maschine wechselt ihren Zustand bei auftreten eines Ereignisses,
eben dem Tstendruck und da beide Zuständswechsel durch dasselbe Ereignis
ausgelöst werden, kann man den vorziehen.
-> Im Grunde ist das eine "Zustandsmaschine", die allerdings so
umgeformt und 'verfremdet' wurde, dass sie als solche nicht unmittelbar
und direkt erkennbar ist. Aber von der Funktionsweise unterscheidet sie
sich in nichts von einer klassisch implementierten.
Karl Heinz Buchegger schrieb:> Um da noch mal auszuholen.
Jup! Danke!
Auch wenn es bereits für mich klar war, durch deine früheren Posts,
finde ich diese Ausformulierung super. Schön zu sehen, wie theoretische
Konstrukte in der Praxis für Formen annehmen.
Also auch, dass sie weiterhin Gültigkeit haben.
Zum thema C und ASM.
lass dass lieber den C compiler machen. Wenn der C compiler nicht gerade
vom hintern Mode kommpt wird der Sehr guten code erzeugen, den man von
hand mit ASM fast nicht besser hinbekommt.
Bei der ASM programmierung gibt es viele teilaspekte mit zu
berúcksichtigen.
Welche zwischenergibnisse soll man sich merken um berechnungen nicht
unnötig doppelt zu machen.
Wo diese werte speichern? im Register oder auf dem Stack.
Wie wirken sich speicherzugriffszeiten aus? (RAM Register Flash)
Ist es z.B. besser irgend eine nonsens berechnung zu machen und dann das
ergebniss wegzuschmeisen oder doch lieber einen sprung? (pipline neu
laden)
Was ist schneller, ein code der in ASM sehr sehr gross ist, weil viele
"Funktionen" nicht angesprungen werden sondern mit copy past immer an
der stele wo sie benötigt werden eingabaut wuden oder der
platzsparendere in dem der code nur einmal existert, und ein call / ret
verwendet wird.
Wie wiren sich caches auf die speicherzugriffzeiten aus? kann man da ggf
was tunen (code im cach vorladen / verfügbar halten)
bei kleinen MCUs wo RAM und Register gleich schnell sind, und der core
selber keine pipline hat mag das ja noch recht überschaubar sein.
sobald register schneller anzusprechen sind als das ram, der core eine
pipline hat und auch noch caches ins spiel kommen wird das nicht mehr
ganz so einfach.
und von den ganz grossen brüdern reden wir mal lieber nicht.
Welche Befehlsreihenfolge ist die bessere? Thema paring von asm
Befehlen.
123 schrieb:> Zum thema C und ASM.>> lass dass lieber den C compiler machen. Wenn der C compiler nicht gerade> vom hintern Mode kommpt wird der Sehr guten code erzeugen, den man von> hand mit ASM fast nicht besser hinbekommt.>
Bwaaahahaahahahaaaaa! Selten so gelacht - Und das nicht nur über die
Rechtschreibschwäche!
Wieder so ein C-Jünger, der sicher auch meint, mit 'nem Spoiler und 100
Oktan Sprit würde sein Polo schneller laufen!
Wenn es auf Geschwindigkeit oder kritisches Timing ankommt, führt kein
Weg an Assembler vorbei!
Gruß,
Günni
guenther schrieb:> 123 schrieb:>> Zum thema C und ASM.>>>> lass dass lieber den C compiler machen. Wenn der C compiler nicht gerade>> vom hintern Mode kommpt wird der Sehr guten code erzeugen, den man von>> hand mit ASM fast nicht besser hinbekommt.>>>> Bwaaahahaahahahaaaaa! Selten so gelacht - Und das nicht nur über die> Rechtschreibschwäche!>> Wieder so ein C-Jünger, der sicher auch meint, mit 'nem Spoiler und 100> Oktan Sprit würde sein Polo schneller laufen!>> Wenn es auf Geschwindigkeit oder kritisches Timing ankommt, führt kein> Weg an Assembler vorbei!>> Gruß,> Günni
Ich mag keine Absolutismen.
Ich wette, es gibt Gruppen von Fällen oder Problemen in denen sich
Assembler fasst aufdrängt, und Fälle, die ebenso Zeitkritisch und
Komplex sind, die sich aber dennoch mit C besser lösen lassen. Zum
Beispiel, wenn die HW zu komplex ist.
Bei Java würde ich mich da aber schon aus dem Fenster legen. :D
guenther schrieb:> Wenn es auf Geschwindigkeit oder kritisches Timing ankommt, führt kein> Weg an Assembler vorbei!
Das es derartige Fälle gibt, mag ich dir gerne zugestehen.
Aber in >90% der Fälle um die es hier im Forum geht, ist Assembler
völlig unnötig und ob seiner langen Programme eher hinderlich, sofern
man ihn nicht wirklich gut beherrscht.
Die Frage ist nämlich nicht, ob der Polo mit Spoiler und 100-Oktan Sprit
schneller läuft, sondern ob er in Normalausrüstung schnell genug läuft.
Das ist ein klitzekleiner Unterschied. Um damit zum Discounter zum
einkaufen zu fahren langt das allemal. Dazu brauch ich keine Blue Flame,
die erst mal 5 Stunden Vorbereitungsarbeit benötigt, nur im dieselbe
Strecke anstat von in 2 Minuten in 25 Sekunden zurückzulegen und wenn
ich einen winzigen Fehler mache, geht der Raketenmotor 20 Meter vor dem
Ziel hoch.
Fabian Hoemcke schrieb:> Wonach ich also im Grunde frage, gibt es eine Art Design-Pattern um> komplexere µC-Programme (jetzt werden sicher die einen oder anderen> lachen ;)) zu planen und zu programmieren?
Ergänzend zu dem bereits gesagten kann man noch das Prinzip der
maximalen Faulheit nennen:
Mache Sachen nur so oft wie nötig und so spät wie möglich.
Es hat z.B. keinen Sinn, jeden neuen ADC-Wert in float umzurechnen, den
Mittelwert zu bilden und auf das LCD auszugeben.
Besser macht man den Mittelwert in integer und rechnet erst zur Ausgabe
nach float um.
Und kein Mensch kann alle 100µs einen neuen Wert ablesen. In der Praxis
haben sich 2..5 Werte/s als ergonomisch erwiesen.
Also muß man die Umrechnung und Anzeige nur alle 200ms machen.
Oder man mißt eine Temperatur. Das ist ne ziemlich träge Angelegenheit,
mehr als eine Messung alle 5..10s ist unnötig.
Das ist sogar sinnvoll, damit man den Meßkreis in den Pausen abschalten
kann, d.h. man spart Strom und der Sensor erwärmt sich nicht selbst.
Peter
Fabian Hoemcke schrieb:> Wonach ich also im Grunde frage, gibt es eine Art Design-Pattern um> komplexere µC-Programme (jetzt werden sicher die einen oder anderen> lachen ;)) zu planen und zu programmieren?
Ja, eigentlich gibt es sowas schon, ich würde es aber eher
"Herangehensweise" nennen - und da hat jeder seine Sichtweise. Das ganze
hängt auch von den näheren Umständen ab. Bei manchen Geräten hab ich
einen Kaltstartlader hineingesetzt, der immer im Gerät bleibt, egal, was
für ne Firmware draufkommt und bei anderen Geräten geht es einfach so
mit ein paar Zeilen Startupcode in Assembler und gleich danach kommt
main().
Aber, um dir nen allgemeinen Rat zu geben, sieht main() bei mir
prinzipiell etwa so aus:
- zuerst die allerwichtigsten Initialisierungen
- dann irgendeine sinnvolle Reaktion, damit der Benutzer sieht, daß die
Kiste zu leben beginnt
- dann die restlichen Initialisierungen
- fast immer mache ich mir mit irgendeinem Timer eine Systemuhr, damit
man langsame Sachen nur gelegentlich nachfragt und damit nicht sinnlos
Systemzeit vergeudet.
- dann eine Schleife, die nie verlassen wird und wo nacheinander alles
aufgerufen wird, was Beachtung braucht. Dabei achte ich darauf, daß
nichts blockierend geschrieben ist.
Also nicht "wait until event arrived", sondern "if event arrived then do
something"
my_loop:
if (CharAvail(com1) HandleChar(GetChar(com1), 1);
if (CharAvail(com2) HandleChar(GetChar(com2), 2);
if (CharAvail(com3) HandleChar(GetChar(com3), 3);
if (oldtick!=CurrentTick)
{ DoSlowFunctions();
oldtick = CurrentTick;
}
und so weiter...
goto my_loop;
}
W.S.
Peter Dannegger schrieb:> In der Praxis haben sich 2..5 Werte/s als ergonomisch erwiesen.
Generell Danke erstmal. "Faulheit" ist sicher ein gutes Dogma.
Was die Ergonomie angeht, geht es darum, dass wenn ich z.B. einen Poti
auslese, es doch immer Wackler gibt und die Werte dann rasch und zackig
wechseln und es schwerer ist, den gewünschten Wert einzustellen?
@W.S. (Gast) Danke!
Vor allem für den Tipp mit dem Blocking-Mode.
Was ist aber, wenn ich etwas im Blocking-Mode abfragen oder machen muss?
Und dann nicht nur eines? Dann bleibt mir ja nur der Non-Blocking-Mode
und ich muss schauen, dass ich alles recht Zeitnah abgefrühstückt
bekomme. Oder?
(Mit ist klar, dass es hier für ja schon die Interrupt-Flags gibt die
man mit Polling oder in Intterupt-Routinen bearbeiten kann. Mir fällt
jetzt auch leider kein gutes Beispiel an. Alles viel zu unkonkret.
Leider.)
Gruß
Fabian
Fabian Hoemcke schrieb:> Generell Danke erstmal. "Faulheit" ist sicher ein gutes Dogma.
Und auch schnell.
Sowohl in der Programmierung als auch im Programmlauf.
"Lazy Evaluation" hat schon so manchem Projekt Beine gemacht.
> Was die Ergonomie angeht, geht es darum, dass wenn ich z.B. einen Poti> auslese, es doch immer Wackler gibt und die Werte dann rasch und zackig> wechseln und es schwerer ist, den gewünschten Wert einzustellen?
In erster Linie geht es darum, dass zb auch ein LCD träge ist. Von daher
hat es keinen Sinn, dem 50 mal in der Sekunde ein Update zu geben, weil
es gar nicht hinterher kommt. Und wenn man das dann auch noch mit der
Methode "LCD löschen, neu hinschreiben" macht, dann flimmert das wie
verrückt und man kann erst recht nichts lesen.
Wenn du einen Benutzer hast, dann folgt aus dieser simplen Tatsache,
dass die I/O Sachen langsam sein können. Menschen sind langsam. Sowohl
was Ablesen als auch einstellen betrifft.
>> @W.S. (Gast) Danke!> Vor allem für den Tipp mit dem Blocking-Mode.> Was ist aber, wenn ich etwas im Blocking-Mode abfragen oder machen muss?
Kann nicht sein. Du 'musst' eben nicht blockend abfragen. Das kannst du
ruhig als Dogma ansehen. Bei einer UART muss man eben vorher nachfragen,
ob ein Zeichen da ist. Aber gewartet wird nicht (ausser vielleicht ganz
kurz). Grundsätzlich. Auf nichts und niemanden. Was im jetzigen
Durchlauf durch die Hauptschleife nicht fertig ist, muss eben bis zum
nächsten Durchlauf warten. (*)
Wenns gar nicht anders geht, dann muss man eben das Gerät wegwerfen, 3
mal heftig fluchen und einen anderes Gerät kaufen, welches eine
getrennte Abfrage ermöglicht. Im AVR kann man jegliche Hardware immer so
benutzen, dass man nicht warten muss (oder wenn, dann nur sehr kurz - zb
ADC)
(*) eine Ausnahme wäre zb I2C in Software, bei dem du am Timing nichts
machen kannst. Aber da sind wir im Bereich: kurz warten.
Fabian Hoemcke schrieb:> Was die Ergonomie angeht, geht es darum, dass wenn ich z.B. einen Poti> auslese, es doch immer Wackler gibt und die Werte dann rasch und zackig> wechseln und es schwerer ist, den gewünschten Wert einzustellen?
Keine Regel ohne Ausnahme. Während des Einstellens kann man natürlich
die Rate erhöhen, z.B. auf 50ms.
Peter
Hey!
Danke erstmal an alle für die tollen Beiträge.
Abschließend kann ich sagen, dass es sicher ein paar Prinzipien gibt,
die man je nach Fall verfolgen kann, aber eine Ansammlung von
Design-Pattern wie ich sie aus der Objekt Orientierten Programmierung
kenne gibt es wohl nicht.
Es gibt kleine Lösungen für immer wiederkehrende Fälle, wie etwa das
Entprellen von Tastern, aber nicht für komplexere Probleme. Vermutlich
schießt man dann auch schon über die Lösung mit einem µ C über das
Ziel hinaus.
Vielen Dank
Fabian
Fabian Hoemcke schrieb:> aber eine Ansammlung von> Design-Pattern wie ich sie aus der Objekt Orientierten Programmierung> kenne gibt es wohl nicht.
Der Grund ist ganz einfach, daß eine MC-Schaltung nicht standard ist.
Es gibt viele tausende MCs und viele tausende ICs, die man auf viele
verschiedene Wege an den MC anschließen kann und haufenweise
Programmiersprachen dazu.
Es gibt somit Billiarden Möglichkeiten, etwas zu realisieren. Und daher
ist es unmöglich, Billiarden Bibliotheken vorrätig zu halten und zu
verwalten.
Schon um ein Standard-LCD (Text oder Grafik) an einem AVR zu betreiben,
kann man es spielend auf 1000 Bibliotheken bringen, je nach Typ, Format,
Interface, Funktionen, gepuffert/ungepuffert, Interrupt/nicht Interrupt
usw..
Peter
Peter Dannegger schrieb:> Fabian Hoemcke schrieb:>> aber eine Ansammlung von>> Design-Pattern wie ich sie aus der Objekt Orientierten Programmierung>> kenne gibt es wohl nicht.> Der Grund ist ganz einfach, daß eine MC-Schaltung nicht standard ist.
Welche Softwareloesung ist das?
> Es gibt somit Billiarden Möglichkeiten, etwas zu realisieren. Und daher> ist es unmöglich, Billiarden Bibliotheken vorrätig zu halten und zu> verwalten.
Design-Patterns haben mit Bibliotheken gar nichts zu tun. Sie sind auch
nicht auf Software-Loesungen beschraenkt. Design-Patterns werden
manchmal mit "Kochrezepte" uebersetzt und das trifft den Nagel auf den
Kopf. Wenn dir Jemand erzaehlt, wie er eine Mehlschwitze fuer eine
dunkle Sosse macht, dann ist das ein Design-Pattern und keine
Bibliothek, wie der Sossenbinder aus der Tuete. Und noch etwas wichtiges
zeigt dieses Gleichnis - es ist gut, viele Design-Patterns zu kennen und
es ist noch besser, wenn man ohne kochen kann.
> aber eine Ansammlung von Design-Pattern wie ich sie aus der> Objekt Orientierten Programmierung kenne gibt es wohl nicht.
Die gibt es schon, in Lehrbüchern über SPS,
in irgendeinem Lehrbuch über den Z80 das ich habe aber nicht mehr finde,
bloss hat man das damals noch nicht so genannt,
denn wie geschildert: Die Wortwahl ändert sich alle 10 Jahre,
damit die nachfolgende Generation den Eindruck so voll top modern zu
sein,
wenn sie den alten Wein in neuen Schläuchen säuft.
In der Realität sind Design Patterns der Ersatz von mangelnder
Intelligenz durch massives Auswendiglernen, denn genau darauf käme es
an: Eine Vielzahl von Design Patterns zu kennen mit ihren jeweiligen
Nachteilen (die nie beschrieben werden sondern die man schmerzhaft
erfahren müsste) und Vorteilen, damit man sie beim aktuellen Problem
gegenüberstellen könnte um das bestpassende auszuwählen.
Da ist der Weg über Intelligenz klar der einfachere, insbesondere bei uC
die von Haus aus nicht so besonders komplex sind und deren Aufgaben in
99% der Fälle von simpelster Natur sind. Wenn einen natürlich schon
nebenläufige Prozesse (auch so ein Design Pattern) verwirren, können
auch uC schwer genug erscheinen.
> aber eine Ansammlung von Design-Pattern wie ich sie aus der Objekt> Orientierten Programmierung kenne gibt es wohl nicht.
Design Patterns kannst du natürlich verwenden. Aber oft ist das
Overkill. Du darfst nicht vergessen, dass wir uns auf kleinen µC
bewegen, die nicht über die Kapazität und die Rechenpower von Desktop
Systemen verfügen. Auch benötigt man oft die Felxibilität nicht, die in
Design Pattern steckt.
Gegebenenfalls muss man das Pattern vielleicht etwas vereinfachen. Aber
im Grunde spricht nichts dagegen zb ein Visitor Pattern einzusetzen.
> In der Realität sind Design Patterns der Ersatz von mangelnder> Intelligenz durch massives Auswendiglernen
So krass würde ich das nicht ausdrücken. Design Pattern giessen einfach
Strukturen, die sich bewährt haben, in eine Form, so dass nur das für
das jeweilige Pattern charakteristische Verhalten übrig bleibt. Ein
"Design Pattern" ist einfach nur ein Name für eine Programmstruktur, so
wie eine spezifische "Datenstruktur" (Liste, Baum) ein Name für eine
spezifische Datenorganisationsform ist. Ein paar typische Design Pattern
für Standardaufgaben zu kennen, schadet nicht.
Aber im Grunde hast du recht. Das ist nichts neues, nur weil es
heutzutage Design Pattern heißt. Neu ist lediglich, dass die
Programmstrukturen aufs wesentliche reduziert und einen Namen bekommen
haben.
Natürlich gibt es auch Leute, die bei Embedded-Systemen auf der
Design-Pattern Welle mitreiten wollen oder wollten.
Da werden zum Beispiel 1000 Seiten Abhandlungen wie diese geschrieben
http://www.tte-systems.com/books/pttes (warum wundert es mich nicht,
dass das von einer Consulting-Bude kommt?) und nachdem es als Buch nicht
mehr so gut läuft verschenkt.