Intensidad de un LED con PIC18F4550 y PWM por Software

Estuve toda la semana leyendo las librerías que usa ICARO para manejar los servos y trantando de entender cómo funcionan. La verdad que no es nada sencillo ponerse a leer código C después de más de 10 años de no tocar una línea de éste. Además, por otro lado, nunca había trabajado a tan bajo nivel y con el manual de un microcontrolador en la mano. ¡Toda una aventura!

Por suerte encontré en Internet miles de manuales y post relacionado con lo que yo quería hacer: utilizar señales PWM. Si bien el PIC que estoy usando (18F4550) ya trae incorpado por hardware 2 pines que se pueden usar para crear señales PWM fácilmente, yo quería hacerlo por software ya que de esa manera me permitiría controlar unas cuántas más. Si no me equivoco, por lo menos 18. Entonces, como yo quería manejar al menos 5 señales PWM, decidí encarar el camino complicado y hacerlo por software.

Me encontré con que no era nada fácil. Pero no es que no era fácil de programar, sino que no era fácil de entender. Hay muchos conceptos que no manejo y muchos otros que los he visto pero que no los tengo muy fresquitos que digamos, así que le dediqué su tiempo a leer y entender los post que estaba leyendo. No es lo mismo pegarle una leída para ver cómo viene la mano, que ponerse uno a hacer los cálculos para que los números coincidan.

Hasta el momento llegué a generar 1 sola señal PWM que controla todas las salidas del PUERTO B (8 bits) con el mismo ciclo de trabajo (porcentaje del período que la señal está en ALTO) utilizando el TIMER 0 y la interrupción que este genera una vez que desborda su contador. Sin embargo, la idea es tunear un poquito más el código cosa de mantener un valor diferente para cada una de los pines del PUERTO B. Quizás mañana... Ya veremos...

Ese ciclo de trabajo se puede cambiar modificando ON_PERCENT en la escala de 0 a 100, con 0 totalmente apagado y 100 totalmente prendido.

Luego de cargar el código en el PIC, conectamos cualquier pin del PUERTO B a una resistencia de 250 ohms, un LED y finalmente la tierra de vuelta a la placa en el pin del medio de ("K2" en la placa) los que están (hay 3) adelante del transistor grande.

El código fuente que se debería poner en user.c es éste:

simple_pwm_icaro.c

/*

Autor: Manuel Kaufmann
Email: humitos@gmail.com
License: GPL

Ejemplo sencillo que muestra el uso de senales PWM en el PIC18f4550
usando la placa ICARO (http://roboticaro.org/) en el PUERTO B.

Todas las salidas del PUERTO B tienen la misma frecuencia y el mismo
ciclo de trabajo. El ciclo de trabajo puede modificarse cambiando el
valor de ON_PERCENT, siendo este el porcentaje de tiempo en ALTO de la
salida del PUERTO B.

 */

#define USERINT 1  // define una interrupcion de usuario
#define TIMER 255  // valor del TIMER 0

#define ON_PERCENT 7 // ciclo de trabajo
                     // porcentaje del tiempo en ON

int count = 0;  // cantidad de veces que se entro en la userinterrupt()
                // este valor se compara contra ON_PERCENT para saber
                // si hay que poner la salida en 1 o 0


// funcion de configuracion
void setup()
{
  TRISB = 0;  // define a todos los pines del PUERTO B como salidas

  // configuracion de interrupciones
  INTCONbits.GIE = 1;  // habilita las interrupciones generales
  INTCONbits.TMR0IE = 1;  // habilita la interrupcion de TIMER 0

  // configuracion del TIMER 0
  T0CONbits.T08BIT = 1;  // usa 8 bits para el timer (2^8 = 256 posibilidades)
  T0CONbits.T0CS = 0;  // selecciona el modo TIMER
  T0CONbits.PSA = 1;  // (0) usa (1) no usa, un PRE SCALER previo

  TMR0L = TIMER;  // setea el valor del TIMER 0
  T0CONbits.TMR0ON = 1;  // arranca el TIMER 0
}


// bucle infinito
void loop()
{
  // no se necesita hacer nada en la funcion principal
  // todo se maneja desde las interrupciones
}


// funcion de interrupcion de usuario
void userinterrupt(void)
{
  if(INTCONbits.TMR0IF)  // chequea que la interrupcion sea del TIMER 0
                         // que es el que nos interesa
    {
      if(count < ON_PERCENT)
        {
          PORTB = 0xFF;  // pone todas las salidas del PUERTO B en 1
        }
      else
        {
          PORTB = 0x00;  // pone todas las salidas del PUERTO B en 0
        }

      count = ++count % 100; // comprueba si count es igual a 100, de ser asi
                             // vuelve su valor a 0 para volver a empezar

      INTCONbits.TMR0IF = 0;  // vuelve a 0 el FLAG de la interrupcion
                              // del TIMER 0
      TMR0L = TIMER;  // setea el valor del TIMER 0
    }
}

Comentarios

Comments powered by Disqus