Вы находитесь на странице: 1из 15

MEDIDOR DE TENSIÓN CON VISUALIZACIÓN EN DISPLAY

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

Abstract- En este documento se describe la programación implementada en un procesador


ATmega2560 utilizando los lenguajes C y Assembler para conseguir un dispositivo capaz de
medir una tensión de entrada al procesador en el rango de 0 V a 5 V con una resolución de
100 mV para luego mostrar dicha variable medida en una pantalla LCD de 16x2. Se describe
a detalle el código implementado y las conexiones físicas realizadas así como el
procedimiento para hacer que el adc del micropocesador tome las muestras de la tensión de
entrada cada 10 ms.
Palabras clave: ATmega2560, Microprocesador, Voltímetro.

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.

Ilustración 1 Arduino mega utilizado. Fuente: Tomado de: https://store.arduino.cc/usa/mega-2560-r3


b. Pantalla LCD 1602A de 16x2 posiciones de caracteres.
Por su disponibilidad comercial y su fácil uso se uso esta referencia de pantalla LCD
además de ser la sugerida para la práctica.

Ilustración 2 Pantalla LCD de 16x2 utilizada. Fuente: Tomado de: https://potentiallabs.com/cart/lcd-16*2-india

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.

Potenciómetro de 10kΩ 1 Ajuste del contraste de la pantalla LCD.


Jumpers macho macho 15 Conexiones entre arduino y pantalla LCD y fuente de
alimentación.
Diodos led azules 8 Visualización de parte entera de la tensión medida en
binario
Resistencias de 1kΩ 8 Protección de los diodos

Cable vehículo A convenir Conexiones de la pantalla LCD a la protoboard (la


pantalla viene sin pines de conexión al comprarla; Se
deben soldar cables o conectar headers macho)
Protoboard 1 Interconexión de componentes.
Tabla 1. Otros componentes utilizados en la implementación de la práctica. Fuente: Propia

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
*/

//se incluyen librerías necesarias básicas

#include <avr/io.h> //(1)


#define F_CPU 8000000UL //(2)
#include <avr/interrupt.h> //(3)
#include <util/delay.h> //(4)

//**********************************************************************************
// (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

//global variables then stored after 0x0200 ----------------------------------------

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 )

float dato_mod; //parte flotante decimal de lo leido por el adc; ejemplo:


0.5 o 0.6 o 0.algo
uint8_t dato_def_mod; //(dato_mod * 10) + 48 para mostrarlo en el lcd como un #
entero de 8 bits

/* Las variables anteriores se usan en el método "ADC_CONFIG" con el fin de obtener


la parte entera y la
parte decimal del valor medido por el adc, el cual se configura con una resolución
de 10 bits y al que se
le habilita el left adjustment para que el registro ADCH tenga los 8 bits más
significativos del valor
medido y el registro ADCL tenga los 2 bits menos significativos de lo medido por el
adc. en el método
"ADC_CONFIG" más adelante se explica más detalladamente el rol de cada variable
*/

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

DDRB = 0xFF; //se define el puerto b como salida


PORTB = 0X00; //el puerto b inicia clareado
//(por los 8 bits de b iran los datos en ascii e
instrucciones de control del LCD)

DDRD = 0xFF; //se define el puerto d como salida


PORTD = 0X00; //el puerto d inicia clareado
//(se usan sólo el bit 0 y el bit 1; el primero conectado
al pin RS del LCD, el
//segundo va conectado al enable del LCD)

DDRF= 0x00; //se deine el puerto f como entrada (para la señal de


entrada del adc canal 1)

DDRA= 0xFF; //puerto a como salida (para visualizar en los leds la


parte entera del valor
//medido por el adc y chequear el correcto funcionamiento
del LCD

//CONFIGURACIÓN DEL ADC-------------------------------------------------------------

ADC_CONFIG(); //ver el método para más detalles

//CONFIGURACIÓN DEL LCD-------------------------------------------------------------


LCD_CONFIG(); //ver el método para más detalles

//CONFIGURACIÓN DEL TIMER1----------------------------------------------------------

/*
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

se calcula el valor de precarga a esta frecuencia de (8MHz/64) que da 64286 =


0xFB1E para que el timer
se desborde cada 10ms

cuando el timer1 se desborda se ejecuta el vector de interrupción por overflow del


timer 1 el cual no hace
nada pero se necesita para que al ser ejecutado reestablezca los valores del
registro TIFR1 ya que si no
se reestablece la bandera de overflow el modo de autotrigger del adc queda obsoleto
disparandose una sola
vez; Al ejecutarse esta, luego de un momento el adc lanzará la interrupción de fin
de conversión, y se
ejecutará el correspondiente vector de atención el cual vuelve y precarga el timer1
y obtiene los valores
entero y decimal de la tensión medida por el adc (ver vectores de interrupción para
más detalles)

*/

