TMR0 Resultado inexacto

0

Soy bastante nuevo en los microcontroladores, especialmente en la serie Pic. Tengo una pregunta sobre el TMR0 del pic16F877A.

Soy consciente de que TMR0 es un temporizador de 8 bits, que toma un total de 256 tics hasta que se produce un desbordamiento. Mi configuración se ejecuta en un cristal de cuarzo de 4MHZ, por lo tanto, cada tic es igual a 1us. Un tiempo total de 256us causará un desbordamiento teniendo en cuenta que el prescaler se elige en 1: 1.

Después de simular este retardo estableciendo el bit RB1 igual a (ALTO), se midió un retardo total de 265 us usando proteus 7. ¿De dónde viene esta discrepancia? Gracias por adelantado.

Código:

#include <xc.h>

#define _XTAL_FREQ 4000000

    #pragma config FOSC = HS        
    #pragma config WDTE = OFF      
    #pragma config PWRTE = OFF      
    #pragma config BOREN = OFF      
    #pragma config LVP = ON        
    #pragma config CPD = OFF        
    #pragma config WRT = OFF       
    #pragma config CP = OFF  



void main()
{
    TRISBbits.TRISB1=0;
    PORTBbits.RB1=0;

    TMR0IE=0;
    TMR0IF=0;
    T0SE=0;
    T0CS=0;
    PSA=1;
    TMR0=0;

    while(1)
    {
    while(TMR0IF!=1);
    PORTBbits.RB1=1;
    TMR0IF=0; //I assume it takes 1us
    TMR0=0; // Also 1us?

    while(TMR0IF!=1);
    PORTBbits.RB1=0;
    TMR0IF=0;
    TMR0=0;
    }
}

    
pregunta Bonavia

4 respuestas

2

Espero que la discrepancia provenga principalmente de sus comprobaciones "while (TMR0IF! = 1)". Incluso después de que se haya establecido el indicador de interrupción, aún debe completar la prueba para ver si TMR0IF es 1. Esto tomará varias instrucciones (en el nivel de ensamblaje), lo que lleva tiempo, así como la inicialización del temporizador (no, esto tomará más más de un "tick" para completar) y las instrucciones requeridas para configurar / borrar RB1.

Si desea una precisión absoluta, realmente no debería usar C, ya que es mucho más difícil tener en cuenta el tiempo. Una línea en C podría tomar una docena de ciclos de instrucción. Es mucho más fácil tener en cuenta el tiempo si usa ASM (aunque es la ÚNICA vez que consideraría usar ASM, personalmente).

    
respondido por el DerStrom8
1

No es necesario que reinicie TMR0 a cero: alcanzará el número máximo de 255, y en su próximo reloj volverá a cero. Ha restablecido correctamente el indicador de interrupción para TMR0 (TMR0IF = 0) para cada evento de desbordamiento. Tu código debería funcionar como esperas si eliminas ambas líneas: TMR0 = 0; Su salida PORTBbits.RB1 ahora debería alternar cada 256 relojes (256 us).

    
respondido por el glen_geek
0

Sin molestarme en explicar cada tic del reloj de cada declaración, diré desde el principio que el uso de temporizadores con este requisito de precisión se maneja mejor con interrupciones de temporizador y no con sondeo constante. El sondeo consumirá las garrapatas del reloj y, además, creará más ruido de fase de lo que desea.

Por lo tanto, en lugar de usar sentencias de decisión y código de bifurcación para hacer las cosas, deje que HARDWARE se ocupe de la sincronización.

    
respondido por el Scott Seidman
0

Primero, debes darte cuenta de que el tiempo de ejecución del código escrito en C variará dependiendo del código de máquina que genere el compilador. No puede asumir que una línea de código en particular se tomará solo porque solo realiza una operación.

Aquí está la salida del ensamblador de su código, compilado con XC8 V1.37 en modo 'libre': -

; while(1)
l19:    
 ; {
 ; TMR0=0;
    bcf status, 5   
    bcf status, 6   
    clrf    (1) 
    goto    l20

 ; while(1)
 l19:   
 ; {
 ; while(TMR0IF!=1);
    goto    l20
 l21:   
 l20:   
    btfss   (90/8),(90)&7
    goto    u11
    goto    u10
 u11:
    goto    l20
 u10:

 l22:   
 ; PORTBbits.RB1=1;
    bcf status, 5   
    bcf status, 6   
    bsf (6),1   

 ; TMR0IF=0;
    bcf (90/8),(90)&7

 l492:  
 ; TMR0=0;
    clrf    (1) 

 ; while(TMR0IF!=1);
    goto    l23
 l24:   
 l23:   
    btfss   (90/8),(90)&7
    goto    u21
    goto    u20
 u21:
    goto    l23
 u20:
 l25:   
  ; PORTBbits.RB1=0;
    bcf status, 5
    bcf status, 6
    bcf (6),1   

  ; TMR0IF=0;
    bcf (90/8),(90)&7

 l494:  
 ; TMR0=0;
    clrf    (1) 
    goto    l20
 l26:
    goto    l20
De hecho,

TMR0IF=0; y TMR0=0; pueden compilarse con instrucciones de un solo código de máquina, pero otras declaraciones como PORTBbits.RB1=1; crean varias instrucciones. Si los retrasos de tiempo involucrados en la configuración de RB1 alto y bajo fueran los mismos, entonces se cancelarían, pero el código está salpicado con muchas instrucciones goto que retrasan la detección de desbordamiento.

Un programa de ensamblador hecho a mano podría acercarse más al eliminar el código innecesario y (si es necesario) agregar NOP para igualar los retrasos. Sin embargo, todavía habría un retraso de 2 a 3 debido al tiempo que lleva encuestar TMR0IF. El uso de interrupciones puede reducir la fluctuación de fase, pero aún debe hacerse un ajuste para la latencia de interrupción y el tiempo de ejecución del código en el ISR.

Finalmente, eliminar Timer0 tarda 2 ciclos en completarse, por lo que el número total de ciclos a desbordar será de 258, no de 256. Esto se documenta en la hoja de datos: -

  

En el modo Timer, el módulo Timer0 incrementará cada instrucción   Ciclo (sin preescala). Si el registro TMR0 está escrito, el   el incremento se inhibe durante los siguientes dos ciclos de instrucción.   el usuario puede solucionar esto escribiendo un valor ajustado en la TMR0   registrarse.

    
respondido por el Bruce Abbott

Lea otras preguntas en las etiquetas