Mostrando entradas con la etiqueta PIC. Mostrar todas las entradas
Mostrando entradas con la etiqueta PIC. Mostrar todas las entradas

PIC-Cal, un PIC hecho calculadora.

... O una calculadora hecha con PIC.
En esta ocación les presento una calculadora hecha con un PIC16F88 y un módulo LCD administrado con el microcontrolador Hitachi 44780 o compatible.
Esta no es una gran calculadora, pero resuelve fórmulas algebraicas reconociendo términos y utilizando parentesis. Esta versión todavía no maneja números negativos ni decimales, ya que solo el hecho de utilizar números float32 aumenta el consumo de memoria flash del PIC desde el 60% hasta cerca del 80%. Si se quisiera hacer una calculadora mas exacta y compleja se tendría que recurrir a un PIC con mas memoria.
El proyecto es una mezcla de la práctica de varios artículos que se pueden encontrar en este blog. Ya que utiliza la librería LCDGAR.c para el display, controlado con un shift register que a su vez se usa para barrer el teclado.
El código es demasiado extenso como para publicarlo en el blog, por ese motivo es que solo dejo el link para descargar el proyecto completo en CCS C y la simulación en ISIS. De todos modos pasaré a comentarlo brevemente:
El bucle principal espera a que se pulse una tecla, cuando esto sucede actua en consecuencia de la tecla pulsada. Esta es la parte sencilla.
Cuando se pulsa cualquier tecla, que no sea el signo igual, la almacena en una cadena y si se pulsa el signo igual, resuelve. Esto en teoría también es sencillo.
Ahora la pregunta es ¿cómo se resuelve?, pues bien, las calculadoras normales van haciendo las operaciones según se las vayan ingresando. Pero sabemos que en matemáticas esto no es así, una calculadora científica, reconoce términos, y eso es lo que se pretendía resolver en este proyecto, luego se agregó el uso de parentesis, pero la forma de resolverlos es igual para ambos casos.
Supongamos que la fórmula ingresada es:

15 X 3 + 8 X 4

Para resolverlo, con lápiz y papel, no podemos hacer:

15 X 3 = 45
45 + 8 = 53
53 X 4 = 212

La forma correcta sería separandolo en términos, que es de esta forma:

15 X 3 = 45
8 X 4 = 32
45 + 32 = 77

15 X 3 + 8 X 4 = 77

Eso es lo que se pretende que haga la calculadora, para eso, una vez pulsada la tecla resolver, la secuencia es como se muestra a continucación:
Paso 1Se recorre toda la fórmula. Si encuentra un + o un - corta la fórmula, de modo que queden tres términos y si hay mas de tres, en el último habrá varios términos.
Paso 2Resuelve el segundo término, dejando en este el resultado.
Paso 3Junta el primer término con el segundo en un único y primer término.
Paso 4Resuelve el primer término, dejando el resultado.
Paso 5Junta el resultado del primer término con el tercero en la fórmula.
Paso 6Si todavía hay cuentas por realizar vuelve al paso 1.
FINPresenta el resultado en pantalla.

En la práctica para la fórmula 3 X 3 + 8 X 4 - 2 X 3, pasaría lo siguiente:
Vuelta 1 Paso 1 Recorre la fórmula buscando + y -, recortándola y dejando:
Primer término: 3 X 3 +
Segundo término: 8 X 4
Tercer término: - 2 X 3
Paso 2 Resuelve el segundo término:
Segundo término: 32
Paso 3Junta el primer y el segundo término:
Primer término: 3 X 3 + 32
Paso 4Resuelve el primer término:
3 X 3 = 9
9 + 32 = 41
Primer término: 41
Paso 5Junta el primer y tercer término, en fórmula:
Fórmula: 41 - 2 X 3
Paso 6Hay cuenta, vuelve al paso 1.
Vuelta 2 Paso 1 Recorre la fórmula buscando + y -, recortándola y dejando:
Primer término: 41 -
Segundo término: 2 X 3
Tercer término: (vacío)
Paso 2 Resuelve el segundo término:
Segundo término: 6
Paso 3 Junta el primer y el segundo término:
Primer término: 41 - 6
Paso 4Resuelve el primer término:
41 - 6 = 35
Primer término: 35
Paso 5Junta el primer y tercer término, en fórmula:
Fórmula: 35 (No hay nada en el tercero)
Paso 6No hay mas cuentas, sale del bucle.
FIN Presenta el resultado en pantalla.
Los parentesis se resuelven utilizando el mismo método, con la diferencia que utiliza el contenido de los mismos como fórmulas independientes y son las primeras en resolverse. El algoritmo detecta parentesis dentro de parentesis.
Para terminar, aclarar que el código no está pulido ni probado al 100%, de modo que puede, y debe, tener muchos bugs. Pero lo comparto por si a alguien le es útil como ejemplo. Si se encuentra algún error o alguna modificación significativa, bienvenidas serán sus sugerencias.
El primer bug que sé que va a tener, es que no se puede ingresar fórmulas de mas de 64 caracteres, y el código no tiene ningún tipo de control para evitar la catástrofe cuando se ingrese el número 65.

Algoritmo antirrebote de pulsadores

Indagando en foros uno se da cuenta que lo que parece tan fácil como capturar la simple pulsación de una tecla conectada a un pin de un microcontrolador, en el fondo lleva consigo algunas técnica a tener en cuenta; Muchos son los que preguntan, sobretodo cuando se está iniciando en materia de programación de microcontroladores, como debería ser un buen algoritmo de antirrebote. Pues bien, en esta ocasión les traigo la implementación de la rutina escrita en CCS C para PIC16F88, que, si bien puede que no sea la mejor, es sin duda, muy funcional y práctica, al menos cumple su función a las mil maravillas. Es el modo en que hago el antirrebote en mis códigos y hasta ahora no ha fallado nunca.

La teoría es bastante sencilla, el programa espera a que se pulse una tecla, cuando detecta la pulsación guarda el número de tecla pulsada en una variable, luego comprueba que sea la primera vez que se pulsó, si es así actúa en consecuencia y si es que está pulsada desde antes, comprueba que haya pasado un tiempo determinado para volver a tenerla en cuenta. De este modo se puede hacer, por ejemplo un reloj que cuando se incremente alguno de sus parámetros, por ejemplo los minutos, y la tecla correspondiente se mantenga pulsada, luego del primer incremento comience a avanzar mas rápido.


He aquí el código en CCS C:
#include <16f88.h>
#use delay(clock=8MHz)
#use fast_io(all)
#fuses MCLR,NOPUT,INTRC_IO,NOWDT,NOBROWNOUT


#define Tec1   PIN_B0
#define Tec2   PIN_B1
#define Tec3   PIN_B2
#define Tec4   PIN_B3   //Pines del PIC correspondientes a cada tecla.

#define Nada   0  
#define Mas    1     // valores validos para Flag
#define Menos  2  

int Segs;            // Segundos.
int Minu;            // Minutos.
int Hora;            // Hora.
int Tmp;             // Temporal.
int Tecla;           // Tecla pulsada.
int anTecla;         // Tecla pulsada en la comprobacion anterior
int Tiempo;          // Tiempo transcurrido desde la ultima comprobacion
                     // de teclado.
int CuantoTiempo;    // Cuanto tiempo debe esperar para la
                     // proxima comprobacion.
int Flag;            // Flag para incrementar o decrementar
                     // el valor deseado.
int Set;             // Flag para seleccionar el valor a cambiar.

