Hallo zusammen,
ich möchte mit einem PIC12, den ich bei 1 Mhz laufen lasse ein
Taktsignal ausmessen. Irgendwie schwankt mein Ergebnis aber gravierend,
daher an euch die Frage, ob ihr wisst, woran das liegen kann.
Mein Signal hat eine Frequenz von ca. 264 Hz (Tsignal = 3,78 ms).
Ich messe, indem ich einen 16 bit Timer mit Ttimer = 7,99 µs pro
Inkrement starte, mit
for(i=0; i<16; i++)
{
while(RA5);
while(!RA5);
}
16 Durchläufe abwarte, und dann meinen Timer auslese (inklusive
eventuellen Overflows).
Nach den 16 Durchläufen sollte ich nun doch eigentlich einen Wert von
ca. 7570 aus dem Timer lesen (16 * Tsignal / Ttimer), tatsächlich
bekomme ich aber nur etwa die Hälfte und das Ergebnis schwankt obendrein
wie gesagt gravierend. Was meint ihr dazu?
264 HZ kannst du mit Kopfhörern oder kleinem Lautspr. noch hören ohne
verstärker. Damit weist du obs schwankt.
Gibts bei dem Proz auch ein ANSELA Register?
Der macht bei nichtabfrage einen Analogen Eingang draus.
Horst schrieb:> bekomme ich aber nur etwa die Hälfte und das Ergebnis schwankt obendrein> wie gesagt gravierend. Was meint ihr dazu?
Wie sehr schwankt denn dein Ergebnis? Wenn es weniger als 1/16 der
Frequenz ist, kann das daran liegen, dass du vor den Beginn deiner
Mess-Schleife ein
1
while(RA5);while(!RA5);
setzen solltest, um einen definierten Startzeitpunkt für deine Messung
zu erhalten.
Es kann passieren, dass du irgendwo in die erste Periode einsteigst und
dadurch zu knapp misst. Der Fehler davon ist worst-case (Signal bei
Beginn der for-Schleife im letzten Zucken der Low-Halbperiode) 1/16-tel
deiner Messfrequenz, also rund 16 Hz.
Bei dem halben Timerwert würde ich als erstes meine Initialisierung des
Timers überprüfen, ob die angenommenen 7,99 µs auch stimmen.
Fuerst-Rene schrieb:> 264 HZ kannst du mit Kopfhörern oder kleinem Lautspr. noch hören ohne> verstärker. Damit weist du obs schwankt.
Nicht nötig, habe es mit Oszi überprüft :)
Fuerst-Rene schrieb:> Gibts bei dem Proz auch ein ANSELA Register?> Der macht bei nichtabfrage einen Analogen Eingang draus.
Ein ANSELA Register gibt es, aber es ist alles auf 0 für I/O gesetzt.
Markus W. schrieb:> Wie sehr schwankt denn dein Ergebnis? Wenn es weniger als 1/16 der> Frequenz ist, kann das daran liegen, dass du vor den Beginn deiner> Mess-Schleife ein
while(RA5); while(!RA5);
>setzen solltest, um einen definierten Startzeitpunkt für deine Messung> zu erhalten.
Das Ergebnis schwankt zwischen 2700-4000 und sogar darüber hinaus. An
den Startzeitpunkt habe ich auch gedacht und es genau so gemacht, wie du
beschrieben hast. Habe ich vergessen zu erwähnen.
Krapao schrieb:> Bei dem halben Timerwert würde ich als erstes meine Initialisierung des> Timers überprüfen, ob die angenommenen 7,99 µs auch stimmen.
Den Timer habe ich auch mit Oszi geprüft.
Vielen Dank für die Tips, leider klappt es immer noch nicht. Habt ihr
noch eine Idee?
Michael Roek schrieb:> zeig doch mal den Assemblercode!
Von Assembler habe ich leider keine Ahnung und weiß nicht genau was du
brauchst. Ich habe das projekt nun extra nochmal neu angelegt, nur mit
den relevanten Funktionen. Unten ist nun der Assembler Code dazu. Ich
habe nicht alles kopiert, was der Compiler ausgespuckt hat, ich hoffe
die relevanten Sachen sind dabei. Wenn nicht, sag Bescheid, dann schicke
ich nochmal alles...
Vorher nochmal Zusatzinfos: Ich habe es gerade mal anders herum
ausprobiert: in einem gesamten Timer Durchlauf (ca. 520 ms) die I/O
steps (ca. 3,7 ms) gezählt. Also
1
while(1)
2
{
3
while(RA5);
4
while(!RA5);
5
i++;
6
}
Dann in der ISR des Timers i abgefragt, und komischerweise liegt i statt
der erwarteten 140 ca. bei 280, aber auch stark schwankend. Auffallend
fand ich den Faktor 2 der in beiden Fällen Probleme macht.
Hier jetzt der Assembler code:
1
1: #include"globals.h"
2
2:
3
3:
4
4: void Get_Speed(void)
5
5: {
6
6: unsigned int time;
7
7: unsigned int overflow;
8
8: unsigned char i;
9
9:
10
10: while(RA5);
11
789 2F8A GOTO 0x78a
12
78A 0020 MOVLB 0
13
78B 1A8C BTFSC 0xc, 0x5
14
78C 2F8E GOTO 0x78e
15
78D 2F8F GOTO 0x78f
16
78E 2F8A GOTO 0x78a
17
78F 2F91 GOTO 0x791
18
11: while(!RA5); //wait for toggle to high
19
790 2F91 GOTO 0x791
20
791 1E8C BTFSS 0xc, 0x5
21
792 2F94 GOTO 0x794
22
793 2F95 GOTO 0x795
23
794 2F91 GOTO 0x791
24
795 2F96 GOTO 0x796
25
12: time = (TMR1H << 8) + TMR1L; //Timer1 start time
Horst schrieb:> Michael Roek schrieb:>> zeig doch mal den Assemblercode!> Von Assembler habe ich leider keine Ahnung und weiß nicht genau was du> brauchst.
Das dürfte auch eine Fehlinformation gewesen sein. Da du in C
programmierst, sollten wir erst mal das Naheliegenste kontrollieren: Du
hast einen Fehler im C-Programm. Und dazu braucht man das C-Programm.
Und zwar nicht nur einen winzigen Ausschnitt, sondern komplett.
Hallo Horst,
> 11: while(!RA5); //wait for toggle to high> 790 2F91 GOTO 0x791> 791 1E8C BTFSS 0xc, 0x5> 792 2F94 GOTO 0x794> 793 2F95 GOTO 0x795> 794 2F91 GOTO 0x791> 795 2F96 GOTO 0x796
ich habe ersty spaeter Zeit, mal in Ruhe ueber die Initalisierung zu
gehen, aber hier oben z.B: ist schon erkennbar, dass je nachdem zu
welchem Zeitpunkt Dein Eingangssignal den Zustand wechselt (vor Zeile
791 oder danach) einige Codezeilen mehr oder weniger ausgefuehrt werden.
Dennoch duefte das bei der Takt- und der EIngangsfrequenz nicht so
erheblich sein wie Du das beschreibst. Ich schaue spaeter nochmal
rein....
@Karl-Heinz: Beim Assemblercode steht ka das C-Programm dabei
Gruss
Michael
Hallo Horst,
Kannst Du das denn nicht erst mal im Simulator laufen lassen und sehen
was passiert?
Oder besser im Emulator?
Dort ein paar Breakpoints setzen und den Programmzaehler
beobachten....Gruss
Michael
Udo Schmitt schrieb:> Sicher daß dein Signal ein sauberer Rechteck ist und keine Störungen> (Peaks) enthält?>> Signalquelle?> Schaltungsaufbau?
Es ist leider kein sauberes Rechtecksignal, sondern was ganz eigenes.
Die ansteigende Flanke "zittert" etwas. Und gerade habe ich mal einen
I/O toggeln lassen mit jeder Flanke, die erkannt wird, und siehe da, die
Hysterese von meinem Pin ist zu klein. Der schaltet bevor er endgültig
High geht nochmal ganz kurz auf Low. Konnte ich erst sehen, als ich ganz
nah herangezoomt habe. Damit dürfte das Problem wohl gegessen sein :)
Vielen vielen Dank an euch alle!
@ Michael Roek
Das ist natürlich auch interessant. Dann muss ich mal sehen, wie genau
es am ende wird und abklären, wie genau ich es brauche. Wenn es zu
ungenau ist, komme ich hier nochmal darauf zurück. Vorerst brauchst du
dir also keine weitere Mühe machen.
Nochmal vielen Dank an alle!
Horst schrieb:> while(RA5);> while(!RA5); //wait for toggle to high> time = (TMR1H << 8) + TMR1L; //Timer1 start time
Zwischen den kritischen Zeilen nur auslesen und in Variablen speichern.
Zusammenrechnen kannst du sie später.
Michael Roek schrieb:> Kannst Du das denn nicht erst mal im Simulator laufen lassen und sehen> was passiert?
Das wäre wegen dem sehr eigentümlichen Eingangssignal nicht so sinnig
gewesen. Habe ich vergessen gleich zu erwähnen, tut mir leid.
A. K. schrieb:> Zwischen den kritischen Zeilen nur auslesen und in Variablen speichern.> Zusammenrechnen kannst du sie später.
Danke für den Tipp, wird umprogrammiert :)
Horst schrieb:> Es ist leider kein sauberes Rechtecksignal, sondern was ganz eigenes.> Die ansteigende Flanke "zittert" etwas. Und gerade habe ich mal einen> I/O toggeln lassen mit jeder Flanke, die erkannt wird, und siehe da, die> Hysterese von meinem Pin ist zu klein. Der schaltet bevor er endgültig> High geht nochmal ganz kurz auf Low. Konnte ich erst sehen, als ich ganz> nah herangezoomt habe. Damit dürfte das Problem wohl gegessen sein :)> Vielen vielen Dank an euch alle!
Schön, dann habe ich heute wenigstens einmal was nützliches gemacht.
Kann ich jetzt wenigstens mit einem guten Gefühl hier an meinem Sch...
weitermachen :-)
> time = (TMR1H << 8) + TMR1L; //Timer1 start time
Das ist auf dem PIC12 kein atomarer Zugriff. Siehe die Hinweisbox dazu
im Datenblatt.
Um das sauber zu machen, darf während des Auslesens des 16-Bit Registers
kein Timerinterrupt kommen. Also diesen lokal sperren! Das Verfahren von
Horst vermindert die Chance, dass TMR1H und TMR1L asynchron gelesen
werden, vorkommen kann es trotzdem.
> TMR1H und TMR1L vs. timer1_overflow
Gleiches Problem nur seltener. Zusätzlich solltest du dem Compiler
mitteilen, dass timer1_overflow volatile ist.
> unsigned int time;> time = (TMR1H << 8) + TMR1L + 0xFFFFFFFF - time + ((timer1_overflow -> overflow) * 65535);
Wieviele Bit hat unsigned int auf dem PIC12?
Deine Teilausdrücke ((timer1_overflow - overflow) * 65535) und
0xFFFFFFFF in der Formel sind mir suspekt. Sobald timer1_overflow -
overflow ungleich 0 ist, bekommst du abenteuerliche Werte.
Die 65535 selbst sind IMHO auch fischig. Ein Overflow heisst, dass 2^16
= 65536 Timerschritte vergangen sind!
Krapao schrieb:> Das ist auf dem PIC12 kein atomarer Zugriff. Siehe die Hinweisbox dazu> im Datenblatt.
Ich weiß leider nicht welche Hinweisbox du genau meinst aber ich habe es
jetzt gemacht, wie ihr empfohlen habt. Zuerst auslesen, dann berechnen,
und außerdem vor dem auslesen den Timer gestoppt.
Krapao schrieb:> Wieviele Bit hat unsigned int auf dem PIC12?
16
Die Code Zeile ist dir zu Recht suspekt, da stimmt auch was nicht. Für
alle, die es interessier, seht ihr nochmal die gesamte Funktion. Zuerst
habe ich die "zittrigen" Flanken versucht per Software zu kompensieren,
indem ich sie einfach mit einem delay "überspringe". Dadurch wurde es
zwar deutlich besser, aber irgendwie hatte ich trotzdem noch starke
Schwankungen, obwohl die Flanken so eigentlich sehr schön erkannt
werden. Nunja, nun hat erstmal ein 470pF Kerko geholfen. Das ist mir
allerdings sehr unlieb, weil bei höheren Frequenzen (die später noch
auftreten werden) das Signal zu stark geglättet werden könnte und ich
irgendwann nur noch "High" erkenne. Falls also jemand noch eine Idee
hat, wie ich das Problem per Software in den Griff bekomme...
Das 0xFFFFFFFF ist mein maximaler Timer Wert. Kann ich das auch
eleganter machen?
1
voidGet_Speed(void)
2
{
3
unsignedinttimer_steps;
4
unsignedintoverflow_old;
5
unsignedintoverflow_new;
6
unsignedchari;
7
unsignedchartimerl;
8
unsignedchartimerh;
9
10
while(RA5);
11
while(!RA5);
12
T1CONbits.TMR1ON=0;
13
timerl=TMR1L;
14
timerh=TMR1H;
15
overflow_old=timer1_overflow;
16
T1CONbits.TMR1ON=1;
17
timer_steps=(timerh<<8)+timerl;
18
for(i=0;i<16;i++)
19
{
20
__delay_us(200);//gegen "zittrige" Flanken
21
while(RA5);
22
__delay_us(200);
23
while(!RA5);
24
}
25
T1CONbits.TMR1ON=0;
26
timerl=TMR1L;
27
timerh=TMR1H;
28
overflow_new=timer1_overflow;
29
T1CONbits.TMR1ON=1;
30
31
if(overflow_new-overflow_old)//mehr als 1 Overflow ist nicht möglich
32
timer_steps=(timerh<<8)+timerl+0xFFFFFFFF-timer_steps;//neuer Wert plus Differenz des alten Wertes bis zum Überlauf
33
else
34
timer_steps=(timerh<<8)+timerl-timer_steps;//neuer Wert minus alter Wert
> if(overflow_new - overflow_old) //mehr als 1 Overflow ist nicht möglich
Wenn du unsigned und alles in 16 Bit rechnest, brauchst du diesen 1
Overflow überhaupt nicht berücksichtigen. Durch die Definition des
unsigned-Rechenhandlings kommt trotzdem immer das richtige raus, wenn du
einfach nur Ende - Anfang rechnest.
Karl Heinz Buchegger schrieb:> Wenn du unsigned und alles in 16 Bit rechnest, brauchst du diesen 1> Overflow überhaupt nicht berücksichtigen. Durch die Definition des> unsigned-Rechenhandlings kommt trotzdem immer das richtige raus, wenn du> einfach nur Ende - Anfang rechnest.
Achso? Aber wenn ich zum Beispiel bei 60000 starte, einen Überlauf habe,
und bei 10000 stoppe. Dann hatte ich ja 15535 Schritte. Wenn ich aber
10000-60000 rechne kommt doch was anderes raus, oder?
Yasin schrieb:> Achso? Aber wenn ich zum Beispiel bei 60000 starte, einen Überlauf habe,> und bei 10000 stoppe. Dann hatte ich ja 15535 Schritte.
15536.
Auch der Schritt von 65535 nach 0 ist ein Schritt
> Wenn ich aber> 10000-60000 rechne kommt doch was anderes raus, oder?
Hast du das schon mal nachgerechnet?
Was kommt den raus?
Aber du hast recht. Ich muss mich präzisieren:
Ausgangssituation: 16 Bit Timer, der seinen vollen Wertebereich
durchzählt, sowie die Annahme, dass die komplette Berechnung in 16-Bit
unsigned Arithmetik durchgeführt wird.
Solange es nur 1 Überlauf geben kann UND der Endwert kleiner als der
Startwert ist, braucht dieser 1 Überlauf nicht berücksichtigt werden.
De facto läuft das darauf hinaus, das der Timer vom Start der
Messperiode bis zum Ende derselben nicht mehr als maximal 65535 Ticks
machen darf. Ist dieser Fall gegeben, dann können die Überläufe komplett
ignoriert werden.
Ein Einbeziehen eines eventuellen Überlaufs würde alles nur
komplizierter machen und durch das Aufblähen auf 32 Bit Arithmetik auch
verlangsamen.