jueves, 1 de agosto de 2024

UART Recepcion GPS

 Recepción GPS con PIC16F

Hola, te doy la bienvenida por visitar este blog, creado para compartir el mutuo interés en la electrónica y en especial la programación de microcontroladores PIC, te recuerdo que estoy atento a cualquier sugerencia o critica constructiva relacionada con esta publicación, al final dejo mis datos de contacto.
Hoy vamos a ver como conectar un receptor GPS tipo OEM, al microcontrolador utilizando el puerto serial UART, una vez establecida la comunicación, revisaremos el tipo de mensajes que emite el receptor GPS y las técnicas para decodificar esta informacion en datos requeridos para una aplicación GPS.
La programación del microcontrolador se realizara utilizando el software  de diseño  MPLABX  y el compilador de lenguaje C para PIC XC8 ambos disponibles en la pagina de microchip de forma gratuita. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo:  <<MPLABX v6.20>>   <<XC8 v2.45>>
Mencionar también que es necesario contar con conocimientos mínimos sobre programación en lenguaje C y el uso de microcontroladores PIC16F.
 
Introducción al Tema

Dejando un poco de lado la teoría que hay detrás del sistema GPS, cuya informacion es bastante amplia en sitios de internet como Wikipedia, <Sistema GPS>, paginas del gobierno Americano <Sistema GPS> o fabricantes de receptores <Sistema GPS>, vamos a centrarnos únicamente en el segmento del usuario, donde revisaremos dos modelos de receptores GPS que tengo a mi alcance.

A manera de una introducción al tema, el Sistema de Posicionamiento Global, más conocido por sus siglas en inglés como GPS, es un sistema que permite determinar la posición de un objeto en la Tierra. De los tres segmentos que lo conforman, el segmento de usuario es aquel que corresponde a los diferentes receptores utilizados en la Tierra. Hoy en día, encontraras módulos receptores de bajo coste y muy accesibles para utilizar en una variedad de aplicaciones que no siempre se relacionan con la posición, en algunos caso su uso esta relacionado con la sincronización del tiempo. 
Los fabricantes de receptores diseñan sus chips GPS, con diversas prestaciones relacionadas con la precisión y el consumo de energía, pero en ocasiones muchas de estas características no siempre son lo mas importante, ya que por ejemplo una buena antena podría compensar ciertas carencias. Algunos de estos chips incluso tienen la capacidad de recibir señal de otros sistemas de posicionamiento Glonass o Galileo.
Los dos receptores que revisaremos, sera con el propósito de mostrar algunas diferencias entre ambos.

Receptor: NEO-6M

 Chipset U-Blox 50CH
  Frecuencia L1 C/A
 SBAS: WAAS, EGNOS, MSAS
 Sensibilidad -156dBm
 Inicio Frío 26s, Caliente 1s
 Precisión 2.5mts
 Altura Max. 50000mts
 Velocidad Max. 500m/s
 Operación 2.7 – 3.6V 50mA

Receptor RGM-3600
Chipset SIRF III 20CH
Frecuencia L1 C/A
SBAS: WAAS, EGNOS, MSAS
Sensibilidad -159dBm
Inicio Frío 35s, Caliente 1s
Precisión 10mts 2D
Altura Max.18000mts
Velocidad Max. 514m/s
Operación 5V 55mA

Dejo los siguientes enlaces con informacion adicional de estos dos receptores:

<Receptor GPS NEO>            <Receptor GPS RGM>

Cuando un receptor GPS se energiza por primera vez, el mismo no tiene la información del almanaque y las efemérides, por lo que debe proceder a la descarga de esta informacion, este proceso suele demorar varios minutos. 
  • Inicio Frio (Cold start): El receptor cuenta con un almanaque válido, pero necesita actualizar las efemérides y la referencia del tiempo,  el proceso puede demorar unos 45 segundos.
  • Inicio Caliente (Hot Start): El receptor cuenta con el almanaque y efemérides válidas, además ya posee una referencia del tiempo. Esta situación suele darse en ciudades cuando se pierde cobertura, y proceso suele demorar entre 3 y 5 segundos.

Ahora resumiremos algunas diferencias de los dos receptores, el primero que posee un Chipset U-blox cuenta con hasta 50 canales para capturar satélites a diferencial del Chipset SIRF-III que posee 20, con relación al tiempo de inicio en frío, la precisión y altura maxima de operación los números favorecen al Chipset U-blox. Pero una característica que me resulta muy practica para implementar en el ejemplo, es con el modelo RGM-3600, que opera con voltaje de 5V y además el receptor esta encapsulado con la antena para un uso externo, cuenta una base de imán para sujetarlo en cualquier superficie metálica. 

Entonces por el momento los aspectos técnico que debemos tomar en cuenta son el voltaje de alimentación que requiere el receptor, la velocidad de comunicación que utiliza por defecto y los niveles lógicos en las lineas TX - RX, en algunos casos se necesitara convertir estos niveles entre 3.3V y 5.0V.

Sin importar cual de los modelos se utilizará, el programa del PIC podrá recibir la informacion de ambos, ya que los mensajes que emiten estos receptores cumplen con un formato estándar denominado NMEA 0183. 

Información del GPS

Una vez establecida la comunicación con el receptor GPS, los mensajes se recibirán cada segundo, al menos es la configuracion por defecto que tienen la mayoría de receptores, estos mensajes cumplen con el estándar NMEA.  
<NMEA> se creó para el intercambio de información digital entre productos electrónicos marinos, el primer protocolo estándar se llamó NMEA 0183, y es el que todavía utilizan y aceptan la mayoría de los receptores del sistema GPS.
Un receptor GPS, transmite diferentes mensajes NMEA 0183, algunas de estos son:
  •     RMC - Recommend Minimum Specific GPS/TRAN Data
  •     GGA - Global Positioning System Fix Data
  •     GSA - GPS DOP and Active Satellites
  •     GSV - GPS Satellites in View
 De hecho si nos conectamos a la salida del receptor con una terminal serial, podremos ver muchos de estos mensajes de forma consecutiva, observe la figura 1. 

Fig1. Datos de salida del receptor GPS
 
El tipo y cantidad mensajes que emite un receptor GPS, depende mucho de cada fabricante, siendo en algunos casos configurable,  de toda la informacion recibida desde el receptor, solo nos centraremos en el mensaje tipo RMC que a mi parecer posee la informacion necesaria para el posicionamiento o sincronización de tiempo, la descripción de los campos para este mensaje se muestra en la figura 2.

Fig2. Mensaje RMC del protocolo NMEA

De este mensaje discutiremos los siguientes campos:
  • El tiempo utiliza el formato UTC, y por lo tanto debes tomarlo muy en cuenta en tu aplicación, para hacer la debida corrección con referencia a tu zona.
  • El campo de navegación es muy importante, ya que el valor los datos de posicionamiento son validos únicamente si este campo tiene la letra 'A'
  • Posterior al asterisco '*' el receptor envía un numero en formato hexadecimal que es la suma exclusiva correlativa de todos los bytes que tiene el mensaje, esto permite realizar una verificación básica del mensaje enviado y el recibido, una diferencia representaría un error de comunicación.
 
Esquema del Circuito GPS     
El esquema del circuito que implementaremos se muestra en la figura 3, como mencione antes, utilizare un GPS RGM-3600 fabricado por Royaltek. En el esquema notara que en el receptor GPS solo conectamos el pin de transmisión al pin de recepción RXD del PIC, el otro pin no sera necesario porque no se llevaran a cabo configuraciones al GPS. Por defecto la mayoría de estos receptores trabajan a [4800 8N1], esto es 4800 baudios, con longitud de 8 bits, sin paridad y un único bit de parada. 
Por otro lado en el PIC utilizaremos el pin de transmisión TXD para enviar mensajes decodificados a la computadora, utilizando un convertidor TTL-USB como el CP2102.

Fig3. Esquema de Circuito GPS RGM-3600

Cuando el receptor GPS y el PIC trabajan con diferentes voltajes de alimentación, por ejemplo 3.3V del GPS y 5.0V del PIC, podría necesitar adaptar los niveles de voltaje en las lineas TXD y RXD, es importante que revise las especificaciones eléctricas VIH/VOL y VOH/VIL de ambos dispositivos. En algún caso tuve que usar un transistor para incrementar el nivel de tensión en la salida del GPS.

El diodo LED conectado al pin RA4 se utilizara para indicar el funcionamiento normal del programa mediante un destello cada segundo.

 
Programación del PIC16F

Muy bien, en este punto toca describir el programa que recibirá la informacion del GPS para lo cual vamos abordar dos enfoques diferentes, considerando las limitaciones que existe en el PIC16F687(ROM 2K y 128 bytes de RAM), para ambos casos la frecuencia del oscilador es Fosc=8MHz. La selección de este PIC es precisamente con el fin de mostrar que un enfoque adecuado en la programación permite optimizar el uso de los recursos. 

Una vez energizado y con las conexiones debidamente establecidas, utilice un pequeño programa que replica cada byte recibido por el receptor RXD al transmisor TXD del PIC16F687, y con una terminal serial en mi CUTECOM, ver figura 4, se logra recibir los siguientes mensajes repetidos cada segundo.

Fig4. Recepción GPS en terminal CUTECOM

Como verán de acuerdo a la figura 4, se recibe bastante informacion del GPS cada segundo, pero no todo es necesariamente útil en una aplicación por esa razón es que trabajaremos con el mensaje RMC y es aquí donde quiero considerar las dos opciones para conseguir los datos que necesitamos:

Primera Opción: Se debe recuperar solo el mensaje RMC, esto es posible si el programa receptor que recibe cada palabra identifica el inicio de un mensaje especifico, por ejemplo si los tres últimos datos recibidos corresponden con las letras <M><C>< , >, podemos afirmar que se trata del mensaje RMC. Entonces una vez identificado el inicio del mensaje, el programa receptor deberá guardar en memoria cada dato recibido hasta que el ultimo valor recibido indique el final, por ejemplo la recepción del asterisco <*> que se envía al completar un mensaje RMC. A fin de mostrar el resultado de esta opción, he creado un programa receptor para guardar el mensaje, y luego enviarlo a la terminal serial, ver la figura 5.

Fig5. Recepción del mensaje RMC en CUTECOM

Algo importante con este método es que debemos considerar la cantidad memoria RAM necesaria para almacenar el mensaje, esto es aproximadamente 64 bytes, la mitad de la memoria disponible en un PIC16F687
 
GPRMC,215536.000,A,1744.7111,S,06308.9987,W,0.00,0.00,300724,,,A*5B
 
Una vez almacenado el mensaje, se procederá con la decodificación del mismo, hay varias maneras hacer esto, una es utilizar la función strtok del lenguaje C, para identificar cada coma < , > y extraer cada cadena de forma secuencial, los campos que son de interés se puede convertir en valores decimales utilizando la función atoi de lenguaje C. El programa de receptor indicara mediante banderas cuando un mensaje ha sido decodificado, esta opción se puede resumir como el método de recibir el mensaje y luego decodificar.
 
Segunda Opción: En este caso, consideramos que no se dispone de la cantidad de memoria para almacenar el mensaje, y en este entendido el programa receptor se encargara de recibir, y decodificar el mensaje RMC al mismo tiempo, lo que implica mayor uso de la memoria de programa (ROM).  
En un inicio el programa receptor identifica el inicio de mensaje RMC de la misma forma que en la primera opción, pero una vez identificado cada dato recibido se almacenara en memoria hasta recibir una coma ( , ), y dependiendo del campo que corresponde se procesara la informacion almacenada, de esta manera la informacion retenida temporalmente nunca supera los 10 bytes(RAM). Eso si, el programa receptor debe verificar en cada dato recibido todas las comas para identificar los campos en el mensaje, que son aproximadamente 11, de los cuales varios no poseen informacion. Esta opción se puede resumir como el método de recibir y decodificar al mismo tiempo. 
Luego de codificar el programa con esta ultima opción, que permite recibir y decodificar el mensaje al mismo tiempo, una vez conectado a la terminal serial, se reciben la informacion que muestra la figura 6. En este caso la informacion ya tiene un formato establecido a partir de los campos decodificados del mensaje RMC.
El programa que se describe en la siguiente sección utiliza este método debido a que se requiere menos uso de la memoria RAM.

Fig6. Mensaje final procesado CUTECOM

La programación del microcontrolador, no es algo simple de explicar con palabras y que a la vez sea entendible,  me disculpo si me he saltado con la descripción de algunas lineas  en el código, prometo lo mas antes posible elaborar un flujo-grama y actualizar esta publicación para comprender mejor como funciona este programa.

En el programa se hace uso de las interrupciones del temporizador TMR0 y el receptor USART del PIC, cabe resaltar que ninguna de las tareas en el programa representa un proceso bloqueante, la codificación en si utiliza una técnica de programación por estados, si quieres conocer mas detalles al respecto, te recomiendo revises antes de continuar, la siguiente publicación. 
 
 
#pragma config FOSC=INTRCIO,WDTE=OFF,PWRTE=ON,BOREN=OFF,IESO=OFF    
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#define LED1pin PORTAbits.RA4  
//Led para destello
volatile __bit tick1ms, rmcok; 
//Banderas
typedef struct
//Estructura de datos GPS
{
    uint32_t lat, lon;
    uint8_t year, month, day;
    uint8_t hour, minute, second;
    uint8_t valid;
} rmcstruct_t;
rmcstruct_t rmc;
char buffer[12];  
//Memoria de datos RAM 12 bytes
void setup(void); 
//Procedimiento de inicializacion
void taskLED(void);
//Tarea para destello LED
void taskGPS(uint8_t data);
//Tarea del Programa Receptor
void __interrupt() isr(void)
{
    uint8_t res;
    if(INTCONbits.T0IF)
//Evento cada 1.0ms
    {
        INTCONbits.T0IF = 0; 
//Limpia bandera
        TMR0 += 131;
//Reinicia contador
        tick1ms = 1;
    }
    while(PIR1bits.RCIF)
//Evento con recepcion RXD
    {
        res = RCREG;
//Recibe el dato UART
        taskGPS(res);
//Llama al programa receptor
    }
}
void main()
{
  setup();
//Inicializa el PIC
  while(1)
  {
    if(tick1ms)
//Valida cada 1ms
    {
      tick1ms = 0;
//Limpia bandera
      taskLED();
//Destello LED
      if(rmcok)
//Si hay dato GPS decodificado
      {
        rmcok = 0;
        if(rmc.valid)
//Verifica si es valido
        {
          printf("Fecha:%u/%02u/%02u ",rmc.year, rmc.month, rmc.day);
          printf("Hora:%02u:%02u:%02u\n",rmc.hour, rmc.minute, rmc.second);
        }       
        RCSTAbits.CREN = 1;
//Activa interrupcion del receptor
      }
    }
  }
}
void setup(void)
{
    OSCCONbits.IRCF = 0b111;
//Ajusta Fosc=8MHz, Tcy=0.5u
    while(OSCCONbits.HTS == 0) {}
   
/* CONFIGURACION PUERTOS*/
    ANSEL = 0;
    ANSELH = 0;
    TRISAbits.TRISA4 = 0;
//Salida LED
    OPTION_REGbits.nRABPU = 0;
//Pull-up
   
/* CONFIGURACION TIMER0 0.1MS */
    OPTION_REGbits.T0CS = 0;
//Modo Termporizador
    OPTION_REGbits.PSA = 0;
//Con prescala
    OPTION_REGbits.PS = 0b011;
//Prescala 1:16
    TMR0 = 131;
//256-[(time*Fosc)/(pre*4)] time=0.001 seg
    INTCONbits.T0IF = 0;
//Limpia bandera
    INTCONbits.T0IE = 1;
//Activa interrupcion del TMR0
   
/* CONFIGURA USART A 4800N1 8MHz*/
    TXSTAbits.BRGH = 1;
//Alta del Generador
    TXSTAbits.TXEN = 1;
//Activa el transmisor
    RCSTAbits.CREN = 1;
//Activa el receptor
    RCSTAbits.SPEN = 1;
//Habilita el modulo USART
    SPBRG = 103;
//Formula [8MHz/(16 * 4800)] - 1
    PIE1bits.RCIE = 1;
//Activa interrupcion del receptor
    INTCONbits.PEIE = 1;
//Activa interrupcion de perifericos
    INTCONbits.GIE = 1;
//Habilita interrupciones
}
void taskGPS(uint8_t data)
//Tarea del Programa receptor GPS
{  
//<$GPRMC,215536.000,A,1744.7111,S,06308.9987,W,0.00,0.00,300724,,,A*5B>
    static uint8_t state = 0, pos;
    switch(state)
    {
        case 0:
//$GPRM
            if(data == 'M')
                state++;
            break;
        case 1:
//C
            if(data == 'C')
                state++;
            break;
        case 2:
//,
            if(data == ',')
//Inicio RMC
            {
                pos = 0;
                state++;
            }
            break;
        case 3:
//215536.
            if(data == '.')
//Campo TIME
            {
                buffer[pos] = 0;
//null end
                rmc.second = (uint8_t) atoi(&buffer[4]);
                buffer[4] = 0;
//null end
                rmc.minute = (uint8_t) atoi(&buffer[2]);
                buffer[2] = 0;
//null end
                rmc.hour = (uint8_t) atoi(buffer);
                state++;
            } else buffer[pos] = data;
            if(pos++ > 6) state = 0;
//Reinicia maquina por error
            break;
        case 4:
//000,
            if(data == ',')
//Campo, no utilizado
                state++;
            break;
        case 5:
//A
            if(data == 'A') rmc.valid = 1;
//Campo VALID
            else rmc.valid = 0;
            state++;
            break;
        case 6:
//,
            if(data == ',')
//Campo no utilizado
                state++;
            break;
        case 7:
//1744.7111,
            if(data == ',')
//Campo LAT, no se utiliza
                state++;
            break;
        case 8:
//S,
            if(data == ',')
//Campo no utilizado
                state++;
            break;
        case 9:
//06308.9987,
            if(data == ',')
//Campo LON, no se utiliza
                state++;
            break;
        case 10:
//W,
            if(data == ',')
//Campo no utilizado
                state++;
            break;
        case 11:
//0.00,
            if(data == ',')
//Campo no utilizado
                state++;
            break;
        case 12:
//0.00,
            if(data == ',')
//Campo no utilizado
            {
                state++;
                pos = 0;
            }
            break;
        case 13:
//300724,
             if(data == ',')
//Campo DATE
            {
                buffer[pos] = 0;
//null end
                rmc.year = (uint8_t) atoi(&buffer[4]);
                buffer[4] = 0;
//null end
                rmc.month = (uint8_t) atoi(&buffer[2]);
                buffer[2] = 0;
//null end
                rmc.day = (uint8_t) atoi(buffer);
                RCSTAbits.CREN = 0; //Desactiva el receptor
                rmcok = 1;
//Activa bandera
                state = 0;
//Reinicia maquina
            } else buffer[pos] = data;
            if(pos++ > 6) state = 0;
//Reiniciar maquina por error
    }
}
void taskLED(void)
//Tarea para destello led
{
    static uint16_t tcnt = 0;
    if(tcnt++ > 999)
    {
        tcnt = 0;
        LED1pin = 1;
    }
    if(tcnt == 200) LED1pin = 0;
}
void putch(char byte)
{
    while(PIR1bits.TXIF == 0) {};
    TXREG = byte;
}

Funcionamiento del Circuito

El programa descrito anteriormente se ensayo en el microcontrolador con una implementación simple del receptor RGM-3600 y una tarjeta de prueba basada en el PIC16F687, la imagen de la figura 7, muestra el montaje final.

Fig7. Implementacion del circuito PIC
La recepción de la informacion se muestra en la imagen en la figura 6, estos datos se envían cada segundo, como el propósito del programa es mostrar la recepción de datos,  no se decodifico la latitud y longitud del mensaje, pero como vera en el código del programa receptor, hacer esta tarea es bastante sencilla.
 
