Saltar al contenido

En esta entrada entenderemos como funciona el Timer del microcontrolador PIC, donde veremos que realmente existen diferentes timers o temporizadores dentro de nuestro sistema embebido.

Antes de comenzar la clase, te invito para que veas el

👉👉👉 CURSO GRATIS DE MICROCONTROLADORES PIC

Y te invito a VER LA LISTA DE REPRODUCCIÓN DE YOUTUBE

Antes de Comenzar suscríbete al canal para aprender todo sobre PIC con el compilador CCS C

Timer PIC

Los temporizadores o Timers son una de las características mas importantes para un programador de sistemas embebidos. Cada aplicación que diseñamos involucrará de alguna manera una aplicación de tiempo, como encender o apagar algún dispositivo después de un intervalo de tiempo específico. 

A diferencia de simplemente usar el delay_ms() del CCS C, los timers son mucho más versatiles y precisos, dado que con el macro de dalay_ms() lo que hacemos es detener la ejecución del PIC, sin embargo con el timer, podemos continuar nuestra ejecución y realizar el conteo o temporización en segundo plano.

El microcontrolador PIC16F887 tiene 3 temporizadores:

  1. Timer 0 (8 bits)
  2. Timer 1(16 bits)
  3. Timer 2(8 bits)

En esta entrada vamos a tratar de entender como podemos configurarlos usando el compilador CCS C PIC C.

Timer 0

A pesar del Timer 0 ser de 8 bits, este es el temporizador principal.

El Timer0 pic también llamado RTCC se puede cargar con un valor cualquiera entre 0 y 255 y puede ser incrementado a través del Reloj interno y dividido por un valor que se puede escoger entre los que se indican a continuación. Esto se conoce como el valor del preescalador (Valor de Preescaler):

RTCC_DIV_2, RTCC_DIV_4, RTCC_DIV_8,
RTCC_DIV_16, RTCC_DIV_32,
RTCC_DIV_64, RTCC_DIV_128,
RTCC_DIV_256.

pero….¿Que es un preescalador?

Un preescalador o preescaler como lo pudimos observar en los valores anteriores predefinidos en el PIC C Compiler, es simplemente la velocidad del microcontrolador dividido por algún número de potencia 2 (2, 4, 8, 16, 32, 64, 128, 256)

Desborde del TIMER 0 en PIC

La interrupción RTCC o timer PIC se produce cada vez que el contador TIMER0 pasa de 255 para 0.

Ya hemos trabajado anteriormente nuestros proyectos con cristales de cuarzo. Recordemos lo siguiente:

Todo microcontrolador requiere un circuito externo que le indique la velocidad a la que debe trabajar. Este circuito, que se conoce como oscilador o reloj, es muy simple pero de vital importancia para el buen funcionamiento del sistema. El PIC16F887 puede utilizar cuatro tipos de oscilador diferentes. Estos tipos son:

  • RC. Oscilador con resistencia y condensador.
  • XT. Cristal.
  • HS. Cristal de alta velocidad.
  • LP. Cristal para baja frecuencia y bajo consumo de potencia.

Si se trabaja el Microcontrolador con un cristal de 4 Mhz, esta frecuencia se divide internamente por 4, es decir realmente trabaja a 1Mhz, o sea que cada ciclo de reloj dura aproximadamente 1 microsegundo.

Si te interesa entender como funcionan los tiempos dentro de un microcontrolador PIC, tenemos una entrada que explica sobre los tipos de reloj (clock) del microcontrolador PIC.

Para entender el funcionamiento del Timer 0, como ejemplo se supone que se necesita generar una interrupción cada 20 ms. (20.000 microsegundos).

¿Cuando se trabaja con el TIMER del PIC, qué fórmula usar para determinar con que valor se debe cargar inicialmente el Timer 0 y que valor de preescaler o división se debe utilizar?

La fórmula para aplicar es la siguiente recordando que primero debemos pasar el tiempo de temporización a segundos es:

CargaT=\dfrac{Tiempo(s)*F.Oscilador}{4*Valor\ Preescaler}=valor\hspace{0.2cm}del\hspace{0.2cm}timer

Sin embargo, se debe tener en cuenta que la interrupción solo se genera cuando el timer pasa de 255 a 0, esto quiere decir que debemos restarle al valor total del timer (256) el valor de carga inicial que tenga el Timer 0:

256 – Valor inicial del Timer 0

Ya habiamos dicho que el valor del Timer 0 puede ser dividido (preescaler) por un valor aplicando alguna de las instrucciones citadas al comienzo del post.

