PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme bei der Frequenzauswertung



cumi
24.01.2007, 14:33
Hallo Zusammen

Um die Geschwindigkeit eines Modellautos zu messen arbeite ich mit einem Weg-Impuls-Geber.

Nun habe ich jedoch noch einige Probleme mit der Software. Das folgende Modul funktioniert ganz gut, muss jedoch irgendwo noch einen Bug haben, da es in zufälligen Abständen (zwischen wenigen hunder Millisekunden und einigen Minuten) zu Fehlauswertungen kommt. freqGet() liefert dann irgend einen komisch Wert, welcher am häufigsten 0 ist. Dabei wird die Frequenz zurzeit noch von einem Rechteckgenerator geliefert. An diesem sollte es also nicht liegen.

Damit man den Code schneller versteht hier ein paar grundsätzliche Dinge:

Das Signal liegt am Analogkomperator, welcher mit einem Kondensator noch einwenig geglättet wird. Getriggert wird bei 2.5V. Das Signal ist ein Rechteck von 0-5V.
Um zusätzlich noch entprellen zu können wird der Analog Comparator Interrupt beim auftreten eines Interrupts kurz deaktiviert und einwenig später vom Timer wieder aktiviert.
timGet() liefert ein DWord (32 Bit langer unsigned Typ). Dieses zählt auf dem Systemtakt hoch und überläuft nach ca. 9min einfach.


Ich komme bei diesem Problem schon seit geraumer Zeit nicht weiter. Finden vielleicht jemand von euch den Fehler?



/// includes --------------------------------------------------------------------
#include <normlib.h>
#include <interrupt.h>
#include "avr.h"
#include "tim.h"
#include "out.h"
#include "dataTable.h"
#include "data.h"
#include "freq.h"

// global variables ------------------------------------------------------------
byte freqPulsLedFlag;
byte freqEnable;

// local definitions -----------------------------------------------------------

// local variables -------------------------------------------------------------
static word actFreq;
static dword t0,tI1; //timestamp of last puls
static dword dt; //delta t
static byte signal;
static byte preload;
static dword pLedTime;

// global function implementation ----------------------------------------------
void freqInit(){
TCCR0=BV(CS02)|BV(CS00); // activate Timer 0, prescaler /1024
preload=dataGetByte(DATATABLE_AC_TCNT0); //preload of Timer0

// Enable Analog Comparator, Enable Analog Comparator Interrupt
if(dataGetByte(DATATABLE_AC_EDGE))
ACSR=BV(ACIS0)|BV(ACIS1); //rising Edge
else
ACSR=BV(ACIS1); //falling Edge

cbi(ACSR,ACD); //Enable Analog Comparator
sbi(ACSR,ACIE); //Enable Analog Comparator Interrupt

actFreq =0;
signal =FALSE;
freqPulsLedFlag=TRUE;
freqEnable =TRUE;
pLedTime =(F_CPU/1000)*dataGetWord(DATATABLE_PULS_LED_FREQ);
}
word freqGet(){
return actFreq;
}
void freqUpdate(){
if(!freqEnable)
return;
cli();
dword t1=tI1;
sei();
if(t0!=t1){
signal=TRUE;
if(t1<t0)
dt=-t0+t1;
else
dt=t1-t0;
if(dt<((dword)(10*F_CPU/0xFFFF)))
actFreq=0xFFFF;
else
actFreq=(word)((dword)(10*F_CPU)/dt);
t0=t1;
if(freqPulsLedFlag)
outClear(OUT_PULS);
}
if(!signal){
actFreq=0;
return;
}
if(t0==t1){
dword t2=timGet();
dword dtN;
if(t2<t0)
dtN=-t0+t2;
else
dtN=t2-t0;
if(dtN>dt){
actFreq=(word)((dword)(10*F_CPU)/dtN);
}
}
if(timReached((dword)F_CPU,t0))
signal=FALSE;
if(freqPulsLedFlag&&timReached(pLedTime,t0))
outSet(OUT_PULS);
}