Conclusiones y Recomendaciones.
Se ha demostrado el funcionamiento adecuado del circuito para recibir y decodificar datos de un GPS, sin duda las posibles aplicaciones que hay para este ejemplo son diversas, por lo que únicamente dejo a consideración algunos aspectos si estas en planes de utilizarlo en tu proyecto electrónico.
  • Te en claro siempre, que la informacion de tiempo de un GPS estará en formato UTC,
  • Considera que para hacer pruebas con este circuito, deberás ubicarte en una zona despejado con vista al exterior, ya que dentro de un edificio el receptor no captara señal de los satélites.
  • Si bien en muchas aplicaciones el mensaje RMC tiene la informacion suficiente para la navegación y referencia temporal, hay otro tipo de mensaje que proveen informacion de la recepción y estado de los satélites, que podrían necesitar para ciertas aplicaciones. 
  • Aunque por defecto la comunicación de un receptor GPS se establece en 4800 8N1, debe revisar las especificaciones del modelo, ya que en algunos casos esto puede variar.
Aquí dejo los enlaces para que puedas descargar el proyecto, se encuentra en formato gzip, debes descomprimir y abrir con MPLABX  
Si quieres ver como compilar e implementar los proyectos de este blog, elaborados con MPLABX mira este vídeo.<Compilando proyectos MPLABX>
 
Sin mas que mencionar agradezco tu visita al blog y espero que el ejemplo visto pueda ser útil en tu formación y el proyecto que desarrollas.

Atentamente, Pablo Zárate Arancibia  email: pablinza@me.com / pablinzte@gmail.com, @pablinzar

Santa Cruz de la Sierra - Bolivia

 

miércoles, 31 de julio de 2024

TMR0 Control de Potencia AC220

Control de Potencia AC220V

Te doy la bienvenida a este sitio, que fue creado para compartir el mutuo interés en la electrónica y en especial la programación de microcontroladores PIC. Agradecido por visitar mi blog quiero recordarte que estoy atento a cualquier consulta o critica constructiva relacionada con esta publicación. 
En esta oportunidad vamos a ver como controlar la potencia o energía que consume una carga resistiva, considerando que el suministro eléctrico es la linea AC220 de una instalación domiciliaria.
La programación del ejemplo se realizara utilizando el software  de diseño  MPLABX  y el compilador de lenguaje C para PIC XC8 ambos disponibles en la pagina de microchip de forma gratuita. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo:  <<MPLABX v6.20>>   <<XC8 v2.45>>
Mencionar también que es necesario contar con conocimientos mínimos sobre programación en lenguaje C y el uso de microcontroladores PIC16.

Introducción al Tema

De manera breve describiremos que la señal de onda que se muestra en la figura 1, es el resultado de la rotación continua y constante de un <fasor> dentro de un campo magnético, donde la longitud del <fasor> determina la amplitud de la señal, y la velocidad de rotación determina su frecuencia, esta explicación corresponde a una señal alterna como la suministrada en una instalación domiciliaria, en mi país, la red de baja tensión posee un voltaje eficaz de E=220V y frecuencia de f=50Hz, La figura 1 muestra la relación amplitud y tiempo.

Fig1.  Ciclo de Señal en linea AC220V, Voltaje (V) / Tiempo(S)

Observe que la tensión maxima es Em=311V, y el tiempo total del ciclo T=0.02 segundos, esto se determina fácilmente aplicando las ecuaciones básicas: 

Cuando consideramos el uso de cargas resistivas consideramos que la onda sinusoidal del voltaje siempre estará en fase con la onda de corriente, eso si con magnitudes diferentes, entonces para controlar la energía suministrada a la carga resistiva, el circuito de control debe ser capaz de abrir y cerrar el circuito eléctrico de manera sincronizada con la fase de onda, este circuito se representa en la figura 2,

Fig2. Circuito básico para control de Carga

Comentaremos dos maneras para realizar el control de potencia en un carga, ambos sincronizados con la fase de onda. 

Forma 1: Control de Potencia por Ciclos activos

En este caso la llave de control, opera en función de la cantidad de semi-ciclos de la onda sinusoidal, abriendo o cerrando el circuito solo en los puntos de cruce por cero, de esta forma  se determina la cantidad energía que consume la carga en un lapso de tiempo, como notara en la figura 3.

Fig3. Control de Potencia por Ciclos

El lapso de tiempo mencionado que viene a ser el periodo (Tpwm) para el control es fijo, y el tiempo de activación (td) es variable, su valor sera siempre un múltiplo del periodo que tiene cada semi-ciclo (THALF), por ejemplo, si la frecuencia de onda es F=50Hz, se pueden calculan los siguientes valores considerando la figura 3:
El consumo de energía se determina por el ciclo de trabajo en porcentaje, aplicando la siguiente ecuación:
Este tipo de control de potencia se utiliza bastante para controlar la temperatura de las resistencias eléctricas de una gran variedad de equipos(horno, plancha, calefactor, etc).

Forma 2: Control de Potencia por Fase de disparo
En este caso, la llave de control se sincroniza con el inicio de cada semi-ciclo, para abrir el circuito y luego cerrarlo en función del ángulo de fase (θ) que tiene la onda, en la practica en ángulo de fase se controla por un tiempo de espera (td) que es variable, y el periodo de control (Tpwm) fijo es el tiempo que dura el semi-ciclo (THALF), tal como se observa en la figura 4. 