Cuando nosotros seleccionemos dicho preescaler o división, debemos tratar de obtener un valor entero al dividir el tiempo del retardo sobre el preescaler.

Este valor no puede ser mayor a 256. En caso de ser mayor, significa que antes de cumplir el retardo el Microcontrolador habrá generado más de una interrupción.

En este caso se combina entonces la programación del Timer 0 PIC y cada vez que el Timer0 PIC genere una interrupción se decrementa un contador tantas veces como sea necesario hasta completar el tiempo de temporización.

Finalmente el procedimiento que se debe seguir para calcular el valor inicial del timer y el valor del contador a decrementar es el siguiente, suponiendo que escogemos un preescaler de 8

RTCC_DIV_8

CargaT=\dfrac{0.02*4000000}{4*8}=2500

Vemos entonces que se generó un valor mayor a 255, por lo tanto es necesario calcular cuantas veces debe generarse una interrupción (a través de un contador) antes de completar el retardo de 20 milisegundos.

El resultado de la división anterior debe ser igual a:

valor\hspace{0.2cm}del\hspace{0.2cm}timer=Valor\hspace{0.2cm}inicial\hspace{0.2cm}del\hspace{0.2cm}Timer0\times Valor\hspace{0.2cm}del\hspace{0.2cm}contador\hspace{0.2cm}a\hspace{0.2cm}decrementar

2500=valor inicial del timer * valor del contador a decrementar

Vemos que tenemos dos incoginitas, esas dos incognitas pueden ser valores aleatorios inferiores a 256. Por ejemplo: 10 y 250. Aqui puedo escoger cualquiera de los dos valores para que sea el Valor Inicial del Timer (10)

Posterior a esta multiplicación, se hace:

256 – Valor inicial del timer 0

256-10=246

Con esto tengo que mi timer del PIC o RTCC es 246 y que mi contador debe contar hasta 250.

Ejemplo  Timer de 1 segundo.

Para generar un retardo de un segundo usando el timer del PIC, con preescaler de 256, se procede de la siguiente manera:

CargaT=\dfrac{1*4000000}{4*RTCC\_DIV\_X}

X = Este valor puede ser cualquiera de los indicados al principio, el que se elija será con el que se seguirá trabajando en la programación. En este caso el RTCC escogido es 256.

CargaT=\dfrac{1*4000000}{4*256}=3906.25\approx 3906

El valor anterior, es decir 3906 debe ser igual a la multiplicación entre el valor inicial del timer 0 y el valor del contador a decrementar.

3906= valor inicial del timer * valor del contador a decrementar

Observación: estos dos valores son aleatorios y la única condición que se debe cumplir es que uno de estos dos números (valor inicial) debe ser siempre menor a 256, el contador puede ser mayor, pero debera declararse entonces en el código como int16.
3906 = 18 * 217

Cualquiera de estos dos valores pueden ser el valor inicial del timer 0. En este caso, se elige 18 como valor inicial del timer 0. Al obtener el valor inicial del timer 0 se debe restar el RTCC utilizado, en este caso 256 para obtener el número donde el temporizador debe iniciar para que en el momento que incremente las 18 veces llegue al valor del RTCC utilizando (256) y produzca la interrupción ó salto. Es decir:

RTCC – Valor inicial del timer
256 – 18 = 238

Ya con estos valores, tenemos el valor que debemos cargar en el Timer y el valor que debemos cargar en el contador. Quedaría de la siguiente manera:

RTCC=238

Contador=217

Así tendriamos el temporizador para 1 segundo usando el timer del pic, porque el RTCC se carga con 238, cada ciclo de reloj, va incrementando y cuando llega a 256 (Pasados 18 ciclos de reloj), decrementa el contador en 1 unidad, y vuelve y carga el RTCC con 238. Así nuevamente el RTCC va incrementando y cuando llega a 256 vuelve y decrementa en 1 unidad el contador. Cuando el contador por fin llegue a Cero (0), quiere decir que ya paso 1 segundo (1000000 micro segundos).

Instrucciones en CCS C

Ahora que conocemos la teoría, es hora de ver cuales son las instrucciones que el compilador de CCS C nos proporciona para poder manipular el Timer en PIC.

Para inicializar el contador del TIMER0 usamos la siguiente instrucción donde la variable value puede ser de 8 o 16 bits, dependiendo del dispositivo que se esté trabajando.

set_rtcc(value);

Para configurar el TIMER0 en modo temporizador tal y como lo hemos visto a lo largo de este post utilizamos la siguiente Función:

setup_timer_0(RTCC_INTERNAL | RTCC_DIV_N | RTCC_M_bits);

RTCC_INTERNAL, indica que estamos usando el timer0 en modo temporizador.

