Contraste de LCD por PWM y un filtro RC

#41
También debes tener en cuenta que las simulaciones no son precisas en cuestión de tiempos y algunas otras cosasXD.
Disculpa que no me he explicado bien, al decir simular me refería a que lo he probado en mi hardware que tengo montado.

Entonces, teniendo en cuenta que cada 4 ciclos de maquina aumenta en 1 el timer, son 40 ciclos ya que esta ajustado a 10... no es suficiente como para que esté ahí el problema?

edito: He probado con varios prescalers ( 1, 2, 4, 8, 16) y con el ajuste a 65535 para ver el máximo. En todos ellos el valor que obtengo es 50 kHz (a pesar de ser en teoría mucho mayores)

También aprecio que a mayor frecuencia a obtener, mayor es el error, aunque eso era previsible
 
Última edición:
#42
El problema está al usar un toggle para cambiar el estado del pin de salida, no con las instrucciones para cargar el Timer 0.
Así sea C o Basic, los registros tendrán el mismo valor que uno asigna para el Timer 0, igual que en lenguaje ensamblador.

Al usar toggle en C, si se genera un código que usa varias instrucciones en ensamblador, y por consecuencia varios ciclos de reloj.
Eso es lo que hace que no obtengas la frecuencia deseada.

Por ese inconveniente, yo me inclinaría a usar PWM por hardware.
 
#43
Por ese inconveniente, yo me inclinaría a usar PWM por hardware.
Precisamente tengo el problema de tener que usar el pwm software para el contraste lcd (duty del 15%) y no me queda otra que apañarme con esto.

Lo próximo es ver como desactivar la interrupcion, que he probado con un if en el while del main y no me ha funcionado
 
#44
Bueno y si intentan hacer el toggle con Ensamblador embebido incluyendo la rutina de la interrupcion, mas complejo pero quizas se logre el objetivo...
 
#45
Bueno y si intentan hacer el toggle con ensamblador embebido, incluyendo la rutina de la interrupción, más complejo pero quizás se logre el objetivo.
Sí, eso mejora la situación, pero no tiene caso que el servicio de interrupción también sea en ensamblador.

Así quedaría el código: Fosc = 48 MHz y PWM = 50 KHz, con ciclo activo de +- 15 %

PHP:
#include <18F4550.h>
#fuses   NOFCMEN
#use     delay(crystal = 4MHz, clock = 48MHz)
#use     fast_io(c)

#byte PORTC = getenv("SFR:PORTC")

#INT_TIMER0
void sdi_desborde_timer0 (void)
{
   #asm
es_cero:
      btfsc PORTC,1
      goto  es_uno
      bsf   PORTC,1
es_uno:
      btfss PORTC,1
      goto  es_cero
      bcf   PORTC,1
   #endasm
   
   set_timer0(65359); 
}

void main (void)
{
   set_tris_c(0b11111101);
   
   enable_interrupts(INT_TIMER0);
   enable_interrupts(GLOBAL);              
   setup_timer_0(T0_INTERNAL | T0_DIV_1);    
   set_timer0(65359);                             
                                                    
   while(true);

}
También hay que compensar los ciclos de instrucción en ensamblador, pero son bastantes ciclos menos.
No usar simulador, físicamente se deben obtener 50,0043 KHz.
Con el osciloscopio se podrá ver el ciclo activo sobre un 15 % +-
 
#46
Hola @jipc. Me parece habértelo explicado ya dos veces en post anteriores, más el ejemplo que te puse; tres veces. Sin embargo aún no has comprendido que al TMR0 no hay que cargarle el valor de ajuste. El valor de ajuste hay que sumárselo a su valor actual.


Intentaré explicarlo nuevamente: En el instante que se produce la interrupción el TMR0 habrá desbordado y su contenido será cero. El micro ejecutará algunas acciones: el propi salto a la dirección al vector de interrupción, el respaldo del “contexto” de la situación del programa en el momento de la interrupción, las propias instrucciones de atención a la interrupción, etc. Imagina que en hacer todo esto transcurren unos 20 ciclos de maquina (solo como ejemplo); como el pre-escalador estaba a 1:8, ya habrá entregado dos pulsos al TMR0 y el valor de este ya no cera cero sino dos (y con los siguientes 4 ciclos de máquina, su valor será tres). Espero estés comprendiendo, si en este momento cargas en el TMR0 el valor de ajuste, estarás botando a la basura el tiempo transcurrido desde la última interrupción, lo correcto es actualizar el contenido del TMR0 con el valor de juste calculado MAS su contenido en ese instante.