Fig4. Control de Potencia por Fase
Para una frecuencia de onda F=50Hz, el periodo de control sera:
TPWM = THALF = 0.001s
El ángulo de fase para cerrar el circuito en función del tiempo se determina por la siguiente ecuación y siendo la figura 5 una tabla con valores de tiempo(td) para un determinado ángulo de fase (θ).
Fig5. Tiempo de espera por ángulo de fase
  
Notara que el tiempo de espera necesario para el disparo, no debe exceder al periodo de control, y que su relación con la potencia entrada a la carga es inversamente proporcional, es decir que a mayor tiempo de espera (td), menor es la  potencia en la carga (dt%)

De la mima manera que el caso anterior, el consumo de energía se determina por el ciclo de trabajo en porcentaje, aplicando la siguiente ecuación:

Vamos a implementar un circuito de control de potencia considerando la forma 2, y para este fin utilizaremos una lampara incandescente de 100W, un circuito de potencia con tiristor (TRIAC) y un microcontrolador (PIC16F887).
 
Circuito de Control AC
En esta parte, ya con los conceptos vistos al inicio, analizaremos el control de fase que se implementara con un <TRIAC> y en la que utilizaremos las siguientes señales que se relacionas con la onda sinusoidal del voltaje AC, la figura 6, ilustra la representación en colores, donde: 
PWM = Representa la Energía que consume la carga en cada semi-ciclo
ZCD = Pulso de entrada para indicar el cruce por cero de la onda sinusoidal
TDT = Pulso de salida para activar el disparo que cerrara el circuito
Fig6. Control de disparo de Fase
Una característica que tiene un TRIAC, es la capacidad de conducir la corriente eléctrica en ambos semi-ciclos positivo y negativo de la onda sinusoidal. Para que este dispositivo cierre el circuito entre sus terminales T1 y T2 una de sus entrada conocida como puerta (G) debe activarse, una vez que este entra en conducción, el circuito se mantendrá cerrado hasta que la corriente que pasa por las terminales T1 y T2, caiga por debajo del valor conocido como corriente de mantenimiento, con lo cual las terminales T1 y T2 dejaran de conducir quedando el circuito abierto. Esta condición ocurre cuando la señal sinusoidal cruza por cero, donde teóricamente la corriente por las terminales T1 y T2 es 0.

Fig7. Simbología de un TRIAC

En la figura 8, se muestra el circuito de control utilizado para nuestro ejemplo, donde se separan dos bloques distinguidas por el color, la zona roja corresponde al bloque AC que trabaja con el suministro eléctrico 220V / 50Hz, la zona verde es el bloque DC cuyas señales operan con niveles lógicos digitales, es importante mantener esta separación de forma física en el circuito que va implementarse, y proteger del contacto humano los elementos que están en el bloque AC. Recordar también que el circuito que se muestra en la figura 6, tiene fines educativos, por lo cual se excluyen varios elementos de protección adicionales necesarias para un producto final.  
Fig8. Circuito de Control con TRIAC
Desde un punto de referencia en el bloque DC, la señal para indicar el cruce por cero, es generado por la polarización de los diodos que posee el opto-acoplador U1 en ambos ciclos, donde la corriente AC que circula se reduce al orden de los miliamperios debido a las resistencias R1 y R2. Cuando la onda sinusoidal pasa por el punto cero, ninguno de los diodos de U1 esta polarizado y por lo tanto el colector y emisor del transistor quedara abierto, conforme la onda pase el punto cero y vaya incrementándose, el voltaje aplicado a los diodos se incrementara hasta que uno de ellos se polarice haciendo que el transistor se cierre; Considerando esto podemos deducir que el nivel logico en el colector de U1, sera 1 solo cuando la señal AC cruce por cero y el resto de tiempo permanecerá con valor 0, esto se representa con el pulso ZCD de la figura 6.
Para controlar la puerta (G) del TRIAC, y mantener aislado el bloque AC del circuito, se utiliza un opto-acoplador tipo triac U2, con esto la activación dependerá de la polarización del diodo en U2, en la figura 6 este evento se representa con la señal TDT. Finalmente el Microcontrolador utilizara como entrada la señal ZCD para detectar el instante donde se produce el cruce por cero y la salida TDT para disparar el TRIAC.
Lista de componentes que muestra el circuito de la figura 8, es la siguiente:
  • Fusible F1 0.5A / 220V
  • Resistencia R1,R2 100kΩ / 1.0W
  • Resistencia R3 220Ω / 0.5W
  • Resistencia R4 10kΩ / 0.25W
  • Resistencia R5 1kΩ / 0.25W
  • Triac T1 BT-136 600V
  • Opto-Acoplador U1 PC-814
  • Opto-Acoplador U2 MOC-3022
En el lado del Microcontrolador, emplearemos un circuito bastante simple utilizando el PIC16F887, observe la figura 9. La señal de entrada ZCD que proviene del circuito de control se conectara al pin de interrupción externa INT, y la señal de salida TDT al circuito de control sera el pin RB1, además se utilizara una resistencia variable para dividir la tensión y hacer lectura de su valor analógico en el pin AN0, finalmente un diodo conectado al pin RE2 indicar con un destello que el programa del PIC esta en funcionamiento.
Fig9. Circuito PIC16F887

Programación del PIC

Ya en esta sección trataremos la programación del microcontrolador PIC, como sabrán no es sencillo describir el funcionamiento de un programa en pocas palabras y que sea fácil de entender,  me disculpo si no he sido claro en alguna parte, por eso para ir entrando en contexto, la figura 10 ilustra el flujo que sigue el programa principal y también la rutina de servicio a la interrupción.
Fig10. Flujo-grama Programa principal e ISR
 
Programa Principal: Posterior a la configuracion de los puertos, el modulo ADC, el temporizador y las interrupciones, el programa principal ingresa en un bucle continuo para ejecutar tres tareas cada milisegundo, estas tres tareas son: destellar el LED, hacer lectura del canal analógico ADC y ajustar el tiempo de disparo. Cabe resaltar que ninguna de estas tareas representa un proceso bloqueante, la codificación utiliza una técnica de programación por estados, si quieres conocer mas detalles al respecto puedes revisar la siguiente publicación. <Programación por estados con Temporizador>
Rutina de Servicio a la Interrupción ISR: El código de esta rutina atiende a dos eventos, la interrupción del temporizador T0IF que permite ejecutar las tareas del programa principal cada milisegundo, y la interrupción externa INT que indica el cruce por cero ZCD, con la que sincronizara el tiempo de disparo TDT basado en un contador. Este contador se incrementa con cada interrupción del temporizador.
Si bien los comentarios en la codificación del programa describen el propósito de cada linea, se ha establecido la configuracion de variables considerando lo siguiente:
  • La interrupción del temporizador se ajusto a 0.0002 segundos, con este tiempo para cada semi-ciclo de onda, la rutina ISR atiende hasta 50 interrupciones T0IF (0.010 / 0.0002 = 50). Un registro contador CNT se inicia en 0 cada vez que ocurre la interrupción externa INT(Cruce por cero) y se incrementa con cada interrupción T0IF,  el control del disparo se da cuando el contador CNT alcanza el valor del tiempo TDT (Pulso de disparo).
  • El valor para el tiempo TDT estará en función de la lectura ADC al canal AN0, cuya voltaje analógico es ajustable por un potenciómetro, entonces para que el resultado de la conversion de 10-bit, permanezca en el rango 0 a 49, se divide entre 21, y con ello se actualiza al valor del tiempo TDT. 
A continuación se muestra el código del programa para el PIC16F887, considerando que la frecuencia del oscilador es Fosc=8MHz.
 
#include <xc.h>
#include <stdio.h>
#define ZCDpin PORTBbits.RB0 //Entrada para indicar Cruce por cero
#define TDTpin PORTBbits.RB1 //Salida para el pulso de disparo TRIAC
#define LEDpin PORTEbits.RE2 //Salida para destellar el LED
volatile uint8_t tick1ms = 0;
volatile uint8_t TDTval = 0; //Variable de control para disparo 0-49
void setupMCU(void);
void taskLED(void);
void taskADC(void);
void main(void)
{
    setupMCU(); //Configuración
    while(1)
    {
        if(tick1ms) //Valida en cada milisegundo
        {
            tick1ms = 0; //Limpia bandera
            taskLED();   //Tarea para destello
            taskADC();   //Tarea de lectura ADC
        }
    }
}
void __interrupt() isr(void) //Rutina ISR
{
    static uint8_t cnt0, cnt1, toffset;
    if(INTCONbits.INTF) //pulso ZCD
    {
        INTCONbits.INTF = 0;
        toffset = 0; //Reinicia corrección del contador
    }    
    if(INTCONbits.T0IF) //Activa cada 0.0002s
    {
        INTCONbits.T0IF = 0;  //Limpia bandera
        TMR0 += 156; //Reinicia contador T0
        if(cnt0++ > 4) //Valida 5 x 0.0002 = 0.001s
        {
           tick1ms = 1; //Activa cada 0.001s
           cnt0 = 0;
        }
        if(toffset == 6) //Valida corrección ZCD
        {
            cnt1 = 0; //Reinicia contador TDT
            toffset++;
        }
        if(toffset < 6) toffset++;
        if(cnt1 == TDTval) TDTpin = 1; //Valida y activa disparo
        else TDTpin = 0;
        if(cnt1 < 50)  cnt1++; //t=50*0.2m = 10m
    }
    
}
void setupMCU(void)
{
    OSCCONbits.IRCF = 0b111; //Ajusta Fosc=8MHz, Tcy=0.5u
    while(OSCCONbits.HTS == 0) {}
    /* CONFIGURACION PUERTOS*/
    ANSEL = 0;
    ANSELH = 0;
    TRISEbits.TRISE2 = 0; //Salida LED
    TRISBbits.TRISB1 = 0; //Salida control TDT
    LEDpin = 0; //Apaga el LED
    TDTpin = 0; //Nivel bajo en pulso TDT
    TRISBbits.TRISB0 = 1; //Entrada ZCD
    OPTION_REGbits.nRBPU = 0; //Pull-up en ZCD
    OPTION_REGbits.INTEDG = 1; //Flanco Ascendente
    INTCONbits.INTF = 0; //Limpia bandera INT
    INTCONbits.INTE = 1; //Activa interrupcion externa
    /* CONFIGURACION TIMER0 0.2MS */
    OPTION_REGbits.T0CS = 0;//Modo Termporizador
    OPTION_REGbits.PSA = 0; //Con prescala
    OPTION_REGbits.PS = 0b001; //Prescala 1:4
    TMR0 = 156; //256-[(time*Fosc)/(pre*4)] time=0.0002 seg
    INTCONbits.T0IF = 0; //Limpia bandera
    INTCONbits.T0IE = 1; //Activa interrupcion del TMR0
    /* CONFIGURACION ADC-10 Canal0*/
    ANSELbits.ANS0 = 1; //Activa canal AN0
    ADCON0bits.ADCS = 0b10; //TAD=4us > 1.6us (8MHz/32)
    ADCON0bits.CHS = 0; //Selecciona Canal 0
    ADCON0bits.ADON = 0; //Desactiva el modulo ADC
    INTCONbits.GIE = 1; //Habilita las interrupciones
}
void taskADC(void) //Ciclo 1ms, Lectura ADC de 10Hz
{
    static uint8_t state = 0, cnt = 0;
    static uint16_t adcraw;
    cnt++;
    switch(state)
    {
        case 0: //Activa ADC para captura
            ADCON0bits.ADON = 1;
            cnt = 0;
            state = 1;
            break;
        case 1: //Inicia conversion AN0
            ADCON0bits.GO = 1;
            state = 2;
            break;
        case 2://Espera fin de conversion AN0
            if(ADCON0bits.GO == 0)
            {
                adcraw = ADRESL; //ADRESL First
                adcraw |= (uint16_t) (ADRESH << 8);
                adcraw >>= 6; //Corrige alineacion de ADH:ADL
                ADCON0bits.ADON = 0;
                state = 3;
            }
            break;
        case 3: //Espera fin de ciclo
            if(cnt++ > 99) //valida 100 x 1ms = 100ms
            {
                TDTval = (uint8_t) (adcraw / 22U); //Ajusta a TDT
                state = 0;
            }
    }
}
void taskLED(void) //Destella led ciclo 1ms
{
    static uint16_t tcnt = 0;
    if(tcnt++ > 999)
    {
        tcnt = 0;
        LEDpin = 1;
    }
    if(tcnt == 200) LEDpin = 0;
}

