Hi Leute,
hab mal in C eine Binärzähler programmiert, der einfach hochzählen soll
1
#include<avr/io.h>
2
3
volatileuint8_tzaehler;
4
volatileunsignedinti;
5
6
voiddelay(){
7
8
for(i=0;i<65535;i++)
9
{
10
11
}
12
}
13
14
15
intmain(void)
16
{
17
DDRB=0xff;
18
DDRD=0x00;
19
PORTB=0xff;
20
PORTD=0xff;
21
zaehler=0;// Anfangswert für Zählvariable ist Null
22
23
while(1)
24
{
25
count++;// counter hochzählen
26
delay();// Funktion delay aufrufen
27
PORTB=~count;//invertierte Ausgabe, da 1 (high) die LEDs ausschaltet
28
29
}
30
return0;
31
}
Leider habe ich die for Schleife eher zufällig bzw. auf gut Glück so
gestaltet. Daher meine Frage, wie das da genau fuktioniert. Man könnte
theoretisch auch zwei ineinander verschachtelte machen.
ich hatte vor, sowas wie eine Warteschlange zu machen mit der delay
funktion, sodass im genauen Sekundentakt bei 8 Mhz Prozessor die Bits
gesetzt werden.
Samson schrieb:> Leider habe ich die for Schleife eher zufällig bzw. auf gut Glück so> gestaltet.
Und sobald du dem Compiler erlaubst zu optimieren, ist es vorbei mit der
Funktionalität :-) Ein guter Compiler, und dazu muss er noch nicht
einmal besonders gut drauf sein, schmeisst das einfach raus, weil es
augenscheinlich keine Funktion erfüllt ausser Rechenzeit verbraten. Und
genau das ist die Domäne des Optimizers: du schreibst das Programm und
der Compiler kümmert sich darum, dass es schnell wird.
> Daher meine Frage, wie das da genau fuktioniert.
Wie was genau funktioniert.
> ich hatte vor, sowas wie eine Warteschlange zu machen mit der delay> funktion, sodass im genauen Sekundentakt bei 8 Mhz Prozessor die Bits> gesetzt werden.
Warum nimmst du nicht einfach die Funktion _delay_ms()
Die ist so geschrieben, dass die angegebenen Millisekunden so
einigermassen genau eingehalten werden. Längere Timings erledigt man
dann sowieso nicht mittels _delay, sondern setzt einen Timer dafür ein.
Dann klappts nämlich auch wieder damit, dass der µC scheinbar mehrere
Dinge gleichzeitig macht.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29
busy-waiting: _delay_ms(), _delay_us(), beider in der delay.h (Achtung,
Doku lesen!). Die leere for-schleife wird dir ein anständiger Compiler
nämlich wegoptimieren.
> [..] sodass im genauen Sekundentakt [..]
Nimm nen Timer, das ist einfacher(tm) und einfacher(tm) genauer(tm).
nunja wollte wie gesagt im Sekundentakt das nächstfolgende Bitmuster
ausgeben. und wollte mit einer geeignet langen Warteschleife das Ganze
selber realisieren. UNd die länge der Warteschleife wollte ich dann
selber runtertunen bis etwa 1 Sekunde.
für den anfang ok
- aber für profis eig nogo^^
aber du sollst ja lernen, da ist kreativität auch gut
Wenn du eine genaue Sekunde mit Timer haben willst guck mal hier im
wiki, da ist ein Artikel drin, das sollte genau genug sein (1,5s/d wenn
ichs richtig im kopf habe)
Hallo Samson,
du könntest in Deine Schleife ein einfaches nop einfügen und schon wirds
besser.
Zur Funktion, du hast deinen Takt und mit jedem Takt wird ein Befehl
ausgeführt, da du in C Proggrammierst, müsstet du Dir mal den
Compilierten Code anschauen und gucken wieviele asm- Befehle der
Compiler aus deiner Schleife macht. Danach einfach die Anzahl Deiner
befehle durch die Frequenz und dann weisst du wie lange du für ein i
brauchst. Danach sollte es auch kein Prob sein das ganze auf eine
Sekunde hinzubekommen. Nur wie schon öfters erwähnt ist der weg mit dem
delay_ms schöner und schneller, da du dir keine Gedanken machen musst.
Gruß
Dennis
Hi,
@quickndirty ist ja manchmal ganz gut, sollte dann aber funktionieren.
In der delay.h stehen maximale die Werte mit denen die delays aufgerufen
werden können und auch noch ungefährt den Wartezeien entsprechen die man
sich erhofft.
@Samson Schau die den disassambler an zähl die benötigte Taktzahl für
die Abarbeitung der Befehle zusammen und du weißt wie lange deine
Schleife braucht.
Gruß MISZOU
Samson schrieb:> ich hatte vor, sowas wie eine Warteschlange zu machen mit der delay> funktion, sodass im genauen Sekundentakt bei 8 Mhz Prozessor die Bits> gesetzt werden.
Das kannste voll vergessen, das wird nie was.
Ein Delay berücksichtigt nicht die Programmlaufzeit und Interrupts.
Genaue Zeiten gehen nur mit einem Timer. Deshalb hat auch jeder MC
mindestens einen Timer.
Peter
samson schrieb:> danke vorab. Werde mir das Ganze genauer anschauen. Ist trotzdem> interessant, sowas mal selber zu programmieren.
Wenn du etwas dabei lernen willst, dann sieh dir den Code der _delay_xx
Routinen an. Du hast den Source Code auf deiner Festplatte. Header Files
sind auch nur Text-Dateien, die man im Editor öffnen und anschauen kann.
Allerdings: Zu durchschauen, warum da einige Einzelheiten genau so sind,
wie sie sind, ist nicht mehr so einfach. Aber probiers einfach.
Abgesehen davon lernst du bei deiner
1
voiddelay(){
2
3
for(i=0;i<65535;i++)
4
{
5
6
}
7
}
nicht viel, ausser dass die Laufzeit eines Programmes von vielen
Faktoren, nicht zuletzt Optimizer, verwendete Datentypen und verwendete
Taktfrequenz abhängt. Denkt man einmal genauer darüber nach, dann sollte
das allerdings nicht sonderlich verwunderlich sein.
Peter Hartmann schrieb:> Hi!>> Ich habe mir mal eine Binärled anzeige gemacht und einfach> hoch bzw runter zählen lassen
Du solltest unbedingt den weiter oben angegebenen Link bzw. die Doku der
Funktion im Header File lesen.
> _delay_ms(i);
Keine Aufrufe der Funktion _delay_ms mit Variablen!
_delay_ms ist darauf angewiesen, dass der Optimizer einige Ausdrücke,
die zur Berechnung der internen Schleifenwiederholungen benötigt werden,
wegoptimieren kann. Das geht aber nur, wenn er die Zahlenwerte kennt!
Um _delay_ms mit einigermassen korrekten Zeiten benutzen zu können sind
2 Dinge erforderlich:
* keine Variablen als Argumente
* Optimizer muss eingeschaltet sein
Variable delay Zeiten macht man so
1
voidmyDelay(unsignedinttime)
2
{
3
unsignedinti;
4
5
for(i=0;i<time;++i)
6
_delay_ms(1);
7
}
Durch die for-Schleife erhält man noch einen kleinen Fehler rein. Da
aber _delay_ms swieso nicht das genaueste ist, spielt das im Regelfall
keine grosse Rolle.
Ehrlich gesagt hat mir das Tutorial auch nix gebracht was die Durchläufe
angeht. Wie genau kann ich das analysieren, wie ich bei einem 8Mhz
Prozessor jeweils 1 Sekunde pro Schalten benötige, verstehe das nicht so
genau ...
Wenns um Sekundengenauigkeit geht, würde ich eher einen geeigneten Quarz
+ internen Prescaler verwenden und dann mit Interrupts arbeiten.
Hab ich bei meiner Binäruhr so erledigt.
Man schreibt zunächst mal provisorischen Code. Dieser Code wird in
seinem Kern aus einer Schleife bestehen.
Den Code jagt man durch den Compiler und sucht sich im Assemblerlisting
besagte Schleife. Man muss allerdings sicher gehen, dass der Compiler
immer denselben Code produzieren wird. Daher ist hier die Einstellung
des Optimizers wichtig und auch die Details wie exakt der Schleifencode
aussieht.
Mit dem Datenblatt des Prozessors geht man dann die Schleife durch und
addiert für jeden Befehl in der Schleife die Anzahl der dafür benötigten
Taktzyklen für jeden Assemblerbefehl.
Kennt man dann die Summe der Taktzyklen für einen Schleifendurchlauf,
dann ist der Rest einfache Dreisatzrechnerei:
Wenn die Taktfrequenz y Megaherz beträgt, hat der µC genau y Takte in 1
Sekunde zur Verfügung. Da 1 Durchlauf durch die Schleife x Takte
benötigt, benötigtt man daher wieviele Durchläufe um möglichst genau z
Takte zu verbraten, wenn z sich aus der in 1 Sekunde zur Verfügung
stehenden Anzahl an Takten und der gewünschten Wartezeit ergibt?
Im Grunde ist das einfach nur eine Abart von:
Ein Arbeiter kann mit einer Schaufelladung 456 Gramm Sand bewegen und
benötigt dazu 15 Sekunden.
Wieviel Sand muss man dem Arbeiter vorsetzen, damit er möglichst genau
28 Minuten und 12 Sekunden beschäftigt ist?
avrn00b schrieb:> Wenns um Sekundengenauigkeit geht, würde ich eher einen geeigneten Quarz> + internen Prescaler verwenden und dann mit Interrupts arbeiten.
Was wäre denn ein nicht geeigneter Quarz???
Wenn man Angst vor Interrupts hat, kann man die Timerflags auch im Main
pollen und dann auf 1 setzen, um sie wieder zu löschen.
Man muß es nur vor dem nächsten Überlauf/Compare gemacht haben.
Peter
Samson schrieb:> nur das ganze in meinem C> Code zu optimieren wird dann der schwierigste Teil :-(
Bravo.
Nun weißt Du also, warum man es nicht so macht.
Peter
Samson schrieb:> Ok, vielen Dank, das bringt mich etwas weiter, nur das ganze in meinem C> Code zu optimieren wird dann der schwierigste Teil :-(
Das sollst du auch gar nicht.
Diese Arbeit hat man dir bereits abgenommen.
Es reicht völlig wenn du im Prinzip weißt, wie _delay_ms 'mit der heißen
Nadel' zusammengestrickt wurde. Ab hier übernimmst du und verwendest
_delay_ms ganz einfach. Du lernst nichts dabei, wenn du dir selbst ein
_delay_ms strickst. Arbeite lieber an den Dingen die dich wirklich
weiterbringen. Zb. Wie man mit einem Timer arbeitet. Das bringt dich
weiter! Nicht wie man Schleifen so hintrickst, dass sie möglichst genau
20 Millisekunden verbraten und der Prozessor in dieser Zeit nichts
anderes machen kann. Das ist eine Sackgasse. Ausser das man dort am Ende
an die Wand fährt, lernt man nichts dabei, wenn man da voller
Enthusiasmus mit Vollgas reindüst.
PS: Wie gut sind zb deine Kenntnisse was Stringverarbeitung in C angeht?
Das wäre zb ein Feld, in dem du deine Zeit sehr viel besser investieren
kannst als mit ödem schleifenzählen.
Was weißt du über Datentypen?
Wie stehts mit Bitmanipulationen?
Stellt dich ein Lauflicht vor große Probleme? Rechts rum / Links rum.
Was, wenn nicht ein einzelnes Licht laufen soll, sondern ein Muster?
Das sind Problemkreise mit denen du dich beschäftigen kannst.
da hast du auch wieder recht, wollte eben von C dann das ganze auch in
Assembler. Aber ich verstehe nicht mal diese GEschichte mit der
BErechnung also in Zusammenhang mit 8MHz , Overlofws etc. ... vielleicht
bin ich einfach zu blöd dafür. Versuche seit 2 Tagen das zu verstehen,
klappt einfach nicht. ...
Samson schrieb:> da hast du auch wieder recht, wollte eben von C dann das ganze auch in> Assembler. Aber ich verstehe nicht mal diese GEschichte mit der> BErechnung also in Zusammenhang mit 8MHz , Overlofws etc. ... vielleicht> bin ich einfach zu blöd dafür. Versuche seit 2 Tagen das zu verstehen,> klappt einfach nicht. ...
:-)
Ein Kind benötigt zum Essen 1 Apfels 2 Minuten.
Wieviele Äpfel musst du dem Kind geben, um es 2 Stunden zu beschäftigen.
Grundschulmathematik
Karl heinz Buchegger schrieb:> Ein Kind benötigt zum Essen 1 Apfels 2 Minuten.> Wieviele Äpfel musst du dem Kind geben, um es 2 Stunden zu beschäftigen.>> Grundschulmathematik
Theorie, Theorie, Theorie...
Das Apfelthema ist wohl etwas komplexer. Ich möchte mal sehen, wie ein
Kind in 2 Stunden 60 Äpfel vertilgt. Da dürfte dann sogar ein
Strafrechtlicher Aspekt zu berücksigtigen sein (vorsätzliche
Körperverletzung) ;o)
* duckundweg *
Naja, ein nicht geeigneter Quarz ist, wenn ich hinterher noch wie dumm
rumrechnen muss, um aus der Zahl der Overflows eine Sekunde
rauszubekommen. Ich will ja, dass mein Register einmal in der Sekunde
überläuft, denn dann ist die Schleife denkbar einfach:
<pseudocode>
wenn interrupt signal overflow
dann zeit.sekunde = zeit.sekunde + 1;
</pesudocode>
ansonsten muss man sich rumschlagen mit
<pseudocode>
wenn interrupt signal overflow
wenn bescheuerte_variable == bescheuerte_zahl
dann zeit.sekunde = zeit.sekunde + 1;
bescheuerte_variable = 0;
sonst
bescheuerte_variable = bescheuerte_variable + 1;
</pseudocode>
Ich hab keine Lust, mich damit rumzuschlagen, am Ende
verrechnet/vertippt man sich, und dann ist alles wieder fürn Arsch.
Ist einfach "sauberer", der Code, meinem Empfinden nach.
avrn00b schrieb:> Ich hab keine Lust, mich damit rumzuschlagen, am Ende> verrechnet/vertippt man sich, und dann ist alles wieder fürn Arsch.
Naja, man kann sich höchstens beim Definieren der Quarzfrequenz
vertippen.
Das Rechnen lasse ich immer den Compiler machen, also verrechnen geht
schonmal nicht.
Ich nehme meistens Baudratenquarze die teilen sich zwar glatt, aber als
Uhr ist ihre Toleranz etwas zu groß.
D.h. da ist ein Abgleich nötig. Dazu lasse ich die Uhr einige Wochen
laufen und ermittele die Abweichung in Sekunden. Dann errechne ich
daraus die wirkliche Frequenz, trage sie ein und compiliere neu. Danach
stimmt die Uhr.
Man könnte sie auch mit nem Trimmer hinziehen, aber dazu braucht man nen
sehr genauen Frequenzmesser oder sehr viel Geduld.
Peter
Peter Dannegger schrieb:> D.h. da ist ein Abgleich nötig. Dazu lasse ich die Uhr einige Wochen> laufen und ermittele die Abweichung in Sekunden. Dann errechne ich> daraus die wirkliche Frequenz, trage sie ein und compiliere neu. Danach> stimmt die Uhr.> Man könnte sie auch mit nem Trimmer hinziehen, aber dazu braucht man nen> sehr genauen Frequenzmesser oder sehr viel Geduld.
Ich hab um Weihnachten rum etwas ausprobiert:
Ich hab mich an die 50Hz Netzfrequenz gehängt und eine Uhr seit
Weihnachten damit betrieben. Funktioniert ausgesprochen gut. Bis jetzt
hab ich nur bei der Sommerzeitumstellung eingegriffen und der Vergleich
mit der danebenstehenden Funkuhr lässt das Teil gut aussehen.
Hardwaremässig war es kein grosses Problem, die Netzfrequenz hinter dem
Trafo abzugreifen.