moin, ich habe nen mega 8 aufm stk500 und will ihn mit quarz auf 8mhz laufen lassen. ich habe alles angeschlossen auch die fuses gesetzt doch wenn ich mitm ossi messe habe ich gerade mal rund 2mhz. ich habe folgendes zum messen in der hauptschleife: while(1) { PORTB=~PINB; } warum ist das so langsam? es wird ja wohl kaum so stark durch c ausgebremst werden, oder? mfg max
Schau im Assemblerlisting (der vom C Compiler ggf. erzeugten *.LST oder der *.LSS Datei oder im AVR Studio Disassembler) nach, wieviele Assemblerbefehle der C-Compiler aus diesem Mini-C-Code gemacht hat. Der AVR lann pro Trakt im günstigsten Fall einen Befehl abarbeiten, bei manchen Befehlen sind sogar mehr Takte notwendig... Die Takte pro Befehl stehen iM Assembler Instruction Set Manual. 2 MHz am IO-Pin bei 8 MHz CPU-Takt sind bei dem Hintergrund nicht übel.
Hallo, Du wolltest sicher schreiben: while(1) { PORTB=~PORTB; } Was hast du gegen 2Mhz. Da ist ein Vergleich und eine Zuweisung bei. Kenne mich nicht mit Assembler aus, sonst könnte man sicher die Takte ermitteln. Besser du stellst den Takt über einen Timer fest. Der läuft über die Hardware und ist von Programmlaufzeiten unabhängig. Gruß Jens
>ich habe nen mega 8 aufm stk500 und will ihn mit quarz auf 8mhz laufen >lassen. Die Quarzfrequenz misst du am Quarzoscillator (XTAL1/XTAL2)
ich werde leider nicht schlau aus dieser datei... wie kann ich denn am besten messen wie schnell er wirklich läuft? ich habe jetzt mal nen 20mhz quarz draufgesteckt und messe jetzt 5mhz an portb kommt ja eigendlich hin: (20mhz_gequarzt/8mhz_gequarzt)*2mhz_gemessen=5mhz_gemessen ich glaube ich probier mich mal in assembler... vielen dank wenn trotzdem jemand noch ne idee hat wäre das toll. ich will eigendlich nur ausloten wie schnell ich mit dem timer1 interupts machen kann und wieviel zeict ich dazwischen habe.
der quarz schwingt wunderbar. ich habe mich nur gewundert das ich mit dem timerinterupt so komische zeiten hatte. wenn ich jetzt über die kommentare nachdenke scheint es vielleicht normal zu sein.
>wie kann ich denn am besten messen wie schnell er wirklich läuft?
Du kannst Dir z. B. ein Mini-Assemblerprogramm schreiben, dass Dir einen
Pin alle exakt 10 Taktzyklen toggelt:
1 | [PORTB als Ausgang konfigurieren] |
2 | |
3 | Loop: |
4 | com r16 |
5 | out PORTB, r16 |
6 | nop
|
7 | nop
|
8 | nop
|
9 | nop
|
10 | nop
|
11 | nop
|
12 | rjmp Loop |
ich habe jetzt mal nen 20mhz quarz draufgesteckt und messe jetzt 5mhz an portb kommt ja eigendlich hin: (20mhz_gequarzt/8mhz_gequarzt)*2mhz_gemessen=5mhz_gemessen Du wirst mit Deinem C-Programm am Pin immer ein Viertel der Quarzfrequenz messen. Ist ja auch klar: Dein Controller führt wahrscheinlich
1 | Loop:
|
2 | com r16 |
3 | out PORTB, r16 |
4 | rjmp Loop |
aus, und das sind 4 Taktzyklen von out bis zum nächsten out (com und out benötigen je 1 Zyklus, rjmp 2 - siehe Datenblatt). >wenn trotzdem jemand noch ne idee hat wäre das toll. Ne Idee für was?? >ich will eigendlich nur ausloten wie schnell ich mit dem timer1 >interupts machen kann und wieviel zeict ich dazwischen habe. Wie schnell brauchst Du es denn?
ich weiss auch nicht was ich gemacht habe aber jetzt ists besser habe jetzt: SIGNAL(SIG_OVERFLOW1) { PORTB=~PORTB; TCNT1=65000; } und ich messe ca 70µs, das ist top (mit 8mhz quarz) vielen dank ihr füchse :) mfg max
wie jetzt, vorhin warst du mit 2MHz unzufrieden und jetzt reichen dir 14kHz?
>TCNT1=65000;
Kleiner Hinweis: Das mit dem Reload von TCNT1 im Overflowinterrupt
funktioniert zwar, allerdings kannst Du nicht wissen, wann genau der
Interrupthandler aufgerufen wird, weil die Instruktionen nicht alle
gleich viel Taktzyklen beanspruchen (clr: 1, rjmp: 2, rcall: 3, ret: 4),
oder auch, weil es noch andere Interrupts oder critical sections geben
kann.
Tip: Wenns genau sein soll, lass den Timer besser im CTC-Mode laufen
(siehe Datenblatt). Dann wird bei Überschreiten eines wählbaren Wertes
ein OutputCompareMatch-Interrupt ausgelöst und der Timer automatisch
sofort wieder auf Null gesetzt.
vielen dank für den tip ich hab mich schon gefragt wie das mit der gleichmäßigkeit dann ist. was ist denn wenn der interupt kommtn wärend ich noch im handler bin?
>was ist denn wenn der interupt kommtn wärend ich noch im handler bin? Die werden erst nach Verlassen der ISR angesprungen, sonfern man die anderen Interruptquellen nicht explizit in der ISR wieder frei gibt. Der AVR besitzt keine Interrupt-Prioritäten, wie der 8051.
>was ist denn wenn der interupt kommtn wärend ich noch im handler bin?
Der gleiche Interrupt A sollte selbstverständlich niemals auftreten,
während das Programm noch im Interrupthandler von A verweilt. Das würde
das Konzept der Interrupts zunichte machen (Erklärungen dazu weiter
unten). Es gilt ja die Regel, dass alle Interrupthandler so kurz wie
möglich zu halten sind, damit der µC immer genug Luft hat, alle
auftretenden Interrupts abzuarbeiten.
Was man natürlich nicht verhindern kann, ist das Auftreten einer
Interruptbedingung B, während sich das Programm gerade im
A-Interrupthandler aufhält. Viele Interrupts werden schließlich von
externen Ereignissen ausgelöst, und wenn Du z. B. ein Zeichen von Deinem
PC über die serielle Schnittstelle abschickst, weißt Du nicht, womit
Dein Programm in dem Moment beschäftigt ist, in dem die
UARTDataRxComplete-Interruptbedingung zutrifft.
Dann passiert folgendes. Grundsätzlich wird beim Auftreten einer
Interruptbedingung immer augenblicklich das zugehörige Interrupt-Flag
von der Interruptlogik gesetzt. Diese Flags stecken in den
I/O-Registern Deines Controllers (z. B. ICF1, TOV1, RXC, TXC, ACI).
Damit ist der Interrupt "vorgemerkt" (engl. pending interrupt). Wann
jedoch der Sprung auf den Interruptvektor (und von dort aus in den
Interrupthandler) erfolgt, hängt vom I-Flag im SREG (globales
Interrupt-Freigabeflag) ab. Solange es gelöscht (alle Interrupts
gesperrt) ist, erfolgt kein Sprung - der Interrupt bleibt vorgemerkt.
Ist es gesetzt, wird in jedem Fall noch die gerade ausgeführte
Instruktion zuende verarbeitet, und dann erfolgt der Sprung auf den
Interruptvektor. Mit diesem Sprung wird automatisch gleichzeitig das
spezifische Interruptflag gelöscht (*) und ebenso das I-Flag.
Interrupthandlers können also nicht durch andere Interrupts
geinterrupted werden (außer Du setzt das I-Flag am Anfang des Handlers
durch ein "sei" - das solltest Du aber wegen des Risikos eines
Stacküberlaufs niemals tun). Das Wieder-Setzen des I-Flags geschieht i.
a. mit dem "reti" am Ende eines Interruptshandlers, wobei dies dem
Programmierer obliegt.
(*) Meistens, aber es gibt spezielle Fälle, wo es anders geregelt ist.
Das I-Flag kann man auch selbst löschen und wieder setzten (mit "cli"
und "sei"), wenn man möchte, dass bestimmte kritische Programmabschnitte
aus irgendeinem Grund nicht von einem Interrupt unterbrochen werden. Das
nennt man eine Critical Section. Auch solche Abschnitte sollten
möglichst kurz gehalten werden.
Und klar, es kann sogar sein, dass im Moment des Wieder-Setzens des
I-Flags zufällig mehrere Interrupts anstehen. Dann wird derjenige
davon ausgeführt, der am weitesten "oben" in der Interruptvektor-Tabelle
steht, während die anderen natürlich pending bleiben. Das ist dann so
eine Art Interrupt-Priorität (aber keine "echte" wie bei anderen
Controllerfamilien).
Bei lange dauernden Interrupts würde man Gefahr laufen, Interrupts zu
"verlieren". Das wäre dann der Fall, wenn ein Interruptflag gesetzt
würde, welches schon gesetzt ist. Hält man dagegen alle
Interrupthandler und alle critical sections wie gesagt kurz, kann das
nicht passieren, weil alle Pending-Flags schnell wieder gelöscht werden.
Man kann diesen Mechanismus übrigens auch im Simulator vom AVRStudio in
allen Einzelheiten nachverfolgen. Gegebenenfalls viel Spaß ;-)
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.