Documentos de Académico
Documentos de Profesional
Documentos de Cultura
LCD
David Alejandro Burbano - 2166402
Jhonathan Stiven Castañeda – 2160859
Departamento de automática y electrónica, Universidad Autónoma de Occidente, Santiago de
Cali, Colombia
david.burbano@uao.edu.co
jhonathan.castaneda@uao.edu.co
I. ELEMENTOS UTILIZADOS
A continuación se exponen los elementos utilizados en la implementación de la práctica
a. Arduino mega
Se utilizó la placa arduino mega para programar su procesador ATmega2560, en
primer lugar por la facilidad a la hora de conectar los pines del procesador mediante los
headers hembra de la placa a cables externos o jumpers; En segundo lugar, por la facilidad
a la hora de programar el procesador desde el software Atmel Studio 7.0 desde un
ordenador utilizando el puerto USB.
c. Otros elementos.
Además del arduino y la pantalla LCD que son los componentes principales de esta
práctica se utilizaron:
Elemento Cantidad Uso
Potenciómetro de 250kΩ 1 Implementación de divisor de tensión de pueba para
generar la tensión de entrada al adc al procesador.
2. METODOLOGÍA
A continuación se muestra el código implementado en el Atmel Studio 7.0 para la práctica;
Se va describiendo a detalle su funcionamiento y las configuraciones que se hacen para el
LCD y el adc del procesador. El código se anexa en su archivo de Atmel Studio 7.0 al presente
documento para una mejor apreciación y por su extensión.
BY: D. BURBANO, J. CASTAÑEDA
*/
//**********************************************************************************
// (1) Para uso de puertos I/O del microprocesador
/* (2) Se define la frecuencia del procesador como 8MHz (con base en esto se
hacen los cálculos para la precarga del timer1 y el periodo de muestreo
de 10ms del adc*/
// (3) Para definir los vectores de intrrupción utilizados
/* (4) Para los retardos de tiempo necesarios al configurar la pantala LCD
(retardos necesarios para los pulsos enviados al configurar el LCD y
ejecutar comandos en él o escribir datos en ASCII*/
//**********************************************************************************
//DEFINE FUNCTIONS-----------------------------------------------------------------
void ADC_CONFIG(); //CONFIGURACIÓN INICIAL DEL ADC
void LCD_CONFIG(); //CONFIGURACIÓN INICIAL DEL LCD
void LCD_INIT(); //SECUENCIA DE COMANDOS Y CARGA INICIAL DE DATOS DEL LCD
void UPDATE_LCD(); //ACTUALIZAR LCD CON VALORES DE TENSIÓN ENTERO Y DECIMAL MEDIDO
uint8_t dato_intH; //ALMACENA EL BYTE ALTO DEL VALOR DE TENSIÓN MEDIDO POR EL
ADC
uint8_t dato_intL; //ALMACENA EL BYTE BAJO DEL VALOR DE TENSIÓN MEDIDO POR EL
ADC
uint16_t dato_int_OUT1; //ENTERO DE 16 BITS PARA UNIR LA PARTE ALTA Y LA BAJA DEL
VALOR MEDIDO POR EL ADC
uint16_t dato_int_OUT2; //16 BITS DE APOYO (para operaciones) PARA UNIR LOS VALORES
QUE LLEVA dato_int_OUT
uint8_t dato_def; //parte entera de 8 bits de lo leido por el adc (0 a 5)
uint8_t dato_int_ASCII; //parte entera de 8 bits de lo leido por el adc (0 a 5) +48
(en ascii para )
volatile char LCDCHAR = 0x00; //char temporal para almacenar en él cada valor ascii
de la secuencia de
// precarga "adc_canal_1 tension=0.0v" se carga uno a
uno cada caracter ya
// almacenado en la IRAM del ATmega
en ascii (mediante el método "LCD_CONFIG")
// y se van pasando a la DDRAM del
LCD mediante el método "LCD_INIT"
//----------------------------------------------------------------------------------
int main(void){
//CONFIGURACIÓN DE PUERTOS----------------------------------------------------------
/*
Se configura el adc para que funcione en el modo de autotrigger y se selecciona por
fuente de disparo al
desbordamiento del timer 1, así, este hace una conversión cada que el timer 1 se
desborda, y como se
requieren muestras cada 10 ms, entonces:
primero se configura el timer para que cuente la señal de reloj dividida entre 64;
Teniendo en cuenta
que la frecuencia de CPU se definió como 8MHz el timer 1 ve una frecuencia de
8MHz/64
*/
//MAIN BUCLE------------------------------------------------------------------------
while (1) {
UPDATE_LCD(); //actualización del lcd
asm("SLEEP"); //entrada a ahorro de energía
//----------------------------------------------------------------------------------
}
}
//SUBFUNCIONES----------------------------------------------------------------------
void ADC_CONFIG(){//se configura de acuerdo al manual del ATmega 2560
ADMUX = 0x21;//Vref para adc externo = 5V. LEFT adjust habilitado. canal 1
seleccionado del multiplexor.
ADCSRB = 0x06;//fuente de disparo para autotrigger: timer1 overflow.
ADCSRA = 0xAD;//adc habilitado. autotrigger habilitado. interrupción por fin de
conversión habilitada
//frecuencia del adc = 8MHz con prescaler de 32 para 250kHz que es lo sugerido por
el fabricante para operar a máxima resolución con 10 bits de resolución y el left
adjust.
}
void LCD_CONFIG(){
/*
se almacenan en la IRAM del ATmega2560 los caracteres que luego se cargarán a la
DDRAM LCD para mostrar al
inicializar y por defecto en la pantalla:
ADC_CANAL_1 <----fila1 del lcd
Tension=0.0V <----fila2 del lcd
en ASCII:
65 68 67 95 67 65 78 65 76 95 49 <---fila1
A D C _ C A N A L _ 1
84 69 78 83 72 224 78 61 48 46 48 86 <---fila2
T E N S I Ó N = 0 . 0 V
en hexa: (estos valores son los que se almacenan en la IRAM para luego cargarlos
a la DDRAM del LCD)
0h 41 44 43 5F 43 41 4E 41 4C 5F 31 <---fila1
54 45 4E 53 48 E0 4E 3D 30 2E 30 56 <---fila2
*/
//-------------------------------------------------------------------------------
LCD_INIT();//se escriben los comandos de configuración del lcd dados por el manual
//de este y posteriormente se escriben en la DDRAM del LCD las dos lineas
//de caracteres previamente cargadas a la IRAM del ATmega2560
}
void LCD_INIT(){
//COMANDOS INICIALES DE CONFIGURACIÓN DEL LCD DADOS POR EL MANUAL DEL FABRICANTE----
/*
NÓTESE QUE PARA LA INICIALIZACIÓN, CONFIGURACIÓN Y ESCRITURA DE DATOS,
SIEMPRE, LUEGO DE
ESCRIBIR LA INSTRUCCIÓN EN EL PUERTO B SE GENERA UN PULSO DE ENABLE CON UN
RETARDO DE
TIEMPO PARA DARLE AL PROCESADOR DEL LCD EL TIEMPO NECESARIO PARA LA
EJECUCIÓN DE LA
INSTRUCCIÓN; ESTE PULSO:
*/
//-------------------------------------------------------------RETARDO INICIAL
_delay_ms(20);
//----------------------------------------------------------------------INIT.1
PORTB = 0x38;//8 BITS; 2 LINEAS. !!!!!
PORTD= 0x22;//PULSE UP
_delay_ms(10);
PORTD = 0x00;//PULSE DOWN
_delay_ms(10);
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------INIT.2
PORTB = 0x38;//8 BITS; 2 LINEAS. !!!!!
PORTD= 0x22;//PULSE UP
_delay_ms(10);
PORTD = 0x00;//PULSE DOWN
_delay_us(150);
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//-------------------------------------------------------------------INIT. DEF.
PORTD= 0x22;//PULSE UP
_delay_ms(10);
PORTD = 0x00;//PULSE DOWN
PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
PORTB = 0x0C;//ON DISPLAY !!!!!
PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
PORTD = 0x22;//PULSE UP
_delay_ms(3); //esta acción se demora más, por eso los 3 ms
PORTD = 0x00;//PULSE DOWN
_delay_ms(3);
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);
//se traen UNO A UNO LOS CARACTERES CARGADOS EN LA IRAM DEL PROCESADOR Y SE
//PONEN EN LA PRIMERA LINEA DE LA DDRAM DEL LCD (A PARTIR DE LA DIRECCIÓN 0x00 DE
//LA ddram del lcd)
PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);
PORTD = 0x33;//PULSE UP
_delay_us(50);
PORTD = 0x01;
_delay_us(50);//PULSE DOWN
/*SÓLO SE PONE EL COMANDO DE SET DIRECTION UNA VEZ PORQUE DE AHÍ EN ADELANTE POR LA
CONFIGURACIÓN YA HECHA EN EL SET MODE DEL LCD AL CARGAR UN DATO, EL APUNTADOR DE
DIRECCIONES PARA ESCRITURA AUTOMÁTICAMENTE SE INCREMENTA EN UNO */
PORTB = LCDCHAR; //
PORTD = 0x33;//PULSE UP
_delay_us(50);
PORTD = 0x01;
_delay_us(50);//PULSE DOWN
}
PORTB = 0xA8;
PORTD = 0x22;
_delay_us(50);
PORTD = 0x00;
_delay_us(50);
PORTB=LCDCHAR;
PORTD = 0x33;
_delay_us(50);
PORTD = 0x01;
_delay_us(50);
{
asm ("LD R18, X+");
asm (" STS LCDCHAR, R18");
PORTB = LCDCHAR;
PORTD = 0x33;
_delay_us(50);
PORTD = 0x01;
_delay_us(50);
PORTD = 0x00;
}
void UPDATE_LCD(){
/*SE ACTUALIZAN LOS VALORES DE LA IRAM Y LA DDRAM DEL LCD QUE MUESTRAN LAS PARTES
ENTERA Y DECIMAL DEL VALOR MEDIDO POR EL ADC */
asm("LDI R29, 0x02"); //INDIRECT LOADING HPART X POINTER to dato_int_ASCII INT PART
loc
asm("LDI R28, 0x09"); //INDIRECT LOADING LPART X POINTER to dato_int_ASCII INT PART
loc
asm("LD R20, Y"); //LOAD DATA TO INTERFACE R18 TO THEN SEND IT TO PORTB
//----------------------------------------------------------------------------------
//DEFINICIÓN DE VECTORES DE INTERRUPCIÓN--------------------------------------------
/*PARA OBTENER LAS PARTES ENTERA Y DECIMAL DEL VALOR MEDIDO POR EL ADC SE HACE LO
SIGUIENTE:
(1) ADCH SE CARGA A dato_intH DE 8 BITS
(3) SE INICIALIZA A dato_int_OUT1 CLAREANDOLO (YA QUE ES UNA VARIABLE DE APOYO PARA
CONCATENAR LAS
PARTES ALTA Y BAJA DE LO LEÍD POR EL ADC Y DEBE EMPEZAR EN CERO)
(7) YA CON EL VALOR ENTERO CALCULADO SE PASA A ASCII SUMANDOLE 48 PARA QUE CUANDO
SEA EL MOMENTO DE
ACTUALIZAR DATOS CON "UPDATE_DISPLAY" SE CARGUE ESTE VALOR AL LCD
(8) SE HALLA LA PARTE DECIMAL DEL VALOR MEDIDO Y SE PONE EN FORMATO ENTERO; ES
DECIR, SE PONE POR
EJEMPLO A 0.1 A VALER COMO 1 O A 0.3 A VALER COMO 3, ESTO, PORQUE EL LCD SÓLO VE
NÚMEROS ENTEROS
O CARACTERES PUNTUALES. Y ASÍ SE PODRÁN VISUALIZAR EN EL DISPLAY AL PONERLOS
LUEGO DE UN PUNTO.
QUE INDICARÁ QUE SON LA PARTE DECIMAL DEL VALOR MEDIDO; PARA ESTO:
(9) A LA PARTE DECIMAL SE LE SUMA 48 PARA PASARLO A ASCII Y SE DEJA LISTO PARA QUE
AL
ACTUALIZAR EL VALOR SEA VISUALIZADO EN EL LCD
*/
dato_intH=ADCH; // (1)
dato_intL = ADCL; // (2)
dato_int_OUT1= 0x00; // (3)
//ENTIRE PART
dato_def= (dato_int_OUT1*0.004883); // (6)
dato_int_ASCII =dato_def +48; // (7)
//DECIMAL PART
dato_mod = ( (dato_int_OUT1*0.004883) - dato_def ); // (8)
dato_def_mod = ((dato_mod*10)+48 // (9)
PORTA= dato_def; /*SE VISUALIZA EN LOS LEDS CONECTADOS AL PUERTO A EL VALOR ENTERO
DEL DATO DE TENSIÓN MEDIDO*/
}
ISR(TIMER1_OVF_vect){
asm("NOP");// NO SE HACE NADA PERO ES NECESARIO DEFINIRLA PARA EL FUNCIONAMIENTO
// DEL AUTOTRIGGER DEL ADC
}
//----------------------------------------------------------------------------------
3. RESULTADOS
Al cargar el código anterior al ATmega2560 y conectar todo se pudo observar cómo la
tensión mostrada en el lcd correspondía a la aplicada a la entrada 1 del adc del
microprocesador; Se pudo constatar el funcionamiento comparando con la medida arrojada
por un multímetro digital que medía la tensión de entrada al adc del ATmega2560 y se
observó que el procesador redondeaba la medida al valor decimal más cercano; Por ejemplo;
A continuación se ve cómo al aplicar 2.42 V a la entrada 1 del adc se registraban 2.4 V en el
LCD y también se verificó que cuando el valor de la centésima del multímetro era mayor a 5
en el LCD la medida se veía redondeada al valor siguiente al de la decima en el multímetro,
por ejemplo si en el multímetro se veían 2.47 V el LCD mostraba 2.5 V inmediatamente.
Además se puede ver cómo los leds conectados al puerto A muestran la parte entera del valor
de tensión medido (2 en este caso) teniendo en cuenta que se puso el MSB más a la izquierda
en el montaje y el LSB más a la derecha en el mismo en la fila de los 8 diodos.
4. CONCLUSIONES
Tras realizar esta práctica se ve la importancia de tener los conceptos claros a la hora de
programar un microprocesador, ya que si bien se puede tener la idea de cómo hacer las cosas;
Hay que tener plena consciencia de lo que se está haciendo; cuidando cada pequeño detalle
en la configuración y programación procesador bit a bit, ya que la más mínima alteración en
un registro del microprocesador puede ocasionar fallas impermisibles en la vida real.
Es también importante ir probando los códigos implementados por partes ya que así se
pueden detectar errores de una manera más rápida y con base en la experiencia que se tuvo
con esta práctica la cual tuvo retrasos para su entrega se aprendió que es demasiado
importante chequear muy bien las conexiones hechas pin a pin para verificar que la
información esté fluyendo dentro del sistema como se espera ya que ocurrió con el circuito
mostrado que primero no funcionaba el LCD pero el código sí lo hacía al simularlo; y resultó
ser que el puerto D no estaba conectado físicamente como se debía conectar; También ocurrió
que la entrada de tensión se estaba aplicando a una entrada del adc incorrecta, lo que hacía
que el sistema no funcionara y se quedara estancado mostrando 2 V o 4 V.
Todos estos errores de conexión hicieron perder mucho tiempo en cosas pequeñas pero esto
es sólo es una experiencia más que recalca la importancia de llevar un orden en los montajes,
ir probando por partes estos y además montar los circuitos con tiempo.
5. REFERENCIAS
[1] “Atmel ATmega640/V-1280/V-1281/V-2560/V-2561/V” manual. Disponible en:
https://ww1.microchip.com/downloads/en/devicedoc/atmel-2549-8-bit-avr-microcontroller-
atmega640-1280-1281-2560-2561_datasheet.pdf
[2] ”LCD 16x2 ” hoja de datos. Disponible en:
https://www.sparkfun.com/datasheets/LCD/ADM1602K-NSW-FBS-3.3v.pdf
[3] ”HOW TO USE INTELLIGENT L.C.D.s” Disponible en:
http://www.wizard.org/auction_support/lcd1.pdf