Morgen,
vll kann mir einer von Euch helfen.
Und zwar erwischt mein Mega 16 mit 16MHz nicht immer alle Interrupts die
an INT0 landen. Ich will an INT0 eine Frequenzmessung durchführen.
Werf ich ihn nun 1 MHZ(TTL mit 5 und 0 Volt ) an den Interrrupt erwischt
er teilweise nicht alle Flanken.
Ich versteh blos nicht warum.
Hier mal der Codeabschnitt:
1
TIMSK=(0<<TOIE0)|(0<<TICIE1)|(0<<TOIE1);//--Timer0 und 1 abgeschalten
2
3
GICR=(1<<INT0);//--Interrupt aktivieren
4
MCUCR=(1<<ISC01);
5
6
Frequenzzaehler=0;//--Frequenzzähler nochmals auf 0 Setzen
Momentan lass ich mir nur den Zählerstand ausgeben.
Die Werte schwanken teilweise richtig Krass oder ergeben gleich 0.
Pfuscht da ein Timer dazwischen oder was?
Normalerweise sollten die ja abgeschaltet sein und der INT0 hat ja
sowieso höhere Priorität.
In einem Testprojekt welches nur die Frequenz zählen sollte, hauts auch
nicht hin.
Jemand von euch eine Idee?
MfG
Matthias
@ Matthias (Gast)
>Und zwar erwischt mein Mega 16 mit 16MHz nicht immer alle Interrupts die>an INT0 landen. Ich will an INT0 eine Frequenzmessung durchführen.>Werf ich ihn nun 1 MHZ(TTL mit 5 und 0 Volt ) an den Interrrupt erwischt>er teilweise nicht alle Flanken.>Ich versteh blos nicht warum.
Nein? Du bist ein Scherzkeks. Dein AVR hat gerade mal 16 Takte Zeit um
- in den Interrupt zu springen
- einen Zähler hochzählen
- zurückspringen.
Allein den Interrupt anspringen und zurückspringen dauert 10 Takte.
Solche Frequenzen mit einem Interrupt zu zählen ist ziemlicher Unsinn.
Das macht man mit einem Timer, welcher extern getaktet wird.
MFG
Falk
Wo ist die Interrupt-Routine?
Bedenke: Bei 1MHz Eingangssignal hast du 16 Takte Befehl zur Verfügung!
Mit ein paar pushes und pops könnte das knapp werden!
Hi Falk,
Hmm, aus reiner Neugierde, ich suche schon länger nach der Worst-Case
Latenzzeit eines AVR uC. Wie kommst Du auf die 10 Takte?
Aus einem AVR-Datenblatt (tiny13, wollte nicht gerade so einen Schinken
runterladen):
* The interrupt execution response for all the enabled AVR interrupts is
four clock cycles minimum. After four clock cycles the Program Vector
address for the actual interrupt handling routine is executed.
* The vector is normally a jump to the interrupt routine, and this jump
takes three clock cycles. (??? Doch nur zwei Taktzyklen mit rjmp?)
* If an interrupt occurs during execution of a multi-cycle instruction,
this instruction is completed before the interrupt is served.
* A return from an interrupt handling routine takes four clock cycles.
Damit komme ich auf
4 (Sprung zu Vektor) + 2 (Sprung zu Handler) + 3 (längste Instruktion,
mit ext. Speicher beliebig mehr) + 4 (Rücksprung) = 13 Taktzyklen.
Dann fehlt noch der Hinweis, dass das Statusregister nicht automatisch
gesichert wird, das darf man also auch noch machen. Wenn man hierfür
folgenden Code annimmt:
1
push d1
2
in d1,sreg
3
...
4
out sreg,d1
5
pop d1
Dann haben wir nochmal 6 Taktzyklen (push/pop je zwei, in/out je eine).
Macht schon 19 Taktzyklen. Wohlgemerkt, ohne, dass der Interrupthandler
irgendetwas machen.
Immerhin, wenn man ein Register über hat, kann man sich push/pop sparen
und mit 15 Taktzyklen auskommen. Bei kleinen AVRs hat eine Anweisung
max. 2 Taktzyklen (IIRC), da sind es dann 14 Taktzyklen worst case (wenn
interrupts gerade erlaubt sind).
Muss man noch etwas berücksichtigen, oder entspricht das den Tatsachen?
Der Worst-Case würde mich für die eine oder andere Anwendung schon
interessieren.
Ach, eins noch:
* If an interrupt occurs when the MCU is in sleep mode, the interrupt
execution response time is increased by four clock cycles. This increase
comes in addition to the start-up time from the selected sleep mode.
Also, aufwecken aus dem Sleep wird dann noch richtig spannend. Hier wohl
weniger relevant, wollte es nur der Vollständigkeit halber angeben.
Gruss aus Dresden, Torsten
Ich frag mich blos warum er dann mal die Werte erwischt und mal
überhaupt nix. Bei gleichem Code?
In der hälfte der Tests bisher hatte er die Werte. In 25% war er etwas
falsch und der Rest war immer null.
Wenn ers nun überhaupt nicht packen würde, sollten doch immer Konstant
Null rauskommen.
Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der
Fällen immer. Ab dann wirds erst Falsch.
@Bluehorn:
Das was im Datenblatt steht, ist nur das, was die µC-Hardware macht,
also quasi noch ein "best case"! Der Compiler sichert meist noch eine
ganze Stange Register (v.a. r0 und r1 sowie evtl. verwendete
Pointer-Register). Und beim ATMega16 wird sowieso automatisch ein jmp
eingebaut, weil der Mega16 nunmal 16 KiB Flash hat, und da reicht rjmp
u.U. nicht mehr aus.
Wenn Du in Assembler programmierst, dann kannst Du vielleicht besser
abschätzen, was wirklich alles gesichert werden muss, aber ein Compiler
muss nunmal gewisse Annahmen treffen. In Assembler kann man z.B. bei
ausreichend Reserven dafür sorgen, dass der Interrupt Handler nur
Rechenregister benutzt, die sonst nicht verwendet werden.
>Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der>Fällen immer. Ab dann wirds erst Falsch.
Der zählt ja auch locker flockig weiter ;)
Du verbietest den INT0 zu spät und setzt den Wert
zu früh auf null.
> Frequenzzaehler = 0;>> CLEAR_BIT(PORTC,2); //--Reset erneut Ziehen>> //--Strom und Spannung hohlen> Strom=get_Strom();> Spannung=get_Spannung();>> GICR = (0<<INT0); //--Interrupt abschalten> MCUCR = (0<<ISC01);
GICR = (0<<INT0); //--Interrupt abschalten
MCUCR = (0<<ISC01);
Frequenzzaehler = 0;
Dürfte zumindest bessere Ergebnisse liefern.
@ Matthias (Gast)
>Na sauber...sprich ich kann die Platine wieder umstricken.>Schöne Aussichten.
Tja, Ce'st la vie.
@ Bluehorn (Gast)
>Hmm, aus reiner Neugierde, ich suche schon länger nach der Worst-Case>Latenzzeit eines AVR uC. Wie kommst Du auf die 10 Takte?
So Pi mal Dauem aus der Erinnerung.
>4 (Sprung zu Vektor) + 2 (Sprung zu Handler) + 3 (längste Instruktion,>mit ext. Speicher beliebig mehr) + 4 (Rücksprung) = 13 Taktzyklen.
Passt.
>Dann fehlt noch der Hinweis, dass das Statusregister nicht automatisch>gesichert wird, das darf man also auch noch machen.
Muss man nur, wenn die Befehle in der ISR die Flags ändern. Eine Additon
tut das aber ;-)
>Macht schon 19 Taktzyklen. Wohlgemerkt, ohne, dass der Interrupthandler>irgendetwas machen.
Sieht du.
>Immerhin, wenn man ein Register über hat, kann man sich push/pop sparen>und mit 15 Taktzyklen auskommen. Bei kleinen AVRs hat eine Anweisung>max. 2 Taktzyklen (IIRC), da sind es dann 14 Taktzyklen worst case (wenn>interrupts gerade erlaubt sind).
Geht aber nur in ASM. In c sind die Interrupts einen Tick ineffizienter,
weil der Compiler (zumindest was ich bein GCC gesehen habe) immer bisser
rumpusht und poppt sowie am Stack fummelt.
>Muss man noch etwas berücksichtigen, oder entspricht das den Tatsachen?
Das passt soweit.
>Also, aufwecken aus dem Sleep wird dann noch richtig spannend. Hier wohl>weniger relevant, wollte es nur der Vollständigkeit halber angeben.Sleep Mode
@ Matthias (Gast)
>Ich frag mich blos warum er dann mal die Werte erwischt und mal>überhaupt nix. Bei gleichem Code?
Ist doch logisch. Dein Programmist ein Zufallsgenerator. dein
_dlay_ms(10) kannst du bei aktivein interrutps KOMPLETT vergessen, bei
200% Auslastung durch einen 1 MHz Interrupt sowieso.
>Wenn ers nun überhaupt nicht packen würde, sollten doch immer Konstant>Null rauskommen.
Nöö, er verschluckt einfach diverse Interrupts.
>Vorallem wenn ichs das erste Mal Abfrage stimmts eigentlich in 99% der>Fällen immer. Ab dann wirds erst Falsch.
Zufall.
MfG
Falk
> GICR = (0<<INT0); //--Interrupt abschalten> MCUCR = (0<<ISC01);>> Frequenzzaehler = 0;
Obige Antwort ignorieren oder löschen :)
Ich war so frei den Code darüber zu ignorieren. AARgh.
@ Severino R. (severino)
>> Tja, Ce'st la vie.> ^^^^^>Error (orthography)! Try "C'est la vie." instead.
Jaja, Französisch kann ich ganz gut, nur mit der Sprache haperts . . .
;-)
duckundwech
Falk
hi,
ist schon 'ne ganze weile her, ich hab' auch mal frequenzzähler
'gespielt' mit den avrs. mit demselben problem, mal hat die anzeige
gestimmt, mal nicht, auffällig war aber, dass die differenz zum
erwarteten wert (die frequenz war bekannt und wurde durch einen anderen
'richtigen' frequenzzähler kontrolliert) und ausgegebenen wert immer die
gleiche war. ursächlich dafür war die tatsache, dass während der
abarbeitung eines ints manchmal schon der nächste ausgelöst wurde, aber
nicht abgearbeitet. das konnte dann per abfrage des interrupt flag
register 'getürkt' werden, wenn ein unbearbeiteter (kann man das so
schreiben?) isr anstand, war das ergebnis schrott, dafür das flag
gesetzt, ergo korrekturmöglichkeit gegeben.
die ganze problematik ging aber wohl davon aus, dass isrs eben synchron
zum systemtakt erfolgen, die zu messende freq. aber nicht.
habe dann den counter verwendet, der erlaubt asynchrone verwendung,
damit konnte ich dann immerhin bis knapp zur halben betriebsfrequenz des
avrs messen, mit immer richtigen ergebnissen.
grüssens, harry
> Na sauber...sprich ich kann die Platine wieder umstricken.
Nicht unbedingt. Soll das ein Messgerät werden? Wenn es nur am
ISR-Overhead scheitert, dann lass die ISR doch weg und werte den Pin bei
abgeschalteten Interrupts aus. Dafür gibt es mehrere Möglichkeite.
1. Beispiel: Schreibe ein exakt <x>us langes Codefragment (Länge
ausrechnen) mit Port-Abfragen. Ohne Sprünge und mit einer
Ausführungszeit unabhängig vom Pin-Zustand. Etwa so:
reading = PINn & MASK; counter += (reading ^ state); state = reading;
reading = PINn & MASK; counter += (reading ^ state); state = reading;
reading = PINn & MASK; counter += (reading ^ state); state = reading;
...
Nach dessen Durchlauf wird die Anzahl der Pegelwechsel in einer
Variablen stehen. Kann man dann über die bekannte Dauer des Codes in
Frequenz umrechnen.
2. Eine andere Möglichkeit wäre, einen Timer mit F_CPU loslaufen zu
lassen. 100 Pegelwechsel per Busy-Pooling des Pins zählen. Dann den
neuen Timerstand auslesen und daraus die Frequenz berechnen:
... // start timer
loops = 100;
start = TCNT0;
while (loops--)
{
while ((PINA & MASK) == 0)
;
while (PINA & MASK)
;
}
stop = TCNT0;
Gefahr hier ist, dass der Code hängt, wenn das Signal ausbleibt.