Fíjate en el ejemplo que te puse en el post #27, estoy usando la instrucción “TMR0L += 0b00001100”; significa: "TMR0L = TMR0L + 0b00001100". Tu sin embargo usas “set_timer0(65036)”; justamente ahí la causa para que no logres 1KHz exacto.


Y bueno, para generar 50KHz, Yo dejaría el pre-escalador en 1:8, como ya mencioné, trabajando el micro a 32MHz se logra una señal con periodo de 1uS (exacto) para alimentar el TMR0. Como el TMR0 hará solo diez cuentas antes de desbordar, lo configuraría a 8 bits y utilizaría 246 como valor de ajuste.


Para quienes recomiendan utilizar ensamblador; estoy de acuerdo. Es importante que la rutina de interrupción sea lo mas veloz posible, para dejar al micro espacio suficiente para atender otras tareas entre interrupciones.



Sin embargo, la propuesta de D@rkbytes:
#asm
es_cero:
btfsc PORTC,1
goto es_uno
bsf PORTC
,1
es_uno
:
btfss PORTC,1
goto es_cero
bcf PORTC
,1
#endasm
Estoy seguro que consume más ciclos de máquina que la instrucción “output_toggle(pin_c1)” puesto que cuando CCS la traduzca a ensamblador, seguro lo hará con “btg PORTC,1”; instrucción perteneciente al set de instrucciones del 18F4550, que permite cambiar el estado de un pin en un solo ciclo de máquina.


Saludos…
 
Última edición:
#47
No creo que se consuma mas ciclos en C con toggle que ASM embebido porque según recuerdo el toggle siempre pone como salida el puerto aunque ya este como salida y otras costillas por el estilo que hace que sean mas ciclos...

 
#50
Ahora lo entiendo. No había visto el += del código de @pilm y creía que lo que decías anteriormente se arreglaba sin esa suma. En fin, al final usando esto:

Código:
#include <18F4550.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOLVP,NODEBUG,USBDIV,PLL6,CPUDIV2,VREGEN,NOPBADEN,//CCP2B3 
#use delay(clock=32MHz)  

int16 numero=15536;  // para 10 Hz p.ej
#INT_TIMER0

void  TIMER0_isr(void)  
{
 
output_toggle(pin_c1);

set_timer0(get_timer0() + numero + 2);     // necesito además sumar 2 para que el ajuste sea más preciso
}

void main()
{
      
   enable_interrupts(INT_TIMER0);                   
   enable_interrupts(GLOBAL);                        
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_8);       
   set_timer0(numero);                         
                                                
   while(TRUE){}
}
   {
Con esto el máximo que es capaz de darme son 43 kHz cuando la variable numero es 65527 (aquí el error es el máximo que obtengo, ya que debería obtener 50 kHz, pero sin embargo cuando quiero obtener 40 kHz el error es de obtener 40.7 kHz, cosa que me vale) y el mínimo son 8 Hz cuando numero=2;

Creo que me voy a quedar con estos valores, ya que ampliar el rango es más lioso de lo que creía.
Pero como no, ahora me surge otro problema para detener las interrupciones... con esto en el while del main no se detiene: editado
Código:
if (INPUT(pin_a0==1)){     // cuando pongo pin_a0 a 5 V
      disable_interrupts(INT_TIMER0);
      disable_interrupts(global);     
      }     
      }
Ya con esto último espero olvidarme del tema, gracias por la ayuda y paciencia

edito: Estaba ya en el punto en el que no te das cuenta de esos fallos, ya esta arreglado con el INPUT
 
Última edición:
#51
Pero como no, ahora me surge otro problema para detener las interrupciones... con esto en el while del main no se detiene:
Código:
if (pin_a0==1){     // cuando pongo pin_a0 a 5 V
      disable_interrupts(INT_TIMER0);
      disable_interrupts(global);     
      }     
      }
Ya con esto último espero olvidarme del tema, gracias por la ayuda y paciencia
Debe ser: if(input(pin_a0)) o if(input(pin_a0) == 1), que sería lo mismo.
Y disable_interrupts(global); ya queda de sobra, a menos que tengas mas interrupciones y las quieras desactivar todas.
 

Temas similares