RTCC_DIV_N configura el preescaler en función de N. Donde N puede tomar uno de los siguientes valores: 1,2,4,8,16,32,64,128,256.

RTCC_M_bits indica el número de bits del temporizador en función de M. Dondem M puede tomar uno de los siguientes valores: 8, 16.

Finalmente, se debe activar la interrupción por desbordamiento del Timer0:

enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);

Y colocar el macro de la interrupción por timer, que corresponde a la función que realizará el PIC una vez el desbordamiento se cumpla. En esta función es donde deberemos decrementar el contador para alcanzar el valor deseado:

#INT_RTCC  //TIMER0
void timer0(void){
   contador--;          //Se decrementa hasta llegar a cero
   set_rtcc(value); //Timer0
   if (contador<=0) // Si llega a cero, se cumplió tiempo
   {
      //Su Codigo aqui......
      contador=valor_contador;  // Inicializa el contador para el próximo periodo
   }
}

Nota 1:

Existe un comando paralelo para configurar el TIMER0 como temporizador o como contador que hace exactamente lo mismo que setup_timer_0:

setup_counters (rtcc_state, RTCC_DIV_N)

rtcc_state: puede ser una de las constantes definidas en el archivo .h de dispositivos.
RTCC_INTERNAL
RTCC_EXT_L_TO_H
RTCC_EXT_H_TO_L

Sin embargo esta última función se proporciona para compatibilidad con versiones anteriores. setup_timer_0 y setup_WDT son los reemplazos recomendados cuando sea posible. Para dispositivos PCB si se usa un reloj RTCC externo y se usa un preescalador WDT, entonces esta función debe usarse.

Nota 2 – Timer0 como Contador

Ademas de configurar el TIMER0 como temporizador, es posible hacer que este trabaje como contador de eventos.

Para configurar el TIMER0 en modo contador se utiliza una de las funciones siguientes:

setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_N );

O bien:

setup_timer_0(RTCC_EXT_H_TO_L | RTCC_DIV_N );

RTCC_EXT_L_TO_H, configura el modo contador y hace que el registro TIMER0 se incremente con cada flanco ascendente en RA4.

RTCC_EXT_L_TO_H, configura el modo contador y hace que el registro TIMER0 se incremente con cada flanco descendente en RA4.

KIT para PIC BARATOS

A continuación te dejo algunas placas de desarrollo BARATAS para que puedas aprender más facilmente sobre los microcontroladores PIC y los puedas adquirir desde cualquier parte del MUNDO, a través del sitio web chino de Aliexpress.

Es importante destacar que para la construcción de nuestros proyectos electrónicos con microcontroladores podemos comprarlos también desde la comodidad de nuestra casa en Amazon que tiene envios para cualquier lugar de latinoamerica y para europa:

 TIMER PIC – EJEMPLO

Realizar un temporizador de 0 a 60 segundos empleando el TIMER del PIC

Multiplexacion con display 7 segmentos
#INCLUDE<16f887.h>
#USE DELAY(CRYSTAL=4000000)
#fuses XT,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
Byte CONST display[10]= {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x67};
#DEFINE U PORTC,0
#DEFINE D PORTC,1
#byte PORTB= 6
#byte PORTC= 7
INT VECES,SEG;
// Función para mostrar los segundos en el Display
VOID MOSTRAR( ) //Rutina mostrar
{
   INT UNI,DEC;   //Declarar las variables UNI, DEC
                  //como un entero, es decir de 8bits
   DEC=SEG/10;
   UNI=SEG%10;
   PORTB=(DISPLAY[UNI]);   //Muestra lo que hay en unidades
                           //en el display
   BIT_SET (U);            //Enciende el display de unidades
   DELAY_MS(1);            //Retardo de 1 milisegundos
   BIT_CLEAR(U);           //Apaga el display de unidades
   PORTB=(DISPLAY[DEC]);   //Muestra lo que hay en unidades
                           //en el display
   BIT_SET (D);            //Enciende el display de decenas
   DELAY_MS(1);            //Retardo de 1 milisegundos
   BIT_CLEAR(D);           //Apaga el display de decenas
   
}
//Rutina de interrupción por RTCC (TIMER)
#INT_RTCC 
RELOJ()
{
   VECES--;          //Se decrementa la variable VECES
   SET_RTCC(238);    //Se carga el timer con 238
   IF(VECES==0)      //Pregunta si VECES ya llego a cero
   {
      SEG++;         //Cuando VECES llega a cero incrementa SEG (Transcurrio 1 seg)
      VECES=217;     //Vuelvo y cargo VECES con el valor 217
   }
}
//Programa Principal
VOID MAIN()
{
   SET_TRIS_B(0);    //Configura PUERTO B como salida
   SET_TRIS_C(0);    //Configura PUERTO C como salida
   VECES=217;        //Carga VECES con 217 para efectuar la cuenta de 1 seg con el timer
   SEG=0;            //Inicializa los segundos en cero
   SET_RTCC(238);    //Cargo valor inicial del timer
   
   // Configuración ANTIGUA del TIMER0
   //SETUP_COUNTERS(RTCC_INTERNAL, RTCC_DIV_256); //Configura interrupcion del timer
   
   // Configuración Recomendada del TIMER0
   SETUP_TIMER_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); 
   
   ENABLE_INTERRUPTS(INT_RTCC);                 //Activa interrupcion del timer
   ENABLE_INTERRUPTS(GLOBAL);                   //Activa TODAS las interrupciones
   WHILE(TRUE)       //Haga por siempre
   {
      IF(SEG==60)    //Pregunta si ya se llego a 60 segundos
         SEG=0;      //Si si, vuelva a SEG a cero
      ELSE           //Si no,
         MOSTRAR();  //Muestre el valor de SEG en los Display 7 Segmentos
   }
}