Pruebas de Funcionamiento

Se ensayo el código inicial en el microcontrolador para llevar a cabo las pruebas de funcionamiento utilice una placa de control TRIAC que disponía de un proyecto anterior, en esta placa de cuatro salidas, solo se utilizo uno de los canales.
 
Fig11. Circuito de control y PIC
En la primera prueba sin considerar la corrección del tiempo de disparo TDT, la lampara comenzó a oscilar cuando se realizo el ajuste total del potenciómetro para entregar toda la potencia. Esto se debe a que el pulso detector ZCD tiene un tiempo de duración,  que se extiende antes y después del cruce por cero, por lo que fue necesario agregar un tiempo offset para corregir el disparo, finalmente con un valor de ajuste igual a 7 (7x0.0002 = 0.0014), se corrige el problema de oscilación. Una forma practica para determinar este valor de corrección, es medir con un instrumento el porcentaje de nivel alto en la señal ZCD, en mi caso registre 28%, del cual la mitad corresponde al instante en que el detector de cruce se activa, esto es 14% del semi-ciclo (0.010 x 0.14 = 0.0014)
 
Abajo dejo un breve vídeo que muestra el funcionamiento de este circuito y el código del proyecto MPLABX.

 
Aquí dejo los enlaces para que puedas descargar el proyecto, se encuentra en formato gzip, debes descomprimir y abrir con MPLABX 
Si quieres ver como compilar e implementar los proyectos de este blog, elaborados con MPLABX mira este vídeo.<Compilando proyectos MPLABX>

Conclusiones

Se ha demostrado el funcionamiento adecuado del circuito de control de potencia, con el microcontrolador PIC16F887, los valores de tiempo que utilice pueden modificarse en base a requisitos propios de cada proyecto, Si estas en planes de poner en practica este tipo de control como recomendación debe considerar los siguiente puntos: 
  • Este circuito como se menciona al inicio, considera el uso de una carga puramente resistivas, donde no habrá desface entre el voltaje y la corriente AC.
  • El control de intensidad de de la luz que se muestra en el ejemplo es solo para lamparas incandescentes, y no se aplica a las luces tipo LED ya que operan con otro principio.
  • Puede incrementar la resolución para el control de energía en la carga a mas pasos de los que se trato en el ejemplo, pero debe saber que la relación del tiempo de disparo con la cantidad de energía que se entrega no es lineal debido a la magnitud variable que la tensión eléctrica. 
  • Por ultimo el punto mas importante de todo: Si no sabe lo que esta conectando en este circuito, deje de armarlo y no continué hasta comprender su funcionamiento. Una mala conexión o manipulación del mismo puede provocar serios daños material y personal(riesgo de muerte).
Sin mas que mencionar agradezco tu visita al blog y espero que el ejemplo visto pueda ser útil en tu formación y el proyecto que desarrollas.

Atentamente, Pablo Zárate Arancibia  email: pablinza@me.com / pablinzte@gmail.com, @pablinzar

Santa Cruz de la Sierra - Bolivia