Temporización TMR0 en XC8

Hola,

He retomado la programación y sigo donde lo dejé:

Estoy haciendo un sencillo programa contador de 0-99 con un 16F84A y dos displays de 7 segmentos, como compilador el XC8. El retraso de cada cuenta ha de ser de 1 segundo.
El problema está en que aparentemente está todo correcto pero no consigo 1 segundo con el TMR0, va ligeramente más rápido y no me refiero a la imprecisión propia del Timer. He hecho los cálculos y configuraciones pertinentes y como dato adicional... diré que aunque modifique el Prescaler, o el valor del TMR0 no afecta a simple vista en la temporización actual del Timer.

Agradecería que alguien pudiera decirme que está mal o que falta en mi código.

Gracias por avanzado.

Saludos

Código:

PHP:
* 
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * PROGRAMA CONTADOR DE 0-99 CADA SEGUNDO USANDO EL TMR0.
 * PARA CONSEGUIR UN RETARDO DE 1 SEGUNDO APROXIMADO SE CONFIGURA
 * EL TMR PARA 50 ms. Y SE MULTIPLICA POR 20.
 * EL VALOR A CARGAR EN EL TMR0 PARA ESTO SERÁ DE 61.
 * 2 DISPLAYS DE 7 SEGMENTOS MULTIPLEXADOS DE ÁNODO COMÚN.
 * RA0 ACTIVA DISPLAY 1
 * RA1 ACTIVA DISPLAY 2
 * PUERTO B PARA SEGMENTOS DE LOS DISPLAYS "abcdefg".
 *
 * 
 * Created on 17 de noviembre de 2012, 18:41
 */

#include <xc.h>

#define _XTAL_FREQ 4000000

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF


/*
 * 
 */


// VARIABLES GLOBALES:

char u=0; // VARIABLE CONTADOR UNIDADES INICIALIZADA A 0.
char d=0; // VARIABLE CONTADOR DECENAS INICIALIZADA A 0.

char s=0, flag=20; // VARIABLE INICIALIZADA A 0 PARA CONTAR HASTA 20 EN LA FUNCION DE 1 SEGUNDO
                  // Y COMPARARLA CON EL FLAG INICIALIZADO 20.

// DECLARACIÓN DE LA FUNCIÓN DE 1 SEG. DE RETARDO CON TMR0:

void un_seg (void); // PROTOTIPO DE FUNCION. LA DECLARO PERO NO SE PORQUE ES NECESARIA.

void un_seg (void) { // FUNCIÓN DE RETARDO DE 1 SEGUNDO.
   
    if (T0IF=1) {
        
       T0IF=0; // FLAG DE DESBORDAMIENTO DEL TIMER0 PUESTO A "0" POR SOFTWARE.
        s++;
       
        if (s==flag) {
           
            
            s=0;
            u++;
            if (u>=10) {
                u=0;
                d++;
            
                if (d>=10) {
                    d=0;
                }
            }
         }
     }
    TMR0=61; // 61 ES EL VALOR CON QUE SE CARGA EL TMR0 PARA EL CÁLCULO DE 1 SEGUNDO.
}

void main(void) {
    
    OPTION_REGbits.T0CS=0; // SELECCIÓN DEL RELOJ INTERNO DEL TIMER0, A 0 COMO                     TEMPORIZADOR, A 1 COMO CONTADOR.

    OPTION_REGbits.PSA=0; // SELECCIÓN DEL PRESCALER AL TIMER0.
    OPTION_REGbits.PS0=1; // BIT0 DE SELECCIÓN DE TIEMPO DEL PRESCALER.
    OPTION_REGbits.PS1=1; // BIT1 DE SELECCIÓN DE TIEMPO DEL PRESCALER.
    OPTION_REGbits.PS2=1; // BIT2 DE SELECCIÓN DE TIEMPO DEL PRESCALER.
    
    
    INTCONbits.GIE=1; // HABILITA LAS INTERRUPCIONES GLOBALES.
    INTCONbits.T0IE=1; // HABILITA LA INTERRUPCIÓN POR DESBORDAMIENTO DEL TMR0.

    int tabla7seg [10]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x98}; // 0-9 EN HEXADECIMAL.

    TRISB=0; // PUERTO B COMO SALIDA.
    TRISA=0; // PUERTO A COMO SALIDA.
    PORTB=1; // Al ser los 7 seg. de Ánodo Común se pone el PORTB a 5 voltios para que se apague.
    PORTA=0; // SE INICIALIZA EL PORTA A 0V. RA0 Y RA1 GESTIONARÁN LOS DOS DISPLAYS.
    TMR0=61;
   
    do {
        un_seg ();
        RA0=1; // SE ACTIVA EL DISPLAY 1 DE UNIDADES.
        PORTB=(tabla7seg [u]); // MUESTRA EN EL PORT B EL VALOR DE LA tabla7seg SEGUN "u" UNIDADES.
        __delay_ms(10); // RETARDO DE PRUEBA, PARA PROBAR, SE ELIMINARÁ EN EL PROGRAMA FINAL.
        RA0=0; // SE DESACTIVA EL DISPLAY 1 DE UNIDADES.
        
        RA1=1; // SE ACTIVA EL DISPLAY 2 DE DECENAS.
        
        PORTB=(tabla7seg [d]); // MUESTRA EN EL PORT B EL VALOR DE LA tabla7seg SEGUN "d" DECENAS.
        __delay_ms(10);
        RA1=0;
        
    } while (1); // BUCLE INFINITO PARA QUE NO ENTRE EN REPOSO Y PIERDA LA CUENTA DEL TMR0.

}
 
