No funciona la configuración del timer con el reloj interno del PIC

Buenas a todos.

Estoy intentando configurar el Timer 0 para que provoque una interrupción cada 20 ms y así realizar un PWM. (Esto porque ya utilicé los 2 módulos CCP como PWM incluidos en el PIC)

Quiero trabajar con el reloj interno del PIC sin reloj de salida para así ahorrar componentes y lograr la frecuencia de 50Hz.
Cuando echo a correr el programa, realiza las interrupciones cada varios segundos.

La programación es la siguiente:
(Sólo estoy probando el PWM con el Timer 0)

PHP:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <18F2420.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES INTRC_IO                 //Internal RC Osc, no CLKOUT
#FUSES NOPROTECT                //Code not protected from reading
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES BORV20                   //Brownout reset at 2.0V
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOCPD                    //No EE protection
#FUSES STVREN                   //Stack full/underflow will cause reset
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES LVP                      //Low Voltage Programming on B3(PIC16) or B5(PIC18)
#FUSES NOWRT                    //Program memory not write protected
#FUSES NOWRTD                   //Data EEPROM not write protected
#FUSES IESO                     //Internal External Switch Over mode enabled
#FUSES FCMEN                    //Fail-safe clock monitor enabled
#FUSES PBADEN                   //PORTB pins are configured as analog input channels on RESET
#FUSES NOWRTC                   //configuration not registers write protected
#FUSES NOWRTB                   //Boot block not write protected
#FUSES NOEBTR                   //Memory not protected from table reads
#FUSES NOEBTRB                  //Boot block not protected from table reads
#FUSES NOCPB                    //No Boot Block code protection
#FUSES LPT1OSC                  //Timer1 configured for low-power operation
#FUSES MCLR                     //Master Clear pin enabled (me asegure de qe este conectado a vcc)

#use delay(clock=31000)

#int_TIMER0
void  TIMER0_isr(void) 
{
set_timer0(0xB3);
output_high(pin_b0);
delay_ms(10);
output_low(pin_b0);
}



void main()
{

   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_OFF|ADC_TAD_MUL_0);
   setup_spi(SPI_SS_DISABLED);
   setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_256);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);

   setup_oscillator(OSC_31KHZ|OSC_INTRC|OSC_31250|OSC_PLL_OFF);

   set_timer0(0xB3);
   set_tris_b(0x00);
   output_b(0x00);

  while(true)
  {
// esto es solo de prueba, aqui ira mi grandioso codigo...
  delay_us(40);
  }

}
PIC utilizado: PIC18F2420
(Si conocen alguno con 3 módulos CCP que esté a la venta en chile se los agradecería porque yo no encontré)
Compilador: PIC C Compiler v4

Además, de forma manual probé calcular los tiempos con www.sistemasorp.es/blog/temporizadores_pic.html

Saludos y muchas gracias por su tiempo. :D
 
Última edición por un moderador:
Estoy intentando configurar el Timer 0 para que provoque una interrupción cada 20 ms y así realizar un PWM. (Esto porque ya utilicé los 2 módulos CCP como PWM incluidos en el PIC)

Quiero trabajar con el reloj interno del PIC sin reloj de salida para así ahorrar componentes y lograr la frecuencia de 50Hz.
Cuando echo a correr el programa, realiza las interrupciones cada varios segundos.
Nunca he usado el oscilador interno de 31KHz, pero no es necesario bajar tanto la frecuencia para obtener 50 Hz. con el Timer 0.
Y si quieres obtener una onda cuadrada de 50 Hz al 50% del ciclo activo, debes establecer el desborde a la mitad del periodo.
O sea, cada 10 ms. Recuerda que se deben generar dos flancos.

Un ejemplo con el oscilador interno @ 4 MHz y usando el Timer 0 a 16 bits:
PHP:
#include <18F2420.h>
#fuses   NOFCMEN
#use     delay(internal = 4MHz)

#INT_TIMER0
void sdi_desborde_timer0 (void)
{
   output_toggle(PIN_B0);
   set_timer0(0xEC78);
}

void main (void)
{
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   setup_timer_0(T0_INTERNAL|T0_DIV_2);
   set_timer0(0xEC78);
   
   
   while (true);
}
Sin mucho código y configuración se puede obtener una onda cuadrada de 50 Hz. por el pin RB0.
Si tienes frecuencímetro podrás ver que la frecuencia está muy cercana a los 50 Hz. o exacta.
Con un osciloscopio también podrás ver que el ciclo activo es del 50 %
 
es que no creo que quiera una onda cuadrada quiere un PWM

es parecido a lo que sugieres

¿cuantos PWM a software puedo hacer?

pues puedes usar todos los pines del micro