// interrupt routines ----------------------------------------------------------
ISR(ANA_COMP_vect){
tI1=timGet();
cbi(ACSR,ACIE); //disable Analog Comparator Interrupt
TCNT0=preload; //preload Timer0
sbi(TIMSK,TOIE0);//enable Timer 0 Interrupt
}
ISR(TIMER0_OVF_vect){
sbi(ACSR,ACI); //clear Analog Comparator Interrupt Flag
sbi(ACSR,ACIE); //enable Analog Comparator Interrupt
cbi(TIMSK,TOIE0);//disable Timer 0 Overflow Interrupt
}

// EOF -------------------------------------------------------------------------


Danke für eure Hilfe!

Gruss
cumi

SprinterSB
24.01.2007, 16:24
Das Problem hast du schon genannt: timGet()

cumi
24.01.2007, 21:53
Meinst du?
Hier noch die Implementation von timGet();

Ich habe übriegens noch die Variabeln volatile deklariert. Daran liegt es jedoch nicht (nur).

// includes --------------------------------------------------------------------
#include <normlib.h>
#include <interrupt.h>
#include "tim.h"

// local variables -------------------------------------------------------------
volatile static word overrun;

// global function implementation ----------------------------------------------
void timInit(){
overrun=0;
b_setH(TCCR1B,CS10); //activate timer, no prescaling
b_setH(TIMSK,TOIE1); //activate Overflow Interrupt
}
dword timGet(){
byte sregSav=SREG;
cli();
dword tmp=(((dword)overrun<<16)&0xFFFF0000)|(((dword)TCNT1)&0x0000FFFF);
SREG=sregSav;
return tmp;
}
dword timGetDifUp(dword *t0){
dword t1=timGet();
dword res;
if(t1<(*t0))
res=-(*t0)+t1;
else
res=t1-(*t0);
*t0=t1;
return res;
}
dword timGetDifNUp(dword t0){
dword t1=timGet();
if(t1<t0)
return -t0+t1;
return t1-t0;
}
byte timReached(dword t,dword t0){
if(timGetDifNUp(t0)>=t)
return TRUE;
return FALSE;
}

// interrupt routines ----------------------------------------------------------
ISR(TIMER1_OVF_vect){
overrun++;
}

// EOF -------------------------------------------------------------------------




/// includes --------------------------------------------------------------------
#include <normlib.h>
#include <interrupt.h>
#include "avr.h"
#include "tim.h"
#include "out.h"
#include "dataTable.h"
#include "data.h"
#include "freq.h"

// global variables ------------------------------------------------------------
byte freqPulsLedFlag;
byte freqEnable;

// local definitions -----------------------------------------------------------

// local variables -------------------------------------------------------------
static word actFreq;
static dword t0;
volatile static dword tI1; //timestamp of last puls
static dword dt; //delta t
static byte signal;
static byte preload;
static dword pLedTime;

// global function implementation ----------------------------------------------
void freqInit(){
TCCR0=BV(CS02)|BV(CS00); // activate Timer 0, prescaler /1024
preload=dataGetByte(DATATABLE_AC_TCNT0); //preload of Timer0

// Enable Analog Comparator, Enable Analog Comparator Interrupt
if(dataGetByte(DATATABLE_AC_EDGE))
ACSR=BV(ACIS0)|BV(ACIS1); //rising Edge
else
ACSR=BV(ACIS1); //falling Edge

cbi(ACSR,ACD); //Enable Analog Comparator
sbi(ACSR,ACIE); //Enable Analog Comparator Interrupt

actFreq =0;
signal =FALSE;
freqPulsLedFlag=TRUE;
freqEnable =TRUE;
pLedTime =(F_CPU/1000)*dataGetWord(DATATABLE_PULS_LED_FREQ);
}
word freqGet(){
return actFreq;
}
void freqUpdate(){
if(!freqEnable)
return;
cli();
dword t1=tI1;
sei();
if(t0!=t1){
signal=TRUE;
if(t1<t0)
dt=-t0+t1;
else
dt=t1-t0;
if(dt<((dword)(10*F_CPU/0xFFFF)))
actFreq=0xFFFF;
else
actFreq=(word)((dword)(10*F_CPU)/dt);
t0=t1;
if(freqPulsLedFlag)
outClear(OUT_PULS);
}
if(!signal){
actFreq=0;
return;
}
if(t0==t1){
dword t2=timGet();
dword dtN;
if(t2<t0)
dtN=-t0+t2;
else
dtN=t2-t0;
if(dtN>dt){
actFreq=(word)((dword)(10*F_CPU)/dtN);
}
}
if(timReached((dword)F_CPU,t0))
signal=FALSE;
if(freqPulsLedFlag&&timReached(pLedTime,t0))
outSet(OUT_PULS);
}