No entiendo la estructura de tu programa.

Deberías de activar las interrupciones, y que el programa principal quede en un bucle que lo único que hace es actualizar el visualizado de segundos a partir de una variable global de segundos "int segundos=99;"(fuera del main).

Después en el tratamiento de la interrupción por desbordamiento del TMR0, puedes tener otra variable global que puede ser desbordamientos, entonces cada vez que entre en la interrupción sumas 1 a desbordamientos y si se 20, lo pones a 0 y restas 1 a segundos.

Después al salir de la interrupción, ya el main te actualiza segundos en el visualizador.

Un saludo
 
Hola Basalto, gracias por la respuesta:

Ten en cuenta que estoy empezando en la programación, en este programa estoy implementando por primera vez el uso de Funciones y el TMR0.

Yo diría que si tengo activadas las interrupciones, tanto del TMR0 como las Globales, el bucle infinito lo hago con el while (1), si te fijas he procurado comentar el código.

No tengo ningún problema con la visualización de los dígitos en el display, todo OK, el problema es la cadencia de visualización... yo querría que fuera cada 1 segundo pero no logro hacerlo, se que hay que hacer pero por algún motivo el código me falla en la sintaxis o algo, no encuentro mucha info en XC8. Me estoy basando en ejemplos de código de CCS y lo adapto a XC8.

A ver si alguien me puede decir que tengo mal y cual sería la forma correcta.

Gracias por la atención.

Saludos
 
Es que al principio habilitas las interrupciones y después no las utilizas.

Imagínate que compruebas T0IF y a la instrucción siguiente el contador desborda. Pasaría mucho tiempo hasta que trates el desbordamiento se ejecuta el resto de las instrucciones un_segundo, el delay 10ms y la actualización del display.

Es muy ineficiente ese programa y pude ser lo que te de fallo.
 
Podrías porfavor sugerirme el código correcto?? no estoy seguro de entenderte.

Tal como yo lo entiendo... en mi código llamo a la función un_seg, comprueba que se ha desbordado T0IF, se pone a 0 T0IF para el siguiente desbordamiento e incrementa el contador de desbordamientos, si el contador ha llegado al valor estipulado (20 desbordamientos) entonces realiza el incremento de los dígitos.

Gracias por tu atención

1 Saludo
 
Observando un poco más he visto en la función un_seg que tengo que poner if (T0IF==1) en lugar de if (T0IF=1), quedando correctamente definida la condición. Antes asignaba un 1 al Flag de desbordamiento, ahora compara el Flag con "1" y si es "1" pregunta si ha pasado 1 segundo y realiza el incremento de los dígitos.

Ahora esta corrección deja al descubierto un problema y es que el contador no incrementa. Es decir... es como si nunca se pusiera a "1" el Flag de desbordamiento del Timer0.

Alguien puede ayudarme a detectar que tengo mal en el código??? sintaxis??? orden de las instrucciones??

Estoy muy perdido.

Gracias por avanzado.

1 Saludo
 
Buenas, sólo decir, a quién pueda interesarle, que parece ser que ya lo he conseguido.

Por lo visto tenía que declarar una rutina de interrupción la cual se atiende con cualquier tipo de interrupción, una vez dentro de la rutina se pregunta si la interrupción viene por el TMR0 y por su desbordamiento y si es así realiza la temporización.

Adjunto el código:

PHP:
/* 
 * File:   main.c
 * Author: nimio
 *
 * 16F84A
 *
 * PROGRAMA CONTADOR DE 0-99 CADA SEGUNDO USANDO EL TMR0.
 * PARA CONSEGUIR UN RETARDO DE 1 SEGUNDO APROXIMADO SE CONFIGURA
 * EL TMR PARA 50 ms. Y SE MULTIPLICA POR 20.
 * EL VALOR A CARGAR EN EL TMR0 PARA ESTO SERÁ DE 61.
 * 2 DISPLAYS DE 7 SEGMENTOS MULTIPLEXADOS DE ÁNODO COMÚN.
 * RA0 ACTIVA DISPLAY 1
 * RA1 ACTIVA DISPLAY 2
 * PUERTO B PARA SEGMENTOS DE LOS DISPLAYS "abcdefg".
 *
 * 
 * Created on 17 de noviembre de 2012, 18:41
 */