asm("LDI R16,0xFB ; TIMER 1 PRE LOAD HPART");


asm("STS 0x85,R16 ; 0xFB1E=64286"); // 0xFB = TCNT1H

asm("LDI R16,0x1E ; TIMER 1 PRE LOAD LPART");


asm("STS 0x84,R16 ; 0xFB1E=64286"); // 0x1E = TCNT1L

asm("LDI R16,0<<2|1<<1|1<<0 ; 64 PRESCALING");


asm("STS 0x81,R16"); // 0x81 = TCCR1B

TIMSK1 = 0x01; //se habilita interrupción por desbordamiento


sei(); //se habilita el global de interrupciones
SMCR = 0x01; //se habilita el modo de ahorro de energía

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

la secuencia de caracteres a cargar en la ram para estas palabras es:

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

cuando se actualice el valor medido en tensión de la parte entera y la parte


decimal,
se actualizarán en la IRAM los valores 30 y 30 de la fila 2 los cuales son en ese
orden
la parte entera y la decimal del valor de tensión medido que por defecto al
inicializar
será cero por estos dos ceros en ascii o 30 y 30.

la actualización de estos valores en las 2 RAM la hace el método "UPDATE_LCD" vease


dicho
método más adelante para más detalles

*/

//carga a la IRAM de la primera linea de caracteres en el ATmega2560----------------


//41 44 43 5F 43 41 4E 41 4C 5F 30
asm("LDI R17, 0x41");
asm("STS 0x0500, R17 ");

asm("LDI R17, 0x44");


asm("STS 0x0501, R17 ");

asm("LDI R17, 0x43");


asm("STS 0x0502, R17 ");

asm("LDI R17, 0x5F");


asm("STS 0x0503, R17 ");

asm("LDI R17, 0x43");


asm("STS 0x0504, R17 ");

asm("LDI R17, 0x41");


asm("STS 0x0505, R17 ");

asm("LDI R17, 0x4E");


asm("STS 0x0506, R17 ");

asm("LDI R17, 0x41");


asm("STS 0x0507, R17 ");
asm("LDI R17, 0x4C");
asm("STS 0x0508, R17 ");

asm("LDI R17, 0x5F");


asm("STS 0x0509, R17 ");

asm("LDI R17, 0x31");


asm("STS 0x050A, R17 ");

//carga a la IRAM de la segunda linea de caracteres en el ATmega2560----------------


// 54 45 4E 53 48 E0 4E 3D 30 56

asm("LDI R17, 0x54");


asm("STS 0x050B, R17 ");

asm("LDI R17, 0x45");


asm("STS 0x050C, R17 ");

asm("LDI R17, 0x4E");


asm("STS 0x050D, R17 ");

asm("LDI R17, 0x53");


asm("STS 0x050E, R17 ");

asm("LDI R17, 0x49");


asm("STS 0x050F, R17 ");

asm("LDI R17, 0x4F");


asm("STS 0x0510, R17 ");

asm("LDI R17, 0x4E");


asm("STS 0x0511, R17 ");

asm("LDI R17, 0x3D");


asm("STS 0x0512, R17 ");

asm("LDI R17, 0x30");


asm("STS 0x0513, R17 ");

asm("LDI R17, 0x2E");


asm("STS 0x0514, R17 ");

asm("LDI R17, 0x30");


asm("STS 0x0515, R17 ");

asm("LDI R17, 0x56");


asm("STS 0x0516, R17 ");

//los caracteres se cargan a partir de la posición de memoria 0x0500, ya que si se


//empiezan a cargar desde 0x0200 se sobreescribirían los valores de las variables
//globales previamente definidas al inicio del código

//-------------------------------------------------------------------------------
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:

*PARA COMANDOS DE CONFIGURACIÓN ES DE LA FORMA:

PORTD = 0x02; //PULSE UP --> ENABLE EN 1, RS EN 0


_delay_us(50);
PORTD = 0x00;//PULSE DOWN --> ENABLE EN 0, RS EN 0
_delay_us(50);

*PARA ESCRITURA DE DATOS ES DE LA FORMA:

PORTD = 0x03;//PULSE UP --> ENABLE EN 1, RS EN 1