Timer 1

Como ya vimos el Timer 1 es un temporizador de 16 bits, y su funcionamiento es similar al que detallamos en el Timer 0, por eso será fundamental que hayas leído y entendido dicho temporizador.

En este timer 1 a diferencia del timer 0, es que al poseer 16 bits su desbordamiento se dan cuando el valor del timer pasa del valor 65536 a 0.

Los valores de Preescaler para el timer 1 también cambian, en este caso solo podemos dividir por 1, 2, 4 y 8, y en CCS C se definen como:

T1_DIV_BY_1 
T1_DIV_BY_2 
T1_DIV_BY_4 
T1_DIV_BY_8

Aplicando el mismo procedimiento visto para el Timer 0, procedemos a encontrar la carga del Timer 1:

\rm CargaT_1=\dfrac{Tiempo(s)\cdot F_{osc}}{4\cdot Preescaler}= Valor\ Timer1

El valor timer1 debe ser igual a la multiplicación entre el valor inicial del timer 1 y el valor del contador a decrementar.

\rm Valor\ Timer1 = Valor\ inicial\ Timer1 \cdot Valor\ del\ Contador

Donde Valor Inicial Timer1 y Valor del Contador son dos valores enteros aleatorios que no pueden ser superiores a 65536.

Cualquiera de estos dos valores pueden ser el valor inicial del timer 1. Al obtener el valor inicial del timer 1 se debe restar el TMR1 utilizado, en este caso 65536 para obtener el número donde el temporizador debe iniciar para que en el momento que incremente las veces establecidas en la variable Valor del Contador llegue al valor del TMR1 utilizando (65536) y produzca la interrupción o salto. Es decir:

\rm value=TMR1- Valor\ inicial\ Timer1

Ya con estos valores, tenemos el valor que debemos cargar en el Timer y el valor que debemos cargar en el contador.

Instrucciones en CCS C

Para inicializar el contador del TIMER1 usamos la siguiente instrucción donde la variable value puede ser de 8 o 16 bits, dependiendo del dispositivo que se esté trabajando.

set_timer_1(value);

Para configurar el TIMER1 en modo temporizador utilizamos la siguiente Función:

setup_timer_1(mode);

Donde mode puede ser configurado como:

T1_DISABLED, T1_INTERNAL, T1_EXTERNAL, T1_EXTERNAL_SYNC
T1_CLK_OUT
T1_DIV_BY_1, T1_DIV_BY_2, T1_DIV_BY_4, T1_DIV_BY_8

Finalmente, se debe activar la interrupción por desbordamiento del Timer1:

enable_interrupts(INT_TIMER1);
enable_interrupts(GLOBAL);

Y colocar el macro de la interrupción por timer, que corresponde a la función que realizará el PIC una vez el desbordamiento se cumpla. En esta función es donde deberemos decrementar el contador para alcanzar el valor deseado:

#int_timer1  //TIMER1
void timer1(void){
   contador--;          //Se decrementa hasta llegar a cero
   set_timer1(value);         //Carga de nuevo el timer1
   if (contador<=0)     // Si llega a cero, se cumplió el tiempo
   {
      //Codigo aqui......
      contador=valor_contador;  // Inicializa el contador para el próximo periodo
   }
}

Temporizador de 8 seg (Timer 1)

Vamos a crear un temporizador de 8 segundo usando el timer 1 y el PIC C Compiler, usando en este caso un reloj de Cristal de Cuarzo de 20MHz. En este caso usamos un preescaler de 8.