void main(){
   
   setup_oscillator(OSC_8MHz);
   
   set_tris_a(0);                // Puerto A como salida
   set_tris_b(0b00001111);       // Puerto B<7:4> salida B<3:0> entradas
   
   // Inicio Variables
   anTecla=255;
   Set=0;
   Hora=0;
   Minu=0;
   Segs=0;  
   
   
   do{   
   
      Tecla=0;
      if (input(Tec1))Tecla=1;        // Comprueba que pulsador se activó
      if (input(Tec2))Tecla=2;        // y lo guarda en Tecla
      if (input(Tec3))Tecla=3;
      if (input(Tec4))Tecla=4;        
      
      if (++Tiempo>CuantoTiempo||Tecla!=anTecla) {  // Incrementa el
                              // tiempo transcurrido, Si se pasa de
                              // CuantoTiempo o cambia el estado de
                              // de las teclas con respecto a la
                              // iteracion anterior.
Flag=Nada;                    // Limpia el flag
            
         if(Tecla!=0){                 // Si hay una tecla pulsada
            if(Tecla!=anTecla){        // si la tecla es la primera
                                       // vez que se pulsa.
               CuantoTiempo=100;       // establece el tiempo de
                                       // espera en 100.               
               if(Tecla==1){           // Si se pulsó la tecla 1
                  if(++Set>3) Set=1;   // Avanza el flag para setear
                                       // segs/minu/hora
               }
               if(Tecla==4) Set=0;     // Se pulsó la tecla 4, desactiva
                                       // el modo configuracion
            
            }else{                     // Si no es la primera vez que se
                                       // pulsa la tecla
               CuantoTiempo=30;        // la proxima comprobacion la
                                       // hará en menos tiempo.
            }
            
            if(Tecla==2) Flag=Menos;   // Si la tecla pulsada es la 2
                                       // flag de decremento
            if(Tecla==3) Flag=Mas;     // y si es la 3 flag de incremento
         }
         
         
         if (Set!=0){                  // Si está en modo configuracion
            
            if(Set==1) Tmp=Segs;       // Si se está configurando los
                                       // segundos Segs a temporal
            if(Set==2) Tmp=Minu;       // lo mismo si está configurando
                                       // los minutos
            if(Set==3) Tmp=Hora;       // o la hora
            
            if (Flag==Menos){          // si el flag es de decremento
               if(--Tmp>200)Tmp=59;    // le resta 1 al temporal si es
                                       // menor a 0 pasa a 59
               if(Set==3&&Tmp==59)Tmp=23;// Si está configurando las
                                       // horas el limite es 23
            }
            
            if (Flag==Mas){            // Si el flag es de incremento
               if(++Tmp>59)Tmp=0;      // Incrementa el temporal y si
                                       // se pasa de 59 vuelve a 0
               if(Set==3&&Tmp>23)Tmp=0;// Si configura las horas y
                                       // se pasa de 23 vuelve a 0
            }
            
            if(Set==1) Segs=Tmp;
            if(Set==2) Minu=Tmp;       // Guarda el valor temporal
            if(Set==3) Hora=Tmp;       // en donde corresponda...
         }      
         anTecla=Tecla;                // Almacena la tecla pulsada
                                       // para la próxima 
         Tiempo=0;                     // iteración, Tiempo a 0 para
                                       // volver a contar el tiempo
            
      }
      delay_ms(5);   // Entre iteración e iteración espera 5 ms que
                     // multiplicado por CuantoTiempo, es el tiempo
                     // que tarda en reaccionar a las pulsaciones y
                     // cuanto tarda en avanzar cuando se mantiene
                     // pulsado un botón.
     
   }while(true);
}

Y aquí un video del algoritmo implantado en un pseudo-reloj, en el ejemplo solo incluí la parte de los pulsadores y el display que están controlados con shift register.



Matriz de 8x8 LED controlada con 2 shift register y PIC

Muchas personas experimentan a diario con arrays de 8x8 LED y un porcentaje de esas personas reflejan sus dudas en distintos sitios desperdigados por la red destinados a este mundillo de la electrónica y los microcontroladores.

Precisamente de una duda que se me planteó hace poco por correo electrónico surgió un pequeño proyecto a modo de ejemplo de un array de 8x8 LED; El cual funciona de maravillas tanto simulado como físicamente y es por eso que lo publico en esta ocación.

Dicho proyecto está basado en una serie de artículos anteriores dedicados a los shift register, en otras palabras es una implementación de los registros para usarlos con una matriz de LED.

Aquí presento el diagrama de conexión:


El PIC utilizado en esta ocación es el PIC16F88 por ser muy fácil de implementar y contar con oscilador interno.

La línea Load se conecta al pin B0 del PIC, Clk al B1, dClm a B2 y dLin a B3.

La lista de componentes es mas cuantiosa que variada pues esta compuesta por:
R1-R88 x Resistencias de 220Ω
R9-R168 x Resistencias de 3,9KΩ
Q1-Q88 x Transistores BC547 o similar

2 x 74HC595

64 x LED rojo de 5mm brillo standard

El código, escrito en CCS C, para probar el hardware es el que sigue a continuación, solo he dejado las letras pertinentes a PICROBOT, ya que sino se hace muy largo y repetitivo para mostrarlo como ejemplo, pero desde este link te puedes descargar el código completo con las letras en mayúsculas A-Z, el .HEX, el .COF para simularlo en el ISIS de Proteus y el .DSN con el diseño.

Hay dos versiones del código en este paquete, matriz8x8Q y matriz8x8. La primera es para cuando se usen los transistores a los cátodos de los LED y la segunda si los cátodos van directamente a las salidas del registro de desplazamiento encargado de controlar las columnas.

La única diferencia entre las dos versiones es que la primer versión (Q) no invierte y la segunda si lo hace, las salidas del registro encargado de controlar las filas.

Se podría haber solucionado el problema declarando o no una macro instrucción dirán algunos, después de todo lo único que varía es un caracter de una versión a otra, pero para no confundir, y como este es un ejemplo sencillo, decidí hacerlo así. En un futuro ejemplo de la implementación tal vez incluya una macro instrucción.

/*************************************************************************
**                                                                      **
**    Ejemplo básico para controlar una matriz de 8x8 LEDs con PIC.     **
**                                                                      **
**                      (c) 2010 Gerardo Ariel Ramírez                  **
**                            picblog@hotmail.com                       **
**                       http://picrobot.blogspot.com/                  **
**                                                                      **
**************************************************************************
**                                                                      **
**  Microcontrolador: PIC16F88           Oscilador: Interno - 8 MHz     **
**          Lenguaje: CCS C                                             **
**                                                                      **
*************************************************************************/

#include <16f88.h>      // Tipo de microcontrolador
#fuses INTRC_IO,MCLR    // Oscilador interno, MCLR activo
#fuses NOPUT,NOBROWNOUT // Sin Brownout reset ni Power up timer 
#use fast_io(all)       // La configuración de los puertos solo se hace al principio.
#use delay(clock=8M)    // Velocidad del oscilador interno 8 MHz

#define Load   PIN_B0   // Load (STCP ambos integrados) B0
#define Clk    PIN_B1   // Clock (SHCP ambos integrados) B1
#define dClm   PIN_B2   // Data para las columnas (DS integrado 1) BC2
#define dLin   PIN_B3   // Data para las lineas (DS integrado 2) B3

char  Memoria[96];      // 96 Bytes para la memoria (0 - 95)
char  Visor[8];         // 8 para el visor (8 columnas)

int1  flag;             // Flags de control
int1  flag2;
int   indx;             // Indice donde almacenará las nuevas columnas.
int   line;             // Linea que a mostrar.
int   time;             // Variables para el control de
int   ptime;            // la velocidad de desplazamiento.
int   t;                // Variable auxiliar.

void CargaMem(char Ascii);
void GuardaClm(char c);

#int_rtcc
void isr(){
   int Mul=128;         // Cada vez que ocurre la interrupcion
   if(++line>7)Line=0;  // selecciona la siguiente linea, si se pasa de 7 vuelve a 0.
   
   if(++ptime>5){      // Suma 1 a ptime. Si se pasa de 20
      ptime=0;          // lo pone en 0 y suma 1 a time.
      if(++time>200){   // Si se pasa de 200
         time=0;        // lo pone en 0
         Flag=true;     // y activa el flag.
      }
   }
   
   
   for(t=0;t<8;t++){    // Bucle 0 - 7 (Lineas)
      
      output_bit(dLin,!!(Visor[Line]&Mul));  // dLin es seteado con el valor
                                             // del bit de la fila actual.     
      if (Line==t)output_high(dClm);         // Si Line es igual a t
                                             // activa el bit correspondiente
      else  output_low(dClm);                // a la columna, sino lo desactiva.
      
      output_low(Clk);  // 
      output_high(Clk); // Rota el contenido interno del 74HC595.
      
      Mul>>=1;          // Divide la mascara que compara con Visor[] (128,64,32...)
   }
      output_low(Load);
      output_high(Load);// El contenido interno del integrado pasa a las salidas.
   
}
void main(){
   int k;   
   set_tris_a(0x00);
   set_tris_b(0x00);
   for (k=0;k<8;k++){
      Visor[k]=0;
   }
   for (k=0;k<96;k++){
      Memoria[k]=0;
   }                    // Limpia la memoria y el visor
   
   flag=true;           // Activo el flag para que cargue la memoria
   
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);  // Configuración del Timer0
   enable_interrupts(int_rtcc);              // Interrupcion por Timer0
   enable_interrupts(global);                // Interrupciones globales
   
   do{
      if (Flag){                 // Si el flag está activado
         flag2=true;             // Activa el flag2
         
         for (k=0;k<8;k++){      // Pasa el contenido de las primeras 8
            visor[k]=Memoria[k]; // columnas en memoria al visor
         }
         
         for (k=0;k<95;k++){        // Rota el contenido de toda la memoria
            Memoria[k]=Memoria[k+1];// a la izquierda 1=1+1, 2=2+1, n=n+1...
            
            if (Memoria[k]!=0){Flag2=false;} // Si hay alguna columna que no
                                             // esté vacía desactiva el flag2
         }         
         Memoria[95]=0;             // Limpia la ultima columna de la memoria
        
        if (Flag2){                 // Si flag2 está activo            
            indx=7;                 // a partir de la columna 7 
            CargaMem("PICROBOT");   // escribe PICROBOT            
         }
         Flag=false;                // Desactiva el flag
         
      }
   }while (true);    // Bucle infinito


}

void GuardaClm(char c){
   if (indx<94){
      Memoria[indx]=c;     // Guarda la columna en la ubicación actual de memoria
      indx++;              // y aumenta el indice
   }
}


void CargaMem(char ascii){    // Carga la memoria con el caracter deseado
   switch (ascii){    
      
      case('B'):
      GuardaClm(0b01111111);
      GuardaClm(0b01111111);
      GuardaClm(0b01001001);
      GuardaClm(0b01001001);
      GuardaClm(0b01111111);
      GuardaClm(0b00110110);      
      break;
      
      case('C'):
      GuardaClm(0b00111110);
      GuardaClm(0b01111111);
      GuardaClm(0b01000001);
      GuardaClm(0b01000001);
      GuardaClm(0b01100011);
      GuardaClm(0b00100010);     
      break;

      case('I'):
      GuardaClm(0b01000001);
      GuardaClm(0b01000001);
      GuardaClm(0b01111111);
      GuardaClm(0b01111111);
      GuardaClm(0b01000001);
      GuardaClm(0b01000001);      
      break;  

      case('O'):
      GuardaClm(0b00111110);
      GuardaClm(0b01111111);
      GuardaClm(0b01000001);
      GuardaClm(0b01000001);
      GuardaClm(0b01111111);
      GuardaClm(0b00111110);      
      break;      
      
      case('P'):      
      GuardaClm(0b01111111);
      GuardaClm(0b01111111);
      GuardaClm(0b00001001);
      GuardaClm(0b00001001);
      GuardaClm(0b00001111);
      GuardaClm(0b00000110);      
      break;
      
      case('R'):
      GuardaClm(0b01111111);
      GuardaClm(0b01111111);
      GuardaClm(0b00001001);
      GuardaClm(0b00011001);
      GuardaClm(0b01111111);
      GuardaClm(0b01100110);
      break;
      
      case('T'):
      GuardaClm(0b00000011);
      GuardaClm(0b00000001);
      GuardaClm(0b01111111);
      GuardaClm(0b01111111);
      GuardaClm(0b00000001);
      GuardaClm(0b00000011);
      break;      
   }
      GuardaClm(0b00000000);
}

Controlar 8 displais de 7 segmentos con shift register

Continuando con el tema de los shift register, ofreceré algunas utilidades a dichos integrados utilizando un microcontrolador a modo de ejemplo y totalmente funcionales.

El ejemplo que aquí propongo es la utilización de los registros para controlar 8 displais de 7 segmentos, como ya vimos anteriormente en este artículo, pero con el agregado de la implementación de un teclado; Pero aquí les proporciono el ejemplo completo con el código fuente en CCS C y su respectivo .HEX para que puedan grabarlo en el PIC y probarlo.

He aquí el diagrama de conexión:

El diagrama es bastante sencillo, solo hace falta conectar cada segmento de cada display a las salidas (Q0-Q7) del shift register encargado de "dibujar" los números y el ánodo común de cada display a cada una de las salidas del segundo integrado. También este diseño es válido si los displais son de cátodo común la única diferencia está en que habría que hacer una pequeña modificación en el código.

Para probar el código que les propongo, al diagrama anterior hay que agregarle algunas cosas como por ejemplo 10 pulsadores, 10 diodos 1N4001-7 y 2 resistencias de unos 10KΩ, como se ve en el siguiente diagrama.

Cada ánodo de los diodos se conecta a una salida diferente del shift register encargado de seleccionar el display que se iluminará, de esta forma multiplexámos el teclado de modo que a cada iteración del bucle encargado de encender un display comprobamos una tecla, como esto nos da la posibilidad de conectar solo 8 pulsadores, se agrega otra entrada para comprobar una segunda línea.

A continuación les dejo el código:
/*************************************************************************
**                                                                      **
**             Ejemplo para controlar 8 display de 7 segmentos          **
**              y un teclado con dos shift register y un PIC.           **
**                                                                      **
**                      (c) 2010 Gerardo Ariel Ramírez                  **
**                            picblog@hotmail.com                       **
**                       http://picrobot.blogspot.com/                  **
**                                                                      **
**************************************************************************
**                                                                      **
**  Microcontrolador: PIC16F88           Oscilador: Interno - 8 MHz     **
**          Lenguaje: CCS C                                             **
**                                                                      **
*************************************************************************/

#include <16f88.h>         // Tipo de microcontrolador
#fuses INTRC_IO,MCLR,NOPUT,NOBROWNOUT // Fuses 
#use fast_io(all)      
#use delay(clock=8M)

#define Load      PIN_B0   // Load (STCP ambos integrados) B0
#define Clk       PIN_B1   // Clock (SHCP ambos integrados) B1
#define dDsp      PIN_B2   // Data para seleccionar display (DS integrado 1) B2
#define dSeg      PIN_B3   // Data para activar segmentos (DS integrado 2) B3
#define Teclado1  PIN_B4   // Entrada de teclas 0-7
#define Teclado2  PIN_B5   // Entrada de teclas 8 y 9

#define Seg_A  0x01
#define Seg_B  0x02
#define Seg_C  0x04
#define Seg_D  0x08
#define Seg_E  0x10
#define Seg_F  0x20        // Los bits correspondientes a cada segmento de cada display.
#define Seg_G  0x40        // Estos bits se comparan mediante la máscara Mask.

int Digit[8];
int Numero[10]={
Seg_A + Seg_B + Seg_C + Seg_D + Seg_E + Seg_F,
Seg_B + Seg_C,
Seg_A + Seg_B + Seg_D + Seg_E + Seg_G,
Seg_A + Seg_B + Seg_C + Seg_D + Seg_G,
Seg_B + Seg_C + Seg_F + Seg_G,
Seg_A + Seg_C + Seg_D + Seg_F + Seg_G,
Seg_A + Seg_C + Seg_D + Seg_E + Seg_F + Seg_G,
Seg_A + Seg_B + Seg_C 
Seg_A + Seg_B + Seg_C + Seg_D + Seg_E + Seg_F + Seg_G,
Seg_A + Seg_B + Seg_C + Seg_D + Seg_F + Seg_G
}; // Establece Numero[0-9] con los datos correspondientes a cada segmento que deberá encender.

void CargaMem(char Ascii);
void GuardaClm(char c);

void main(){
   int d;
   int b;
   int Mask;   
   int Tecla;
   int anTecla;
   
   setup_ccp1(CCP_OFF);
   set_tris_a(0);                      // Puerto A como salida
   set_tris_b(0b00110000);             // Puerto B como salida excepto B4:B5 como entradas
   
   setup_oscillator(OSC_8MHZ);   
   
   d=0;
   anTecla=10;
   
  do{      
      Mask=0x80;                       // Carga la mascara, solo el MSB en high                  
      for (b=0;b<8;b++){               // Un bucle de 8 pasos (0-7)
         
         output_bit(dSeg,!(Mask&Numero[Digit[d]]));         // Le envía los bits al shift
                                                            // register encargado de encender
                                                            // los segmentos.
         if (b==d) output_high(dDsp); else output_low(dDsp);// Pone en alto el bit corres-
                                                            // pondiente al display que se va a
                                                            // encender en esta iteración.
         
         Mask>>=1;                     // Divide la mascara por 2 : 128, 64, 32...
         output_low(Clk);              // Low-to-High en el Clk
         output_high(Clk);             // hace que la memoria del shift register rote
      }
      
      output_low(Load);                // Low-to-High en Load hace que la memoria
      output_high(Load);               // del shift register se represente en sus salidas 
      
      if (input(Teclado2)) Tecla=d+9;  // Comprueba que se haya detectado la pulsación en las
      if (input(Teclado1)) Tecla=d+1;  // entradas de teclado y lo guarda en la variable Tecla.
      
      if (++d>7){                      // Incrementa el display a mostrar
                                       // si se pasa de 7 vuelve a 0-         
         if (Tecla!=0 && AnTecla==0){  // Si Tecla es diferente a 0 (se ha pulsado una tecla)
            for (b=7;b>0;b--){
               Digit[b]=Digit[b-1];    // Hace un scroll en los display
            }
            Digit[0]=Tecla-1;          // y guarda la tecla pulsada en el ultimo display.
         }
         AnTecla=Tecla;                // Guarda la tecla pulsada y 
         Tecla=0;                      // pone Tecla a 0 para comprobar la proxima vez.
         d=0;                          // d a 0 para encender el primer display y comenzar
      }                                // otra vez.
   }while (true);                      // Bucle infinito
}

Espero que les haya gustado y lo próximo que les entregaré será un artículo sobre el control de un array de 8x8 LED con el mismo sistema.