_delay_us(50);
PORTD = 0x01;
_delay_us(50);//PULSE DOWN --> ENABLE EN 0, RS EN 1

*/

//cada comando escrito se resalta con un !!!!! a la derecha para distinguir


//mejor entre comando enviado y pulso de habilitación.

//INICIALIZACIÓN DEL LCD (ESTABLECIMIENTO DE COMUNICACIÓN CON EL PROCESADOR DEL


LCD:)

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

//COMANDOS DE CONFIGURACIÓN DEL LCD:


PORTB = 0x38;//8 BITS; 2 LINEAS. !!!!!

PORTD= 0x22;//PULSE UP
_delay_ms(10);
PORTD = 0x00;//PULSE DOWN

//FIN DE LA INICIALIZACIÓN DEL LCD.

//FIRST CONFIG SEQUENCE


LOADED//////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
//COMANDOS DE CONFIGURACIÓN DEL
LCD_________________________________________________________________________________

PORTB = 0x3C;//dos lineas, 8 bits; resolución 5x10 !!!!!

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);

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

PORTB = 0x01;//CLEAR ALL !!!!!

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);

////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////

PORTB = 0x06;//SET MODE !!!!!

PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);

//secuencia de escritura de la primera linea del lcd******************************

//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)

PORTB = 0x80;//SE INDICA LA DIRECCIÓN 0x00 PARA CARGAR EL PRIMER CARACTER


// (LETRA A DE LA SECUENCIA "ADC_CANAL_1")

PORTD = 0x22;//PULSE UP
_delay_us(50);
PORTD = 0x00;//PULSE DOWN
_delay_us(50);

//CARGA INDIRECTA CON APUNTADOR XH (PRIMERA DIRECCIÓN EN LA


//IRAM DEL PROCESADOR QUE ALMACENA LA PRIMERA LETRA DE LASECUENCA A ESCRIBIR
asm("LDI R27, 0x05");
asm("LDI R26, 0x00");
/*CARGA Y POSTERIOR INCREMENTO DEL APUNTADOR PARA LA
SIGUIENTE LETRA ALMACENADA EN LA SIGUIENTE DIRECCIÓN DE LA IRAM
DEL ATmega2560. */
asm("LD R18, X+");

asm (" STS LCDCHAR, R18");

PORTB=LCDCHAR;//SE ESCRIBE EN LA POSICIÓN INDICADA LA LETRA A

PORTD = 0x33;//PULSE UP
_delay_us(50);
PORTD = 0x01;
_delay_us(50);//PULSE DOWN

//ESCRITURA DEL RESTO DE LA PRIMERA LINEA (EL APUNTADOR SE VA MOVIENDO Y PERMITE


CARGAR TODA LA LINEA1)

/*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 */

for(int i=0; i<10; i++)


{
asm ("LD R18, X+");
asm (" STS LCDCHAR, R18");

PORTB = LCDCHAR; //

PORTD = 0x33;//PULSE UP
_delay_us(50);
PORTD = 0x01;
_delay_us(50);//PULSE DOWN
}

//secuencia de escritura de la segunda linea del lcd******************************


//se hace lo mismo de la primera linea...

PORTB = 0xA8;
PORTD = 0x22;
_delay_us(50);
PORTD = 0x00;
_delay_us(50);

asm("LDI R27, 0x05");


asm("LDI R26, 0x0B");
asm("LD R18, X+");
asm (" STS LCDCHAR, R18");

PORTB=LCDCHAR;

PORTD = 0x33;
_delay_us(50);
PORTD = 0x01;
_delay_us(50);

for(int i=0; i<11; i++)

{
asm ("LD R18, X+");
asm (" STS LCDCHAR, R18");

PORTB = LCDCHAR;

PORTD = 0x33;
_delay_us(50);
PORTD = 0x01;
_delay_us(50);
PORTD = 0x00;
}

//LCD LISTO EN ESTE PUNTO!


}

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

asm("STS 0x0513, R20 ");//ACTUALIZACIÓN DE LA PARTE ENTERA

asm("LDI R31, 0x02"); //INDIRECT LOADING HPART X POINTER to dato_int_ASCII DECIMAL


PART loc
asm("LDI R30, 0x02"); //INDIRECT LOADING LPART X POINTER to dato_int_ASCII DECIMAL
PART loc
asm("LD R21, Z"); //LOAD DATA TO INTERFACE R18 TO THEN SEND IT TO PORTB

asm("STS 0x0515, R21 ");//ACTUALIZACIÓN DE LA PARTE DECIMAL