\rm CargaT_1=\dfrac{8\cdot 20.000.000}{4\cdot 8}= 5000000

Vemos entonces que se generó un valor mayor a 65536, por lo tanto es necesario calcular cuantas veces debe generarse una interrupción (a través de un contador) antes de completar el retardo de 8 segundos.

Por lo tanto el valor inicial del timer puede ser 50.000 y el valor del contador puede ser 100 para alcanzar la carga del timer1:

\rm 50.000.000 = 50.000 \cdot 100

Por último realizamos la resta del valor inicial del timer con el valor máximo del timer1 para saber con cuanto inicializamos nuestro temporizador.

\rm Timer1 = 65.536-50.000 = 15536
#include <18f4550.h>
#DEVICE ADC=10
#USE DELAY(crystal=20000000)
#FUSES HS,NOPROTECT,NOWDT,NOBROWNOUT,PUT,NOLVP
#byte porta = 0xf80 // Identificador para el puerto A. 
#byte portb = 0xf81 // Identificador para el puerto B. 
#byte portc = 0xf82 // Identificador para el puerto C. 
#byte portd = 0xf83 // Identificador para el puerto D. 
#byte porte = 0xf84 // Identificador para el puerto E.

int16 Ts_cont=0;


// ********************************* //
// ******* Interrupción Timer 1 **** //
// ********************************* //
#int_timer1  //TIMER1
void sampling_time(void){
   Ts_cont--;          //Se decrementa hasta llegar a cero
   set_timer1(15536);//Carga de nuevo el timer1
   if (Ts_cont<=0) // Si llega a cero, se cumplió el periodo de muestreo
   {
      output_toggle(pin_d0);
      Ts_cont=100;  // Inicializa el contador para el próximo periodo
   }
}

// ********************************* //
// ****** Programa Principal ******* //
// ********************************* //
void main() {  

   set_tris_d(0b0); //Configura el pin D0 como salida (LED)


   // Configura el Timer 1
   setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_8 );
   Ts_cont=100;        //Carga Contrador para Calcular el Periodo de Muestreo
   set_timer1(15536); // Inicializa el Timer 1 para calcular 8 Segundos de Ts
 
   // Habilitar las interrupciones del PIC
   enable_interrupts(int_timer1);
   enable_interrupts(GLOBAL);
   while(1){
   }
}

No olvides compartir el contenido en Redes sociales, para que me ayudes a hacer crecer el sitio, y que la información pueda llegarle a mas personas que lo necesiten y quieran aprender sobre este interesante mundo de los microcontroladores. Hasta la próxima.

Eso es todo por la entrada del dia de hoy, espero les haya gustado y hayan aprendido algo nuevo. Si te ha servido el contenido de esta entrada, de los videos y los códigos de implementación y deseas apoyar mi trabajo invitandome a un café super barato, puedes hacerlo en el siguiente link:

👉 Invitar a Sergio a un Café ☕️

Que esten muy bien, nos vemos en la siguiente entrada.

Entradas relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Comentarios (29)

Olá, temos 2 tempos aqui, um tempo usando a frequência da rede de 60 Hz, para que eu tenha 10 minutos com as saídas em nível lógico alto e depois 10 minutos em nível lógico baixo, então terei contagem, minuto, segundo e i em 0, vai está repetindo o ciclo, se o pino A3 estiver em 0. E um tempo usando o timer 0 para que eu tenha 3 segundos na transição de saída alta para saída baixa e vice-versa. Com o pino A3 estando em 0 e depois em 1, ou em 1 e depois em 0. os 3 segundo usando o timer0 é para não usar delay, a contagem fica bem superior se eu usar o delay, por isso não quero usar ele, quero uma pausa sem que isso atrapalha minha contagem.consigo fazer isso desse jeito ou tem outro funcional. Obrigada desde já

Responder

Muchas Gracias Sergio por la informacion,
Como hacer para leer pulsos mas cortos, p.e. una frecuencia de 300.000 Hz

Gracias de antemano

Responder

Hola, no se que hice mal, pero mi codigo no quiere funcionar, hice el calculo pero el resultado no coincide con lo que esperaba, por ejemplo, pin_a0 no cambia cada 1 segundo y pin_a5 debe permanecer encendido por 20 minutos y después de apagarlo, sin embargo, hizo una gran diferencia, más o menos un 50% más, he intentado todo y no tuve éxito, no sé si entienden lo que yo Digo, porque no hablo tu idioma. Gracias. Ese es el código.

#include