#include <xc.h>


#define _XTAL_FREQ 4000000

#pragma config FOSC=XT, WDTE=OFF, PWRTE=ON, CP=OFF


/*
 * 
 */


// VARIABLES GLOBALES:

char u=0; // VARIABLE CONTADOR UNIDADES INICIALIZADA A 0.
char d=0; // VARIABLE CONTADOR DECENAS INICIALIZADA A 0.

char s=0, flag=20; // VARIABLE INICIALIZADA A 0 PARA CONTAR HASTA 20 EN LA FUNCION DE 1 SEGUNDO
                  // Y COMPARARLA CON EL FLAG INICIALIZADO 20.
void timer0() { // FUNCIÓN DE RETARDO DE 1 SEGUNDO.

    TMR0=60; // 61 ES EL VALOR CON QUE SE CARGA EL TMR0 PARA EL CÁLCULO DE 1 SEGUNDO.

    OPTION_REGbits.T0CS=0; // SELECCIÓN DEL RELOJ INTERNO DEL TIMER0, A 0 COMO TEMPORIZADOR, A 1 COMO CONTADOR.
    OPTION_REGbits.PSA=0; // SELECCIÓN DEL PRESCALER AL TIMER0.
    OPTION_REGbits.PS0=1; // BIT0 DE SELECCIÓN DE TIEMPO DEL PRESCALER.
    OPTION_REGbits.PS1=1; // BIT1 DE SELECCIÓN DE TIEMPO DEL PRESCALER.
    OPTION_REGbits.PS2=1; // BIT2 DE SELECCIÓN DE TIEMPO DEL PRESCALER.

    INTCONbits.GIE=1; // HABILITA LAS INTERRUPCIONES GLOBALES.
    INTCONbits.T0IE=1; // HABILITA LA INTERRUPCIÓN POR DESBORDAMIENTO DEL TMR0.

}

void interrupt un_seg (void) { // FUNCIÓN DE INTERRUPCIÓN. SE ATIENDE ESTA INTERRUPCIÓN CUANDO SE
                               // DETECTA ALGÚN TIPO DE INTERRUPCIÓN COMO LA ACTIVACIÓN DE UN FLAG.
                               // SI NO SE ESPECIFICA ES DE ALTA PRIORIDAD.

    if (INTCONbits.T0IE==1 && INTCONbits.T0IF==1) { // SI LA INTERRUPCIÓN POR DESBORDAMIENTO DEL TMR0
                                                    // ESTÁ ACTIVADA Y SE HA ACTIVADO EL FLAG DE
                                                    // DESBORDAMIENTO DEL TMR0 ENTONCES...
        INTCONbits.T0IF=0;
       
        s++;
        TMR0=60;

        if (s==flag) {

            s=0;
            u++;

            if (u>=10) {

                u=0;
                d++;

                if (d>=10) {

                    d=0;
                }
            }
         }
        
     }
    
}
    void main(void) {

    timer0(); // LLAMA A LA FUNCIÓN DE CONFIGURACIÓN DE LOS BITS DE TIMER0

    int tabla7seg [10]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x98}; // 0-9 EN HEXADECIMAL.

    TRISB=0; // PUERTO B COMO SALIDA.
    TRISA=0; // PUERTO A COMO SALIDA.
    PORTB=1; // Al ser los 7 seg. de Ánodo Común se pone el PORTB a 5 voltios para que se apague.
    PORTA=0; // SE INICIALIZA EL PORTA A 0V. RA0 Y RA1 GESTIONARÁN LOS DOS DISPLAYS.

    do {
        
        RA0=1; // SE ACTIVA EL DISPLAY 1 DE UNIDADES.
        
        PORTB=(tabla7seg [u]); // MUESTRA EN EL PORT B EL VALOR DE LA tabla7seg SEGUN "u" UNIDADES.
        __delay_ms(10); // RETARDO DE PRUEBA, PARA PROBAR, SE ELIMINARÁ EN EL PROGRAMA FINAL.
        RA0=0; // SE DESACTIVA EL DISPLAY 1 DE UNIDADES.

        RA1=1; // SE ACTIVA EL DISPLAY 2 DE DECENAS.
        
        PORTB=(tabla7seg [d]); // MUESTRA EN EL PORT B EL VALOR DE LA tabla7seg SEGUN "d" DECENAS.
        __delay_ms(10);
        RA1=0;

    } while (1); // BUCLE INFINITO PARA QUE NO ENTRE EN REPOSO Y PIERDA LA CUENTA DEL TMR0.

}

Saludos
 
Atrás
Arriba