Bueno, vamos a hacer el primer Post de aporte en este subforo, espero que no haya se haya publicado antes, aunque recuerdo haber leido por ahí informacion al menos a nivel teorico sobre como hacerlo.
Primeramente, con que trabajo yo:
Para poder recrear este juguete necesitaran proteus (me imagino que todos lo tendran) y si quieren recompilar codigo / modificar necesitaran el compilador Hitec - picc
Bueno, vamos un poco explicando paso a paso.
El Lcd se puede controlar a travez de un bus de datos de 8 bits, mas dos señales de control (en realidad tres pero casi nunca usaras la tercera por que muy rara vez querrás saber que escribiste en el LCD), de las cuales esas dos mas señales corresponden a la linea de clock, y a la linea que le indica al LCD que estamos enviando caracteres que debe imprimir en su pantalla.
Por lo tanto, podemos reemplazar esos 8 bits de datos por un registro de desplazamiento de 8 bits de salida (ojo que tambien hay de 4), y permita introducir los datos de forma serie, por lo tanto tendríamos reducido el bus de 8 bits a simplemente la linea de dato y la linea de clock, pero DEL REGISTRO DE DESPLAZAMIENTO.
Por lo tanto contando tenemos 2 pines para el registro de desplazamiento + 2 pines para el LCD, pero eso suman 4, cierto?
Pues bien la magia aqui consiste en saber que el LCD NO HACE NADA SI NO LE ENVIAS LA SEÑAL DE CLOCK, AL IGUAL QUE EL REGISTRO DE DESPLAZAMIENTO, y que quiero decir con esto?
que podemos utilizar el pin que genera la señal del dato en la entrada del registro de desplazamiento, para utilizarla como el bit "RW" del LCD (el pin encargado de discriminar entre instrucciones y datos).
Con lo cual tendriamos solo 3 pines para controlar un LCD, a nivel teorico (aun), el problema ahora es hacer que todo calze, y funcione correctamente, pero eso ya está hecho, por lo tanto aqui viene la parte util, el codigo y la simulacion:
La libreria: (funciones_LCD_serial.h)
//NO INCLUYE LA FUNCION DELAY
void convertir_lcd(unsigned long number);
void limpiar_lcd(void);
void setup_lcd(void);
void enable(void);
void escribir_string_lcd(const char *data);
void posicion_lcd(char x, char y);
void transmicion_serie(char letra);
void clock(void);
void escribir_lcd(char data);
#ifndef tiempo_enable //si no ha sido definido se utiliza tiempo para
#define tiempo_enable 2 //funciones delay(2) = 2uS (el doble por seguridad)
#endif // (reloj a 4mhz)
#ifndef pin_data
#define pin_data RA7
#endif
#ifndef pin_clock
#define pin_clock RA6
#endif
#ifndef EN
#define EN RB1
#endif
void convertir_lcd(unsigned long number)
{
unsigned long potencia;
potencia = 1;
while(potencia*10 <= number)
{
potencia = potencia*10;
}
while(potencia != 1)
{
transmicion_serie((number / potencia)+48);
pin_data = 1;
enable();
delay(tiempo_enable*41);
number = number - ((number/potencia)*potencia);
potencia = potencia/10;
}
transmicion_serie(number+48);
pin_data = 1;
enable();
delay(tiempo_enable * 41);
pin_data = 0;
}
void limpiar_lcd(void) //clear & return home
{
transmicion_serie(1);
pin_data = 0;
enable();
delay(tiempo_enable * 1520);
}
void posicion_lcd(char x, char y)
{
switch(x)
{
case 0: transmicion_serie((0x80 + y));
break;
case 1: transmicion_serie((0xC0 + y));
break;
case 2: transmicion_serie((0x94 + y));
break;
case 3: transmicion_serie((0xD4 + y));
break;
}
pin_data = 0;
enable();
delay(tiempo_enable * 41);
}
void escribir_string_lcd(const char *data)
{
while(*data != 0)
{
transmicion_serie(*data++);
pin_data = 1;
enable();
delay(tiempo_enable * 41);
}
}
void setup_lcd(void)
{
limpiar_lcd();
transmicion_serie(0b1100); // 1, display, underscore, blink
pin_data = 0;
enable();
delay(tiempo_enable * 1520);
transmicion_serie(0b111000);
pin_data = 0;
enable();
delay(tiempo_enable * 41);
}
void enable(void)
{
EN = 1;
delay(tiempo_enable);
EN = 0;
delay(tiempo_enable);
}
void transmicion_serie(char letra)
{
pin_data = (letra & 0b10000000)>>7;
clock();
pin_data = (letra & 0b1000000)>>6;
clock();
pin_data = (letra & 0b100000)>>5;
clock();
pin_data = (letra & 0b10000)>>4;
clock();
pin_data = (letra & 0b1000)>>3;
clock();
pin_data = (letra & 0b100)>>2;
clock();
pin_data = (letra & 0b10)>>1;
clock();
pin_data = (letra & 0b1);
clock();
}
void clock(void)
{
pin_clock = 1;
delay(tiempo_enable*10);
pin_clock = 0;
delay(tiempo_enable*10);
}
void escribir_lcd(char data)
{
transmicion_serie(data);
pin_data = 1;
enable();
delay(tiempo_enable * 41);
}
Y por ultimo el codigo del programa (bastante simple)
#include <pic.h>
void delay(unsigned int tiempo); // 1 = 1us
void setup(void);
#include <funciones_lcd_serial.h>
main()
{
setup();
setup_lcd();
while(1)
{
limpiar_lcd();
escribir_string_lcd("Prueba de libreria");
posicion_lcd(0,1);
escribir_string_lcd("LCD Serial v1");
while(1)
{
posicion_lcd(0,3);
escribir_string_lcd("Funcionando. ");
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
posicion_lcd(0,3);
escribir_string_lcd("Funcionando.. ");
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
posicion_lcd(0,3);
escribir_string_lcd("Funcionando...");
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
delay(0xFFFF);
}
}
}
void setup(void)
{
OSCCON = 0b01101100;
T1CON = 0b00000100;
TRISA = 0b00000011;
PORTA = 0x0;
ANSEL = 0b11;
ADCON0 = 0b01001001;
ADCON1 = 0b10000000;
TRISB = 0b100;
PORTB = 0x0;
}
void delay(unsigned int tiempo)
{
tiempo = 0xFFFF - tiempo;
TMR1H = tiempo >>8;
TMR1L = (tiempo & 0xFF);
TMR1ON = 1;
while(!TMR1IF);
TMR1IF = 0;
}
Cualquier consulta duda o sugerencia hacerla, con animos les respondo.
PD: Si no se dieron cuenta, los tiempos estan calculados a partir del tiempo de "enable" del LCD, asi solo definimos un tiempo (el cual por defecto si no se define es 1)
PD2: FIJENSE QUE EL LCD ES DE 20 X 4, NO DE 16 X 4, ASI QUE DEBEN CORREGIR LOS VALORES EN LA FUNCION "posicion_lcd" PARA QUE SE AJUSTE CORRECTAMENTE, SI UTILIZAN UNO DE 16 X 4
Deben corregirlo asi:
switch(y)
{
case 0: transmicion_serie((0x80 + x));
break;
case 1: transmicion_serie((0xC0 + x));
break;
case 2: transmicion_serie((0x90 + x));
break;
case 3: transmicion_serie((0xD0 + x));
break;
}