Tach liebe Gemeinde,
Ich expirementire grad mit Frequenzmessung per ICP des ATmega88a und
4xSieben-Segment-Anzeigen. Leider flackert die Anzeige mit steigender
Frequenz immer mehr zwischen Werten "Null" und der richtigen Frequenz :(
Überschneiden sich da etwa die Interrupts?
LG
Du machst deine LED Matrixausgabe in der Main Loop.
Das kann so nicht gehen. Die wird je nach Interruptlast
mehr oder weniger flackern. Und noch schlimmer, wenn
ein Interrupt komplett blockiert können dir die LEDs
wegen zu hohem Strom kaputt gehen.
Deine LED Matrixausgabe muss in einen Timerinterrupt.
Die Berechnung was ausgegeben werden soll kannst du in der
Main Loop machen.
So war auch die letzte Version - in der aktuellen ist in
ISR(TIMER2_OVF_vect) ja nur die Positionierung des eingeschalteten
Segments.
Mein Gedanke war, dass ICP so mehr Priorität bekommt. Völlig unwichtig,
funktioniert beides nicht.
Aber prinzipiell dürfte ICP+Multiplxing nichts im Wege stehen oder?
>ISR (TIMER2_OVF_vect) // timer0 overflow interrupt>{>> if(UpdateDisplay) // Das Display wurde mit den Ergebnissen der >vorhergehenden> { // Messung noch nicht aktualisiert. Die nächste Messung> return; // verzögern, bis die Start- und EndTime-Variablen >wieder> } // gefahrlos beschrieben werden können
Mach dieses if(UpdateDisplay)
da weg. Dein Matrixdisplay muss immer upgedatet werden,
egal ob deine Messung noch läuft oder nicht. Zeig dort
halt immer den letzten Wert an. Du kannst das
multiplexen deiner Matrix nicht einfach unterbrechen.
Dann flackert das Viech wie Hölle.
Oh danke, nicht aufgepasst. Das Problem ist eben folgendes: Es gibt kein
Flimmern wie beim falscheingestellten Multiplex. Die Anzeige flackert
immer zwischen Null und der richtigen Frequenz. Nur wird das Flackern
bei steigender Freqenz immer schneller.
Und ich weiß dafür einfach keinen Grund
Fan_AT schrieb:> Aber prinzipiell dürfte ICP+Multiplxing nichts im Wege stehen oder?
Nein, natürlich nicht. Funktionierende Schaltung + Code kannst dir hier
ansehen: Frequenzzählermodul
Da flackert gar nix, obwohl sich die LED-Displays die Controllerpins
sogar mit dem Vorteiler und den Jumpern teilen.
Ein Vorschlag zur allgemeinen Strukturierung:
1. mach einen Display-Buffer für den darzustellenden Inhalt deines
Displays. Bei 7-Segment bietet es sich an, hier direkt die Segmente
abzuspeichern.
2. such dir eine Interruptquelle, die ca. 100Hz * Anzahl Stellen hat.
Das kann ein Timerüberlauf sein oder ein Compare Match. Wichtig:
regelmäßig muß es sein und nicht zu langsam (dann flimmert die Anzeige)
3. schreib eine ISR für diesen Interrupt, der den Display-Buffer
zyklisch ausliest und die jeweils nächste Stelle ausgibt.
Die eigentliche Messung machst du in der Mainloop. Und wenn du ein neues
Ergebnis hast, dann schreibst du es in den Display-Buffer. Auf diese
Weise ist es auch trivial, einen Meßwert "einzufrieren".
Wenn es nur kurzzeitig ist, darfst du dann in der Mainloop auch cli/sei
verwenden. Mit etwas Geschick kann man sogar das Display für ein paar µs
ausschalten, die Pins für was anderes verwenden (Vorteiler und Jumper
auslesen) und dann wieder einschalten. Obiger Frequenzzähler macht das
in main.c Zeile 136 bis 180.
XL
Die Multiplexroutine kümmert sich nicht mehr um logische Zusammenhänge.
Das ist alles der Job des Codes, der die auszugebenden Bitmuster für die
Anzeigen zusammenstellt. Die Multiplexroutine schaufelt nur noch Daten
raus. Nicht mehr und nicht weniger.
PS: Anstatt Einzelvariablen für Tausender, Hunderter etc. ist ein Array
eine ganz einfache Sache. Deine Multiplexroutine sieht dann ganz einfach
so aus
1
volatileuint8_tdigits[4];// Speicher für die eizelnen Digits
fertig, Das ist deine ganze Multiplexroutine.
Wenn irgendein andere Code was auf die Anzeige zaubern will, dann hat er
gefälligst die entsprechenden Bytes in das digit-Array zu stellen.
Ob da jetzt bei Zahlen eine führende 0 angezeigt wird oder nicht, das
entscheidet die Funktion, die einen int in die einzelnen Digits zerlegt
und die zugehörigen auszugebenden Bitmuster in das digit Array schreibt.
Das entscheidet NICHT die Multiplexroutine! Die schaufelt nur Bytes raus
auf die Anzeige. Und wenn in den digits enthalten ist, dass immer nur
die oberen Querbalken der einzelnen Segmente leuchten sollen, dann ist
das eben so - das hat die Multiplexroutine nicht zu kümmern. Ab sofort
gibt es für den Rest des Programms nur noch dieses digits-Array. Was
auch immer da an auszugebendem Leuchtmuster in die einzelnen Stellen
reingeschrieben wird, das wird angezeigt.
1
voidzahl_ausgeben(uint16_tzahl)
2
{
3
uint8_ti;
4
5
digits[4]=numbers[zahl%10];
6
zahl/=10;
7
8
for(i=3;i!=255;i--)
9
{
10
if(zahl>0)
11
{
12
digits[i]=numbers[zahl%10];
13
zahl=zahl/10;
14
}
15
else
16
digits[i]=numers[10];
17
}
18
}
und nein, die zahl_ausgeben darf auch durch einen Interrupt unterbrochen
werden. Du hast dann ganz kurz eine falsche Zahl an der Anzeige. Das ist
aber so kurz, dass du es nicht sehen wirst. Viel wichtiger ist, dass die
Multiplex-ISR in gleichen Abständen arbeiten darf. Ob da ein paar
Millisekunden lang eine zusammengewürfelte Anzeige sichtbar ist oder
nicht, ist für uns Menschen nicht merkbar.
> Die Anzeige flackert immer zwischen Null und der richtigen Frequenz
Nachdem du den Multiplex korrekt gemacht hast, musst du dann eben
nachsehen, warum in deinen Berechnungen immer abwechselnd 0 und ein
anderer Wert rauskommt.
Wow, da sitzt man, programmiert - und dann geht das noch tausendfach
kompakter -.-
Aktuell sieht es so aus, leider noch irgendwo ein kleiner Fehler drin -
es wird gar nichts angezeigt. Aber vielen Dank schonmal für die Tipps.
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include<avr/io.h>
10
#include<avr/interrupt.h>
11
#include<util/atomic.h>
12
13
volatileunsignedshortz=0;// Zählvariable des Timer1
14
volatilecharUpdateDisplay=FALSE;// char-1Byte // Einen Wert TRUE definiert
Nur um sicher zu gehen. Teste mal das Multiplexing ganz alleine. Zuviele
Änderungen gleichzeitig sind nicht gut in der Entwicklung. D.h. wir
gehen mal einen Schritt zurück und sehen nach, ob die Basis auch
wirklich funktioniert.
1
intmain(void)
2
{
3
uitn16_tcnt=0;
4
5
DDRC=0xFF;// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
6
DDRD=0xFF;// PORTD als Ausgang
7
PORTC=0b00000000;
8
9
TIMSK|=(1<<TOIE2);
10
TCCR2|=(1<<CS22)|(1<<CS21);// Vorteiler auf 256
11
12
sei();
13
14
while(1)
15
{
16
zahl_ausgeben(cnt++);
17
if(cnt==10000)
18
cnt=0;
19
20
_delay_ms(200);
21
}
22
23
return0;
24
}
Sobald dann von dir die Bestätigung kommt, dass das funktioniert, ist
damit dann das Thema Multiplexing vom Tisch. Darum brauchst du dich dann
nicht mehr kümmern.
In deinem Quote, nur eine Zeile später kommt sei(); vor. Damit aktiviere
ich doch auch global die Interrupts?
Wie dem auch sei - irgendwo habe ich einen Fehler eingebaut - der
Multiplex funktioniert gar nicht. Die Anzeige ist aber intakt.
Hier der Code für reinen Multiplex-Betrieb:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include<avr/io.h>
10
#include<avr/interrupt.h>
11
#include<util/atomic.h>
12
#include<util/delay.h>
13
14
15
volatileuint8_tdigits[4];// Speicher für die eizelnen Digits
Fan_AT schrieb:> Ich melde mich, sobald ich den Fehler finde, wird vllt. noch etwas> dauern :D
Moment. Hast du den Overflow Interrupt jetzt freigegeben?
In deinem Programm hat das hier
1
TIMSK|=(1<<TOIE2);
gefehlt!
> Gut, digitControl kann man als volatile deklarieren. Ändern tut sich aber
nichts.
Wer hat was von einem fehlenden volatile gesagt? Da fehlt keiner.
> In deinem Quote, nur eine Zeile später kommt sei(); vor. Damit aktiviere ich
doch auch global die Interrupts?
Argh.
sei() ist das Rundumschlag - 'Jetzt gilt es. Alle aktivierten Interrupts
sind auch aktiv und können Funktionsaufrufe auslösen'. Sozusagen der
Hauptschalter.
Aber nichts desto trotz musst du auch mal wo festlegen, welche
Interrupts auch zugelassen sind. Der Mega hat viele mögliche
INterrupt-Quellen. Die interessieren dich aber alle nicht.
Dich interessiert der Interrupt vom Overflow des Timers 2. Daher
Hmm.
Hab ich deine Hardware-Belegung aus dem Original-Code falsch
rausgelesen?
Am Port-D hängen die Segmentleitungen. Ein 1-bit aktiviert das Segment
Am Port-C, Pins 0 bis 3, hängen die Digit-Leitungen. Ein 1 Bit aktiviert
das entsprechende Digit.
> TIMSK |= (1<<TICIE1) | (1<<TOIE1) | (1 << TOIE2); // Interrupts akivieren, >Capture und Overflow, Overlow-Interrupt aktivieren
Die Freigabe erfolgte nur zusammen in einer Zeile mit dem Rest - aber
sie war da.
Von 'E' halte ich sehr viel, wenn es funktioneirt. Du wirst wohl lachen,
aber der Code funktioniert nicht. Hier:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
#include<util/delay.h>
6
7
8
volatileuint8_tdigits[4];// Speicher für die eizelnen Digits
4x 7-Segment Anzeigen mit je gemeinsamen Kathode. Die Steuerleitungen
für die Digits liegen auf PORTC=0b0000XXXX.
Die einzelnen Segmente liegen auf PORTD.
Fan_AT schrieb:> Von 'E' halte ich sehr viel, wenn es funktioneirt.
Gut ich poste dann den Code nochmal. Hab ihn wieder rausgelöscht, damit
der Thread kein Durcheinander wird.
> Du wirst wohl lachen,> aber der Code funktioniert nicht.
Ich lache gar nicht.
Ich sollte eher weinen, weil ich dir aus deinem Urprogramm geglaubt
habe, dass
1
TIMSK|=(1<<TOIE2);
richtig ist, wenn es das tatsächlich nicht ist.
1
TIMSK2|=(1<<TOIE2);
Ich hätte es eigentlich mit dem Datenblatt kontrollieren sollen. Getreu
dem Motto: nimm nichts als richtig an, ehe du es nicht überprüft hast.
Nein, keine Sorge - es funktioniert immer noch nicht. Teste es aktuell
auf 'nem ATmega8 und vergessen, den Codeschnipsel zu ändern. Tut mir
Leid.
Also: klappt weder auf dem uC, noch auf dem anderen. Ich gehe nochmal
alle Register durch.
Fan_AT schrieb:> Mir ist die Schleifenbedingung allerdings nicht ganz geheuer oO
Allerdings hast du recht.
Asche auf mein Haupt
> ginge das nicht mit>
1
>for(i=2;i>=0;i--)
2
>
So zwar nicht, denn i ist sowieso als unsigned Wert IMMER größer oder
gleich 0. i kann nicht negativ sein
Allerdings hat ich noch einen kapitalen Bock geschossen. Einen richtigen
Anfängerfehler.
Hier ist die Korrektur
Hoppla! Das war echt die for-Bedingung :(
Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit
der max.Größe des uint8_t zusammen?
Vielen, vielen Dank schonmal dafür - ich sah den Wald vor lauter Bäumen
nicht mehr...
Fan_AT schrieb:> Hoppla! Das war echt die for-Bedingung :(> Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit> der max.Größe des uint8_t zusammen?>> Vielen, vielen Dank schonmal dafür - ich sah den Wald vor lauter Bäumen> nicht mehr...
Puh.
Da muss ich mir jetzt erst mal den Schweiss von der Stirn wischen.
Ich seh grad, den anderen Fehler, der mich wie einen absoluten Tölpel
hat aussehen lassen, hast du selbst schon korrigiert gehabt und
Stillschweigen darüber bewahrt. Anständig von dir!
1
digits[4]=numbers[zahl%10];
könnt mich jetzt noch in den Allerwertesten beissen deswegen.
Fan_AT schrieb:> Hoppla! Das war echt die for-Bedingung :(> Hätte mir, Deppen, auch einfallen können... die ungleich 255 hängt mit> der max.Größe des uint8_t zusammen?>
Jep.
Ist quasi -1 für einen unsigned Wert mit 8 Bit
Ja ich weiss. ist nicht so toll. Aber mir ist auf die Schnelle nichts
anderes eingefallen :-)
Das ist alles besser, als das von mir Geleistete ;)
Hier nochmal der gesamte Code - der in seiner Gesamtheit - wieder nichts
anzeigt. Ob du den Fehler auf Anhieb siehst?
Das Ganze läuft jetzt auf ATmega8 - die Register scheinen alle richtig
zu sein. Überprüfe ich aber grade. Danke dir für die ganze Mühe...
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include<avr/io.h>
10
#include<avr/interrupt.h>
11
#include<util/atomic.h>
12
13
volatileunsignedshortz=0;// Zählvariable des Timer1
14
volatilecharUpdateDisplay=TRUE;// char-1Byte // Einen Wert TRUE definiert
Fan_AT schrieb:> Das ist alles besser, als das von mir Geleistete ;)>> Hier nochmal der gesamte Code - der in seiner Gesamtheit - wieder nichts> anzeigt.
D.h. das Multiplexing, Zahlenausgabe, Überlauferkennung in der
Zahlenausgabe läuft erst mal?
(Sorry wenn ich nachfrage. Aber ich hab nur das, was du schreibst. Und
ich brauch klare Bestätigungen was geht und was nicht geht. Ist für mich
wichtig, damit ich weiß, welche Teile ich aus der Fehlersuche
ausklammern kann)
Das hat in der Funktion nichts verloren.
Disziplin!
Eine Funktion hat eine Aufgabe.
Die Aufgabe der Funktion zahl_ausgeben ist es, eine Zahl auszugeben.
Egal welche. Ob freq 0 ist oder nicht, interessiert hier nicht.
Wenn der restliche Code eine Anzeige von 0 haben will, dann soll er
zahl_ausgeben(0) aufrufen. Aber nicht hier in der Funktion irgendwelche
Hacks einbauen. Das sind alles Dinge, die dir früher oder später auf den
SChädel fallen.
Frag dich immer:
Welche Aufgabe hat eine Funktion? Was gehört zu dieser Aufgabe? Was
gehört NICHT zu dieser Aufgabe?
Gut, Disziplin, gemerkt. Und gelöscht.
Gut, also... Nein, eher schlecht. Unser letztes Programm, reines
Multiplxing zeigt zwar eine Null an - aber dabei bleibt's auch. Ich bin
noch nicht fündig geworden.
Lass uns den Teil in main mal korrekt einrücken. Dann sieht man schon
mal mehr
1
while(1)// unendliche Schleife
2
{
3
4
if(UpdateDisplay)// Erst nach zweiter Flanke ausführen
5
{
6
ATOMIC_BLOCK(ATOMIC_FORCEON)
7
{
8
zaehlschritte=(unsignedlong)((65536.0*zaehler1_ovf)+Zeit);// sonst am Ende: +Zeitdifferenz;
9
}
10
11
12
if(zaehlschritte==0)
13
{
14
freq=0;
15
}
16
else
17
{
18
ATOMIC_BLOCK(ATOMIC_FORCEON)
19
{
20
// Die abschließende Berechnung der Schritte pro Sekunde
21
freq=(uint16_t)(F_CPU/(zaehlschritte));
22
}
23
zahl_ausgeben(freq);
24
25
26
UpdateDisplay=FALSE;
27
}
28
}
29
}
30
31
return0;
32
}
Hä?
Da stimmt doch in der logischen Struktur was nicht.
Damit zahl_ausgeben aufgerufen werden kann, müssen ein paar
Vorbedingungen gültig sein.
Aber noch viel schlimmer.
Damit UpdateDisplay je FALSE werden kann, müssen ebenfalls ein paar
Vorbedingungen gültig sein. Bei einem 0 Ergebnis in zaehlschritte, wird
UpdateDisplay nicht FALSE. Was dann wiederrum im Gegenzug dazu führt,
das in der ISR
1
ISR(TIMER1_CAPT_vect)
2
{
3
4
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
5
{// Messung noch nicht aktualisiert. Die nächste Messung
6
return;// verzögern, bis die Start- und EndTime-Variablen wieder
7
}// gefahrlos beschrieben werden können
8
...
keine weitere Messung mehr durchgeführt wird, die dafür sorgen könnte,
dass zaehlschritte ungleich 0 werden könnte.
Das muss in main anders angeordnet werden!
Ein 0 Ergebnis ist kein fehlerhaftes Ergebnis, sondern einfach nur 0.
Auch das wird angezeigt und UpdateDisplay auf FALSE gesetzt, damit eine
weitere Messung durchgeführt werden kann.
Saubere Einrückungen sind nicht einfach nur eine optische Sache!
Sie helfen dir die Codestruktur zu überblicken!
Hm, UpdateDisplay muss außerhalb der if-Abfrage stehen.
Struktur ist sehr gut, man muss aber auch die Sprache beherrschen :) Ist
jetzt auf mich bezogen.
Mein letzter Post bezog sich auf das reine Multiplex-Programm, was
lediglich eine Null anzeigt und nicht weiterzählt.
if(UpdateDisplay)// Erst nach zweiter Flanke ausführen
sollte anders aussehen.
Das ist nicht 'wenn zweite Flanke'. D.h. technisch ist es das schon.
Aber an dieser Stelle ist es besser zu lesen als:
Wenn eine Messung fertig ist.
Dieses Flag hat 2 Aufgaben:
Zum einen ist es die Benachrichtigung aus der ISR in die main() "Ich
habe eine Messung fertig". Zum anderen ist es die Verriegelung der ISR
mit isch selbst, so lange keine weitere Messung zu machen, bis die
main() das Ergebnis verarbeitet hat.
D.h. wenn main() die Messergebnisse der ISR ausgewertet hat, dann muss
sie UpdateDisplay wieder auf FALSE setzen. Egal was dann bei der
Auswertung rauskommt. Das FALSE-setzen von UpdateDisplay ist quasi die
Rückmeldung von main() in die ISR "OK, ich hab mir deinen Messwert
geholt, du kannst wieder loslegen und die nächste Messung machen". Das
aber, muss IMMER passieren. Egal was dann bei der Auswertung rauskommt
Fan_AT schrieb:> Mein letzter Post bezog sich auf das reine Multiplex-Programm, was> lediglich eine Null anzeigt und nicht weiterzählt.
Was jetzt?
Wir reden aneinander vorbei.
Können wir uns erst mal auf eine Sache, und nur auf eine Sache
konzetrieren?
1
intmain(void)
2
{
3
4
5
DDRC=0xFF;// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
6
DDRD=0xFF;// PORTD als Ausgang
7
PORTC=0b00000000;
8
9
TIMSK|=(1<<TOIE2);
10
TCCR2|=(1<<CS22)|(1<<CS21);// Vorteiler auf 256
11
12
sei();
13
14
while(1)
15
{
16
zahl_ausgeben(cnt++);
17
if(cnt=10000)// <------- *** das ist kein Vergleich ***
18
cnt=0;
19
20
delay_ms(200);
21
}
22
23
return0;
24
}
läuft das (mit der Korrektur): ja oder nein.
Zeigt die Anzeige hochzählende Zahlen: ja oder nein.
Gut, bleiben wir erstmal beim reinen Multiplex.
Ja, das Programm läuft und zeigt eine Null an, ohne Flimmer, Flackern.
Nein, das Programm zählt nicht, sondern bleibt konstant bei Null.
Fan_AT schrieb:> Gut, bleiben wir erstmal beim reinen Multiplex.>> Ja, das Programm läuft und zeigt eine Null an, ohne Flimmer, Flackern.>> Nein, das Programm zählt nicht, sondern bleibt konstant bei Null.
Ok. Dann korrigier mal
Fan_AT schrieb:> Genau, mit dem Vergleich läuft es nun durch und beginnt von neu.
Gut. Wie willst du weitermachen?
Mit dem Overflow-E bei der Zahlausgabe?
Wär vernünftig, damit zu große Rechenergebnisse in der
Frequenzberechnung nicht alles durcheinander schmeissen.
Die Frequenzbegrenzung ist bereits fertig und wird noch vor den
Frequenzzähler geschaltet. Aber das "E"-Programm funktioniert
einwandfrei.
Jetzt das Programm in seiner Gesamtheit. UpdateDisplay ist nun außerhalb
der if-Abfrage. Es wird nichts angezeigt.
Hier der Code:
1
#define F_CPU 16000000UL // uC läuft mit 16MHz
2
3
#ifndef TRUE
4
#define TRUE 1
5
#define FALSE 0
6
#endif
7
8
9
#include<avr/io.h>
10
#include<avr/interrupt.h>
11
#include<util/atomic.h>
12
13
volatileunsignedshortz=0;// Zählvariable des Timer1
14
volatilecharUpdateDisplay=TRUE;// char-1Byte // Einen Wert TRUE definiert
Ok. Sieht wohl so aus, als ob die Ausgabe damit zuverlässig wäre. Auch
wenn ich nicht verstehe, warum du schon wieder Code präsentierst, in dem
die Overflow-Behandlung in zahl_ausgabe nicht enthalten ist.
Konzentrieren wir uns mal auf die Capture ISR und schreiben die mal
übersichtlicher. Dein Klammern Einrückschema macht mich nämlich
wahnsinnig.
1
ISR(TIMER1_CAPT_vect)
2
{
3
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
4
{// Messung noch nicht aktualisiert. Die nächste Messung
5
return;// verzögern, bis die Start- und EndTime-Variablen wieder
6
}// gefahrlos beschrieben werden können
7
8
staticcharErsteFlanke=TRUE;
9
staticuint8_tLowByte=0;
10
staticuint8_tHighByte=0;
11
12
LowByte=ICR1L;// LowByte zuerst, HighByte später gepuffert
13
HighByte=ICR1H;
14
15
if(ErsteFlanke)
16
{
17
Startzeit=ICR1;
18
z=0;
19
ErsteFlanke=FALSE;// Nach der nächsten Flanke beginnt die Ausgabe
20
}
21
22
if((HighByte<128)&&(TIFR&(1<<TOV1)))
23
{
24
// wartenden Timer Overflow Interrupt vorziehen
25
++z;
26
TIFR=(1<<TOV1);// Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
27
}
28
else
29
{
30
Zeit=0;
31
Zeit=ICR1-Startzeit;
32
UpdateDisplay=TRUE;// Eine vollständige Messung. Sie kann ausgewertet werden
33
ErsteFlanke=TRUE;// Bei der nächsten Flanke beginnt der nächste Messzyklus
34
35
36
zaehler1_ovf=z;// Anzahl der Überläufe wird in zaehler1_ovf kopiert
37
Startzeit=0;
38
z=0;// Nach dem Sichern - annullieren
39
}
40
}
Hmm. Das kann nicht stimmen. Das 'else' in dem der UpdateDisplay=TRUE
sitzt, gehört zum
1
if((HighByte<128)&&(TIFR&(1<<TOV1)))
das ist aber ganz sicher nicht die Bedingung, die einen Update
verhindern sollte. Das else sollte zur Abfrage der ersten Flanke
gehören. Zweck des ganzen ist es doch, abwechselnd den Capture Wert dem
Beginn eines Pulses bis zum Beginn des nächsten Pulses zuzuordnen.
PS: mit welcher Eingangsfrequenz testest du eigentlich? Welche Frequenz
wirfst du dem Teil vor?
Nur damit man mal ein bischen nachrechnen kann, was da so an
Zählerwerten rauskommen müsste.
Nope. Das sieht nicht richtig aus.
Können wir uns darauf einigen, die Overflowbehandlung erst mal beiseite
zu lassen und du gibst dem Tei lnur Frequenzen zu schlucken, die keinen
zu berücksichtigenden Overflow auslösen werden?
IMOH müsste das ganze zumindest so aussehen
1
ISR(TIMER1_CAPT_vect)
2
{
3
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
4
{// Messung noch nicht aktualisiert. Die nächste Messung
5
return;// verzögern, bis die Start- und EndTime-Variablen wieder
6
}// gefahrlos beschrieben werden können
7
8
staticcharErsteFlanke=TRUE;
9
staticuint8_tLowByte=0;
10
staticuint8_tHighByte=0;
11
12
LowByte=ICR1L;// LowByte zuerst, HighByte später gepuffert
13
HighByte=ICR1H;
14
15
if(ErsteFlanke)
16
{
17
Startzeit=ICR1;
18
z=0;
19
ErsteFlanke=FALSE;// Nach der nächsten Flanke beginnt die Ausgabe
20
}
21
else
22
{
23
Zeit=ICR1-Startzeit;
24
UpdateDisplay=TRUE;// Eine vollständige Messung. Sie kann ausgewertet werden
25
ErsteFlanke=TRUE;// Bei der nächsten Flanke beginnt der nächste Messzyklus
26
27
28
zaehler1_ovf=z;// Anzahl der Überläufe wird in zaehler1_ovf kopiert
Wie ich bereits schrieb - die Frequenzbegrenzung ist bereits fertig und
wird noch vor den Frequenzzähler geschaltet. Darum habe ich mich bereits
gekümmert. Die genaue Frequenz kann ich an der Skala des
Funktionsgenerators ablesen, die ich zuvor mit 'nem Oszilloskop
eingezeichnet habe.
Ist also nicht das Problem, das Testen.
Der Frequenzbereich ist mit 1-1400 Hz klein gehalten und einige
Variablen könnten noch kleiner deklariert werden.
1
ISR(TIMER1_CAPT_vect)
2
{
3
4
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
5
{// Messung noch nicht aktualisiert. Die nächste Messung
6
return;// verzögern, bis die Start- und EndTime-Variablen wieder
7
}// gefahrlos beschrieben werden können
8
9
10
staticcharErsteFlanke=TRUE;
11
staticuint8_tLowByte=0;
12
staticuint8_tHighByte=0;
13
14
LowByte=ICR1L;// LowByte zuerst, HighByte später gepuffert
15
HighByte=ICR1H;
16
17
18
if((HighByte<128)&&(TIFR&(1<<TOV1)))
19
{
20
// wartenden Timer Overflow Interrupt vorziehen
21
++z;
22
TIFR=(1<<TOV1);// Timer Overflow int. löschen, da schon hier ausgeführt // macht der uC angeblich selbst
23
}
24
25
26
27
28
if(ErsteFlanke)
29
{
30
Startzeit=ICR1;
31
z=0;
32
ErsteFlanke=FALSE;// Nach der nächsten Flanke beginnt die Ausgabe
33
34
}
35
36
else
37
{
38
Zeit=0;
39
Zeit=ICR1-Startzeit;
40
UpdateDisplay=TRUE;// Eine vollständige Messung. Sie kann ausgewertet werden
41
ErsteFlanke=TRUE;// Bei der nächsten Flanke beginnt der nächste Messzyklus
42
43
44
zaehler1_ovf=z;// Anzahl der Überläufe wird in zaehler1_ovf kopiert
45
Startzeit=0;
46
z=0;// Nach dem Sichern - annullieren
47
48
}
49
50
51
}
Du hast Recht, es ist die Bedingung, die einen verpassten Interrupt
wieder miteinbezieht.
Karl Heinz Buchegger schrieb:> PS: mit welcher Eingangsfrequenz testest du eigentlich? Welche Frequenz> wirfst du dem Teil vor?> Nur damit man mal ein bischen nachrechnen kann, was da so an> Zählerwerten rauskommen müsste.
Was hast du an Werten.
Du hast 16Mhz und Prescaler 1.
D.h. der Timer1 mit seinen 16 Bit wird in 1 Sekunde ...
1
16000000 / 65536 = 244.1
... 244 mal überlaufen.
D.h. jede Eingangsfrequenz, die höher als 244Hz ist, dürfte dann
eigentlich keinen zu berücksichtigenden Overflow während des Captures
verursachen. Sicherheitshalber noch höher gehen. 1kHz, wenn du hast,
sollte eine gute Basis sein, um damit zu testen.
> Der Frequenzbereich ist mit 1-1400 Hz
1 ist zuwenig!
Du brauchst erst mal eine Eingangsfrequenz, so dass dir der Timer keine
Differenz liefern müsste, die größer als 65535 ist! Also keinen zu
berücksichtigenden Overflow!
Stell mal 1kHz ein. Rechnet sich leichter als 1.4kHz :-)
Was ergibt sich daraus?
1kHz bedeutet, dass der Capture Interrupt alle 1ms aufgerufen werden
müsste.
Wie weit kann der Timer in 1ms zählen?
In 1 Sekunde würde er bis 16000000 zählen. In 1ms (also 1/1000) Sekunde
kommt er logischerweise nur 1/1000 so weit. Also bis 16000.
D.h.
Aus
1
ISR(TIMER1_CAPT_vect)
2
{
3
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
4
{// Messung noch nicht aktualisiert. Die nächste Messung
5
return;// verzögern, bis die Start- und EndTime-Variablen wieder
6
}// gefahrlos beschrieben werden können
7
8
staticcharErsteFlanke=TRUE;
9
staticuint8_tLowByte=0;
10
staticuint8_tHighByte=0;
11
12
LowByte=ICR1L;// LowByte zuerst, HighByte später gepuffert
13
HighByte=ICR1H;
14
15
if(ErsteFlanke)
16
{
17
Startzeit=ICR1;
18
z=0;
19
ErsteFlanke=FALSE;// Nach der nächsten Flanke beginnt die Ausgabe
20
}
21
else
22
{
23
Zeit=ICR1-Startzeit;
24
UpdateDisplay=TRUE;// Eine vollständige Messung. Sie kann ausgewertet werden
25
ErsteFlanke=TRUE;// Bei der nächsten Flanke beginnt der nächste Messzyklus
26
27
28
zaehler1_ovf=z;// Anzahl der Überläufe wird in zaehler1_ovf kopiert
29
Startzeit=0;
30
z=0;// Nach dem Sichern - annullieren
31
}
32
}
müssten sich Werte für 'Zeit' von um die 16000 ergeben.
16000 kannst du auf einer 4 stelligen 7_seg nicht anzeigen. ABer 1/10
davon.
Also
1
intmain()
2
{
3
...
4
5
while(1)// unendliche Schleife
6
{
7
if(UpdateDisplay)// Erst nach zweiter Flanke ausführen
8
{
9
ATOMIC_BLOCK(ATOMIC_FORCEON)
10
{
11
zaehlschritte=Zeit/10;
12
}
13
zahl_ausgeben(zaehlschritte);
14
UpdateDisplay=FALSE;
15
}
16
}
17
return0;
18
}
Hast du bei 1kHz die erwarteten 1600 (+- ein paar Zerquetschte) auf der
Anzeige?
:-)
Und du siehst hoffentlich jetzt auch, warum ich so drauf gedrängt habe,
die Zahlenausgabe erst mal fehlerfrei zu machen.
Denn die brauchen wir jetzt! Die muss zum jetzigen Zeitpunkt über jeden
Fehler erhaben sein.
Denn was ich jetzt nicht brauchen kann, das ist, das ich nicht weiß ob
ein Fehler in der Messung oder in der Ausgabe passiert. Die Ausgabe und
Anzeige MUSS zum jetzigen Zeitpunkt etwas sein, um das ich mich nicht
mehr kümmern brauchen darf.
Daher die ganzen Vortests um sicher zu stellen, dass die Zahlenausgabe
auf jeden Fall korrekt funktioniert.
Wenn jetzt auf der Anzeige 832 steht, dann muss auch zahl_ausgeben mit
832 aufgerufen worden sein. Oder eben mit 0, wenn in der Anzeige 0
steht. Da darf es keinerlei Unsicherheiten mehr geben.
So... Noch einbisschen geändert.
Jap, du hast absolut Recht. Knapp über 1600.
Ich habe es jetzt mal mit dem Gesamtcode getestet: die Messung war ja
auch davor schon korrekt, aber die Anzeige flimmerte.
Nun ist es folgendermaßen:
Bei Frequenzen kleiner 500Hz sieht man den schnellen Wechsel zwischen
der richtigen Frequenz und Null
Bei Frequenzen kleiner 1000Hz flimmern die letzten 1-2 Ziffern.
Bei größeren Frequenzen flimmert nur manchmal die letzte Ziffer, was
vllt. auch nur ein Rundungsfehler ist.
Ich glaube, deine Arbeitsweise muss ich mir mal angewöhnen, gehe etwas
chaotisch vor udn versuche über alles einen gleichzeitigen Überblick zu
behalten ;)
Fan_AT schrieb:> So... Noch einbisschen geändert.> Jap, du hast absolut Recht. Knapp über 1600.
gut.
> Ich habe es jetzt mal mit dem Gesamtcode getestet:
Ich gebs auf.
Vergiss den Gesamtcode.
Immer noch nicht begriffen? Wir bauen das ganze systematisch neu auf!
> gehe etwas chaotisch vor
Du wärst schon längst fertig, wenn du nicht so am bestehenden
Originalcode hängen würdest, sondern das ganze Programm systematisch neu
aufbauen würdest
Fan_AT schrieb:> Entschuldige bitte. Also - die zaehlschritte ergeben irgendwas mit 1600.>> Wie ist das weitere Vorgehen?
OK. wenn du die Frequenz variierst, ändert sich der Wert.
Wie muss er sich verändern? Je schneller die Frequenz, desto weniger
weit kann der Zähler zählen.
Wieder etwas rechnen:
1.4kHz, macht eine Periodendauer von 1/1400 = 0.7ms.
0.0007 * 16000000 = 11428
Ein Zehtnel davon in der Anzeige. Die Anzeige muss 1142 anzeigen
In der anderen Richtung: 500Hz
500Hz sind 2ms. 0.002 * 16Mio = 32000. 1/10 davon in der Anzeige, macht
3200
Mit 3200 ist noch Spielraum. (6535 ist unsere Grenze)
300Hz
300Hz sind 0.33 ms. 0.0033 * 16Mio = 52800. 1/10 davon in der Anzeige
macht 5280
D.h. du hast
1
eingestellte Frequenz Anzeige
2
3
1.4kHz 1142
4
1.0kHz 1600
5
500Hz 3200
6
300Hz 5280
Wenn dein Programm tatsächlich diese Werte, bei den eingestellten
Frequenzen auf die 7-Segment zaubert, dann geb ich mich erst mal
zufrieden. Das akzeptiere ich als Funktionstest der grundlegenden
Messung.
Ich hab jetzt absichtlich noch nicht auf die Frequenz in der Anzeige
umgerechnet, auch wenns trivial wäre.
Aber unser Problem wird der Zählbereich ab 65535 sein. D.h. da ist der
Zählerwert der interessante.
Ach ja. Flimmern.
Ganz exakt wird deine Anzeige wahrscheinlich nicht ruhig stehen. Die
letzte Stelle, schätze ich mal, wird ein wenig verwaschen sein. Aber der
Rest muss ruhig stehen, ohne irgendwas. Da darf auch kein Wechsel
zwischen 0 und einer anderen Zahl sein.
Wenn dein Frequenzgenerator es zulässt exakt 1Khz einzustellen, dann
muss da auf der Anzeige 1600 rock solid dort stehen. Vielleicht mal
1599, vielleicht mal 1601. Aber mehr darf das Ding da nicht rumzappeln.
Wenns zu sehr (zu schnell) zappelt, denn machen wir uns da mal eine
Pause zwischen die einzelnen Messzyklen rein um zu sehen, zwischen
welchen Werten es zappelt.
Also, alles nachgemessen:
1400Hz - 1083-1082
1000Hz - 1865-1686
500Hz - flimmert schnell zwischen 3146 und 59xx
300Hz - flimmert stark, nichts zu erkennen
Das ist auch mein ursprüngliches Problem, dem wir wohl näher kommen :)
1400 werden bis auf die letzte Stelle auch genau registriert. Aber da
schwankt es um eine Ziffer. Das Problem ist wohl beim Überlauf, bei
kleineren Frequenzen
Fan_AT schrieb:> Also, alles nachgemessen:>> 1400Hz - 1083-1082> 1000Hz - 1685-1686> 500Hz - flimmert schnell zwischen 3146 und 59xx> 300Hz - flimmert stark, nichts zu erkennen
Hmm.
Sollte eigentlich nicht sein.
Wie sehr vertraust du deinem Frequenzgenerator?
Probier mal
1
while(1)// unendliche Schleife
2
{
3
if(UpdateDisplay)// Erst nach zweiter Flanke ausführen
4
{
5
ATOMIC_BLOCK(ATOMIC_FORCEON)
6
{
7
zaehlschritte=Zeit/10;
8
}
9
zahl_ausgeben(zaehlschritte);
10
UpdateDisplay=FALSE;
11
}
12
13
_delay_ms(500);
14
}
Ziel der Sache ist es, den Display Refresh zu verlangsamen um einen
Überblick über die Zahlenwechsel zu bekommen. Kannst auch mal ein wenig
mit den Delay Zeiten variieren, so dass du noch einigermassen gut
ablesen kannst, was gemessen wird.
> Das Problem ist wohl beim Überlauf, bei kleineren Frequenzen
Bei den Frequenzen sollte sich ein Timerüberlauf noch nicht bemerkbar
machen. Die unsigned Rechnung gleicht diesen einen Überlauf aus, so dass
man sich nicht drum kümmern braucht.
Das ist auch genau mein Problem. Auf eine andere Weise bin ich eben auch
auf den Wechsel der long-Variable gekommen, aber kenne keine Lösung.
Der Generator ist genau. Zumindest war er das bei dem letzten
Oszilloskop-Test. Ich gehe jetzt einfach mal vom Fehler meinerseits aus,
da ich nicht annehmen kann, dass der Generator PLUS Oszilloskop kaputt
sind ;)
Das Dealay-Programm ist nun ausprobiert worden. in den unteren beiden
Bereichen (300,500) ist ein stärkeres Schwanken zu sehen.
300Hz - 5446,7989, 8117
Bei 1400 Hz ist mal konstant bei 1086, aber zwischendurch springt es auf
3870. Und ich verstehe beim besten Willen nicht den Grund.
Fan_AT schrieb:> Der Generator ist genau.
ok, zur Kenntnis genommen.
> Bei 1400 Hz ist mal konstant bei 1086, aber zwischendurch springt es auf> 3870. Und ich verstehe beim besten Willen nicht den Grund.
Versteh ich auch noch nicht.
Dem müssen wir aber auf den Grund gehen. Solange das nicht geklärt ist,
macht es keinen Sinn, darauf aufbauend das Programm weiter zu treiben.
Die Zwischenziele müssen korrekte Werte ergeben. Sonst ist alles darauf
aufbauende automatisch falsch.
Zeig nochmal das Programm in seiner vollen Pracht.
Hmm. Lass mich mal überlegen.
Könnte es sein, das wir ein Problem mit pending Interrupts haben?
Ich denke nicht, denn genau dazu gibt es den Capture Interrupt. Auf der
anderen Seite, wenn wir einen Capture verpassen ...
Das muss ich mir durch den Kopf gehen lassen. Moment
@Georg.G.
Danke für's Mitlesen und für die Anmerkung. Bei der Deklaration wird in
der eckigen Klammer die Gesamtanzahl der beinhaltenden Elemente
angegeben. Und aktuell sind es doch 11 oder irre ich mich?
@Karl Heinz Buchegger
Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1
Flag gesetzt ist...
@Georg.G.
Danke für's Mitlesen und für die Anmerkung. Bei der Deklaration wird in
der eckigen Klammer die Gesamtanzahl der beinhaltenden Elemente
angegeben. Und aktuell sind es doch 11 oder irre ich mich?
@Karl Heinz Buchegger
Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1
Flag gesetzt ist... Denn die Überprüfung auf einen verpassten Interrupt
wird ist doch bereits implementiert und High-/LowBytes werden in
richtiger Reihenfolge ausgelesen.
Fan_AT schrieb:> @Karl Heinz Buchegger> Die einzige Idee von mir wäre vllt. noch die Überprüfung, ob der ICF1> Flag gesetzt ist... Denn die Überprüfung auf einen verpassten Interrupt> wird ist doch bereits implementiert und High-/LowBytes werden in> richtiger Reihenfolge ausgelesen.
Ja ich weiß.
Das sollte eigentlich alles stimmen.
Nur tut es das nicht. Verblüffender Weise.
Fan_AT schrieb:> Und aktuell sind es doch 11 oder irre ich mich?
Ich zähle 0...9, Leerstelle und das E. Meine Enkeltochter meint, das
wären 12 Elemente.
Die Enkeltochter ist sehr gut :)
In der aktuellen Version ist das 'E' nicht vorhanden, da keine
Frequenzbegrenzung benötgt wird und somit auch kein zu großer Wert
angezeigt werden kann.
Aber habe grad gesehen, dass der Timer2 höhere Priorität besitzt, als
CaptureEvent. Wage einen kurzen Versuch.
So... Der Timer0 hat eine niedrigere Priorität als das Capture Event.
Folgendermaßen sieht das jetzt aus:
Bei TCNT0 flackert die ganze Anzeige. Doch umgerechnet wird bis 700Hz
wird die völlig korrekte Frequenz dargestellt.
Ab da wird wieder die zwischendurch erscheinende Null erkennbar.
Hast du neue Ideen?
Ich denke nicht, dass es irgendwas mit den 'Prioritäten' zu tun hat.
Dazu sind deine Abweichungen einfach zu groß. Die ISR sind allesamt
klein, die sind in kürzester Zeit, in der Größenordnung von 1 bis 2 µs
(maximal) abgearbeitet. Im Capture darf man das überhaupt nicht sehen,
dazu hat man ja den Hardware-Capture. Und auf den Anzeigen ist dieser
Jitter zu kurz, dals das er sich bemerkbar machen kann. So gut sind
deine Augen nicht.
Ich denke in eine ganz andere Richtung: Kann es sein, dass dein µC
zwischendurch ein paar Reset macht?
Guten Tag,
Ideen... Habe noch einpaar Versuche gestartet, wie atomare Zugriffe auf
Variablen größer 8Bit, doch ohne Erfolg.
Wenn nichts Besseres einfällt, werde ich wohl den uC auswechseln,
Treiber.
Ich packe mal den Schaltplan hier rein - den nehrlich gesagt weiß ich
nicht, wieso ein Reset durchgeführt werden sollte.
Aber Gott sei Dank gibt es ja auch erfahrene Leute ;)
die Idee ist aber nicht schlecht, habe erstmal Brown-out detection level
auf VCC=2,7V gemacht - keine Änderung.
Dann werde ich mal die Spannung nicht aus dem Generator beziehen,
sondern extern.
So, habe es nun mit stabilisierter Spannung durchgezogen - minimale
positive Änderung zu sehen, aber immer noch der gleiche Fehler mit
schnellem Wechseln zu Null und zurück.
Ja, danke für die Anmerkung - wird das nächste Mal beachtet :)
Was könnte noch einen ständigen Reset auslösen?
Ist zwar eien Idee, aber dafür muss ja das Programm in einer
unpassierbaren Schleife stecken o.ä., nicht wahr?
Habe ihn zwar nicht mal angemacht, aber das Deaktivieren ginge doch
unter Verwendung von <avr/wdt.h> mit dem Befehl
Beim ersten wird die Unterbrechung eigentlich auch nur bedingt benötigt
- zaehler1_ovf wird bereits in ISR gesichert.
Wie gesagt, ich habe vosichtshalber bei jeder Schreiboperation, die mehr
als 1 Taktzyklus benötigt die Interrupts pausiert.
Habe soeben alle ATOMIC_BLOCK(ATOMIC_FORCEON) entfernt - kein Erfolg.
Es muss doch was geben, was ich übersehe?
Gut, vielleicht ist es etwas Grundlegendes, nicht die Software selbst.
Den uC habe ich bereits ausgetauscht. Eine andere Spannungsquelle
benutzt.
Bei den Fuses setze ich Ext.Crystal Resonator High Freq.; Start-Time
16CK+64ms. Hinzu kommt noch das CKOPT fuse.
Das Ganze schreiben tue ich mithilfe des mySmartUSB MK2.
Watchdog-Timer ist von vornherein nicht aktiviert.
if(UpdateDisplay)// Das Display wurde mit den Ergebnissen der vorhergehenden
2
{// Messung noch nicht aktualisiert. Die nächste Messung
3
return;// verzögern, bis die Start- und EndTime-Variablen wieder
4
}// gefahrlos beschrieben werden können
Was dieses "UpdateDisplay" macht, ist mir noch nicht klar. Es könnte
Probleme machen.
Ich würde einfach jeden Timestamp abspeichern, mehr nicht.
Und das Main nimmt sich einfach 2 aufeinanderfolgende Timestamps, wenn
es Lust dazu hat.
1
ISR(TIMER1_CAPT_vect)
2
{
3
last_time=current_time;
4
current_time=ICR1;
5
}
6
// main:
7
uint16_tdelta_time;
8
ATOMIC_BLOCK(ATOMIC_FORCEON)
9
delta_time=current_time-last_time;
10
machwas(delta_time);
Beschreib dochmal in Prosa, wie das Programm funktionieren sol.
Wie ist überhaupt der gewünschte Meßbereich?
Karl Heinz Buchegger, warum hast du mich verlassen? :D
@Peter Dannegger:
[QUOTE]
Dieses Flag hat 2 Aufgaben:
Zum einen ist es die Benachrichtigung aus der ISR in die main() "Ich
habe eine Messung fertig". Zum anderen ist es die Verriegelung der ISR
mit isch selbst, so lange keine weitere Messung zu machen, bis die
main() das Ergebnis verarbeitet hat.
D.h. wenn main() die Messergebnisse der ISR ausgewertet hat, dann muss
sie UpdateDisplay wieder auf FALSE setzen. Egal was dann bei der
Auswertung rauskommt. Das FALSE-setzen von UpdateDisplay ist quasi die
Rückmeldung von main() in die ISR "OK, ich hab mir deinen Messwert
geholt, du kannst wieder loslegen und die nächste Messung machen". Das
aber, muss IMMER passieren. Egal was dann bei der Auswertung rauskommt
[/QUOTE]
Der gewünschte Messbreich ist 1-1400Hz, was eigentlich kein Problem
darstellen sollte, aber trotzdem nicht per Capture-Event klappt.
Die aktuelle Version des Programms kann man eben in zwei Prozeduren
gliedern: Capture, Multiplex.
Mittlerweile werden alle Messvariablen gleich in den ISR gesichert - das
erspart das Ausschalten von Interrupts zwischendurch, mit evtl. Ausnahme
von unsigned long Berechnungen. (zwischenschritte in main() )
Bei steigender Flanke des eingehenden TTL-Signals erfolgt das
Capture-Event, bei dem der Zählerstand gesichert wird - unter
Voraussetzung, dass die Anzeige (zahl_ausgeben) abgearbeitet wurde
(UpdateDisplay = FALSE)
Erst bei zweiter Flanke wird die Differenz der Start-Und End...
OMFG....LEUTE....
1
Zeit=ICR1-Startzeit;
Und was, wenn nach einem Timerüberlauf ICR1 kleiner ist als Startzeit?
Dann ergbit dann... genau richtig: Null!
Folglich muss es heißen:
[c]
Zeit = zaehler1_ovf*65536.0 + ICR1 - Startzeit;
/c]
Ich laufe udn teste es ausgiebig, wünscht mir Glück oO
Es hat funktioniert! Der Fehler mit dem ständigen Flimmern zu Null und
zurück ist weg! Ich müsste mir jetzt paar Gedanken zur
Optimierungsroutine machen - Einer-, manchmal Zehnerstelle flackert ab
und zu ganz unschön, so dass man die Zahl nicht ablesen kann.
Das flackern der Anzeige kann verschiedene Ursachen haben. Ein Problem
ist das wohl ein Update der Anzeige sehr schnell erfolgt wenn die
Frequenz hoch ist. Damit kann die letzte Stelle immer leicht flackern.
Da ist auch noch ein kleiner Fehler mit drin: Die Zahl der Überläufe
sollte aus der Variable z kommen, nicht der alten Kopie von der letzten
Periode. Dazu sollte man Fließkomma Zahlen vermeiden, also:
[c]
Zeit = z*65536UL + ICR1 - Startzeit;
/c]
Die Multiplikation mit 65536 ließe sich ggf. über den Zugriff als union
auch noch einsparen - es könnte sein das der Compiler das auch schon hin
bekommt, ich fürchte aber nicht.
Hallo Ulrich,
danke für die Rückmeldung, korrigiert - auch bei Kleinigkeiten spart man
sich schon paar Taktzyklen.
Interessant wird dann die Behebung des Problems, denn das ist wirklich
ein Ableseproblem - an manchen Stellen kann man die Zahl bei Zehner-und
Einerstellen nicht richtig einstellen. Du hast 'ne auf 8 flimmernde 7,
doch eher du die 8 flimmerfrei machen kannst, kriegst du schon die 9! oO
Also meinst du, dass man bei den Frequenzen, wo das Problem anfängt, in
meinem Fall ab 70-80Hz, kurzzeitig den Multiplex... einfrieren lässt?
Mal den Timer1 Vorteiler und Noise Canceler ausprobiert - ohne Erfolg,
beim Vorteiler sogar schlimmer.
Beim Vorteiler dachte ich, es wäre so, als ob ich die eingehende
Frequenz teilen würde, was mir somit einen langsameren Multiplex
beschert hätte...
Gibt es Alternativen, etwa in Richtung D-FlipFlops, der die Frequenz
halbiert oder Methoden, wie man das Flackern bekämpft?
Für eine Stabilere Anzeige könnte man eine neue Messung erst nach einer
gewissen zeit (z.B. 0,5 s) starten, oder besser erst dann übernehmen.
Wenn man mag könnte man auch die Zeit für mehr als eine Periode Messen,
so dass man z.B. auf eine "Torzeit" von z.B. 0,5-1 s kommt. Der Start
geht so wie jetzt, aber das Ende wird erst nach etwa 0,5 s (wohl ein
gewisser Wert für z) genommen und davor werden nur die Perioden gezählt.
Bei z.B. 1 kHz wird dann nicht die Zeit für eine Periode gemessen,
sondern die Zeit für z.B. 505 Perioden und dann durch die Zahl geteilt.
Das gibt ein höhere Auflösung und auch gleich eine langsamere Messung.
Der Vorteiler beim Timer reduziert die Auflösung der Messung. Den
braucht man bei der Messung über ICP mit zählen der Überläufe nicht.
Sinnvoll wäre ggf. ein Vorteiler für das Signal, wenn die Frequenz zu
hoch (so ab 50-500 kHz) wird. Für niedriger Frequenzen geht es aber
besser ohne Vorteiler.
Naja, das Ding ist, ich hatte die Schaltung bereits mit Torzeiten auf
T0, da war mir jedoch die Wartezeit/Offset viel zu groß. Mit ICP habe
ich mir eben viel schnellere Messungen erhofft.
Bei solchen kleinen Frequenzen habe ich erwartet, dass es funktionieren
würde.
Die Messung geht ja auch schneller, nur halt jetzt zu schnell um das
noch auf der Anzeige noch gut zu erkennen. Vor allem bei den höheren
Frequenzen (so ab etwa 100 Hz) muss man für mehr Auflösung dann halt die
Zeit für mehr Perioden messen. Der Unterschied zum Zählen mit fester
Torzeit ist, dass man über ICP eine etwa von der Frequenz unabhängige
relative Auflösung bekommt.
Gut, dann bleibt mir nichts anderes übrig. Ich versuche es erstmal mit
insgesamt zwei Messungen ab 80Hz.
Vielen Dank für die Hilfe - falls neue Ideen kommen, würde ich sie
selbstverständlich wissen wollen :)