LCD_INIT();/*SE ACTUALIZAN LOS GRÁFICOS DEL LCD REESCRIBIENDO COMO EN LA PRIMERA
ESCRITURA DE INICIALIZACIÓN PERO AHORA CON LA TENSIÓN MEDIDA ACTUALIZADA
*/
}

//----------------------------------------------------------------------------------
//DEFINICIÓN DE VECTORES DE INTERRUPCIÓN--------------------------------------------

ISR(ADC_vect){ // ADC EOC SUBRUTINA DE ATENCIÓ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

(2) ADCL SE CARGA A dato_intL 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)

(4) SE HACE UNA OR A dato_intH DESPLAZADO DOS BITS A LA IZQUIERDA Y dato_int_OUT1


(INT DE 16 BITS)
QUE INICIALMENTE ESTÁ CLAREADO; EL DESPLAZAMIENTO A LA IZQUIERDA ES DE 2 BITS
PARA DAR ESPACIO
A LA PARTE BAJA QUE SE UNIRÁ A CONTINUACIÓN AL HACER OTRA OR

(5) SE HACE OTRA OR AHORA CON dato_intL DESPLAZADO 6 POSICIONES A LA DERECHA Y


dato_int_OUT1; ASÍ
SE LOGRA PONER LOS 10 BITS LEÍDOS POR EL ADC A FULL RESOLUCIÓN EN UNA VARIABLE
INT DE 16 BITS

(6) SE MULTIPLICA EL VALOR DE 10 BITS LEÍDO POR EL ADC YA CONCATENADO EN LA VARIABLE


DE 16 BITS POR
LA RESOLUCIÓN DEL ADC (5V/1024 APROXIM.= 0.004883) PARA OBTENER EL VALOR EN
TENSIÓN MEDIDO DE 0V
A 5V; COMO EL RESULTADO ES UN ENTERO DE 8 BITS, ESTA SERÁ LA PARTE ENTERA DEL
VALOR MEDIDO; EJ:
0V, 1V, 2V, 3V, O 4V

(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:

* SE HALLA EL VALOR MEDIDO PERO ESTA VEZ SE ALMCENA EN UN DATO EN COMA


FLOTANTE TIPO FLOAT
* SE LE RESTA A ESTE VALOR EL DATO ENTERO HALLADO EN (7) Y ESO DA LA PARTE
DECIMAL FALTANTE
* ESTA PARTE DECIMAL SE MULTIPLICA POR 10 Y SE ALMACENA EN UNA VARIABLE INT
DE 8 BITS QUE
ASEGURA QUE SE MOSTRARÁ LA PARTE DECIMAL COMO UN ENTERO PROCESABLE POR EL
LCD

LO QUE SUCEDE EN 8 SERÍA POR EJEMPLO....


SI EL DATO CALCULADO ES 3.83 V:

* LA PARTE ENTERA DEL DATO MEDIDO ES 3


* 3.83(FLOAT) - 3(U_INT8) = 0.83
* 0.83*10 = 8.3
* COMO ESE 8.3 SE ALMACENA EN UNA VARIABLE INT DE 8 BITS SE ACABA GUARDANDO
SÓLO EL 8

LUEGO EN EL LCD SE ESCRIBE LA PARTE ENTERA, LUEGO UN PUNTO Y LUEGO LA PARTE


DECIMAL
(TODO EN ASCII) ASÍ ENTONCES SE VISUALIZARÍA EN EL LCD: 3 . 8 Y SE
GARANTIZA
LA VISUALIZACIÓN DE LA INFORMACIÓN Y UNA RESOLUCIÓN PRUDENTE.

(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)

dato_int_OUT2 = dato_intH <<2 | dato_int_OUT1; // (4)


dato_int_OUT1 =dato_intL >> 6 | dato_int_OUT2;//10 bits adc value // (5)

//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*/

//SE PRECARGA EL TIMER DE NUEVO Y POSTERIORMENTE SE VUELVE AL BUCLE PRINCIPAL.

asm("LDI R18, 0x00");


asm("STS 0x0036, R18");

asm("LDI R16,0xFB ; TIMER 1 PRE LOAD HPART");


asm("STS 0x85,R16 ; 0xFB1E=64286"); // 0xFB = TCNT1H

asm("LDI R16,0x1E ; TIMER 1 PRE LOAD LPART");


asm("STS 0x84,R16 ; 0xFB1E=64286"); // 0x1E = TCNT1L

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

Ilustración 3 Circuito funcionando. Fuente: Propia.

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

Вам также может понравиться