¿si quiero mas PWM`s a software y las patitas de micro se me acaban?

puedes hacer PWM´s a software con registros de corrimiento.

lo que debes hacer es :

fijar una periodo de desborde digamos 20ms
hasta ahi todo es correcto.

ahora dividir 20ms en 255 partes.

y quedaremos con un pedacito de frecuencia.

despues debemos fijar la frecuencia en el main y hacer unas lineas de codigo que hagan el PWM



basicamente es algo asi:
interrupcion
{

contador++; //desborda cada X us "debes calcularlo"

if(contador>=255) //cuando son 255 veces formamos los 20ms
{
contador=0; // reseteamos contador
}

if(contador>= PWM) //la variable PWM es la que hace el ciclo de trabajo
{
//pin_X=1 prendemos el pin
}
else
{
//pin_x=0 apagas el pin
}


}
 
Última edición:
Nunca he usado el oscilador interno de 31KHz, pero no es necesario bajar tanto la frecuencia para obtener 50 Hz. con el Timer 0.
Y si quieres obtener una onda cuadrada de 50 Hz al 50% del ciclo activo, debes establecer el desborde a la mitad del periodo.
O sea, cada 10 ms. Recuerda que se deben generar dos flancos.

Un ejemplo con el oscilador interno @ 4 MHz y usando el Timer 0 a 16 bits:
PHP:
#include <18F2420.h>
#fuses   NOFCMEN
#use     delay(internal = 4MHz)

#INT_TIMER0
void sdi_desborde_timer0 (void)
{
   output_toggle(PIN_B0);
   set_timer0(0xEC78);
}

void main (void)
{
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   setup_timer_0(T0_INTERNAL|T0_DIV_2);
   set_timer0(0xEC78);
   
   
   while (true);
}
Sin mucho código y configuración se puede obtener una onda cuadrada de 50 Hz. por el pin RB0.
Si tienes frecuencímetro podrás ver que la frecuencia está muy cercana a los 50 Hz. o exacta.
Con un osciloscopio también podrás ver que el ciclo activo es del 50 %

Muchas gracias!! PERO! si incremento la frecuencia a 4Mhz ya no podria trabajar con los modulos CCP1 y 2 porque la frecuencia seria muy alta... ademas disculpen pero no mencione que es para realizar el control de servomotores con una frecuencia de 50Hz y el ciclo de trabajo es de 1 a 2 ms...
 
OK. Estuve tratando de hacer funcionar el oscilador interno a 31 KHz con PIC Compiler, pero no lo logré.
Me sucedía lo mismo que mencionas, sobre un desborde tras varios segundos.
Al ver que con las instrucciones de configuración del compilador no lo lograba, decidí usar otro compilador. (Proton)
Es lenguaje Basic pero muy cómodo y superior a otros entornos del Basic.
Con éste compilador si logré hacer funcionar la interrupción por desborde del Timer 0 a 31 KHz.

Este es el programa que usé de prueba:
PHP:
Device  18F2420
    Reminders = OFF
    Config_Start
    OSC = INTIO7, WDT = Off, PWRT = On, LVP = Off
    FCMEN = On, BOREN = Off, PBADEN = Off
    Config_End
    
    Declare Create_Coff On

Inicio:
    ADCON1 = 0x0F               ; Puertos como Digital I/O
    OSCCON = %00000010          ; Oscilador interno a 31 KHz.
    INTCON = %11100000          ; Interrupción por Timer 0
    T0CON = %11001000           ; Timer 0 con prescaler 1:2 a 8 bits.
    TMR0L = 0xD9                ; Cargar el Timer 0 para que desborde cada 10 ms.
    
    On_Hardware_Interrupt GoTo Servicio_Interrupciones 
    
    
Programa:

    GoTo Programa
    
Servicio_Interrupciones:
    Context Save
    
    If INTCONbits_TMR0IF = 1 Then
        Toggle LATB.0           ; Cambiar de estado RB0
        TMR0L = 0xD9            ; Recargar el Timer 0
        INTCONbits_TMR0IF = 0   ; Limpiar flag TMR0IF
    End If
    
    Context Restore
    
    
    End
Lo probé usando un PIC18F4520 ya que no tengo el PIC18F2420, pero son similares en cuanto funciones, sólo que el PIC18F4520 tiene más puertos.

Así que el problema con PIC C Compiler, es debido a que realiza mal la configuración de los registros.
En el programa adjunto podrás ver el tipo de configuración que usé y declarar los registros en PIC C.

Configuré un desborde cada 10 ms. y la carga del Timer 0 a 8 bits más cercana, fue de 217 (0:LOL:9), ya que quise obtener una frecuencia de 50 Hz. por RB0, pero se obtiene un 0,645 % de error.

También configuré el oscilador interno para obtener la frecuencia de oscilación por el pin (CLKO)
La frecuencia de salida siempre es divida entre 4, y obtuve 7520 Hz. Que por 4 nos da: 30080 Hz.
Algo fuera de lo esperado: 7750 Hz. de los 31000 Hz.

Con todo esto y al usar la instrucción "Toggle", obtuve 70 Hz. Pero al menos logré que funcionara.

Saludos.
 
Última edición:
D@rkbytes si no te importa quisiera saber el while de esta parte del codigo que funcion hace
Código:
void main (void)
{
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);
   setup_timer_0(T0_INTERNAL|T0_DIV_2);
   set_timer0(0xEC78);
   
   
   while (true);
}  

Es que lo he visto mas de una vez y no lo comprendo
 
Lo que hace es un bucle infinito, porque "True" equivale a 1.
Como "True" siempre será 1, será: Mientras 1 = 1

Se usa como rutina de bucle vacío (Sin acciones) para que se ejecuten interrupciones.
 
Puede usarse también para programas usando RTOS, pero a fin de cuentas también son interrupciones por Timers.
No tiene otra finalidad, más que hacer un bucle infinito sin ejecución de código.
 
Atrás
Arriba