Mostrando entradas con la etiqueta Registro de desplazamiento. Mostrar todas las entradas
Mostrando entradas con la etiqueta Registro de desplazamiento. 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.

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

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

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: