Control de velocidad PWM






            En este apartado veremos como construir un control de velocidad para motores de corriente continua por modulación de ancho de pulso (PWM de sus siglas Pulse Width Modulation). El control de velocidad por PWM consiste en conmutar el motor con una onda cuadrada de frecuencia constante, y con la posibilidad de modificar su ciclo de trabajo. Esto quiere decir que nosotros podemos controlar la potencia que entregamos al motor modificando sólo el tiempo que la onda está en estado alto con respecto a cuando está en estado bajo.


             El periodo es la característica de la onda que nos indica el tiempo que dura un ciclo completo, por ejemplo 1ms (la frecuencia será de 1kHz). Dentro de este periodo, nosotros podremos modificar el tiempo para cuando la onda esté en estado alto o en estado bajo. Por ejemplo, si tenemos una onda con un periodo de 100µs, podemos hacer que esté en estado alto durante 30µs y en estado bajo durante 70µs y el periodo seguirá siendo 100µs (por lo tanto frecuencia constante). En este caso tendríamos un ciclo de trabajo del 30%, ya que de los 100µs, están en estado alto los primeros 30µs. En cambio, si hacemos que la onda esté en estado alto durante 65µs y en estado bajo durante 35µs, el periodo será 100µs igual, pero el ciclo de trabajo es ahora del 65%.


            Por lo tanto, si juntamos un circuito electrónico que nos entregue una onda cuadrada con ciclo de trabajo variable mediante un potenciómetro y buscamos la manera de poder conmutar el motor según esa onda, habremos realizado un control de velocidad.


            Para ello vamos a utilizar un famoso circuito integrado, el NE555 (DataSheet). Se trata de un temporizador muy versátil que nos permite generar pulsos a partir de fuentes de alimentación continuas. Utilizando la configuración adecuada, podremos variar el ciclo de trabajo de la onda que genera. Dicho circuito es el siguiente:


            Este circuito entrega a la salida (OUTPUT) una onda cuadrada de aproximadamente 14kHz y nos permite variar su ciclo de trabajo con el potenciómetro RV1. El condensador C1 se carga a través de la resistencia R1, la mitad del potenciometro RV1 y el diodo D2 y se descarga a través del diodo D1 y la otra mitad del potenciómetro RV1 en el pin 7 del NE555. Al variar el potenciómetro, estamos aumentando y disminuyendo o viceversa los tiempos de carga y descarga respectivamente, variando así el momento en el que la onda está arriba y en el que está abajo. El condensador C3 se encarga de filtrar la alimentación y el condensador C2 forma parte de la configuración normal del NE555 para evitar posibles interferencias.


            Ya solo queda hacer un circuito que nos permita conmutar un motor a partir de la onda generada. Para ello vamos a utilizar un transistor MOSFET IRFZ44N (DataSheet), ya que tiene una resistencia de fuente-drenador muy baja (en torno a los 20mΩ) y no tendrá prácticamente perdidas por calor. El circuito final queda así:


            Circuito impreso en ARES: Layout_Download

            El diodo D3 es un diodo de protección para evitar que las corrientes auto-inducidas del motor puedan quemar el transistor MOSFET.

Display LED con PIC16F628A






      En este post veremos como podemos manejar cuatro displays LED de 7 segmentos y punto decimal con un microcontrolador PIC16F628A. Veremos también como realizar una serie de proyectos de aplicación (contador de 0 a 9, contador de 0 a 9999 y reloj digital), todo ello programado en lenguaje C con el compilador CCS.

      Para esta práctica vamos a necesitar los siguientes materiales:
  • Una placa para prototipos
  • Un PIC16F628A
  • Un cristal de 8MHz
  • Cuatro Displays LED con punto decimal de cátodo común
  • Cuatro transistores MOSFET BS170
  • Dos pulsadores micro-switch
  • Dos condensadores cerámicos de 33pF
  • Dos resistencias de 4.7kΩ
  • Ocho resistencias de 470Ω
  • Cable rígido
  • Fuente de alimentación
  • Hardware y Software necesario para programar el PIC (¿Cómo programar un PIC?)


      Empezaremos explicando como funciona un Display LED. Los displays LED son componentes optoelectrónicos que por el uso de LEDs, son capaces de generar luz con muy poca intensidad de corriente. Estos LEDs están dispuestos en una carcasa en forma de 8. . Encendiendo los LEDs como convenga, podremos realizar cualquier número del 0 al 9.


      Un display LED de 7 segmentos que tenga punto decimal, constará de 8 LEDs (uno para cada segmento y otro para el punto decimal). De la carcasa salen una serie de patillas de conexión, por lo general en un display de estas características deben ser 10 (8 para cada LED y las 2 sobrantes están internamente conectadas y son el común). Existen dos tipos de displays: de ánodo común y de cátodo común. Nosotros utilizaremos de cátodo común, es decir, el positivo es la patilla del LED en cuestión mientras que el negativo será el común. En nuestro caso, manteniendo una de las patillas del común a tierra (o negativo) y aplicando un voltaje positivo (5V) a una de las patillas a través de una resistencia de protección de 470Ω, se encenderá el LED correspondiente a esa patilla:



      Cada segmento del display se diferencia mediante una letra de la siguiente manera. Podemos ver también la disposición de las patillas del display (imagen vista desde arriba):


      Ahora procedemos a montar en la placa para prototipos un display de cátodo común y un microcontrolador PIC16F628A (DataSheet) y realizaremos el siguiente montaje:



      RB0 → A
      RB1 → B
      RB2 → C
      RB3 → D
      RB4 → E
      RB5 → F
      RB6 → G

      Hecho esto, procederemos a realizar el código del programa para poder mostrar los diez símbolos numéricos: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
      Para ello creamos un nuevo archivo en nuestra carpeta de proyectos CCS con el nombre que queramos y extensión *.c, por ejemplo “contador09.c” y lo abrimos.

      Escribimos el siguiente código:

//
//            CONTADOR 0-9
//
// Cuenta del 0 al 9 cada segundo 
//
//             PIC16F628A
//
// Uso de display de 7 segmentos
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,nomclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)

int8 x=0;
const int8 Seg7[10]={63,6,91,79,102,109,125,7,127,111};

void main(void){
      set_tris_b(0);
      while(1){
            output_b(Seg7[x]);
            delay_ms(1000);
            x++;
            if(x==10) reset_cpu();
      }
}


      Este programa cuenta del 0 al 9 cada segundo. Para poder generar los dígitos, utilizamos un array de longitud 10 (Seg7[]) y en cada posición de éste, se encuentra un número entero que al ser representado de forma binaria en el puerto B, se encenderán los LEDs necesarios para generar el símbolo numérico correspondiente a esa posición en el array. Por ejemplo, el número 2, para generarlo es necesario sacar por el puerto B el valor entero 91 (ya que el 0 es el 63 y el 1 es el 6). El número 91 en binario es 01011011, al ser representado en el puerto B, el bit menos significativo coincide con el pin RB0. De esta manera, quedarán en estado alto los pines RB0, RB1, RB3, RB4 y RB6 y tal y como tenemos conectado el display, se encenderán los LEDs correspondientes a los segmentos A, B, D, E y G, generando así el número 2. Cada segundo se aumenta en una unidad el valor de la variable x y en cuanto ésta llega a 10, se resetea el microcontrolador para volver a empezar desde 0.



      Ahora bien, ¿y si queremos representar mas cifras? … pues será necesario incorporar mas displays. En la siguiente practica realizaremos un contador de 0 a 9999 utilizando 4 displays. Si conectásemos cada display como lo hemos hecho anteriormente, necesitaríamos 4 puertos del microcontrolador (32 pines de salidas digitales), pero el PIC16F628A solo tiene 2. Así que vamos a recurrir a una técnica llamada barrido secuencial.
      Consiste en encender solo un display en cada instante representando el valor de éste y después encender el siguiente y mostrar el valor de éste otro, todo ello utilizando solo un puerto para encender los LEDs del display y otros cuatro pines del otro puerto para encender el display correspondiente en cada momento. Por lo tanto, será necesario conectar todos los displays en paralelo excepto sus comunes y colocar un transistor MOSFET BS170 (DataSheet) en cada uno de ellos, con el objetivo de poder manejar el encendido de cada display por separado desde el otro puerto del PIC.

      Si se realiza el barrido completo a una frecuencia lo suficientemente alta (más de 50Hz), no se notará en ningún momento que esté solo encendido uno, debido a la persistencia de la visión.
De esta manera, podremos controlar hasta 8 displays con solo 2 puertos del microcontrolador.

      Realizaremos el siguiente montaje en la placa para prototipos:


      RB0 → A
      RB1 → B
      RB2 → C
      RB3 → D
      RB4 → E
      RB5 → F
      RB6 → G
      RB7 → P (punto decimal)

      RA0 → Q1
      RA1 → Q2
      RA2 → Q3
      RA3 → Q4

      En este circuito se han añadido otros tres displays conectados en paralelo y controlados cada uno por los cuatro transistores MOSFET BS170. También se ha añadido una octava resistencia de 470Ω para poder manejar el punto decimal en el siguiente proyecto, así como dos pulsadores con sus respectivas resistencias pullup de 4.7kΩ en MCLR y RA4.

      Abrimos CCS y escribimos el siguiente código:
//
//            CONTADOR 0-9999
//
// Cuenta del 0 al 9999 aumentando 
// en una unidad cada vez que se 
// pulsa un pulsador conectado a
// RA4.
//
//             PIC16F628A
//
// Barrido secuencial de displays
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,mclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)
#define puls pin_a4

int16 x=0;
const int8 Seg7[10]={63,6,91,79,102,109,125,7,127,111};

void Display_num(void){
      output_a(0b00000001);
      if(!((x/1000)%10)) output_b(0);
      else output_b(Seg7[(x/1000)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00000010);
      if((!((x/100)%10))&&(x<1000)) output_b(0);
      else output_b(Seg7[(x/100)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00000100);
      if((!((x/10)%10))&&(x<100)) output_b(0);
      else output_b(Seg7[(x/10)%10]);
      delay_ms(4);
      output_b(0);
      output_a(0b00001000);
      output_b(Seg7[x%10]);
      delay_ms(4);
      output_b(0);
}

void main(void){
      set_tris_a(0x10);
      set_tris_b(0x00);
      while(1){
            if(!input(puls)){
                  while(!input(puls)) Display_num();
                  x++;
                  if(x==10000) reset_cpu();
            }
            Display_num();
      }
}


      Con este programa cargado en el microcontrolador, tendremos un contador de 0 a 9999. Al pulsar y soltar el pulsador ++, aumenta en uno el contador y al llegar a 9999, se resetea el sistema para volver a empezar.
      Se utiliza una función (Display_num()) para realizar el barrido a los displays. Primero enciende el display de la izquierda, después representa el dato durante 4ms y finalmente vuelve a poner a 0 el puerto de datos para que quede libre en el siguiente display. Unos condicionales if(), nos permiten encender sólo el display cuando se necesite, con el objetivo de evitar que aparezcan ceros a la izquierda. Se utilizan operaciones de residuo de división para poder extraer las cifras de las unidades, decenas, centenas y miles de la variable x, para poder representar cada cifra en un display.
      El botón marcado como R que está conectado al pin MCLR del microcontrolador, nos permite resetearlo para poder poner a 0 el sistema en cualquier momento.


      Vamos a realizar un último proyecto con el circuito que tenemos montado en la placa de prototipos, se trata de un reloj digital. Para ello vamos a utilizar el Timer1 del microcontrolador y un algoritmo que vaya actualizando la hora mediante las interrupciones de éste. Manteniendo el circuito anterior, los pulsadores van a ser ahora, para aumentar las horas: el que está conectado a RA4 y para aumentar los minutos: el que está conectado a MCLR.


      Abrimos CCS y escribimos este código:

//
//            RELOJ DIGITAL
//
// Muestra hora y minutos con 4 displays 
// de 7 segmentos de catodo comun. Se  
// realiza un barrido a los 4 displays 
// cada 16ms con el objetivo de hacer  
// parecer que estan todos encendidos 
// cuando en realidad solo se enciende  
// uno, simplificando considerablemente 
// el circuito.
//
// La hora puede ser ajustada mediante
// dos pulsadores que aumentan las horas
// y los minuto respectivamente con cada
// pulsacion.
// 
//             PIC16F628A
//
//           TIMER1 (16bits)
//
// Copyright © 2015 Aaron G.
// All rights reserved.


#include <16f628a.h>
#fuses hs,nowdt,noput,nomclr,nobrownout,nolvp,nocpd,noprotect
#use delay(xtal=8000000)
#define freq_inst 2000000
#define ph pin_a4
#define pm pin_a5

         // Cabecera: dispositivo, configuracion, frecuencia, constantes.



int1 dt=true;
int32 Ticker;
int8 Hora=0,Minuto=0,Segundo=0;
const int8 Seg7[2][10]={{63,6,91,79,102,109,125,7,127,111},
              {191,134,219,207,230,237,253,135,255,239}};

         // Variables y constantes globales: dt (encender y apagar el punto
         // decimal cada medio segundo); Ticker (mantener constante la base de 
         // tiempo real); Hora,Minuto,Segundo (Hora actual); Seg7[][] 
         // (Representar cada simbolo numerico del 0 al 9 con punto y sin punto 
         // respectivamente a cada dimension).
         


#int_TIMER1

void TIMER1_isr(void){
   Ticker-=131072;
   if(Ticker<131072){
      Ticker+=freq_inst;
      dt=!dt;
      if(dt){
         Segundo++;
         if(Segundo==60){
            Minuto++;
            Segundo=0;
            if(Minuto==60){
               Hora++;
               Minuto=0;
               if(Hora==24) Hora=0;
            }
         }
      }
   }
}

         // TIMER1_isr(): funcion de interrupcion del timer1. Mantiene una base
         // de tiempo constante y preciso basandose en el tiempo de instrucción
         // (1/4 de la frecuencia del cristal). Refresca las variables de la hora.
         
         
         
void RTC_Init(void){
   Ticker=freq_inst;
   setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);
   enable_interrupts(INT_TIMER1);
}

         // RTC_Init(): funcion para iniciar las interrupciones por 
         // desbordamiento del timer1.



void Display_hora(void){
   output_a(0b00000001);
   if(!((Hora/10)%10)) output_b(0);
   else output_b(Seg7[false][(Hora/10)%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00000010);
   output_b(Seg7[dt][Hora%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00000100);
   output_b(Seg7[false][(Minuto/10)%10]);
   delay_ms(4);
   output_b(0);
   output_a(0b00001000);
   output_b(Seg7[false][Minuto%10]);
   delay_ms(4);
   output_b(0);
}

         // Display_hora(): funcion para hacer el barrido de los displays
         // representando los valores de las variables Hora y Minuto.



void Puls_read(void){
   if(!input(ph)){
      while(!input(ph)) Display_hora();
      Hora++;
      if(Hora==24) Hora=0;
   }
   if(!input(pm)){
      while(!input(pm)) Display_hora();
      Minuto++;
      if(Minuto==60) Minuto=0;
   }
}

         // Puls_read(): funcion para leer el estado de los pulsadores y si
         // estos se pulsan, incrementar las variables Hora y Minuto.



void main(){                           // funcion main()
   RTC_Init();                         // Inicio del programa: iniciar el RTC
   enable_interrupts(GLOBAL);          // asi como establecer los pines de 
   setup_comparator(nc_nc_nc_nc);      // entrada-salida.
   set_tris_a(0x30);
   set_tris_b(0x00);
   while(1){                           // funcion while(): bucle infinito, 
      Display_hora();                  // ejecuta las funciones Display_hora()
      Puls_read();                     // y Puls_read()
   }
}



      En el código podemos ver que estamos utilizando un array de dos dimensiones. Esto es para poder manejar el punto decimal, ya que lo que pretendemos es hacerlo parpadear cada medio segundo. De esta manera, al referirse a la posición 0 (o valor booleano false) de la primera dimensión del array, podremos generar el dígito que especifiquemos en la segunda dimensión (símbolos del 0 al 9) sin punto. En cambio, si especificamos que vamos a utilizar la posición 1 de la primera dimensión del array (o valor booleano true), se generará el símbolo numérico que especifiquemos en la segunda dimensión, pero ahora con el punto decimal. Se define también un flag (dt) para hacer parpadear el punto decimal del segundo display cada medio segundo. Este flag se invierte cada medio segundo, haciendo parpadear el punto decimal del segundo display.