//int16 analogico1 = 0;
//int16 analogico2 = 0;
//int16 analogico3 = 0;
int16 minuto = 0;
int16 segundo = 0;
#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 10
#use delay(clock=32000000)
#use fast_io(A)
int32 cnt = 0;
int32 liga = 0;
int32 li = 0;
//*************************************************************************************************************
#INT_TIMER0
void TIMER0_isr(void)
{
clear_interrupt(INT_TIMER0); // Clear timer0 interrupt flag bit

set_timer0(131);
cnt++;
if(cnt == 250) // if(cnt >= 3750)//if(cnt >= 125)
{
cnt=0;
segundo++;
//output_toggle(PIN_A0);

}
/*set_timer0( 130 + get_timer0());*///set_timer0(130);
}
// ************************************************************************************************************

#INT_RA // interrupção RA.
//————————————————————————
void RA_isr(void) // interromper a rotina de serviço
{
clear_interrupt(INT_RA3);
if(input(PIN_A3)) // Se RA3 for HIGH
{

liga++;
}
if (liga >= 3600 )
{
liga = 0;
li++;
}
}

//***************************************************************************************************************
void main(){
// set_timer0( 130 + get_timer0());
set_timer0(131);
setup_adc_ports(sAN1|sAN2|sAN3);
setup_adc(ADC_CLOCK_INTERNAL);
setup_oscillator(OSC_8MHZ | OSC_PLL_ON); // Set internal oscillator to 32MHz (8MHz and PLL)
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128|RTCC_8_bit); //4,0 ms overflow (Uso do timer0) EM 32MHz
set_tris_a (0b00011110 ); //(0b00011110); // HEX 1E RA Tris 76543210 0X1E
enable_interrupts(INT_TIMER0); // (uso do interrupts)
enable_interrupts(GLOBAL); // (uso do interrupts)
clear_interrupt(int_timer0);
output_high(pin_a0);
output_low(pin_a5);
enable_interrupts(GLOBAL); // Enable global interrupts
enable_interrupts(INT_RA3);
clear_interrupt(INT_RA3); // Clear RA IOC flag bit

while(TRUE) {

if (segundo == 60 )
{
segundo=0;
minuto ++;
output_toggle(pin_a0);
}
if (minuto =20 )
{
output_low(pin_a5);

}}}

Responder

1. Parece que você esqueceu de especificar a biblioteca que está utilizando no início do código com #include. Deveria ser algo como #include <16F877A.h>, mas depende do microcontrolador que você está usando.

2. Você tem uma linha #fuses NOMCLR INTRC_IO PLL_SW. Estes fuses dependem do microcontrolador que você está utilizando. Certifique-se de que são corretos para o seu microcontrolador.

3. Na linha if (minuto = 20), você está usando o operador de atribuição (=) em vez do operador de comparação (==). Deveria ser: if (minuto == 20).

4. A sua lógica para lidar com as variáveis segundo e minuto parece correta. No entanto, você não está usando as variáveis liga e li em nenhuma parte do loop principal while(TRUE).

5. Você está inicializando o pin_a0 como HIGH e o pin_a5 como LOW no começo do programa. Mas, posteriormente, dentro do loop, você só está manipulando esses pinos com base nos valores de segundo e minuto.

Corrigindo alguns destes problemas, o código fica assim:

#include <16F877A.h> // Altere para o microcontrolador correto

int16 minuto = 0;
int16 segundo = 0;

#fuses NOMCLR INTRC_IO PLL_SW
#device ADC = 10
#use delay(clock=32000000)
#use fast_io(A)

int32 cnt = 0;

#INT_TIMER0
void TIMER0_isr(void) {
clear_interrupt(INT_TIMER0);
set_timer0(131);
cnt++;
if(cnt == 250) {
cnt = 0;
segundo++;
}
}

#INT_RA
void RA_isr(void) {
clear_interrupt(INT_RA3);
if(input(PIN_A3)) {
liga++;
}
if (liga >= 3600) {
liga = 0;
li++;
}
}

void main() {
set_timer0(131);
setup_adc_ports(sAN1|sAN2|sAN3);
setup_adc(ADC_CLOCK_INTERNAL);
setup_oscillator(OSC_8MHZ | OSC_PLL_ON);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_128|RTCC_8_bit);
set_tris_a(0b00011110);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
output_high(pin_a0);
output_low(pin_a5);
enable_interrupts(INT_RA3);
clear_interrupt(INT_RA3);

while(TRUE) {
if(segundo == 60) {
segundo = 0;
minuto++;
output_toggle(pin_a0);
}
if(minuto == 20) {
output_low(pin_a5);
}
}
}

Responder