Desde este link se puede bajar el código y el .HEX

Grabador de PIC

Cuando la mayoría de la gente está empezando en la programación de PIC, casi siempre opta por comprarse el grabador, y es algo muy razonable ya que si uno no tiene demasiados conocimientos de electrónica y se construye su propio grabador y este tiene algún problema, es mas complicado saber donde está el fallo, si en el grabador, en el PIC, en la programación, etc.

Pero lo que les propongo aquí es la construcción del Programador PIC Pablin II , ya que es muy sencillo y le funciona a la gran mayoría de la gente por no decir que a todos. A este grabador se le conoce un problema y es que a veces no graba al tener poca tensión, yo lo solucioné con las recomendaciones de este artículo Programador PIC Pablin 2 Reloaded de Heli, pero aún así le encontré algunos fallos, no tantos como antes pero si que generaban contratiempos.

Ahora lo que propongo es hacer el mismo circuito, pero obtener 13V exactos a partir de cualquier fuente y para ello utilizaremos dos reguladores de tensión uno de 5V y otro de 8V conectando el GND de este último a la salida del primero.


Lista de componentes:
D1,D2 Diodos 1N4001 al 1N4007 o 1N4148
D3 LED Verde
D4 LED Rojo
R1-R6 Resistencias de 1KΩ
R7, R8 Resistencias de 10KΩ
Q1, Q2 Transistores 2N3904, 2N2222 o BC546 al BC550
IC1 Regulador de voltaje LM7805
IC2 Regulador de voltaje LM7808
IC3 Sextuple Hex inverter 74LS04 o su versión Smith Trigger 74LS14
CN1 Conector para plug de fuente de alimentación
CN2 Conector DB25 Macho
CN3 Tira de pines hembra cortada a 6 pines

El conector del ICSP aunque tenga 6 contactos solo usa 5 para que no se invierta la polaridad por error. En la placa donde se encuentra el PIC a grabar se debe quitar un contacto al conector e introducir un pin macho, soldándolo al PCB y cortarlo al raz y luego al contacto macho del grabador se le debe quitar tal pin para que entre sin problemas.

Estos son los respectivos pinout de los transistores válidos para el diseño:



Mi preferencia particular es hacer un grabador que solamente tenga ICSP (In Circuit Serial Programming) ya que podremos grabar cualquier PIC que este montado en su placa definitiva, y si lo que queremos es grabar el PIC antes de ponerlo en la placa, o la placa definitiva no tiene conector ICSP se puede hacer una placa con los 3 zócalos y agregarle el conector ICSP aparte.

A continuación, el pinout de los respectivos PIC a grabar:

PICPGDPGCVPP5VGND
18 pinesPIN 13PIN 12PIN 4PIN 14PIN 5
28 pinesPIN 28PIN 27PIN 1PIN 20PIN 8 y 19
40 pinesPIN 40PIN 39PIN 1PIN 11 y 32PIN 12 y 31

Personalmente siempre utilicé el programa IC-Prog pero el WinPic800 es notablemente mas rápido; para hacerlo funcionar con cualquiera de los dos programas, se debe configurar como ProPic 2 Programmer.

Generar música con PIC

Antes que nada debo aclarar que no estamos hablando de música con la calidad un CD, ni stereo, ni nada por el estilo, es mas, dudo que tenga alguna calidad, lo digo para no crear falsas expectativas.

En esta ocasión vamos a interpretar alguna melodía utilizando la técnica que vimos en "Generar sonido con PIC" y sólo lo haremos a modo didáctico, ya que es muy útil para comprender como el microcontrolador administra los tiempos y como se utilizan los puertos. Este ejercicio bien podría reemplazar al ya mítico parpadeo de un LED con PIC16F84A ya que es, en teoría, el mismo principio pero con el agregado del control de la frecuencia.

Ahora bien, aquí viene lo mas interesante, investigando un poco me enteré de como es esto de las notas al encontrar este artículo: Frecuencias de las notas musicales , en el que se explica la fórmula para obtener la frecuencia de cada nota musical. Una de las fórmulas, la mas sencilla para llevar a cabo en un programa, es esta:

Con esta fórmula pude sacar las frecuencias para las octavas 0 a la 6, que son las que mejor se ejecutan en el PIC, mas arriba o mas abajo ya es molesto o inaudible. Dichas frecuencias estan en la siguiente tabla:

0123456
DO32,7065,40130,81261,62523,251046,502093,00
DO#34,6469,29138,59277,18554,361108,732217,46
RE36,7073,41146,83293,66587,321174,652349,31
RE#38,8977,78155,56311,12622,251244,502489,01
MI41,2082,40164,81329,62659,251318,512637,02
FA43,6587,30174,61349,22698,451396,912793,82
FA#46,2492,49184,99369,99739,981479,972959,95
SOL48,9997,99195,99391,99783,991567,983135,96
SOL#51,91103,82207,65415,30830,601661,213322,43
LA55,00110,00220,00440,00880,001760,003520,00
LA#58,27116,54233,08466,16932,321864,653729,31
SI61,73123,47246,94493,88987,761975,533951,06
Tabla 1. Frecuencia en Hz de cada nota musical.

En esta otra tabla están representadas los microsegundos necesarios entre estado alto y bajo para generar dichas frecuencias:

0123456
DO15289,027644,513822,251911,12955,56477,78238,89
DO#14430,917215,453607,721803,86901,93450,96225,48
RE13620,976810,483405,241702,62851,31425,65212,82
RE#12856,486428,243214,121607,06803,53401,76200,88
MI12134,906067,453033,721516,86758,43379,21189,60
FA11453,825726,912863,451431,72715,86357,93178,96
FA#10810,975405,482702,741351,37675,68337,84168,92
SOL10204,205102,102551,051275,52637,76318,88159,44
SOL#9631,484815,742407,871203,93601,96300,98150,49
LA9090,904545,452272,721136,36568,18284,09142,04
LA#8580,674290,332145,161072,58536,29268,14134,07
SI8099,074049,532024,761012,38506,19253,09126,54
Tabla 2. Microsegundos de pausa correspondiente a medio periodo de cada nota musical.

Observando la última tabla con atención se ve que la nota de la octava siguiente es igual a la octava actual dividido por 2, o con la equivalencia en CCS C una rotación a la derecha.
De esto se deduce que teniendo un array con los valores de las diferentes notas se puede ir rotando a la derecha tantas veces como octavas queramos subir para obtener la nota y octava precisas.

En C, la función encargada de hacerlo se ve así:

#define Speaker PIN_B0 #define nDO 0 // DO #define nDO_ 1 // DO# #define nRE 2 // RE #define nRE_ 3 // RE# #define nMI 4 // MI #define nFA 5 // FA #define nFA_ 6 // FA# #define nSOL 7 // SOL #define nSOL_ 8 // SOL# #define nLA 9 // LA #define nLA_ 10 // LA# #define nSI 11 // SI int16 FreqNota[12]={ // retardos entre estado alto // y bajo para generar las notas 15289, // DO 14430, // DO# 13620, // RE 12856, // RE# 12134, // MI 11453, // FA 10810, // FA# 10204, // SOL 9631, // SOL# 9090, // LA 8580, // LA# 8099 // SI }; void Play(int nota, int octava, int16 duracion){ int16 fn; int16 mS_Transcurridos=0; // Contadores necesarios // para controlar la duración int16 CiclosL=0; // Contandor de uS fn=FreqNota[nota]; // Define los retardos para generar // la frecuencia de cada nota fn>>=(octava); // Adapta la frecuencia a la octava actual // haciendo una rotación // a la derecha por octava do{ output_high(Speaker); // Genera la frecuancia delay_us(fn); // con los retardos mientras CiclosL+=(fn); // aumenta el contador de // ciclos transcurridos output_low(Speaker); // en dos partes para repartir el delay_us(fn); // trabajo entre estado alto y bajo. CiclosL+=(fn); // CiclosL+=25; // Compensador. while(CiclosL>999){ // Se queda en el bucle mientras CiclosL // sea menor a 1000 (1 mS) CiclosL-=1000; // Le resta 1000 a CiclosL mS_Transcurridos++; // y le suma 1 a mS_Transcurridos. CiclosL+=25; // Compensador. } }while (duracion>mS_Transcurridos); // Repite el bucle hasta que haya // pasado el tiempo indicado. }

Bueno, sabiendo como ejecutar las notas musicales ahora es tiempo de interpretar una melodía.

Como aclaré antes de música no tengo conocimientos, pero buscando alguna melodía a interpretar en el PIC recordé que en BASIC (el antíguo) había una función llamada PLAY y que interpretaba las notas musicales con el PC Speaker. Buscando en Google encontré un artículo titulado DJ QBASIC donde hay diez canciones conocidas. Ahora lo que resta es adaptar el código BASIC a CCS con la función PLAY para C que vimos en la entrada "Generar sonido con PIC", y es cuando vemos otro pequeño inconveniente, las notas musicales en BASIC están en el sistema de notación musical inglés y nosotros usamos el latino, en la WikiPedia encontré este artículo donde hablan de eso y se muestra la equivalencia: Escala musical .

En base a eso hice este ejemplo que interpreta una melodía según se pulse una tecla, si se pulsa la tecla conectada a RB1 suena "Pop Corn", si se pulsa RB2 suena "Ecuador" y si se pulsa RB3 suena "The lion sleep tonight".

/////////////////////////////////////////////////////////////////////// // // // PICMusic v.1.00 // // (c) 2010 Gerardo Ariel Ramírez. // // // /////////////////////////////////////////////////////////////////////// // // // uControlador: PIC16F876A Lenguaje: CCS C // // Xtal: 4MHz // // // /////////////////////////////////////////////////////////////////////// #include <16f876a.h> #use delay(clock=4000000) #use fast_io(all) #fuses HS #FUSES NOPUT #FUSES NOBROWNOUT #define Speaker PIN_B0 #define nDO 0 // DO #define nDO_ 1 // DO# #define nRE 2 // RE #define nRE_ 3 // RE# #define nMI 4 // MI #define nFA 5 // FA #define nFA_ 6 // FA# #define nSOL 7 // SOL #define nSOL_ 8 // SOL# #define nLA 9 // LA #define nLA_ 10 // LA# #define nSI 11 // SI int16 FreqNota[12]={ // retardos entre estado alto // y bajo para generar las notas 15289, // DO 14430, // DO# 13620, // RE 12856, // RE# 12134, // MI 11453, // FA 10810, // FA# 10204, // SOL 9631, // SOL# 9090, // LA 8580, // LA# 8099 // SI }; void Play(int nota,int octava,int16 duracion); void PlayCancion(int cancion); void main(){ set_tris_b(14); // B<3:1>: Pulsadores B0: Speaker while (true){ if(input(PIN_B1))PlayCancion(1); //Si pulso switch 1 toca // Pop Corn if(input(PIN_B2))PlayCancion(2); //Si pulso switch 2 toca // Ecuador if(input(PIN_B3))PlayCancion(3); //Si pulso switch 3 toca // The lion sleep tonight } } void Play(int nota, int octava, int16 duracion){ int16 fn; int16 mS_Transcurridos=0; int16 CiclosL=0; fn=FreqNota[nota]; // Define los retardos para generar // la frecuencia de cada nota fn>>=(octava); // Adapta la frecuencia // a la octava actual do{ output_high(Speaker); // Genera la frecuancia delay_us(fn); // con los retardos mientras CiclosL+=(fn); // aumenta el contador de // ciclos transcurridos output_low(Speaker); // en dos partes para repartir el delay_us(fn); // trabajo entre estado alto y bajo. CiclosL+=(fn); // CiclosL+=25; // Compensador. while(CiclosL>999){ // Se queda en el bucle mientras // CiclosL sea menor a 1000 (1 mS) CiclosL-=1000; // Le resta 1000 a CiclosL mS_Transcurridos++; // y le suma 1 a mS_Transcurridos. CiclosL+=25; // Compensador. } }while (duracion>mS_Transcurridos); // Repite el bucle hasta // que haya pasado el // tiempo indicado. } void PlayCancion(int cancion){ switch (cancion){ case 1: //POP CORN play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL ,4,166); play (nRE_ ,4,166); play (nSOL ,4,166); play (nDO ,4,166); delay_ms (166); play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL ,4,166); play (nRE_ ,4,166); play (nSOL ,4,166); play (nDO ,4,166); delay_ms (166); play (nDO ,5,166); play (nRE ,5,166); play (nRE_ ,5,166); play (nRE ,5,166); play (nRE_ ,5,166); play (nDO ,5,166); play (nRE ,5,166); play (nDO ,5,166); play (nRE ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nLA_ ,4,166); play (nDO ,5,166); play (nSOL_ ,4,166); play (nDO ,5,166); break; case 2: //ECUADOR play (nLA ,3,100); delay_ms (200); play (nMI ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nLA ,3,100); delay_ms (200); play (nMI ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (200); play (nSOL ,3,100); delay_ms (200); play (nMI ,4,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nMI ,4,100); delay_ms (100); play (nRE ,4,100); delay_ms (100); play (nSOL ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (200); play (nLA ,3,100); delay_ms (200); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nDO ,4,100); delay_ms (100); play (nSI ,3,100); delay_ms (100); play (nSOL ,3,100); break; case 3: //The lion sleep tonight play (nDO ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (125); play (nMI ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (250); play (nMI ,3,125); play (nFA ,3,125); delay_ms (250); play (nMI ,3,125); delay_ms (125); play (nRE ,3,125); delay_ms (250); play (nDO ,3,125); delay_ms (250); play (nRE ,3,125); play (nMI ,3,125); delay_ms (250); play (nRE ,3,125); delay_ms (125); play (nDO ,3,125); delay_ms (250); delay_ms (125); play (nMI ,3,125); delay_ms (125); play (nRE ,3,500); break; } }

Si no lo quieres copiar, lo quieres modificar o lo quieres ya compilado, te puedes bajar el proyecto completo haciendo click en este link .

Generar sonido con PIC

En los tiempos que corren estamos acostumbrados a vivir rodeados de tecnología, hoy en día cualquier movil supera en millones de operaciones por segundo al primer ordenador que tuve, hasta un PIC puede funcionar a mas velocidad que un 80286, pero hubo un tiempo en que un teléfono movil no estába al alcance de cualquiera, y en esa época no existían aparatos capaces de brindarnos la experiencia multimedia que hoy damos por sentado de que es así. Y estamos hablando de poco menos de 20 años atras, en esa epoca por lo menos los PC normales, no disponían de una placa de sonido y cualquier juego ejecutaba a duras penas un, a veces, desesperante pitido a modo de música.

Bueno, es por eso que en un arrebato de nostalgia se me ocurrió que podría emular esas "dulces" melodías con un PIC y es por eso que ahora tu estás leyendo esto. Inmediatamente despues me acordé de algo muy importante y es que no tengo ni la mas remota idea de música. Entonces decidí comenzar por el lugar mas obvio, o sea por el principio, fue cuando deduje que para que haya música primero tiene que haber sonido, entonces lo primero que hay que hacer es que el PIC emita algo de sonido controlado, ya habrá tiempo de generar notas musicales.

Primero hay que tener un concepto aunque sea básico de lo que el sonido es, en principio el sonido es un golpe en un medio, en nuestro caso el aire y, dependiendo de la frecuencia de ese "golpe", el sonido producido será mas agudo o mas grave; El oido humano puede percibir sonidos con una frecuencia que está comprendida entre los 20Hz y los 20KHz, en otras palabras que una persona puede escuchar frecuencias de esos golpes desde unas 20 veces por segundo hasta unas 20 mil aproximadamente. Entonces aquí ya tenemos un dato: para generar el sonido con el PIC debemos conectar un altavoz a una salida del microcontrolador y hacer alternar su estado de alto a bajo a una frecuencia determinada. Cuanto mas rápido se varíe el estado mas agudo será el sonido y viceversa.

Para obtener la duración del periodo (estado alto o bajo) para una frecuencia f solo hace falta usar la siguiente formula:

tP = 1
f

Para generar un sonido dentro de los margenes perceptibles, digamos de unos 650Hz realizaremos la siguiente operación:

tP = 1
650
= 0,0015385

El resultado obtenido es el tiempo, en segundos, que debe tardar cada periodo para producir un sonido de 650Hz. En este caso es 1538uS que es la medida que nosotros necesitamos para operar con el PIC, recuerda que un PIC con un cristal corriendo a 4 MHz realiza una operacion cada 1uS.

Cada 1538uS o, lo que es lo mismo, cada 1,538mS se debe producir un cambio de estado del la salida del microcontrolador para producir un sonido a 650Hz, entonces lo que necesitamos ahora es dividir ese valor en 2, una mitad para el estado alto y la otra mitad en estado bajo.

Con todo esto tenemos que: 1538 / 2 = 769, si traducimos esta teoría en lenguaje C quedaría de esta forma:

#include <16f876a.h>          // Tipo de microcontrolador
#use delay(clock=4000000)     // Delay con xtal de 4MHz
#use fast_io(all)             // I/O todas rápidas, de esta forma cuando se
                              // escribe en los puertos, no se configura el tris
                              // acelerando el proceso.  
#fuses HS                     // FUSES cristal HS >=4MHz 
#FUSES NOPUT                  // No power up Timer, no espera a estabilizar la tension 
#FUSES NOBROWNOUT             // No Brown out reset, no se resetea si cae la tension  
#define Speaker   PIN_B0      // Altavoz conectado a RB0  