// interrupt routines ----------------------------------------------------------
ISR(ANA_COMP_vect){
tI1=timGet();
cbi(ACSR,ACIE); //disable Analog Comparator Interrupt
TCNT0=preload; //preload Timer0
sbi(TIMSK,TOIE0);//enable Timer 0 Interrupt
}
ISR(TIMER0_OVF_vect){
sbi(ACSR,ACI); //clear Analog Comparator Interrupt Flag
sbi(ACSR,ACIE); //enable Analog Comparator Interrupt
cbi(TIMSK,TOIE0);//disable Timer 0 Overflow Interrupt
}

// EOF -------------------------------------------------------------------------

SprinterSB
25.01.2007, 08:23
:-k schwer zu sagen...

Steht signal vielleicht auf FALSE?

In timGetDifNUp() und anderen Stellen versuchst du Betrag zu berechnen. Nehmen wir mal an, wir wollen den Betrag für 8-Bit berechnen.

a = 0x80 = -128 und b = 0, es ist also a < b

if (a < b) c = b-a

=> c = 0x80 = -128 < 0

Analog bei breiteren Typen

cumi
25.01.2007, 17:21
Was meinst du mit Signal auf FALSE? Da schau ich nicht ganz durch.

Also wegen dem Betrag. Also ich rechne mit dword, das ist ein 32-Bit langer unsigned Integer. Ja ich weiss, ich solte ev. einmal die Typenbezeichnung der AVR-Lib-c, oder wie die genau heisst, übernehmen.
Und weil der unsigned ist kann der nicht negativ werden. Ich möchte nicht wirklich den Betrag ausrechnen sondern etwas anderes:
timGet gibt ja einen Time-Spamp zurück, welcher die Zeit in Systemtakten seit dem boot angiebt. Dieser Timestamp überläuft jedoch so nach ca. 9min (dann sind 2^32 Takte vorbei). Dies ist jedoch nicht weiter tragisch, da ich sowieso nur Zeitunterschiede messen möchte. Diese können dann einfach nicht länger als 9min sein.
Wenn nun t1 (der Timestamp beim aufrufen von timGetDifNUp) grösser als t0 (der alte Timestamp) ist, kann ich einfach t1-t0 rechnen. Wenn nun, was jedoch sehr selten passiert (nur alle 9min), t1<t0 ist, ist der Zähler gleich überlaufen, seit t0 gesetzt wurde. Daher muss ich doch 2^32-t0+t1 rechen um den Zeitunterschied zwischen t0 und t1 zu kriegen, oder?

Habe ich da in der Implementierung einen Fehler?

Die obige Implementation ist ja äquivalent zu dieser. Hier sieht man es jedoch etwas besser:

dword timGetDifNUp(dword t0){
dword t1=timGet();
if(t1<t0)
return (dword)0xFFFFFFFF-t0+t1;
return t1-t0;
}

Ganz vielen Dank für deine Hilfe Georg-Johann!

Gruss
cumi

SprinterSB
26.01.2007, 08:40
Was meinst du mit Signal auf FALSE? Da schau ich nicht ganz durch. *g* Was deine Variablen machen und wozu die da sind, solltest du schon wissen... Wenn signal auf FALSE ist word ja actFreq=0.

Vielleicht ist es besser, die Variable nicht überlaufen zu lassen sondern immer direkt den Happen abzuziehen und nur den Zeitunterschied zurückzuliefern.