Hola! Quisiera consultar algo para un proyecto que estoy haciendo:
Necesito que dentro de un lapso de 10 segundos se testee si mi sensor detectó una figura. Y en caso de no detectarlo, se reinicie el lapso de 10 segundos (Y así sucesivamente hasta que se detecte una figura en ese sensor). ¿Cómo podría hacerlo?
Tengo problemas con la última parte, que sería el reinicio del timer.

Tengo armado algo como esto:

SET_TIMER0(156);
if(circuloS==1) // Si el CNY70 de la figura seleccionada (CIRCULO) capta un objeto
{
generate_tone(C_NOTE[1],800), generate_tone(D_NOTE[1],800), generate_tone(E_NOTE[1],800); // Se genera música WIN
delay_ms(2000);
//Luego del delay se tendría que seguir adelante con las demás instrucciones
}
else
{
generate_tone(E_NOTE[1],800), generate_tone(D_NOTE[1],800), generate_tone(C_NOTE[1],800); // CASO CONTRARIO: Se genera música FAIL
delay_ms(2000);
// Luego de este delay, se tendría que reiniciar el timer.
}

Responder

hola, tengo una pregunta, que sucede si yo quiero utilizar dos timer al mismo tiempo?
lo que trato de decir es que quiero utilizar el timer0 para llevar una cuenta de reloj (me incremente una variable cada 1 segundo ) y al mismo tiempo genere un pwm de 1KHz , entiendo que el PWM utiliza el TIMER2 y quisiera saber si de alguna forma uno puede interferir con el otro?
en concreto cuando en mi codigo escribo este conjunto de instrucciones:

//configuracion pwm

setup_timer_2(t2_div_by_4,Timer2,Poscaler); //Configuracion de Timer 2 para establecer frec. PWM a 1kHz
setup_ccp1(ccp_pwm); //Configurar modulo CCP1 en modo PWM
setup_adc_ports(all_analog); //Configurar ADC
setup_adc(adc_clock_internal);

//configuracion del timer0

setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256|RTCC_8_bit); //13.1 ms overflow
ENABLE_INTERRUPTS(INT_RTCC); //Activa interrupcion del timer
ENABLE_INTERRUPTS(GLOBAL); //Activa TODAS las interrupciones

enable_interrupts(INT_EXT);
enable_interrupts(INT_EXT1);

cuando compilo el codigo pareciera que la cuenta del timer0 se alenta, a comparacion de un codigo similar pero sin el uso del pwm, como puedo usar ambos al mismo tiempo? el timer0 y el PWM?

Responder

Hola Wilhem, aparentemente tienes todo bien configurado, revisa el cálculo con el que inicializas el timer (set_rtcc()) para trabajar junto con el contador. Yo particularmente, siempre uso el timer0 con el PWM para mis proyectos de control y trabajan sin inconvenientes. Saludos.

Responder

¿La rutina del timer tiene que estar en el fichero main? ¿Podría tener un fichero aparte timer.c donde estuviera definida la acción de la interrupción de la finalización del timer? Definir #INT_RTCC en un fichero aparte.

Responder

Buenas tardes Sergio, primero quiero felicitarte por tus tutoriales y material de apoyo es muy bueno.
Voy comenzando en los PIC y tengo una duda, en Arduino para no usar delay y trabar el programa ahí uso la función millis(), quería saber si para los PIC existe una función similar o eso se puede hacer con los timers cómo se podría implementar. Gracias y sigue adelante.

Responder

Hola Francisco, efectivamente en el PIC debes usar los timers. En esta entrada por ejemplo, vimos como configurar un timer para que aumente el contador seg cada segundo. Si tu quisieras por ejemplo, activar un led cada 5 segundos, simplemente en el void main, haces un condicional que cuando seg==5 active el led. Este condicional, también puedes hacerlo dentro de la interrupción, caso necesites mucha presición en el tiempo y que se active exactamente cada 5 segundos.

Responder

Hola buenas, me podrías facilitar el código de un tiempo de 5 minutos para luego activar un LED es para observar y simularlo en proteus estoy trabajando un proyecto y quiero implementarlo te agradecería

Responder

hola amigo, felicitaciones tus videos son muy buenos y utiles, amigo tengo una pregunta me podrias decir como usar las enable_interrupts y disable_interrupts, estoy haciendo un codigo para controlar un motor por pwm y debo contar sus rpm pero , el pin RB0 por donde se puede usar la INT_EXT lo tengo ocupado con teclado, asi que quisiera hacerlo por medio de cualquier otro pin para que la interrupcion se genere y me cuente los pulsos que entran el ese pin (ejemplo C0) para determinar las rpm, tengo el pic 18f4550, muchas gracias