void main(){
   
   set_tris_b(2); // RB<7:2> Salida RB1 entrada (Pulsador) RB0 Salida (Speaker)
   
   do{                              // Bucle infinito
      
      while(input(PIN_B1)){         // Mientras se presione el
                                    // interruptor conectado a RB1.
                                    
         output_high(Speaker);      // Altavoz encendido
         delay_us(769);             // espera 769 uS
         
         output_low(Speaker);       // Altavoz apagado
         delay_us(769);             // espera 769 uS
      }
      
   }while(true);
}

Este ejemplo lo único que hace es emitir un sonido, ni agudo, ni grave, cuando se activa el pulsador conectado a RB1.

Nuestro PIC acaba de emitir su primer sonido!

Ahora podremos crear una funcion que emita un sonido con la frecuencia y duracion deseados.
El siguiente ejemplo hace sonar una sirena cuando se pulsa un interruptor en RB1.

/////////////////////////////////////////////////////////////////////////////
//                                                                         //
//                            PICSirena 1.00                               //
//                   (c) 2010 Gerardo Ariel Ramírez.                       //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////
//                                                                         //
//       uControlador: PIC16F876A            Lenguaje: CCS C               //
//               Xtal: 4MHz                                                //
//                                                                         //
/////////////////////////////////////////////////////////////////////////////


#include <16f876a.h>          // Tipo de microcontrolador
   
#use delay(clock=4000000)     // Delay con xtal de 4MHz
#use fast_io(all)             // I/O todas rápidas, de esta forma cuando se
                              // escribe en los puertos, no se configura el tris
                              // acelerando el proceso.

#FUSES HS                     // FUSES cristal HS >=4MHz
#FUSES NOPUT                  // No power up Timer, no espera a estabilizar la tension
#FUSES NOBROWNOUT             // No Brown out reset, no se resetea si cae la tension

#define Speaker   PIN_B0      // Altavoz conectado a RB0

void Sound(int16 frecuencia, int16 duracion);

void main(){
   int t;
   set_tris_b(2);
   
   while(true){      //Bucle infinito
   
      if (input(PIN_B1)){                 // Si se activa el pulsador
      
         for (t=0;t<30;t++)               // Bucle ascendente incrementando la
            sound(((int16)t*15)+1600,20); // frecuencia del sonido
         for (t=30;t>0;t--)               // Bucle decrementando
            sound(((int16)t*15)+1600,20); // la frecuencia del sonido
      
      }
   }
}

void Sound(int16 frecuencia, int16 duracion){
   
   int16 mS_Transcurridos=0;
   int16 CiclosL=0;
   int16 uS;
   int32 tmp;
   
   if (frecuencia>=20&&frecuencia<=20000){ //si la frecuancia se encuentra entre
                                           // los margenes de 20Hz y 20 KHz se ejecuta
      tmp=100000;                          // de los contrario no.
      tmp/=frecuencia;           // convierte los Hz a microsegundos para la pausa
      tmp*=5;   
      uS=tmp;
      do{
         output_high(Speaker);   // Genera la frecuancia deseada
         delay_us(uS);           // con los retardos mientras
         CiclosL+=(uS);          // aumenta el contador de ciclos transcurridos
         output_low(Speaker);    // en dos partes para repartir el 
         delay_us(uS);           // trabajo entre estado alto y bajo.
         CiclosL+=(uS);          // 
         CiclosL+=25;            // Compensador.
         
         while(CiclosL>999){     // Se queda en el bucle mientras CiclosL sea
                                 // menor a 1000 (1 mS)
            CiclosL-=1000;       // Le resta 1000 a CiclosL 
            mS_Transcurridos++;  // y le suma 1 a mS_Transcurridos.
            CiclosL+=25;         // Compensador.
         }
      }while (duracion>mS_Transcurridos);// Repite el bucle hasta que haya pasado el
                                         // tiempo indicado.
   }
}

Desde este link te puedes bajar el proyecto y el hex.

Bueno, hasta aquí hemos llegado, ahora resta por hacer que el PIC reproduzca alguna nota musical e interprete alguna canción, pero eso lo veremos en Generar música con PIC.

Librería para el manejo de un módulo LCD en CCS C

Ya vimos Conexión y funciones de un módulo LCD y Control de un módulo LCD con PIC y CCS C, partiendo de esa base podemos seguir adelante y ¿por qué no directamente con ejemplos? Bueno, en esta ocasión les traigo una librería lista para operar con módulos LCD con buses de 4 u 8 bits, y conexión al PIC de modo serial o paralelo. Se trata de una librería que bauticé con el nombre, no muy creativo, de lcdgar.c.
Lo bueno que tiene esta librería es que para configurarla sólo hacen falta definir o no algunas macros, de modo que es bastante flexible y puede usarse de muchas maneras.
Aquí les presento el primer ejemplo, y es la forma mas rápida de configurar lcdgar, ya que es predeterminada, sin definir nada la librería se auto-configura.
#include <16f88.h>         // Tipo de PIC y declaraciones
#use delay(internal=8MHz)  // Usar delay con un reloj interno a 8 MHz
#Fuses INTRC_IO            // Oscilador interno
#Fuses PUT                 // Espera unos mS a que se estabilice la
                           // tensión antes de iniciar.
#Fuses BROWNOUT            // Si la tensión cae por debajo de un límite
                           // reinicia el PIC

// Defino la configuración para que LCDGAR establezca comunicación con
// el módulo LCD. Debe definirse toda la configuración antes
// de llamar a lcdgar.c

// Como se usará la configuración predeterminada no se definirá nada,
// y al llamar a  se autoconfigurará. Si deseamos cambiar
// dicha configuración aquí deberán aparecer todos los cambios.

#include <lcdgar.c>

void main(){

   init_LCD();       // Inicio el Módulo LCD

   locate(7,0);      // ubico el cursor en la columna 7 de la primer 
   print("HOLA");    // línea, escribe HOLA

   locate(6,1);      // ubico el cursor en la columna 6 de la segunda 
   print("MUNDO");   // fila y escribo MUNDO.
   
   do{               // Entra en un bucle infinito
   }while(true);
}
Con este código cargado en el PIC se puede controlar un módulo LCD con bus de 8 bits usando todo el puerto B para la comunicación PIC-LCD, el Pin A0 será el encargado de la señal ENABLE y el Pin A1 el encargado de la señal RS.
Si quisieramos, por ejemplo, en lugar del Puerto B, utilizar el Puerto C como bus de datos, deberíamos definir, antes de llamar a la librería mediante la directiva #INCLUDE, el macro Bus_Puerto_C:
#define Bus_Puerto_C
Ahora bien, se puede usar un shift register para controlar el LCD y de esa forma ahorrarnos pines del microcontrolador, en la entrada SHIFT REGISTER ¿que son y como se usan? puedes ver más sobre dichos integrados. En el caso del PIC16F877A y con sólo el display conectado no hace falta ahorrar, pero cuando se trata de una aplicación donde no nos quede muchos pines libres, es algo muy valioso, ya que pasaremos de requerir 10 pines en modo paralelo a sólo 4 en el modo serial y sólo el Pin que corresponde a la señal ENABLE debe ser exclusivo para el manejo del módulo, de modo que los demás pines podrán ser compartidos, inclusive los pines del shift register se podrían usar para, por ejemplo, controlar un teclado.
Para utilizar la líbreria en modo serial solo hace falta definir el macro "LCD_Serial" del siguiente modo:
#define LCD_Serial
Si queremos usar el bus de datos de 4 bits debemos definir, tambien antes de llamar a la librería, el macro "LCD_4Bits", mediante:
#define LCD_4Bits
Cabe destacar que cuando se utiliza el Bus de 4 bits, conexión paralela y está definido un puerto como bus, el dato se envía completo al puerto usado, pero el display LCD sólo reconoce los 4 MSB o sea <4:7>. Si no se desea escribir en todo el puerto cada vez que se utiliza el display, puede definir los 4 pines que serán los bits del bus al LCD del siguiente modo:
#define Bit0 Pin_X 
#define Bit1 Pin_X 
#define Bit2 Pin_X 
#define Bit3 Pin_X
Aquí puedes bajarte la última versión de la librería actualizada al día 6/11/2010 versión 1.03. Ahora disponible desde Dropbox

NE555

Sin lugar a dudas el NE555 es uno de los integrados mas conocidos por cualquier hobbista de la electrónica y/o robótica que se aprecie, pero para muchos puede resultar un poco complicado comprender que es lo que realmente hace. En este artículo intentaré explicar, de una manera sencilla, no tanto funcionamiento sino la utilidad, que es lo que realmente a la mayoría de los lectores les importa.

El paso obligado a la hora en empezar a comprender el funcionamiento de cualquier integrado creo que es revisar su datasheet y por eso aquí se los dejo para que lo puedan revisar, aunque como este artículo está basado en la información que ahí figura puede salteárselo el que no entienda mucho de inglés o no sepa todavía leerlos bien. Bueno, para el que no se lo leyó (y para el que se lo leyó también) en la Figura 1 se observa el patillaje o pinout del integrado en cuestión.

Figura 1. Pinout del IC NE555.

Este integrado normalmente se define como un timer, que vendría a ser algo así como un contador, pero también se lo podría definir como un oscilador. Se sabe que un oscilador es un componente capaz de marcar el tiempo, y la forma que tiene nuestro integrado de marcarlo es por medio del paso de alto a bajo del pin salida (Output) a una determinada frecuencia.

La velocidad a la que trabaje nuestro integrado dependerá de la conexión que se le haga a los componentes externos, mas concretamente a las resistencias y capacitores que se elijan. En los ejemplos que daré a continuación se explica las dos configuraciones posibles, monoestable, que es cuando el IC trabaja como un delay, y astable, que es cuando trabaja en modo oscilador o multivibrador. Las dos modalidades se basan en un principio muy fácil de entender, la frecuencia con que oscila el integrado dependerá exclusivamente de la velocidad con que se cargue y descargue el capacitor que se le conecte. Cuando el capacitor se cargue, la salida estará en nivel alto y cuando se descargue en bajo. Pero la principal diferencia entre uno y otro es la utilización del trigger.
El primer circuito que les presento (Figura 2) es para configurar el integrado como monoestable, que significa que solo en un estado, el tiempo es estable, donde se ve que el pin 2 (Trigger) queda libre para conectarlo a una señal de control, cuando dicha señal se activa, la salida (pin 3) se mantiene un tiempo determinado en estado alto para pasar luego a estado bajo hasta un nuevo disparo del pin 2.

Figura 2. Configuración monoestable.
 
El pin 4 (Activación o Reset) se puede conectar directamente a un microcontrolador para activar/desactivar la salida de la señal, esto no es tan necesario en este modo ya que el circuito queda a la espera de una pulso bajo en el pin 2 para activar la salida, pero en el modo astable el uso de activación es fundamental para activarlo cuando se desee ya que de lo contrario en la salida estaría permanentemente la señal presente, pero eso lo veremos mas adelante. Ahora bien ya sabemos como es la conexión en teoría, del integrado, pero falta algo fundamental a la hora de armar el circuito, los valores de cada componente, estos varían dependiendo de la frecuencia que queremos obtener como se habló anteriormente. Para saber que valores usaremos, primero debemos comprender el funcionamiento del circuito monoestable.

En la Figura 3 se ve una gráfica de los valores de la salida dependiendo el estado del trigger a cada momento:

Salida respecto a Trigger Figura 3. Comportamiento del pin 3 (Output) respecto al pin 2 (Trigger) en configuración monostable.
 
Como se observa a simple vista la salida pasa a estado alto durante un cierto tiempo cada vez que se pasa de alto a bajo el pin 2. Mientras el integrado no perciba ese cambio de estado, la salida permanece a estado bajo. El tiempo que la salida permanecerá en estado alto viene determinado por la fórmula tH = 1.1 R1 C1. 

Este circuito es aplicable, por ejemplo, si queremos hacer un circuito capaz de funcionar un determinado tiempo luego de activar un pulsador. Pero como ya se dijo hay otro modo de usar el integrado y es el astable. La configuración astable se utiliza mucho en robótica ya que es totalmente válida para modular la frecuencia con que parpadea un LED infrarrojo o un altavoz, por ejemplo. Se sabe que fuentes de luz como el sol o un foco incandescente producen luz infrarroja y esta puede causar errores de lectura con fototransistores, para evitar esto, la luz del emisor es modulada y por medio de un decodificador en la recepción, la frecuencia es interpretada para obtener una detección segura.

Pero esta modulación significa alternar estados altos y bajos a un ritmo muy alto, y como se ve en el modo monoestable, solo podremos asegurar el tiempo del estado en alto, para poder crear una onda que alterne entre un estado y otro, necesitaremos configurar el NE555 en modo astable. Y para ello deberemos conectarlo como se ve en la Figura 4; Para saber a que frecuencia oscila, deberemos aplicar las fórmulas de la Tabla 1.

Figura 4. Conexión del IC NE555 en modo astable.

C1 Salida (Pin 3) Fórmula
Carga Alto t1 = 0,693 (R1 + R2) C1
Descarga Bajo t2 = 0,693 R2 C1
Tabla 1. Fórmulas para calcular los tiempos del IC NE555 en configuración astable.

El diagrama y las fórmulas nos sirven también para comprender un poco el funcionamiento del circuito. De todo esto se deduce que la corriente pasa por R1, luego por R2 cargando C1, si hojeamos el datasheet verificamos que cuando C1 se carga con 2/3 de Vcc se dispara el pin 7 (descarga - discharge) poniéndose a GND, lo que hace descargar el capacitor (de ahí su nombre) hasta 1/3 de Vcc que es cuando el pin 7 vuelve a ponerse en alta impedancia, abriendo el circuito, volviendo a cargar C1 e iniciando la secuencia nuevamente.

Ahora bien, con estos datos se ve que siempre la carga es mas lenta que la descarga, ya que la carga se hace por medio de R1 + R2 y la descarga solo con R2, lo que nos impide hacer que el circuito oscile con un duty cycle del 50%, es decir que tarde lo mismo estando a nivel bajo que en alto. Para lograrlo necesitaremos impedir que la corriente circule por R2 cuando C1 se esté cargando, ¿Cómo se hace eso? Fácil, con un diodo ubicado en paralelo a R2, nuestro archifamoso compañero, el 1N4007 (o similar) nos servirá de maravilla.

En la Figura 5 se observa como quedaría el circuito para logran un duty cycle del 50% (siempre y cuando R1 sea igual a R2).

Figura 5. Circuito para lograr que el IC NE555 oscile con un duty cycle del 50%.

Y en la tabla de fórmulas lo único que varía es que, para el cálculo de la carga, no interviene R2, por lo tanto quedaría de esta forma:

C1 Salida (Pin 3) Fórmula
Carga Alto t1 = 0,693 R1 C1
Descarga Bajo t2 = 0,693 R2 C1
Tabla 2. Fórmulas para calcular los tiempos del IC NE555 con un duty cycle del 50%.

Como se observa en el diagrama de la Figura 5, para realizar la carga, la corriente pasa por R1 y luego al tener menos resistencia por D1 "corta camino" (el camino de menor resistencia) y pasa directamente a C1. En la descarga D1 impide el paso de corriente en esa dirección lo que provoca que pase por medio de R2 por lo que si las dos resistencias son del mismo valor tardará lo mismo la descarga que la carga (en teoría, ya que la realidad nunca es igual a la teoría, pero para la práctica el error es mínimo).

Bueno, eso es todo por el momento, espero que les haya servido de ayuda y recuerden que esto es a modo de apunte, cualquier corrección o duda que tengan coméntenla y será contestada con la mayor brevedad posible. También invito al público a participar haciendo sus consultas, sugerencias y/o pedidos, por los medios pertinentes, toda colaboración será bienvenida, y no olviden que aquí todo el que quiera puede aprender y/o enseñar algo...

Reguladores de voltaje 7805

La familia 78xx y 79xx son una gama de integrados dedicados a la regulación de voltaje, hay muchas variables: regulables, fijos, de tensión positiva o negativa...

Pero el mas común, y el que mayormente usaremos en el mundo de los PICs, es el famoso 7805, que es un regulador de tensión positiva de 5 Volts a 1A, la tensión justa y mucho mas corriente de la que necitan nuestros PICs para funcionar. Se sabe que el buen funcionamiento del firmware que grabemos en el PIC está sujeto, no sólo a la buena programación que hayamos hecho a la hora de diseñarlo, sino que también a una alimentación fija, constante y regulada a la hora de ejecutarlo. Entonces la manera mas segura, económica y sencilla de obtener ese voltaje, es la utilización de un integrado regulador de voltaje, y el 7805 es uno de los mas indicados ya que mantendrá fija la tensión en 5V, siempre y cuando en su entrada reciba al menos 6V. Por lo tanto a la entrada podremos despreocuparnos de la alimentación superando por mucho el voltaje de trabajo del PIC.

Para trabajar con baterías sólo basta con conectar la entrada del IC (PIN 1) al terminal positivo de la misma y el común (PIN 2) al negativo, a la salida tenemos 5V que es la tensión de trabajo del microcontrolador, podremos añadir un capacitor entre GND y la salida, como se aprecia en la Figura 1, para eliminar cualquier fluctuación de voltaje que pueda ocurrir, pero esto es siempre recomendable hacerlo con el microcontrolador independientemente del origen que tenga la alimentación.


Figura1. Conexión de un IC de la familia 78xx a una batería.

