Hallo,
Ich habe ein kleines Problem mit meinem kleinen attiny2313 (8Mhz
intern):
Vorweg, ich bin alles andere als Profi in Sachen µC!
Also ich versuche einen Frequenzzähler zu bauen, der ant INT0 einfach
misst wie oft ein Puls ankommt, und das per internem Timer dann nach 1
Sekunde zu analysieren. Klappt soweit auch sehr gut, nur ab ~170 kHz
hängt sich mein 2313 auf.
Ich hab dem guten stück schon gesagt, mach ab 150 kHz INT0 aus, weil ich
dachte das einfach zu wenig zeit für andere Sachen bleibt wenn der
ständig Interrupts scheißt, aber das löst mein Problem nicht, das ganze
hängt immernoch...
1
PCMSK&=~(1<<PIND2);
2
GIMSK&=~(1<<INT0);
An dem Problem bastel ich jetzt schon 2 Tage und find auch nix tolles
über Google, wobei ich dazu auch sagen muss, das mir dazu die richtigen
Suchbegriffe Fehlen...
Wäre echt klasse wenn mir jemand einen Tipp geben kann.
Hier noch mein ganzer Code:
http://nopaste.info/483b7e8ee5.html
Ich weiß, der Code ist unübersichtlich, schlecht kommentiert und
stellenweise sogar blödsinn und zuviel, aber es ist ja noch kein Meister
vom Himmel gefallen :D
Verbessrungsvorschläge erwünscht!
Hi
>Verbessrungsvorschläge erwünscht>PCMSK &=~ (1 << PIND2);
PCMSK hat absolut nichts mit INT0 zu tun. Die Bits in PCMSK heißen auch
nicht PINDn sondern PCINTn. Kannst du also weglassen.
MfG Spess
Deine Interrupt Routine enthält eine ganze Menge Multiplikationen und
Divisionen. Schau Dir mal an, wieviel Assembler Code dabei heraus kommt!
Das ist für 125 kHz einfach viel zu viel Mathematik.
Versuche, eine andere Methode zu finden, bei der die Interrupt-Routine
VIEL simpler ausfällt.
Du könntest z.B. einen Hardware-Zähler nutzen, der die externen Impulse
zählt. Und wenn er über läuft, incrementierst Du eine Variable.
Wenn der Hardware Zähler z.B. 16 Bit hat und deine Variable 16 Bit hat,
dann erhälst Du so insgesamt 32 Bit Auflösung, kannst also bis etwa 4
Millionen Zählen.
Nun brauchst Du noch einen zweiten Timer, der die Zählung nach einer
Sekunde (oder so) stoppt.
Im Hauptprogramm wartest Du, bis der Zweite Timer das Ende der Sekunde
signalisiert und dann dividierst Du (NACH der Messung!) 1 Sekunde durch
den Zählwert, was die Frequenz ergibt. Danach setzt Du den
Hardware-Zähler, die Zähl-Variable und den Sekunden-Timer wieder auf 0
und startest die nächste Messung.
Ok, das ganze jetzt mal langsam für Leute die keine Ahnung haben (wie
ich)...
Ich kommte mit dem Counter gerade überhaupt nicht klar...
Meinen Timer1, um die Inputs zu zählen initalisiere ich so:
1
TIMSK=(1<<TOIE0)|(1<<ICIE1);//timer0 für sekunden zählen, und ICIE1 um PD6/ICP zu zählen
2
TCCR1B=(1<<CS10)|(1<<ICES1);//kein vorteiler + ansteigende flanke soll TCNT1 + 1 machen
3
TCNT1=0;
Ist das erstmal richtig so?
Weiterhin ist das ganze sehr seltsam, ich hab das ganze jetzt so weiter
gemacht:
1
ISR(TIMER1_OVF_vect)//Wenn der Timer1 überläuft
2
{
3
pre++;//Das passiert leider nie, warum auch immer
4
}
5
6
ISR(TIMER1_CAPT_vect)//Wenn ich die Funktion nicht mit reinschreib hänt sich der Chip auf, warum auch immer
7
{
8
}
Ich dachte immer ich kann halbwegs programmieren, aber diese fiesen µC
bringen mich zur verzweiflung :P
Benedikt W. schrieb:> ISR(TIMER1_CAPT_vect) //Wenn ich die Funktion nicht mit reinschreib hänt> sich der Chip auf, warum auch immer
Nicht "warum auch immer"
Ein freigegebener Interrupt, wie zum Beispiel der Capture Interrupt, den
du hier freigibst
TIMSK = ...... (1 << ICIE1);
benötigt eine ISR. Schreibst du selber keine ISR, dann kommt ein
Standardhandler zum Zug, welcher den µC resettet.
Daher: Man gibt keinen Interrupt frei, für den man keine ISR hat!
Hi
>Ich dachte immer ich kann halbwegs programmieren, aber diese fiesen µC>bringen mich zur verzweiflung :P
Na ja, ich dachte immer, wer programmieren kann, der kann auch lesen,
aber diese fiesen Programmierer bringen mich zur Verzweiflung
Sorry, das ist nicht ernst gemeint und mangels geeigneter Smilies muß
ich die Entschuldigung eben so nachliefern. Aber was ich damit sagen
will: es gibt m.W. für jeden Controller Datenblätter, wo auch die
Register und Bits erklärt sind. Bisher hab ich dazu auch Beispielcode in
Asm. und C gesehen. Lad dir doch einfach mal das Datenblatt herunter und
schau dir an, wie die Timerarbeiten und wozu die entsprechenden Bits da
sind.
Da ich kein C kann (und vermutlich auch nicht mehr erlerne mangels
verbleibender Lebenszeit...) kann ich dir nur mit diesem Tip helfen. Des
weiteren merke:
Interrupts sollten nur sehr wenige Aufgaben erledigen. Berechnungen
gehören nicht dazu denn: wie schnell kannst du lesen ? Im mSek. Bereich?
Sicherlich nicht. Also reicht es, deine Anzeige vielleicht alle
hundertstel oder gar nur zehntel Sekunden zu aktualisieren und da hast
du Ewigkeiten Zeit, einen gültig berechneten Wert der Anzeige verfügbar
zu machen.
Gruß oldmax
Benedikt W. schrieb:> ISR(TIMER1_OVF_vect) //Wenn der Timer1 überläuft> {> pre++; //Das passiert leider nie, warum auch immer> }
Du musst pre auch als "volatile" deklarieren, da der Compiler dir das
Increment für die Hauptschleife sonst gnadenlos wegoptimiert.
Der Compiler optimiert das Increment nicht weg. Aber zwischen zwei
Lesezugriffen kann die Änderung unsichtbar bleiben, weil der Compiler
die Lesezugriffe optimiert. Beispiel:
1. Lesezugriff: Kopiert pre in irgendein Register
2. Variable pre wird durch den Interrupt incrementiert
3. Lesezugriff: Der Wert wird aus dem Register gelesen, statt (wie
erwartet) aus der Variablen.
Das kann passieren, muss aber nicht. Durch "volatile" sagst Du dem
Compiler, daß Zugriff auf diese Variable nicht durch irgendwelche
Caching Mechanismen optimiert werden dürfen. Ein pre++ incrementiert
also wirklich sofort die Variable und ein Lesezugriff liest immer aus
der Variable.
Benedikt W. schrieb:> Ich dachte immer ich kann halbwegs programmieren, aber diese fiesen µC> bringen mich zur verzweiflung :P
sieh das mal von der MC Seite:
"Ich dachte immer, ich kann halbwegs arbeiten, aber diese fiesen
Programmierer bringen mich immer zum Reset"