Responder

Juan dale un vistazo a al entrada de interrupciones donde se explica eso. En tu proyecto, quizas lo más adecuado sea que coloques el teclado en el puerto D y dejes el puerto B libre para usar la interrupción externa.

Responder

Hola, si estuve allí revisando el archivo de interrupciones pero no explica cómo usar el enable y disable, según tu respuesta tengo otro problema por que debo mostrar la info por lcd y el lcd lo tengo conectado en el otro puerto, así que no sé donde más pueda conectar, intente usar el TMR1 pero no sé cómo hacer para que entre y salga de la interrupción en un tiempo determinado

Responder

¿Puedo habilitar y deshabilitar un timer a mitad programa?
Imagina que quiero lanzar un timer cuando detecto un nivel alto en una digital y que cuando cambie a nivel bajo, parar el timer.

Responder

Usando enable_interrupts(INT_RTCC); y disable_interrupts(INT_RTCC);

saludos

Responder

Buenas tardes Sergio.
Pregunta. ¿como sería la conversión BCD para tres dígitos? es decir, si yo tuviese un número como el 100, 342, ETC. Permitiendo la conversión a cada dígito para poder ver dicho número en los display (3).

En el tutorial lo tienes para 2 dígitos
DEC=SEG/10;
UNI=SEG%10;
¿Cómo sería para centenas?
De antemano agradecido con tus tutoriales y a la espera de mas proyectos con respecto a PID.

Responder

Puedes usar un integrado conversor a BCD, o hacer la lógica tu mismo como lo hicimos con el arreglo del display. En la multiplexación puedes ir incrementando el número de dígitos que quieras, CEN=SEG/100; Saludos

Responder

Cordial Saludo
Sergio

felicitaciones de ante mano por el amplio conocimiento en programacion, tengo unas interrogantes que me gustaria pedirle el favor si puede y esta a su alcance le agradeceria mucho. quiero construir una maquina pero necesito programarle ciclos de trabajo en minutos y realmente no se si un pic tenga la capacidad de sostener en memoria el tiempo por ejemplo activar la salida manteniendo el ciclo 30 minutos y luego que mande el pulso para ser apagada.

Responder

Hola Edison, si puede hacerlo. Puedes hacer un ejemplo parecido a este post, donde calculas el timer a que se ejecute cada segundo y cada que entre a la interrupción que incremente un contador. Por lo tanto como dicho contador está incrementando a cada segundo, basta con incrementarlo hasta 1800 que corresponde a 30 minutos y ahí puedes realizar lo que tengas que hacer. Saludos.

Responder

Hola Sergio, muy buenos tus videos amigo. Disculpa ¿podrías realizar un tutorial de como usar el RTOS en pic por favor? Te estaría sumamente agradecido.

Responder

hola Gustavo, gracias por el comentário. Es un buen tema para ser abordado. Lo tendré en consideración para futuras entradas. Saludos.

Responder

Se puede usar el oscilador interno?

Responder

si se puede

Responder

Buenas amigo sergio muy bueno tu contenido del timer 0 pero se me genero una pregunta.

Al momento que haces el calculo de la variable contador y el valor inicial del tmr0

«»»»»El valor anterior, es decir 3906 debe ser igual a la multiplicación entre el valor inicial del timer 0
y el valor del contador a decrementar.

3906= valor inicial del timer * valor del contador a decrementar

Observación: estos dos valores son aleatorios y deben ser menores a 256.
3906 = 18 * 217″»»»»»»»

Dices que esos dos valores son aleatorios y que deben ser menores de 256 cada uno y aqui es donde viene mi pregunta.
se sabe que el valor de timer si debe estar entre el valor que dices pero al crear la variable del contador ¿por que dices que debe ser menor a 256?
en el codigo se podria generar como una variable tipo long (de 16bits) y se puede salir del rango que dices.

Amigo tengo esa inquietud

Responder

Hola Luis, lo hice de esa manera solo para evitar confusiones. Pero si, el contador puede ser mayor a 256 desde que lo definas como una variable long. Eso si nunca confundir la carga del Timer0. Saludos!!

Responder

Sergio en la parte de CargaT=(tiempo(s)*Foscilador)/(prescaler*4), puede ser una forma general no crees ya que la formula que das es con respecto a un cristal de 4Mhz en mi caso trabajé con uno de 20, un saludo y gracias por compartir conocimiento.

Responder

Hola Kenso, si tienes razón. Gracias por el aporte. Saludos.

Responder

Gracias gracias gracias me gustan tus tutoriales

Responder