Si al IC lo usaremos para regular la tensión proveniente de una fuente de alimentación, el filtrado debe hacerse más concienzudamente, a parte del capacitor luego de la regulación, necesitará dos mas antes, en el diagrama de la figura 2 se ve el circuito para conectarlo a una fuente de alimentación regulada o estabilizada de mas de 5 V.



Figura 2. IC 7805 conectado a una fuente de alimentación regulada o estabilizada de una tensión superior.

Para hacer una fuente completa que se conecte a 220V se necesita agregar un transformador de corriente alterna y rectificar la tensión saliente para convertirla en continua y poder acoplarla al circuito antes visto, todo esto se aprecia en la Figura 3.



Figura 3. Conexión de IC a un transformador de 220V:12V.

Obsérvese que lo único que se añadió fue el transformador para obtener 12V de corriente alterna y 4 diodos que la convierten en corriente continua.

Para terminar basta con aclarar que los capacitores C1 y C2 deben ser no polarizados de .1uF y el C3 polarizado de 1000uF y al menos 16V para soportar los 12V que entrega el transformador, no está de mas aclarar que la tensión sube un poco al rectificarla y no es conveniente que los capacitores operen al límite. Los diodos pueden ser 1N4001 al 1N4007.

Los diagramas de conexión son válidos para cualquier integrado de la familia 78xx, por ser de tensión positiva, en el ejemplo de la figura 3 se necesita un transformador que entregue al menos 1V mas de lo que regule el integrado y nunca debe superar los 35V ya que es la máxima tensión que soportan los reguladores 78xx y el C3 debe ir acorde a la tensión de entrada del IC.

Atención: cuando trabaje con transformadores de tensión debe tener especial cuidado por riesgo de electrocución, ya que se está trabajando con 220V y debe utilizar el respectivo fusible de protección.

Puente H

Navegando por uno y otro lado, siempre me encuentro con el dilema de la gente a la hora de controlar un motor con un microcontrolador. Hay muchas soluciones en forma de integrados, pero estos son algo limitados y, hasta en algunos casos, demasiado costosos o difíciles de conseguir.

La mejor solución, sin lugar a dudas, es utilizar un puente H, pero con este tema surge otro problema en la gente, ¿como funcionan?, ¿como se arman?, ¿cuál es mejor?... para responder a todas esas preguntas, trataré de explicar aquí de una forma sencilla el funcionamiento de estos.

Se sabe que en un motor de corriente continua, el sentido de giro viene dado por la conexión que se le haga a sus bornes. Entonces para hacer que un robot vaya hacia adelante o hacia atrás, bastará con invertir la polaridad del motor que mueva sus ruedas, el problema es como hacer esto, el PIC solo puede suministrar 5v y 20mA por pin totalmente insuficiente para mover un motor, lo que se intenta lograr es poder controlar una tensión y una corriente superior que la que el integrado pueda darnos por medio de una señal que será enviada por el PIC.

En definitiva, el encargado de controlar esa tensión será el Puente H y el PIC controlará a este último.

Figura 1. Motor de corriente continua conectado a dos interruptores inversores.

Cuando conectamos un motor como en el gráfico de la figura 1, girará en un sentido u otro, o se parará según la combinación de los interruptores, el puente H hace lo mismo con la diferencia de que el control se realizará digitalmente y los interruptores mecánicos se reemplazarán por transistores PNP y NPN. De modo que se puede controlar el motor de una forma muy precisa, y con cambios giro/parada a una velocidad muy elevada, pudiendo controlar la velocidad de giro por una señal PWM (Pulse Width Modulation o Modulación por Ancho de Pulsos).

Figura 2. Puente en H. 

Si se compara la imagen de la figura 1 con el de la figura 2 se observa que es muy similar, puesto que los interruptores de la primera están reemplazados por un par de transistores, cada uno, en la segunda.

Buscando entre los datasheets encontramos el BD677 (NPN) y BD678 (PNP), que son unos transistores complementarios capaces de soportar 60V y 4A. Suficiente para mover cualquier robot de medianas dimensiones.

Entonces sabiendo esto podremos crear nuestro puente H del diagrama utilizando estos componentes:

Q1,Q2Transistores 2N2222
Q3,Q4Transistores BD678
Q5,Q6Transistores BD677
D1,D2,D3,D4Diodos 1N4007
R1,R2Resistores de 470Ω
R3,R4Resistores de 22Ω

Dos puentes H como el aquí mostrado son los encargados de mover al robot SiLMuP, que a su vez son controlados por señales PWM enviadas por un PIC16F876.

Aquí les dejo un vídeo con la demostración del Puente H funcionando, debido a la reductora del motor (es un potenciómetro con motor de equipo de audio) el movimiento es lento, pero el Puente H es apto para mover motores relativamente potentes, hasta 60V y 4A.

En el disipador hay un regulador de voltaje LM7805 y solo se incluyó en el circuito para suministrar la señal de activación avance/retroceso, se podría haber activado con la misma tensión que alimenta al Puente H pero lo incluí para demostrar que el Puente H puede ser perfectamente controlado con un PIC. También sería recomendable, en las enatradas de avance/retroceso, ponerle resistencias pull-down para que no se activen por alguna interferencia.

Controlar display de 7 segmentos con shift register

Ya vimos lo que es y como se controla un shift register, ahora veremos una de las tantas utilidades que tiene. Aquí se explicará como utilizar un shift register para controlar un display de 7 segmentos.

Para el que todavía no lo conozca, un display de 7 segmentos es un panel conformado por 7 LED que comparten entre si sus ánodos o cátodos según si sean de ánodo o cátodo común respectivamente.

A un display de ánodo común deberemos aplicar tensión positiva al pin compartido y GND a cada uno de los pines correspondientes a los segmentos que se quieran iluminar, para un display de cátodo común deberemos invertir la polaridad. La mayoría de displais poseen también un octavo segmento que normalmente es un punto o dos.

Hasta aquí una pequeña introducción para conocer un poco su funcionamiento, ahora bien, vimos que un shift register serial-paralelo “memoriza” el estado (0 o 1) de un pin (entrada) y va rotando esos valores por cada pulso de reloj para representar la secuencia completa, cuando se activa el pin Load, en unos pines asignados que tiene el integrado a tal fin.

Si a cada pin de salida (Q0-Q7) del integrado le conectamos un segmento del display, como en la figura 1, logramos encender y apagar dichos segmentos independientes unos de los otros. La principal utilidad es representar un número del 0 al 9 o una letra de la A a la F.

La ventaja de usar el shift register es que sólo necesitaremos 3 pines del microcontrolador: Clock (Amarillo), Data out (Verde) y Load (Rojo).


Figura 1. Conexión de un display de 7 segmentos a un shift register.

Si en lugar de emplear un solo integrado usamos dos (Figura 2), podremos multiplexar los datos. Utilizando el segundo integrado para seleccionar el display que deseamos encender en determinado momento, aumentaremos el numero de displais de 1 a 8 con un integrado mas.

8 display de 7 segmentos conectados a 2 shift register
Figura 2. Conexión de 8 display de 7 segmentos a 2 shift register.

Si encendemos un display, lo apagamos y luego encendemos otro, y hacemos todo esto de manera muy rápida el cerebro humano lo interpreta como si en realidad todos los display permanecieran encendidos al mismo tiempo. Jugando con esto podemos ahorrarnos muchísimos pines de nuestro PIC, por ejemplo si quisiéramos hacer un reloj que indique la hora, los minutos y los segundos, y conectáramos cada segmento a cada pin de un microcontrolador necesitaríamos uno con 42 pines solo para los displais.

Sin shift register también se puede multiplexar, pero aun así utilizaríamos demasiados pines, 7 para los segmentos y 1 para cada display, para diseñar el mismo reloj del ejemplo anterior necesitaríamos un total de 13 pines contra los 4 que se necesitan implementando los registros de desplazamiento.

Para usar los dos integrados, se conectan siguiendo el diagrama de la figura 2, el segmento A de todos los displais con la Q0 del primer integrado, los segmentos B con el Q1, los C con Q2… y así sucesivamente. La salida Q0 del segundo integrado se conecta al común del primer display, el pin Q1 al segundo, el Q2 al tercero… así hasta un máximo de 8 displais.

Para controlarlo enviaremos los bits que conforman el caracter del primer display al primer integrado y el bit correspondiente a dicho display en 0, a cada pulso de reloj, cuando hayamos enviado todos los bits se manda el pulso Load y es cuando aparece el caracter en el primer display, luego se repite la operación pero con el bit del segundo display en 0, de esa forma, y si los displais son de cátodo común, encenderán de a uno. A una alta frecuencia esto crea la ilusión de estar todos encendidos.

Utilizando dos shift registers y 4 pines del microcontrolador tendremos capacidad para controlar 8 displais, y si utilizáramos un integrado y un pin del microcontrolador más, aumentaríamos la cantidad de displais a 16.

Tal vez le interese: