Mostrando entradas con la etiqueta 74xx595. Mostrar todas las entradas
Mostrando entradas con la etiqueta 74xx595. 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.

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 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.

SHIFT REGISTER ¿que son y cómo se usan?

Un registro de desplazamiento (shift register en inglés), es un integrado capaz de almacenar bits y presentarlos en sus pines.

Hay varios tipos pero los que aquí nos interesan son los del tipo Serial-Paralelo y Paralelo-Serial, esto significa que en el primer caso los bits "entran" en forma serial (uno a uno) y "salen" de forma paralela, en el segundo caso entrar en paralelo (todos juntos) y salen en serie.

Unos de los integrados que hacen esto, entre muchos otros, son el 74HC595 y el 74HC165, que son Serial/Paralelo y Paralelo/Serial respectivamente.

El pinout del 74HC595 es el siguiente:

Los pines marcados como Q0-Q7 son salidas y reflejan el estado interno de cada bit cuando es activado poniendo a nivel alto el pin 12 (STCP), los datos ingresan de forma serial por el pin 14(DS) cada vez que el pin SHCP pasa de estado bajo a alto ( de 0v a 5v).

También se pueden enlazar varios integrados iguales de modo que ampliamos la cantidad de bits. para ello agregamos un segundo integrado y conectamos la patilla DS a la patilla Q7' del primero.

La secuencia seria la siguiente:
1.Se pone el pin DS en el estado del bit que se quiera ingresar
2.Pin SHCP en bajo
3.Pin SHCP en alto
4.Se repite el proceso hasta enviar los 8 bits
5.Se coloca el pin STCP en bajo
6.Se coloca el pin STCP en alto

y de esa forma aparece el byte en las salidas.

Pinout del 74HC165:

De manera similar funciona el 74HC165 solo que a los bit los "lee" todos juntos.

Aquí las entradas son D0 a D7 y la salida es Q7, PL es el Load y cuando pasa a estado bajo carga los valores de las patas D0-D7 en "memoria" y dandole pulsos altos y bajos a CP los datos van saliendo bit a bit.

Para encadenar varios basta con conectar Q7 de un integrado con DS del siguiente y leer la pata Q7 del último.

Este es el diagrama de conexión para leer 16 bits (2bytes) con dos integrados enlazados:

La forma de proceder sería asi:
Se pone en bajo el Load para tomar el estado de todas las entradas (b0 a b15) luego se envia la señal de reloj poniendo en bajo y luego en alto Clk y se lee el estado de DI (Data-In). Recordar que en DI aparecerá primero el bit mas significativo (MSB).

Esta técnica es válida para controlar un display LCD, o multiplexar cualquier dato.

Aquí esta el código en CSS C:
#include <16f84a.h> 

#define  Clk    Pin_A0 
#define  Load   Pin_A1 
#define  DI     Pin_A2 

#use     fast_io(a) 
#use     fast_io(b) 
 
int Leer_Shift (void); 
 
void main(void){

   set_tris_A(0b10100);
   set_tris_B(0b00000000);

   do{
      if (input(pin_a4)==true) output_b(leer_shift());
   }while(true);

}
int Leer_Shift (void){
   int t;
   int Lectura=0;
   output_low (Load);      // Pongo a low Load para "cargar" el estado de las
   output_high(Load);      // entradas y paso a high para poder leerlas.     

   for(t=0;t<=7;++t){      // Hago un ciclo de 8 pasos (0 - 7) 

      Lectura<<=1;         // Roto los bits de la variable temporal
      Lectura+=input(Di);  // y le sumo el valor del ultimo bit leido      

      output_low(Clk);     // La transicion low-to-high hace que la      
      output_high(Clk);    // memoria interna del integrado rote.
   }
   return Lectura;
}

En el ejemplo, Clock se conectaría al pin 0, Load al pin 1 y DI al pin 2 del Puerto A. Pero este programa solo leerá de b8 a b15, para leer b0 a b15 se deberá usar Int de 16 bits para guardar los datos o dos de 8 bits y guardarlos en variables diferentes. Aparte de todo esto se deberá hacer un bucle de 16 ciclos en lugar de los 8 para leer 1 byte.

Con este circuito el único pin exclusivo para el funcionamiento del registro es el pin A2 (Data in) ya que los otros se pueden conectar a otros circuitos sin que afecten a este.

SiLMuP (todavía en construcción) utiliza un 74HC595 para controlar el display LCD y por el mismo bus controlar las teclas de función y un 74HC165 para leer el estado de los sensores de líneas.

De esta forma controla un display LCD, 4 teclas de función, 8 sensores siguelineas, y proximamente mas sensores y bumpers para obstaculos con 6 pines del pic y solo 3 son exclusivos.

Tal vez le interese: