Ich versuche, den Wert eines Zählers, der in Timer1 (32 Bit) läuft, an einen anderen Chip zu senden, idealerweise über SPI. Der Timer löst alle 1 ms einen Interrupt aus, um den Zähler zu erhöhen. Insgesamt erhöht sich die Variable ständig, aber aus irgendeinem Grund geht die Variable gelegentlich rückwärts.
unsigned long t=0;
void timer1ISR(){
timerFlagBit=0;//clear interrupt flag
t++;//increment the counter
}
void sendValue(){
static unsigned long tempT;
tempT=t; //save the value to a temporary variable
Spi_Write(tempT>>16); //most sig byte. I only want a three byte value
Spi_Write(tempT>>8);
Spi_Write(tempT); //least sig byte
}
void main(){
init();
startTimer();
while(1){
Delay_us(500);
sendValue();
}
}
Irgendwelche Ratschläge, wie man einen schnell inkrementierenden Wert überträgt?
Hier sind einige Beispiele (ich habe wiederkehrende Werte herausgenommen): Es erhöht sich um einen Schritt von 0 bis 20. Der nächste Wert direkt nach 20 ist 99. Es erhöht sich dann von 99 auf 114 um einen Schritt und dann auf den nächsten Wert ist 95. Nach 95 kommen 96, 97, 98, dann 197, 198, 199, 200, 201. Nach 201 springt es zurück auf 182 und beginnt von 182 bis 196 zu inkrementieren. Nach 196 springt es auf 276.
Also springt es nur ganz am Anfang nach einem Muster, wenn ich alle Werte grafisch darstelle. Das sieht so aus: Nach kurzer Zeit stabilisiert sich der Wert. Ich habe mit der Priorität mit dem Timer gespielt, aber es hat sich nichts geändert.
Dies geschieht auf einem Microchip Pic.
Sieht so aus, als hätten Sie eine Race-Bedingung, bei der der Interrupt zum Erhöhen des Zählers auftritt, während der Wert ausgelesen wird. Die Lösung besteht darin, die Zählervariable als voltatil zu deklarieren und dann den Timer-Interrupt zu deaktivieren, während Sie den Zählerwert in ein temporäres Register kopieren.
Jetzt wurde die Racebedingung behoben. Etwas anderes, das mit Ihrem Code verbessert werden könnte, ist der Jitter, falls dies für Ihre Anwendung von Bedeutung ist. Der wahrscheinliche Grund dafür ist, dass der Hardware-Timer auf die Hauptoszillatorfrequenz verriegelt ist, während Funktionen wie diese Delay_us
normalerweise als Befehlsschleife implementiert sind, sodass der Timer-Interrupt die Genauigkeit bis zu einem gewissen Grad beeinträchtigt. Irgendwann kommt es zu folgendem Übergang:
sendValue()
sendValue()
, und jetzt haben Sie eine zusätzliche Verzögerung von 500 uS.Wenn Ihr anderer Code das Senden von drei SPI-Bytes aus dem Interrupt tolerieren kann (dh Sie haben keine anderen Interrupts oder Code, der für eine inakzeptable Zeit blockiert würde), können Sie den SPI-Schreibvorgang einfach innerhalb des Interrupts verschieben:
unsigned long tempT = 0;
void timer1ISR(){
tempT++;
Spi_Write(tempT>>16);
Spi_Write(tempT>>8);
Spi_Write(tempT);
timerFlagBit=0;
}
Da die Funktion jedoch Delay_us
nur in einer Schleife sitzt, können Sie auch so etwas wie den folgenden Code verwenden, um ein Flag zu erhalten, dass ein neuer Wert verfügbar ist:
volatile unsigned long t = 0;
volatile unsigned char new_val = FALSE;
void timer1ISR(){
t++;
new_val = TRUE;
timerFlagBit=0;
}
void sendValue(){
static unsigned long tempT;
disable_ints();
tempT=t;
enable_ints();
Spi_Write(tempT>>16);
Spi_Write(tempT>>8);
Spi_Write(tempT);
}
void main() {
init();
startTimer();
while(1){
if (new_val) {
sendValue();
new_val = FALSE:
}
}
}
Letzteres ist wahrscheinlich eine gute Möglichkeit, wenn Ihre Hauptverarbeitungsschleife andere Dinge erledigt. Wenn es nichts anderes tut und das Senden mehrerer identischer Werte an Ihren SPI-Port kein Problem darstellt, können Sie natürlich auch eine kürzere Verzögerung oder gar keine verwenden.
Samuel
PeterJ
tempT=t;
es in diesem Fall wahrscheinlich als flüchtig deklarieren und Interrupts deaktivieren sollten, damit keine Chance besteht, dass der Interrupt nach der Hälfte des Lesens auftritt. Wie erfassen Sie die Daten für das Diagramm, sind Sie sicher, dass diese Seite der Dinge in Ordnung ist?Apalopohapa