Enviando una variable que cambia rápidamente a través de SPI

3

Estoy intentando enviar el valor de un contador que se ejecuta en timer1 (32 bits) a otro chip, idealmente a través de SPI. El temporizador dispara una interrupción cada 1 ms para incrementar el contador. En general, la variable aumenta constantemente, pero por alguna razón, la variable retrocede ocasionalmente.

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();       
   }
}

¿Algún consejo sobre cómo transmitir un valor que se incremente rápidamente?

Aquí hay algunos ejemplos (saqué valores repetitivos): Se incrementa en un paso de uno a 0 a 20. El siguiente valor justo después de 20 es 99. Luego aumenta de 99 a 114 en un paso de uno y luego el siguiente valor es 95. Después de 95 vienen 96, 97, 98, luego 197, 198, 199, 200, 201. Después de 201, vuelve a 182 y comienza a aumentar de 182 a 196. Después de 196, salta a 276.

Entonces, solo al principio, salta de acuerdo con un patrón si grafico todos los valores. Se ve así: Al cabo de un rato el valor se estabiliza. He jugado con la prioridad con el temporizador pero nada ha cambiado.

Esto se hace en una foto de Microchip.

    
pregunta Timtianyang

2 respuestas

5

Parece que tiene una condición de carrera en la que se produce la interrupción para incrementar el contador mientras se lee el valor. La solución es declarar la variable del contador volátil y luego deshabilitar la interrupción del temporizador mientras copia el valor del contador en un registro temporal.

    
respondido por el alex.forencich
2

Ahora, la condición de carrera se ha corregido y otra cosa que podría mejorarse con su código es el jitter si eso es una preocupación para su aplicación. La razón por la que es probable que ocurra es que el temporizador de hardware se bloqueará a la frecuencia del oscilador principal, mientras que las funciones como Delay_us normalmente se implementan como un bucle de instrucciones, por lo que la interrupción del temporizador interferirá con la precisión hasta cierto punto. En algún momento ocurrirá la siguiente transición:

  • La interrupción del temporizador de 1 ms se disparará justo antes de sendValue()
  • La interrupción del temporizador de 1 ms se disparará justo después de sendValue() y ahora tendrás un retraso adicional de 500 uS.

Si su otro código puede tolerar el envío de tres bytes SPI desde la interrupción (es decir, no tiene otras interrupciones o código que se detendría por un tiempo inaceptable), simplemente podría mover la escritura SPI dentro de la interrupción:

unsigned long tempT = 0;

void timer1ISR(){
   tempT++;
   Spi_Write(tempT>>16);
   Spi_Write(tempT>>8);  
   Spi_Write(tempT);
   timerFlagBit=0;
}

Pero de lo contrario, como la función Delay_us solo estará en un bucle, también podría usar algo como el siguiente código para recibir una marca de que hay un nuevo valor disponible:

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:
      }
   }
}

Esta última es probablemente una buena manera si tu ciclo de procesamiento principal está haciendo otras cosas. Si no hace nada más y el envío de múltiples valores idénticos a su puerto SPI no es un problema, también puede usar un retraso más corto o ninguno en absoluto.

    
respondido por el PeterJ

Lea otras preguntas en las etiquetas