Logo Studenta

SISTEMA_DE_MICROPROCESADORES

¡Este material tiene más páginas!

Vista previa del material en texto

SISTEMA DE MICROPROCESADORES 
GERMÁN JESÚS PEREIRA MUÑOZ, Ph. D. (c) 
LA PAZ, BOLIVIA 2021
PROPIEDAD INTELECTUAL DEL AUTOR
 
Ing. Germán Jesús Pereira Muñoz Página 2 
 
1. Introducción. 
Un micro controlador es una microcomputadora ya que posee las mismas 
características que son la unidad de procesamiento, la memoria y los dispositivos de 
entrada salida principalmente. 
Este dispositivo es programable, es decir, posee una memoria de programa donde 
contiene una secuencia de instrucciones aritmético lógicas introducidas por el 
programador mediante un software especializado. 
Son diseñados para reducir el costo económico y el consumo de energía de un 
sistema en particular. Por eso el tamaño de la unidad de procesamiento, la cantidad de 
memoria y los periféricos incluidos dependerán de la aplicación. El control de un 
electrodoméstico sencillo como una batidora, utilizará un procesador muy pequeño (4 u 
8 bit) por que sustituirá a un autómata finito. En cambio un reproductor de música y/o 
vídeo digital requerirá de un procesador de 32 bit o de 64 bit. 
Los microcontroladores representan la inmensa mayoría de los chips de 
computadoras vendidos, sobre un 50% son controladores "simples" y el restante 
corresponde a DSPs más especializados. Mientras se pueden tener uno o dos 
microprocesadores de propósito general en casa (vd. está usando uno para esto), usted 
tiene distribuidos seguramente entre los electrodomésticos de su hogar una o dos 
docenas de microcontroladores. Pueden encontrarse en casi cualquier dispositivo 
electrónico como automóviles, lavadoras, hornos, teléfonos, etc. 
2. Microcontroladores AVR. 
Los AVR son una familia de microcontroladores RISC deAtmel. La arquitectura de 
los AVR fue concebida por dos estudiantes en el Norwegian Institute of Technology, y 
posteriormente refinada y desarrollada en Atmel Norway, la empresa subsidiaria 
deAtmel, fundada por los dos arquitectos del chip. 
El AVR es una CPU de arquitectura Harvard tiene 32 registros de 8 bits. Algunas 
instrucciones sólo operan en un subconjunto de estos registros. La concatenación de los 
32 registros, los registros de entrada/salida y la memoria de datos conforman un espacio 
de direcciones unificado, al cual se accede a través de operaciones de 
carga/almacenamiento. 
El AVR fue diseñado desde un comienzo para la ejecución eficiente de código C 
compilado. Como este lenguaje utiliza profusamente punteros para el manejo de 
variables en memoria, los tres últimos pares de registros internos del procesador, son 
usados como punteros de 16 bit al espacio de memoria externa, bajo los nombres X, Y y 
Z. Esto es un compromiso que se hace en arquitecturas de ocho bit desde los tiempos de 
Intel 8008, ya que su tamaño de palabra nativo de 8 bit (256 localidades accedidas) es 
pobre para direccionar. Por otro lado, hacer que todo el banco superior de 16 registros 
de 8 bit tenga un comportamiento alterno como un banco de 8 registros de 16 bit, 
complicaría mucho el diseño, violando la premisa original de su simplicidad. 
 
 
Ing. Germán Jesús Pereira Muñoz Página 3 
 
 
El set de instrucciones AVR está implementado físicamente y disponible en el 
mercado en diferentes dispositivos, que comparten el mismo núcleo AVR pero tienen 
distintos periféricos y cantidades de RAM y ROM: desde el microcontrolador de la 
familia Tiny AVR ATtiny11 con 1KB de memoria flash y sin RAM (sólo los 32 
registros), y 8 pines, hasta el microcontrolador de la famila Mega AVRATmega2560 con 
256KB de memoria flash, 8KB de memoria RAM, 4KB de memoria EEPROM, 
conversor análogo digital de 10 bits y 16 canales, temporizadores, comparador 
analógico, JTAG, etc. La compatibilidad entre los distintos modelos es preservada en un 
grado razonable. 
Los microcontroladores AVR tienen una cañería ('pipeline' en inglés) con dos etapas 
(cargar y ejecutar), que les permite ejecutar la mayoría en un ciclo de reloj, lo que los 
hace relativamente rápidos entre los microcontroladores de 8-bit. 
3. Arquitectura de un microcontrolador AVR. 
La arquitectura de estos controladores: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 4 
 
A manera de maximizar el desempeño y el paralelismo, tenemos que la arquitectura 
Harvard separa memoria y buses para programa y para datos. Las instrucciones se 
ejecutan seguidamente, mientras una se ejecuta, la siguiente ya ha sido rescatada de la 
memoria de programa y colocada a la pila. 
En la grafica puede notarse el núcleo del procesamiento que es la ALU, las memorias de 
datos (RAM y ROM), la memoria de programa (Flash), el contador de programa, las 
líneas de entrada y salida y finalmente todos los módulos que se anexan al bus de datos 
que son las características de cada microcontrolador. 
 
4. Software de programación (WIN-AVR). 
Uno de las decenas de software que existen para manejar este tipo de 
microcontroladores que resulta muy sencillo manejar es el WIN-AVR. Este software se 
diseñó para trabajar con lenguaje C y realiza la compilación de los programas de manera 
rápida y además puede programar físicamente el microcontrolador de la misma manera. 
La compatibilidad que tiene con los grabadores también es bastante amplia. 
Este programa muestra un ‘notepad’ como el siguiente donde se puede programar en el 
lenguaje C por ejemplo las secuencias que deba seguir el microcontrolador. 
 
 
 
 
Cuando se abre el programa, se muestra el espacio de trabajo al centro, los proyectos a 
la izquierda y el estado de la compilación o programación abajo. 
 
 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 5 
 
 
 
 
-Creación de un proyecto sobre el cual trabajar. 
Para crear un proyecto, simplemente se debe seleccionar ‘nuevo proyecto’ en la ventana 
‘file’. Luego se da el nombre al proyecto y donde quiera guardárselo. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Una vez creado, debería aparecer en la parte izq. de la ventana del notepad. 
 
 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 6 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Luego, guardamos el espacio de trabajo que por defecto aparece como ‘new’. 
Vamos a ‘file’, ‘save as’ y guadamos en una carpeta, preferentemente donde hayamos 
guardado el proyecto, con la extensión .c. 
 
 
Finalmente añadimos este espacio de trabajo al proyecto, para que cada vez que se abra 
el proyecto, directamente nos muestre este espacio de trabajo y todos los que tengamos 
añadidos. 
 
 
 
 
 
 
 
 
 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 7 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5. Grabadores del AVR. 
Existen varios grabadores para este microcontrolador de entre los cuales los más 
comunes son: 
Grabador por puerto paralelo: 
 
 
 
 
 
 
 
 
 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 8 
 
 
Este grabador es sencillo de armar y funciona correctamente con el WIN-AVR. Solo 
debe conectarse a los pines de Reset, MISO, MOSI y SCK tal como se muestra en la 
figura. 
La única dificultad que se tiene con este grabador es que solo las maquinas antiguas 
Pentium 3 poseen un puerto paralelo y no con todos los sistemas operativos es posible 
utilizarlo. 
Grabador serial: 
 
 
Este grabador de la misma manera que el anterior es bastante fácil de armar y solo 
debe conectarse al microcontrolador con los pines indicados. Un puerto serial es 
mucho más común que un puerto serial ya que muchas maquinas de escritorio lo 
poseen. Funciona bien con el WIN-AVR y es bastante practico ya por su tamaño. 
 
Grabador USB: 
 
Ing. Germán Jesús Pereira Muñoz Página 9 
 
 
Este grabador necesita de un microcontrolador maestro que graba a los demás y eso lo 
hace más caro y menos atractivo, pero la gran ventaja es que graba a otros 
microcontroladores por puerto USB que tienen casi todas las maquinas actuales, sobre 
todo las laptop. 
Pero existe una variación que es mucho más aplicativa para fines académicos como 
entrenador y se trata de una versión de boot loader.Esta versión solo requiere un 
microcontrolador (con el que se quiere trabajar) para grabarle un programa con 
cualquier grabador anterior; el programa comunica al microcontrolador con el puerto 
usb de una PC y puede grabarse cualquier programa diferente ya que lo reconoce como 
un dispositivo externo. El programa a grabarse no borrara al boot loader ya que este 
graba este programa después de él mismo en la memoria. 
Lo único deficiente de este grabador es que solo se graba a sí mismo y no a otros 
microcontroladores pero lo hace atractivo para fines académicos. 
El loader: 
 
Ing. Germán Jesús Pereira Muñoz Página 10 
 
 
 
6. Programación. 
 
Para programar en el microcontrolador AVR no solo se necesita conocer el lenguaje C 
o el lenguaje ensamblador sino también los registros internos que posee este chip. Para 
acceder a las distintas funciones de este controlador, este consta de registros los cuales 
deben ser configurados por programa para un funcionamiento deseado. 
Veremos las herramientas que tiene analizando algunos ejemplos de programas en base 
a lenguaje C. 
 
 
6.1. Uso de instrucciones básicas de entrada / salida en displays. 
Los componentes más sencillos a controlar son los leds ya que solo requieren una salida 
digital de 5V del microcontrolador. Entonces para familiarizarnos con el manejo de 
instrucciones de salida, una aplicación bastante sencilla es el despliegue en displays de 7 
segmentos que son arreglos de leds de manera de desplegar números o letras. 
 
Ing. Germán Jesús Pereira Muñoz Página 11 
 
El programa más sencillo a realizar es el de un contador ascendente descendente en el 
que tenemos instrucciones de entrada e instrucciones de salida: 
#include<avr/io.h> 
#include<util/delay.h> 
#define asce (PINC&1) 
//Hasta aqui definimos las librerias a utilizar donde avr/io contiene los registros del avr, útil/delay permite el 
uso de retardos en procesamiento y #define que permite la definición de variables. 
int a; 
a=0; 
void delay_ms(int n){ 
while(n--) 
_delay_ms(1); 
} 
//Una sencilla rutina de retardo. 
void ascendente(void){ 
 switch(a){ 
 case 0: 
 PORTB=0xFE; 
 a=a+1; 
 break; 
 case 1: 
 PORTB=0xB0; 
 a=a+1; 
 break; 
 case 2: 
 PORTB=0xED; 
 a=a+1; 
 break; 
 case 3: 
 PORTB=0xF9; 
 a=a+1; 
 break; 
 case 4: 
 PORTB=0xB3; 
 a=a+1; 
 break; 
 case 5: 
 PORTB=0xDB; 
 a=a+1; 
 break; 
 case 6: 
 PORTB=0xDF; 
 a=a+1; 
 break; 
 case 7: 
 PORTB=0xF0; 
 a=a+1; 
 break; 
 case 8: 
 PORTB=0xFF; 
 a=a+1; 
 break; 
 case 9: 
 PORTB=0xF3; 
 a=0; 
 break; 
 } 
 } 
void descendente(void){ 
 switch(a){ 
 case 0: 
 PORTB=0xFE; 
 a=9; 
 break; 
 case 1: 
 
Ing. Germán Jesús Pereira Muñoz Página 12 
 
 PORTB=0xB0; 
 a=a-1; 
 break; 
 case 2: 
 PORTB=0xED; 
 a=a-1; 
 break; 
 case 3: 
 PORTB=0xF9; 
 a=a-1; 
 break; 
 case 4: 
 PORTB=0xB3; 
 a=a-1; 
 break; 
 case 5: 
 PORTB=0xDB; 
 a=a-1; 
 break; 
 case 6: 
 PORTB=0xDF; 
 a=a-1; 
 break; 
 case 7: 
 PORTB=0xF0; 
 a=a-1; 
 break; 
 case 8: 
 PORTB=0xFF; 
 a=a-1; 
 break; 
 case 9: 
 PORTB=0xF3; 
 a=a-1; 
 break; 
 } 
 } 
//Para cada caso ascendente o descendente tenemos las instrucciones de salida correspondientes a un número. 
int main (void){ 
 DDRB=0xFF; 
 DDRC=0; 
while(1){ 
 if (asce==0) 
 descendente(); 
 else ascendente(); 
 delay_ms(500); 
 } 
 } 
//La rutina principal pregunta un PIN del controlador y en base a este decide si el contador será ascendente o 
descendente y para visualizarlo tenemos un retardo de 500 ms. 
 
 
Lo que realiza el programa es un conteo interno del 0 al 9 que se guarda en una 
variable ‘a’ y preguntamos con el switch que numero tiene ‘a’ y en base a esto sacamos 
un dato para el display de 7 segmentos y decrementamos o incrementamos ‘a’ de 
acuerdo al funcionamiento del contador. 
Nótese que para entradas de datos definimos el registro DDRC del puerto C con 0 y 
para salidas DDRB del puerto B con ff. 
 
Ing. Germán Jesús Pereira Muñoz Página 13 
 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
12
XTAL1
13
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
R1
10k
 
 
 
 
6.2. Aplicación básica. (Semáforo) 
Otra aplicación sencilla de este controlador es un semáforo. Este tipo de control es 
secuencial ya que el semáforo sigue una secuencia sencilla que es rojo, verde, amarillo 
un tiempo corto y luego rojo nuevamente. 
La aplicación será para un par de ellos: 
#include<avr/io.h> 
#include<util/delay.h> 
unsigned char estado; 
unsigned char s,c; 
unsigned char previo; 
void delay_ms(int n){ 
 while(n--) 
 _delay_ms(1); 
 } 
#define R1 PA0 
#define A1 PA1 
#define V1 PA2 
#define R2 PA3 
#define A2 PA4 
#define V2 PA5 
#define AA 0 
#define RV 1 
#define RA 2 
#define VR 3 
#define AR 4 
#define cambio (PINC&1) 
#define emergencia (PINC&2) 
void cambiar(char nuevo){ 
 estado=nuevo; 
 c=0; 
 s=0; 
 } 
void condiciones(void){ 
 switch(estado){ 
 case AA: 
 if(cambio==0) 
 cambiar(RV); 
 break; 
 case RV: 
 if ((cambio==0)||(s==10)){ 
 s=0; 
 
Ing. Germán Jesús Pereira Muñoz Página 14 
 
 cambiar(RA);} 
 if(emergencia==0) 
 cambiar(AA); 
 break; 
 case RA: 
 if(s==2){ 
 s=0; 
 cambiar(VR);} 
 if (emergencia==0) 
 cambiar(AA); 
 break; 
 case VR: 
 if((cambio==0)||(s==10)){ 
 s=0; 
 cambiar(AR);} 
 if (emergencia==0) 
 cambiar(AA); 
 break; 
 case AR: 
 if(s==2){ 
 s=0; 
 cambiar(RV);} 
 if (emergencia==0) 
 cambiar(AA); 
 break; 
 } 
 } 
void ejecutar(void){ 
 switch(estado){ 
 case RV: 
 PORTA=(1<<R1)|(1<<V2); 
 break; 
 case RA: 
 PORTA=(1<<R1)|(1<<A2); 
 break; 
 case VR: 
 PORTA=(1<<V1)|(1<<R2); 
 break; 
 case AR: 
 PORTA=(1<<A1)|(1<<R2); 
 break; 
 case AA: 
 if(c<50) 
 PORTA=(1<<A1)|(1<<A2); 
 else 
 PORTA=0; 
 } 
 if (++c>100){c=0; s++;} 
 delay_ms(10);} 
int main(void){ 
 DDRA=0xFF; 
 DDRC=0; 
 
 cambiar(AA); 
 while(1){ 
 condiciones(); 
 ejecutar(); 
 } 
 } 
El programa tiene las variables estado, s, c y las entradas externas de ‘emergencia’ y 
‘cambio’ 
Lo primero que tenemos para los semáforos es el ‘stand by’ cuando no fucnionan, sino 
que despliegan luces amarillas intermitentes. Entonces nos definimos los estados en los 
que estarán los semáforos, por ejemplo, AA para amarillo-amarillo, RV para un 
 
Ing. Germán Jesús Pereira Muñoz Página 15 
 
semáforo en rojo y el otro en verde, etc. Las variables ‘s’ y ‘c’ solo se utilizan para la 
intermitencia del estado AA. 
 
Entonces los semáforos se mantienen en amarillo intermitente hasta que alguien 
presiona cambio que cambia los semáforos a RV y desde allí el lenguaje C condiciona 
las acciones a seguir para la secuencia que se repite una y otra vez. Si alguien presiona 
‘emergencia’ los semáforos van de nuevo a AA. 
 
Nótese la instrucción para poner a uno lógico bits que es: PORTA=(1<<A1)|(1<<A2); 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
D1
LED-YELLOW
D2
LED-YELLOW
D3
LED-RED
D4
LED-RED
D5
LED-GREEN
D6
LED-GREEN
R1
100R
R2
100R
 
 
 
6.3. Multiplexación de displays. 
Otro ejemplo, el más sencillo de todos, es la multiplexacion de displays. Esto consiste 
en la utilización de varios displays con un común bus de datos y se habilitan cada uno 
mediante otro puerto que será el de control.En nuestro caso el bus de datos esta dado por el puerto B y el de control por el puerto 
C. 
Como es bastante sencillo, solo se necesitan botar datos por puerto o jalarlos desde 
una tabla, pero con instrucciones de salida basta. Cada dato a sacar representa una 
letra. 
#include<avr/io.h> 
#include<util/delay.h> 
void mostrar (void){ 
 PORTC=0xFE; 
 PORTB=0x6E; 
 PORTB=0x00; 
 PORTC=0xFD; 
 PORTB=0xFC; 
 PORTB=0x00; 
 PORTC=0xFB; 
 PORTB=0x1C; 
 PORTB=0x00; 
 PORTC=0x07; 
 PORTB=0xEE; 
 PORTB=0x00; 
} 
int main (void){ 
 DDRB=0xff; 
 DDRC=0xff; 
while(1){ 
 mostrar(); 
 } 
 } 
Nótese que puede escribirse cualquier cosa y con cuantos displays se quiera. 
 
 
Ing. Germán Jesús Pereira Muñoz Página 16 
 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
 
 
 
6.4. Manejo de pantalla LCD. 
 
Una pantalla LCD puede desplegar de una mejor manera cualquier texto y por ello son 
más utilizadas. Pero, a la vez, son más complicadas de utilizar comparándolas con los 
simples leds de los displays. A estas pantallas se les debe enviar datos de comando y los 
datos de cada letra en ASCII. 
JUEGOS DE INSTRUCCIONES: 
 
Estas son las instrucciones para el control del modulo LCD 
 
Hitachi 44780 o compatible 
 
CLEAR DISPLAY 
 
Borra el módulo LCD y coloca el cursor en la primera posición 
 
(dirección 0). Pone el bit I/D a 1 por defecto. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 0 0 0 0 0 1 
 
Tiempo de ejecución: 1.64mS 
 
HOME 
 
Coloca el cursor en la posición de inicio (dirección 0) y hace que el display comience a 
desplazarse desde la posición original. El contenido de la memoria RAM de datos de 
visualización (DD RAM) permanece invariable. La dirección de la memoria RAM de 
datos para la visualización (DD RAM) es puesta a 0. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 0 0 0 0 1 X 
 
Tiempo de ejecución: 1.64mS 
 
 
 
Ing. Germán Jesús Pereira Muñoz Página 17 
 
ENTRY MODE SET 
 
Establece la dirección de movimiento del cursor y especifica si la visualización se va 
desplazando a la siguiente posición de la pantalla o no. Estas operaciones se ejecutan 
durante la lectura o escritura de la DD RAM o CG RAM. Para visualizar normalmente 
poner el bit S=0. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 0 0 0 1 I/D S 
 
Tiempo de ejecución: 40µS 
 
 
DISPLAY ON/OFF CONTROL 
 
Activa o desactiva poniendo en ON/OFF tanto al display (D) como al cursor (C) y se 
establece si este último debe o no parpadear (B). 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 0 0 1 D C B 
 
Tiempo de ejecución: 40µS 
 
 
CURSOR OR DISPLAY SHIFT 
 
Mueve el cursor y desplaza el display sin cambiar el contenido de la memoria de datos 
de visualización DD RAM. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 0 1 S/C R/L X X 
 
Tiempo de ejecución: 40µS 
 
 
FUNCTION SET 
 
Establece el tamaño de interfase con el bus de datos (DL), número de líneas del display 
(N) y tipo de carácter (F) 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 0 1 DL N F X X 
 
Tiempo de ejecución: 40µS 
 
 
SET THE CG RAM ADDRESS 
 
El módulo LCD además de tener definidos todo el conjunto de caracteres ASCII, 
 
Ing. Germán Jesús Pereira Muñoz Página 18 
 
permite al usuario definir 4 u 8 caracteres gráficos. La composición de estos caracteres 
se va guardando en una memoria llamada CG RAM con capacidad para 64 bytes. Cada 
carácter gráfico definido por el usuario se compone de 16 u 8 bytes que se almacenan en 
sucesivas posiciones de la CG RAM. 
 
Mediante esta instrucción se establece la dirección de memoria CG RAM a partir de la 
cual se irán almacenando los bytes que definen un carácter gráfico. Ejecutando este 
comando todos los datos que se lean o escriban posteriormente, lo hacen desde esta 
memoria CG RAM. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 0 1 Dirección de la CG RAM 
 
Tiempo de ejecución: 40µS 
 
 
SET THE DD RAM ADDRESS 
 
Los caracteres o datos que se van visualizando, se van almacenando en una memoria 
llamada DD RAM para de aquí pasar a la pantalla. 
 
Mediante esta instrucción se establece la dirección de la memoria 
 
DD RAM a partir de la cual se irán almacenando los datos a visualizar. Ejecutando este 
comando, todos los datos que se escriban o lean posteriormente lo harán desde esta 
memoria DD RAM. Las direcciones de la 80h a la 8Fh corresponden con los 16 
caracteres del primer renglón y de la C0h a la CFh con los 16 caracteres del segundo 
renglón, para este modelo de LCD. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 0 1 Dirección de la DD RAM 
 
Tiempo de ejecución: 40µS 
 
 
READ BUSY FLAG & ADDRESS 
 
Cuando el modulo LCD esta ejecutando cualquiera de estas instrucciones, tarda un 
cierto tiempo de ejecución en el que no se debe mandar ninguna instrucción. Para ello 
dispone de un flag llamado BUSY (ocupado) que indica que se está ejecutando una 
instrucción previa. 
 
Esta instrucción de lectura informaciónrma del estado de dicho flag además de 
proporcionar el valor del contador de direcciones de la CG RAM o de la DD RAM 
según la última que se haya empleado. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
0 1 BF Dirección de la CG RAM o DD RAM 
 
 
Ing. Germán Jesús Pereira Muñoz Página 19 
 
Tiempo de ejecución: 40µS 
 
 
WRITE DATA TO GG OR DD RAM 
 
Mediante este comando se escribe en la memoria DD RAM los datos que se quieren 
presentar en pantalla y que serán los diferentes códigos ASCII de los caracteres a 
visualizar. 
 
Igualmente se escribe en la memoria CG RAM los diferentes bytes que permiten 
confeccionar caracteres gráficos a gusto del usuario. 
 
El escribir en uno u otro tipo de memoria depende de si se ha empleado previamente la 
instrucción de direccionamiento DD RAM o la de direccionamiento CG RAM. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
1 0 Código ASCII o byte del carácter gráfico 
 
Tiempo de ejecución: 40µS 
 
 
READ DATA FROM CG RAM OR DD RAM 
 
Mediante este comando se lee de la memoria DD RAM los datos que haya almacenados 
y que serán los códigos ASCII de los caracteres almacenados. 
 
Igualmente se lee de la memoria CG RAM los diferentes bytes con los que se ha 
confeccionado un determinado carácter gráfico. 
 
El leer de uno u otro tipo de memoria depende de si se ha empleado previamente la 
instrucción de direccionamiento de la DD RAM o la de direccionamiento CG RAM. 
 
RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0 
1 1 Código ASCII o byte del carácter gráfico 
 
Tiempo de ejecución: 40µS 
 
Entonces, conociendo nuestra pantalla, el programa lcd.c que será nuestro modelo para 
trabajar como un subprograma: 
#include<avr/io.h> 
#include<util/delay.h> 
#include"Lcd.h" 
#include<stdio.h> 
 
int Lcd_out(char c, FILE *f){ 
 if((c==10)||(c==13)) 
 Lcd_cmd(0xC0); 
 else 
 Lcd_char(c); 
 return 0; 
 } 
FILE lcd=FDEV_SETUP_STREAM( 
 Lcd_out,NULL,_FDEV_SETUP_WRITE); 
 
Ing. Germán Jesús Pereira Muñoz Página 20 
 
 
void delay_ms(long n){ 
 while(n--) 
 _delay_ms(1); 
 } 
void Lcd_gen(char dato,char rs){ 
 LCD_PORT=(dato&0xF0)>>(4-D4); 
 if(rs==1) 
 LCD_PORT|=(1<<RS); 
 LCD_PORT|=(1<<EN); 
 _delay_us(1); 
 LCD_PORT&=~(1<<EN); 
 LCD_PORT=(dato&0x0f)<<(D4); 
 if(rs) 
 LCD_PORT|=(1<<RS); 
 LCD_PORT|=(1<<EN); 
 _delay_us(1); 
 LCD_PORT&=~(1<<EN); 
 _delay_us(40); 
 } 
void Lcd_clear(void){ 
Lcd_cmd(1); 
_delay_ms(2); 
} 
void Lcd_init(void){ 
LCD_PORT=(1<<RS)|(1<<EN)|(0x0f<<D4); 
LCD_DDR=(1<<RS)|(1<<EN)|(15<<D4); 
_delay_ms(5); 
LCD_PORT=(0x30)>>(4-D4); 
LCD_PORT|=(1<<EN); 
_delay_ms(1); 
LCD_PORT&=~(1<<EN); 
_delay_ms(5); 
LCD_PORT=(0x30)>>(4-D4); 
LCD_PORT|=(1<<EN); 
_delay_ms(1); 
LCD_PORT&=~(1<<EN); 
_delay_ms(5); 
LCD_PORT=(0x30)>>(4-D4);LCD_PORT|=(1<<EN); 
_delay_ms(1); 
LCD_PORT&=~(1<<EN); 
_delay_ms(5); 
LCD_PORT=(0x20)>>(4-D4); 
LCD_PORT|=(1<<EN); 
_delay_us(1); 
LCD_PORT&=~(1<<EN); 
_delay_ms(5); 
Lcd_cmd(0x28); 
Lcd_cmd(0x0C); 
Lcd_clear(); 
stdout=&lcd; 
} 
 
Lo que realiza esta parte de programa es inicializar la lcd con comandos 
correspondientes y habilita la función para escribir datos a la pantalla. 
El nexo de este programa es Lcd.h: 
#define LCD_PORT PORTB 
#define LCD_DDR DDRB 
#define RS PB0 
#define EN PB1 
#define D4 PB2 
#define D5 PB3 
#define D6 PB4 
#define D7 PB5 
 
Ing. Germán Jesús Pereira Muñoz Página 21 
 
void Lcd_gen(char dato,char rs); 
#define Lcd_cmd(c) Lcd_gen(c,0) 
#define Lcd_char(d) Lcd_gen(d,1) 
void Lcd_clear(void); 
void delay_ms(long n); 
void Lcd_init(void); 
 
Aquí están las definiciones de cada pin de la lcd y los prototipos de las funciones del 
programa anterior, como Lcd_init para iniciar y Lcd_cmd para mandar comando. 
 
Entonces usamos los programas anteriores con un programa principal: 
 
#include<avr/io.h> 
#include "lcd.h" 
#include<stdio.h> 
#include<util/delay.h> 
 
int main (void){ 
 Lcd_init(); 
 while(1){ 
 Lcd_cmd(0x80); 
 printf("ETN 505"); 
 delay_ms(200); 
 Lcd_clear(); 
 Lcd_cmd(0x80); 
 printf("programacion"); 
 delay_ms(200); 
 Lcd_clear(); 
 Lcd_cmd(0x80); 
 printf("y metodos"); 
 
 delay_ms(200); 
 Lcd_clear(); 
 Lcd_cmd(0x80); 
 printf("numericos"); 
 delay_ms(200); 
 Lcd_clear(); 
 } 
 } 
 
El programa solo llama a las funciones definidas anteriormente para escribir un 
mensaje. 
 
Nótese el uso de la instrucción ‘printf ’ para escribir en la pantalla lcd, cosa que es 
posible solo con el stream de lcd.c. 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
D
7
1
4
D
6
1
3
D
5
1
2
D
4
1
1
D
3
1
0
D
2
9
D
1
8
D
0
7
E
6
R
W
5
R
S
4
V
S
S
1
V
D
D
2
V
E
E
3
LCD1
LM016L
RV1
10
 
 
6.5. LCD graficadora. 
 
Ing. Germán Jesús Pereira Muñoz Página 22 
 
 
De la misma manera que la lcd anterior, esta posee diferentes comandos. El modelo a 
utilizar es la pantalla LGM1264. 
 
El programa glc.c: 
#include <avr/io.h> 
#include <util/delay.h> 
 
#include "glcd.h" 
unsigned char glcd_general_read(char flags){ 
 char temp; 
 GLCD_CTRL&=~((1<<GLCD_CS1)|(1<<GLCD_CS2)); 
 if(flags & _CS1) 
 GLCD_CTRL|=(1<<GLCD_CS1); 
 else 
 GLCD_CTRL|=(1<<GLCD_CS2); 
 if(flags & _RS) GLCD_CTRL|=(1<<GLCD_RS); else GLCD_CTRL&=~(1<<GLCD_RS); 
 GLCD_PORT=0x0; 
 GLCD_DDR=0; // entrada 
 GLCD_CTRL |=(1<<GLCD_RW); //1 lectura 
 GLCD_CTRL |= (1<<GLCD_EN); //1 
 _delay_us(10); 
 temp=GLCD_IN; 
 GLCD_CTRL &=~(1<<GLCD_EN); //0 
 return temp; 
} 
 
void glcd_general_write(char data,char flags){ 
 //char i=0; 
 if(flags & _CS1) GLCD_CTRL|=(1<<GLCD_CS1); else GLCD_CTRL&=~(1<<GLCD_CS1); 
 if(flags & _CS2) GLCD_CTRL|=(1<<GLCD_CS2); else GLCD_CTRL&=~(1<<GLCD_CS2); 
 GLCD_DDR=0xFF; // salida 
 GLCD_PORT=data; 
 if(flags & _RS) GLCD_CTRL|=(1<<GLCD_RS); else GLCD_CTRL&=~(1<<GLCD_RS); 
 GLCD_CTRL &=~(1<<GLCD_RW); //0 escritura 
 GLCD_CTRL |= (1<<GLCD_EN); //1 
 _delay_us(1); 
 GLCD_CTRL &=~(1<<GLCD_EN); //0 
 _delay_us(9); 
 //_delay_us(20); 
 //while((++i)&&(glcd_status(flags) & BUSY)); 
 
} 
 
unsigned char gx,gy,gcs; 
 
void glcd_goto(unsigned char x,unsigned char y){ 
 if(x>=128){x=0;y++;} 
 if(y>=8) y=0; 
 glcd_cmd(GLCD_X(y),_BOTH); 
 if(x<64){ 
 //primer controlador... 
 gcs=_CS1; 
 glcd_cmd(GLCD_Y(x),_CS1); 
 }else{ 
 //segundo controlador... 
 gcs=_CS2; 
 glcd_cmd(GLCD_Y(x),_CS2);//la macro le quita el 64 
 } 
 gx=x;gy=y; 
} 
#ifdef GLCD_USE_GRAPHICS 
 #ifndef GLCD_USE_READ 
unsigned char glcd_matrix[128][8]; //internal matrix... just for drawing... 
 //theorically, you could read the data 
directly from 
 // the GLCD, but proteus doesn't like that 
way... 
 #endif 
void glcd_set_pixel(unsigned char x,unsigned char y){ 
 unsigned char page,bv,temp; 
 
Ing. Germán Jesús Pereira Muñoz Página 23 
 
 x &=0x7F; y&=0x3F; 
 page=y>>3; 
 bv=1<<(y&7); 
 glcd_goto(x,page); 
 #ifdef GLCD_USE_READ 
 temp=glcd_read(gcs); 
 temp=glcd_read(gcs); 
 glcd_goto(x,page); 
 #else 
 temp=glcd_matrix[x][page]; 
 temp|=bv; 
 glcd_matrix[x][page]=temp; 
 #endif 
 glcd_dat(temp,gcs); 
} 
 
void glcd_clear_pixel(unsigned char x,unsigned char y){ 
 unsigned char page,bv,temp; 
 x &=0x7F; y&=0x3F; 
 page=y>>3; 
 bv=1<<(y&7); 
 glcd_goto(x,page); 
 #ifdef GLCD_USE_READ 
 temp=glcd_read(gcs); 
 temp=glcd_read(gcs); 
 glcd_goto(x,page); 
 #else 
 temp=glcd_matrix[x][page]; 
 temp&=~bv; 
 glcd_matrix[x][page]=temp; 
 #endif 
 glcd_dat(temp,gcs); 
} 
 
void glcd_line(unsigned char x1, unsigned char y1, 
 unsigned char x2, unsigned char y2){ 
 signed char deltax,deltay,dirx,diry; 
 unsigned char acc=0,cnt; 
 deltax=x2-x1; 
 if((deltax)<0){ 
 deltax=-deltax; 
 dirx=-1; 
 }else 
 dirx=1; 
 
 deltay=y2-y1; 
 if((deltay)<0){ 
 deltay=-deltay; 
 diry=-1; 
 }else 
 diry=1; 
 glcd_set_pixel(x1,y1); 
 if(deltay>deltax){ 
 cnt=deltay; 
 while(cnt--){ 
 y1+=diry; 
 acc+=deltax; 
 if(acc>=deltay){ 
 acc-=deltay; 
 x1+=dirx; 
 } 
 glcd_set_pixel(x1,y1); 
 } 
 } 
 else{ 
 cnt=deltax; 
 while(cnt--){ 
 x1+=dirx; 
 acc+=deltay; 
 if(acc>=deltax){ 
 acc-=deltax; 
 y1+=diry; 
 } 
 glcd_set_pixel(x1,y1); 
 
Ing. Germán Jesús Pereira Muñoz Página 24 
 
 } 
 } 
} 
 
void glcd_rectangle(unsigned char x,unsigned char y, 
 unsigned char a,unsigned char b){ 
 unsigned char j; 
 for (j = 0; j < a; j++) { 
 glcd_set_pixel(x, y + j); 
 glcd_set_pixel(x + b - 1, y + j); 
 } 
 for (j = 0; j < b; j++) { 
 glcd_set_pixel(x + j, y); 
 glcd_set_pixel(x + j, y + a - 1); 
 } 
} 
 
void glcd_circle(unsigned char xcenter, unsigned char ycenter, 
 unsigned char radius){ 
 int tswitch, y, x = 0; 
 unsigned char d; 
 
 d = ycenter - xcenter; 
 y = radius; 
 tswitch = 3 - 2 * radius; 
 while (x <= y) { 
 glcd_set_pixel(xcenter + x, ycenter + y); 
 glcd_set_pixel(xcenter + x, ycenter - y); 
 glcd_set_pixel(xcenter - x, ycenter + y); 
 glcd_set_pixel(xcenter - x, ycenter - y); 
 glcd_set_pixel(ycenter + y - d, ycenter + x); 
 glcd_set_pixel(ycenter + y - d, ycenter - x); 
 glcd_set_pixel(ycenter - y - d, ycenter + x); 
 glcd_set_pixel(ycenter - y - d, ycenter - x); 
 
 if (tswitch < 0) tswitch += (4 * x + 6); 
 else { 
 tswitch += (4 * (x - y) + 10); 
 y--; 
 } 
 x++; 
 } 
} 
 
 
#endif //GLCD_USE_GRAPHICS 
 
#ifdef GLCD_USE_TEXT 
 #include <avr/pgmspace.h> 
 prog_char glcd_cg[][5]={ 
 {0x00,0x00,0x00,0x00,0x00}, // 
 {0x00,0x00,0x4f,0x00,0x00}, //! 
 {0x00,0x07,0x00,0x07,0x00}, //" 
 {0x14,0x7f,0x14,0x7f,0x14}, //# 
 {0x24,0x2a,0x7f,0x2a,0x12}, //$ 
 {0x23,0x13,0x08,0x64,0x62}, //% 
 {0x36,0x49,0x55,0x22,0x50}, //& 
 {0x00,0x00,0x07,0x00,0x00}, //' 
 {0x00,0x1c,0x22,0x41,0x00}, //( 
 {0x00,0x41,0x22,0x1c,0x00}, //) 
 {0x14,0x08,0x3e,0x08,0x14}, //* 
 {0x08,0x08,0x3e,0x08,0x08}, //+ 
 {0x00,0x50,0x30,0x00,0x00}, //, 
 {0x08,0x08,0x08,0x08,0x08}, //- 
 {0x00,0x60,0x60,0x00,0x00}, //. 
 {0x20,0x10,0x08,0x04,0x02}, /// 
 {0x3e,0x51,0x49,0x45,0x3e}, //0 
 {0x00,0x42,0x7f,0x40,0x00}, //1 
 {0x62,0x51,0x49,0x45,0x42}, //2 
 {0x21,0x41,0x45,0x4b,0x31}, //3 
 {0x18,0x14,0x12,0x7f,0x10}, //4 
 {0x27,0x45,0x45,0x45,0x39}, //5 
 {0x3c,0x4a,0x49,0x49,0x30}, //6 
 {0x01,0x71,0x09,0x05,0x03}, //7 
 
Ing. Germán Jesús Pereira Muñoz Página 25 
 
 {0x36,0x49,0x49,0x49,0x36}, //8 
 {0x06,0x49,0x49,0x29,0x1e}, //9 
 {0x00,0x36,0x36,0x00,0x00}, //: 
 {0x00,0x76,0x36,0x00,0x00}, //; 
 {0x08,0x14,0x22,0x41,0x00}, //< 
 {0x14,0x14,0x14,0x14,0x14}, //={0x00,0x41,0x22,0x14,0x08}, //> 
 {0x02,0x01,0x51,0x09,0x06}, //? 
 {0x3e,0x41,0x5d,0x55,0x1e}, //@ 
 {0x7e,0x11,0x11,0x11,0x7e}, //A 
 {0x7f,0x49,0x49,0x49,0x36}, //B 
 {0x3e,0x41,0x41,0x41,0x22}, //C 
 {0x7f,0x41,0x41,0x22,0x1C}, //D 
 {0x7f,0x49,0x49,0x49,0x41}, //E 
 {0x7f,0x09,0x09,0x09,0x01}, //F 
 {0x3e,0x41,0x49,0x49,0x7a}, //G 
 {0x7f,0x08,0x08,0x08,0x7f}, //H 
 {0x00,0x41,0x7f,0x41,0x00}, //I 
 {0x20,0x40,0x41,0x3f,0x01}, //J 
 {0x7f,0x08,0x14,0x22,0x41}, //K 
 {0x7f,0x40,0x40,0x40,0x40}, //L 
 {0x7f,0x02,0x0c,0x02,0x7f}, //M 
 {0x7f,0x04,0x08,0x10,0x7f}, //N 
 {0x3e,0x41,0x41,0x41,0x3e}, //O 
 {0x7f,0x09,0x09,0x09,0x06}, //P 
 {0x3e,0x41,0x51,0x21,0x5e}, //Q 
 {0x7f,0x09,0x19,0x29,0x46}, //R 
 {0x26,0x49,0x49,0x49,0x32}, //S 
 {0x01,0x01,0x7f,0x01,0x01}, //T 
 {0x3f,0x40,0x40,0x40,0x3f}, //U 
 {0x1f,0x20,0x40,0x20,0x1f}, //V 
 {0x3f,0x40,0x38,0x40,0x3f}, //W 
 {0x63,0x14,0x08,0x14,0x63}, //X 
 {0x07,0x08,0x70,0x08,0x07}, //Y 
 {0x61,0x51,0x49,0x45,0x43}, //Z 
 {0x00,0x7f,0x41,0x41,0x00}, //[ 
 {0x02,0x04,0x08,0x10,0x20}, // /invertido 
 {0x00,0x41,0x41,0x7f,0x00}, //] 
 {0x04,0x02,0x01,0x02,0x04}, //^ 
 {0x40,0x40,0x40,0x40,0x40}, //_ 
 {0x00,0x00,0x03,0x05,0x00}, //` 
 {0x20,0x54,0x54,0x54,0x78}, //a 
 {0x7f,0x44,0x44,0x44,0x38}, //b 
 {0x38,0x44,0x44,0x44,0x44}, //c 
 {0x38,0x44,0x44,0x44,0x3F}, //d 
 {0x38,0x54,0x54,0x54,0x18}, //e 
 {0x04,0x04,0x7e,0x05,0x05}, //f 
 {0x08,0x54,0x54,0x54,0x3c}, //g 
 {0x7f,0x08,0x04,0x04,0x78}, //h 
 {0x00,0x44,0x7d,0x40,0x00}, //i 
 {0x40,0x40,0x44,0x3d,0x00}, //j 
 {0x7f,0x10,0x28,0x44,0x00}, //k 
 {0x00,0x41,0x7f,0x40,0x00}, //l 
 {0x7c,0x04,0x7c,0x04,0x78}, //m 
 {0x7c,0x08,0x04,0x04,0x78}, //n 
 {0x38,0x44,0x44,0x44,0x38}, //o 
 {0x7c,0x24,0x24,0x24,0x18}, //p 
 {0x18,0x24,0x24,0x24,0x7C}, //q 
 {0x7c,0x08,0x04,0x04,0x00}, //r 
 {0x48,0x54,0x54,0x54,0x24}, //s 
 {0x04,0x04,0x3F,0x44,0x44}, //t 
 {0x3c,0x40,0x40,0x7c,0x40}, //u 
 {0x1c,0x20,0x40,0x20,0x1c}, //v 
 {0x3c,0x40,0x30,0x40,0x3c}, //w 
 {0x44,0x28,0x10,0x28,0x44}, //x 
 {0x0c,0x50,0x50,0x50,0x3c}, //y 
 {0x44,0x64,0x54,0x4C,0x44}, //z 
 {0x08,0x36,0x41,0x41,0x00}, //{ 
 {0x00,0x00,0x77,0x00,0x00}, //| 
 {0x00,0x41,0x41,0x36,0x08}, //} 
 {0x08,0x04,0x08,0x10,0x08}, //~ 
 {0x3E,0x22,0x22,0x22,0x3E}, //127 
 {0x3e,0x55,0x55,0x41,0x22}, //? 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 
Ing. Germán Jesús Pereira Muñoz Página 26 
 
 {0x00,0xA0,0x60,0x00,0x00}, //? 
 {0x44,0x44,0x3e,0x05,0x05}, //? 
 {0x20,0x10,0x20,0x40,0x20}, //? 
 {0x00,0x40,0x40,0x40,0x00}, //? 
 {0x00,0x40,0x7F,0x40,0x00}, //? 
 {0x00,0x24,0xFF,0x24,0x00}, //? 
 {0x00,0x02,0x01,0x02,0x00}, //? 
 {0x13,0x6B,0x64,0x62,0x61}, //? 
 {0x58,0x55,0x56,0x55,0x68}, //? 
 {0x00,0x08,0x14,0x22,0x00}, //? 
 {0x3E,0x41,0x7f,0x49,0x41}, //? 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x00,0x00,0x01,0x02,0x00}, //? 
 {0x00,0x02,0x01,0x00,0x00}, //? 
 {0x00,0x01,0x02,0x01,0x02}, //? 
 {0x02,0x01,0x02,0x01,0x00}, //? 
 {0x00,0x1c,0x1c,0x1c,0x00}, //? 
 {0x00,0x08,0x08,0x08,0x00}, //- 
 {0x08,0x08,0x08,0x08,0x08}, //- 
 {0x07,0x07,0x00,0x07,0x07}, //? 
 {0x01,0x07,0x01,0x07,0x07}, //? 
 {0x48,0x55,0x56,0x55,0x24}, //? 
 {0x00,0x22,0x14,0x08,0x00}, //? 
 {0x3C,0x44,0x78,0x54,0x58}, //? 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x3E,0x22,0x22,0x22,0x3E}, //• 
 {0x0E,0x09,0x70,0x09,0x0E}, //? 
 {0x00,0x00,0x00,0x00,0x00}, // 
 {0x00,0x00,0x79,0x00,0x00}, //¡ 
 {0x38,0x44,0xc6,0x44,0x44}, //¢ 
 {0x4f,0x60,0x50,0x40,0x60}, //£ 
 {0x44,0x28,0x28,0x28,0x44}, //¤ 
 {0x07,0x28,0x70,0x28,0x07}, //¥ 
 {0x00,0x00,0x77,0x00,0x00}, //¦ 
 {0x26,0x55,0x55,0x55,0x32}, //§ 
 {0x00,0x01,0x00,0x01,0x00}, //¨ 
 {0x3E,0x49,0x55,0x55,0x3E}, //© 
 {0x08,0x15,0x15,0x1E,0x00}, //ª 
 {0x08,0x14,0x2A,0x14,0x22}, //« 
 {0x00,0x08,0x08,0x38,0x00}, //¬ 
 {0x00,0x08,0x08,0x08,0x00}, //- 
 {0x3E,0x5D,0x4D,0x55,0x3E}, //® 
 {0x01,0x01,0x01,0x01,0x01}, //¯ 
 {0x00,0x02,0x05,0x02,0x00}, //° 
 {0x28,0x28,0x3e,0x28,0x28}, //± 
 {0x00,0x0D,0x0D,0x0a,0x00}, //² 
 {0x00,0x09,0x0B,0x07,0x00}, //³ 
 {0x00,0x00,0x02,0x01,0x00}, //´ 
 {0x40,0x7c,0x40,0x40,0x3c}, //µ 
 {0x06,0x09,0x7f,0x09,0x7F}, //¶ 
 {0x00,0x1c,0x1c,0x1c,0x00}, //· 
 {0x40,0xa8,0xa8,0xF0,0x00}, //¸ 
 {0x00,0x09,0x0F,0x08,0x00}, //¹ 
 {0x00,0x06,0x09,0x06,0x00}, //º 
 {0x22,0x14,0x2a,0x14,0x08}, //» 
 {0x24,0x17,0x38,0x24,0x07}, //¼ 
 {0x24,0x17,0x58,0x74,0x52}, //½ 
 {0x25,0x17,0x38,0x24,0x07}, //¾ 
 {0x30,0x48,0x45,0x40,0x20}, //¿ 
 {0x78,0x25,0x26,0x24,0x78}, // 
 {0x78,0x24,0x26,0x25,0x78}, //M 
 {0x78,0x26,0x25,0x26,0x78}, //• 
 {0x78,0x25,0x25,0x25,0x78}, //Í 
 {0x78,0x25,0x24,0x25,0x78}, //c 
 {0x78,0x24,0x25,0x24,0x78}, //o 
 {0x7E,0x09,0x7F,0x49,0x41}, //? 
 {0x1e,0x21,0x61,0x21,0x12}, //A 
 {0xFC,0x95,0x96,0x94,0x84}, //? 
 {0xFC,0x94,0x96,0x95,0x84}, //? 
 {0xFC,0x96,0x95,0x96,0x84}, //? 
 {0xFC,0x95,0x94,0x95,0x84}, //_ 
 
Ing. Germán Jesús Pereira Muñoz Página 27 
 
 {0x00,0x45,0x7E,0x44,0x00}, //? 
 {0x00,0x44,0x7E,0x45,0x00}, //? 
 {0x00,0x46,0x7D,0x46,0x00}, //? 
 {0x00,0x45,0x7C,0x45,0x00}, //? 
 {0x7f,0x49,0x49,0x22,0x28}, //? 
 {0x7C,0x09,0x11,0x21,0x7C}, //? 
 {0x78,0x85,0x86,0x84,0x78}, //? 
 {0x78,0x84,0x86,0x85,0x78}, //? 
 {0x78,0x86,0x85,0x86,0x78}, //? 
 {0x78,0x85,0x85,0x85,0x78}, //? 
 {0x78,0x85,0x84,0x85,0x78}, //? 
 {0x00,0x05,0x02,0x05,0x00}, //? 
 {0x3e,0x71,0x49,0x43,0x3e}, //? 
 {0x7C,0x81,0x82,0x80,0x7C}, //? 
 {0x7C,0x80,0x82,0x81,0x7C}, //? 
 {0x7C,0x82,0x81,0x82,0x7C}, //? 
 {0x7C,0x81,0x80,0x81,0x7C}, //? 
 {0x0E,0x08,0x72,0x09,0x0E}, //? 
 {0x41,0x7F,0x55,0x55,0x08}, //? 
 {0x40,0x7f,0x41,0x49,0x36}, //? 
 {0x20,0x55,0x56,0x54,0x78}, //J 
 {0x20,0x54,0x56,0x55,0x78}, //? 
 {0x20,0x56,0x55,0x56,0x78}, //? 
 {0x20,0x55,0x55,0x55,0x78}, //? 
 {0x20,0x55,0x54,0x55,0x78}, //? 
 {0x20,0x54,0x56,0x56,0x78}, //? 
 {0x35,0x54,0x78,0x54,0x58}, //? 
 {0x1C,0x22,0x62,0x22,0x22}, //? 
 {0x38,0x55,0x56,0x54,0x18}, //? 
 {0x38,0x56,0x55,0x54,0x18}, //? 
 {0x38,0x56,0x55,0x56,0x18}, //? 
 {0x38,0x55,0x54,0x55,0x18}, //? 
 {0x00,0x45,0x7A,0x40,0x00}, //? 
 {0x00,0x44,0x7A,0x41,0x00}, //? 
 {0x00,0x46,0x7d,0x42,0x00}, //? 
 {0x00,0x45,0x7C,0x41,0x00}, //? 
 {0x38,0x47,0x45,0x47,0x38}, //J 
 {0x7c,0x09,0x05,0x05,0x78}, //? 
 {0x38,0x45,0x46,0x44,0x38}, //? 
 {0x38,0x44,0x46,0x45,0x38}, //? 
 {0x38,0x46,0x45,0x46,0x38}, //? 
 {0x38,0x45,0x45,0x45,0x38}, //? 
 {0x38,0x45,0x44,0x45,0x38}, //? 
 {0x00,0x08,0x2A,0x08,0x00}, //? 
 {0x38,0x64,0x54,0x4C,0x38}, //? 
 {0x3c,0x41,0x42,0x7c,0x40}, //? 
 {0x3c,0x40,0x42,0x7D,0x40}, //? 
 {0x3c,0x42,0x41,0x7E,0x40}, //? 
 {0x3D,0x40,0x40,0x7D,0x40}, //? 
 {0x0c,0x50,0x52,0x51,0x3c}, //? 
 {0x41,0x7F,0x14,0x14,0x08}, //? 
 {0x0c,0x51,0x50,0x51,0x3c} //? 
 }; 
 char gstyle; 
 void glcd_chr(unsigned char chr){ 
 // escribe en la posicion ctual... 
 unsigned char i,d; 
 if(chr<0x20){ 
 if((chr==13)|(chr==10)) glcd_crlf(); // mejor... 
 if(chr==27) glcd_clear(); 
 if(chr!=9) 
 return; 
 chr=0x20; 
 } 
 if(gx>((gstyle & GLCD_BOLD)?117:123)) 
 glcd_crlf(); 
 
 for(i=0;i<5;i++){ 
 d=pgm_read_byte(&glcd_cg[chr-0x20][i]); 
 if(gstyle & GLCD_NEG) d=~d; // negado 
 glcd_byte(d); 
 if(gstyle & GLCD_BOLD) 
 glcd_byte(d); //repete para negrita. 
 } 
 
Ing. Germán Jesús Pereira Muñoz Página 28 
 
 if (gx<127) 
 glcd_byte((gstyle & GLCD_NEG)? 0xFF:0x00); 
 } 
#endif //GLCD_USE_TEXT 
 
void glcd_byte(unsigned char data){ 
 if(gx==64){ 
 gcs=_CS2; 
 glcd_cmd(GLCD_Y(0),_CS2); 
 } 
 if(gx==128) 
 glcd_crlf(); 
 glcd_dat(data,gcs); 
 #if defined(GLCD_USE_GRAPHICS) && !defined(GLCD_USE_READ) 
 glcd_matrix[gx][gy]=data;//directly! 
 #endif 
 gx++; // el contador interno tambi?crece... 
} 
 
void glcd_clear(void){ 
 unsigned char i,j; 
 for(j=0;j<8;j++){ 
 glcd_cmd(GLCD_X(j),_BOTH); //primera fila (de 8 (de 8 bits)) 
 glcd_cmd(GLCD_Y(0),_BOTH);//primera columna (de 64) 
 for (i=0;i<64;i++){ 
 glcd_dat(0,_BOTH); // borrar todos los datos de memoria 
 #if defined(GLCD_USE_GRAPHICS) && !defined(GLCD_USE_READ) 
 glcd_matrix[i][j]=0; 
 glcd_matrix[i+64][j]=0; 
 #endif 
 } 
 } 
 
 glcd_cmd(GLCD_X(0),_BOTH); //primera fila (de 8 (de 8 bits)) 
 glcd_cmd(GLCD_Y(0),_BOTH); //primera columna (de 64) 
 gx=0; 
 gy=0; 
 gcs=_CS1; 
 
} 
void glcd_clear_line(unsigned char y){ 
 unsigned char i; 
 glcd_cmd(GLCD_X(y),_BOTH); 
 glcd_cmd(GLCD_Y(0),_BOTH); //primera columna (de 64) 
 for (i=0;i<64;i++){ 
 glcd_dat(0,_BOTH); // borrar todos los datos de memoria 
 #if defined(GLCD_USE_GRAPHICS) && !defined(GLCD_USE_READ) 
 glcd_matrix[i][y&7]=0; 
 glcd_matrix[i+64][y&7]=0; 
 #endif 
 } 
 glcd_cmd(GLCD_X(y),_BOTH); 
 glcd_cmd(GLCD_Y(0),_BOTH); //primera columna (de 64) 
 gx=0; 
 gy=y; 
 gcs=_CS1; 
} 
 
#ifdef GLCD_USE_STDOUT 
static FILE mystdout = FDEV_SETUP_STREAM(glcd_putchar,NULL,_FDEV_SETUP_WRITE); 
int glcd_putchar(char c, FILE *stream) 
{ 
 glcd_chr(c); // mantiene el estilo... 
 return 0; 
} 
#endif 
 
void glcd_init(void){ 
 GLCD_PORT=0; 
 GLCD_DDR=0xFF; // salida 
 GLCD_CTRL&=~((1<<GLCD_RS)|(1<<GLCD_RW)|(1<<GLCD_EN) 
 |(1<<GLCD_CS1)|(1<<GLCD_CS2)|(1<<GLCD_RST)); 
 GLCD_CDDR|=(1<<GLCD_RS)|(1<<GLCD_RW)|(1<<GLCD_EN) 
 |(1<<GLCD_CS1)|(1<<GLCD_CS2)|(1<<GLCD_RST); 
 
Ing. Germán Jesús Pereira Muñoz Página 29 
 
 _delay_ms(1); 
 GLCD_CTRL|=(1<<GLCD_RST); //nunca mas... 
 _delay_ms(1); 
 glcd_off(); 
 glcd_cmd(GLCD_Z(0),_BOTH); //coordenadas iniciales 
 glcd_clear(); 
 glcd_on(); // encender! 
 #ifdef GLCD_USE_STDOUT 
 stdout = &mystdout; 
 #endif} 
 
El programa anterior tiene todos los símbolos que se pueden introducir a la pantalla 
graficadora y las funciones para llamarla como en el caso anterior. 
 
El nexo, con algunos comentarios: 
#ifndef GLCD_H 
#define GLCD_H 
 
//comentar la siguiente linea si no se usará la pantalla para 
//funciones gráficas 
#define GLCD_USE_GRAPHICS 
 
 
//comentar la siguiente linea se no se usará la pantalla para 
//dseplegar texto... 
#define GLCD_USE_TEXT 
 
//comentar la siguiente línea se no se usará la pantalla como 
//dispositivo estándar de salida [stdout para printf y printf_P] 
#define GLCD_USE_STDOUT 
 
 
//comentar la siguiente línea se se desea una matrix para 
//los gráficos, o leer los datos directamente; 
//#define GLCD_USE_READ 
//se debe comentar para simular en Proteus! 
 
//configuracion de pines, se utiliza dos puertos, 
//uno de 8 bits para datos/comando y otros 6 pines para control 
//Puerto de Datos 
#define GLCD_PORT PORTB 
#define GLCD_IN PINB 
#define GLCD_DDR DDRB 
//Puerto de Control 
#define GLCD_CTRL PORTC 
#define GLCD_CDDR DDRC 
//pines de control en el puerto de control 
#define GLCD_RS PC0 
#define GLCD_RW PC1 
#define GLCD_EN PC2 
#define GLCD_CS1 PC3 
#define GLCD_CS2 PC4 
#define GLCD_RST PC5 
//fin de configuración de Hardware... 
/*********************************************************************/ 
#if defined(GLCD_USE_STDOUT) && !defined(GLCD_USE_TEXT) 
#error "Configure glcd.h para usar texto si desea utilizarlo como dispositivo estándar de salida..." 
#endif 
 
//definicion de posibles chip select, para optimizar las rutinas 
#define _CS1 1 
#define _CS2 2 
#define _RS 16 
#define _BOTH (_CS1|_CS2) 
#define _BUSY 0x80 //bit 7 de estado 
 
Ing. Germán Jesús Pereira Muñoz Página 30 
 
 
//comandos de pantalla GLCD 
#define GLCD_ON 0x3F 
#define GLCD_OFF 0x3E 
#define GLCD_Y(y) (0x40|((y)&63)) 
#define GLCD_X(x) (0xB8|((x)&7)) 
#define GLCD_Z(z) (0xC0|((z)&63)) 
 
 
void glcd_general_write(char data,char flags); 
#define glcd_cmd(cmd,cs) glcd_general_write(cmd,cs) 
#define glcd_dat(dat,cs) glcd_general_write(dat,(cs)|_RS) 
unsigned char glcd_general_read(char flags); 
#define glcd_read(cs) glcd_general_read((cs)|_RS) 
void glcd_init(void); 
void glcd_clear(void); 
void glcd_clear_line(unsigned char y); 
#define glcd_on() glcd_cmd(GLCD_ON,_BOTH) 
#define glcd_off() glcd_cmd(GLCD_OFF,_BOTH) 
 
extern unsigned char gx,gy,gcs; 
void glcd_byte(unsigned char data); 
void glcd_goto(unsigned char x,unsigned char y); 
#define glcd_cr() glcd_cmd(GLCD_Y(gx=0),gcs=_CS1) 
#define glcd_lf() glcd_cmd(GLCD_X(++gy),_BOTH) 
#define glcd_crlf() {glcd_cr();glcd_lf();} 
 
#ifdef GLCD_USE_GRAPHICS 
void glcd_set_pixel(unsigned char x,unsigned char y); 
void glcd_clear_pixel(unsigned char x,unsigned char y); 
void glcd_rectangle(unsigned char x,unsigned char y, 
 unsigned char a,unsigned char b); 
void glcd_circle(unsigned char xcenter, unsigned char ycenter, 
 unsigned char radius); 
void glcd_line(unsigned char x1, unsigned char y1, 
 unsigned char x2, unsigned char y2); 
#endif //GLCD_USE_GRAPHICS 
 
#ifdef GLCD_USE_TEXT 
 void glcd_chr(unsigned char chr); 
 
 extern char gstyle; 
 #define set_glcd_style(a) gstyle=a 
 #define GLCD_NEG 0x01 
 #define GLCD_BOLD 0x02 
 
 #ifdef GLCD_USE_STDOUT 
 #include <stdio.h> 
 int glcd_putchar(char c, FILE *stream); 
 #endif 
#endif //GLCD_USE_TEXT 
 
#endif 
 
El programa principal entonces solo llama a las funciones anteriores. Por ejemplo para 
graficar un eje de coordenadas y unos botones: 
#include <avr/io.h> 
#include <avr/pgmspace.h> 
#include <util/delay.h> 
 
#include "glcd.h" 
 
void delay_ms(unsigned int n){ 
 while(n--) 
 _delay_ms(1); 
 
Ing. Germán Jesús Pereira Muñoz Página 31 
 
} 
 
 
int main(void){ 
 int i=0; 
 glcd_init(); 
 glcd_rectangle(55,40,20,20); 
 glcd_rectangle(10,40,20,20); 
 glcd_rectangle(100,40,20,20); 
 glcd_goto(17,6); 
 glcd_chr('A'); 
 glcd_goto(62,6); 
 glcd_chr('B'); 
 glcd_goto(107,6); 
 glcd_chr('C'); 
 glcd_line(10,18,125,18); 
 glcd_line(65,30,65,0); 
 glcd_goto(10,2); 
 glcd_chr('<'); 
 glcd_goto(120,2); 
 glcd_chr('>'); 
 glcd_goto(63,8); 
 glcd_chr('^'); 
 glcd_goto(63,4); 
 glcd_chr('v'); 
 glcd_goto(70,8); 
 glcd_chr('y'); 
 glcd_goto(120,3); 
 glcd_chr('x'); 
 delay_ms(1000); 
 while(1){ 
 
 //gstyle ^=(GLCD_NEG); 
 
 delay_ms(100); 
 } 
} 
 
C
S
1
1
C
S
2
2
G
N
D
3
V
C
C
4
V
0
5
D
I
6
R
/W
7
E
8
D
B
0
9
D
B
1
1
0
D
B
2
1
1
D
B
3
1
2
D
B
4
1
3
D
B
5
1
4
D
B
6
1
5
D
B
7
1
6
R
S
T
1
7
-V
o
u
t
1
8
LCD1
LGM12641BS1R
PA0/ADC0
40
PA1/ADC1
39
PA2/ADC2
38
PA3/ADC3
37
PA4/ADC4
36
PA5/ADC5
35
PA6/ADC6
34
PB0/XCK/T0
1
PB1/T1
2
PB2/INT2/AIN0
3
PB3/OC0/AIN1
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
PA7/ADC7
33
RESET
9
XTAL1
13
XTAL2
12
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP
20
PD7/OC2
21
AVCC
30
AREF
32
U1
ATMEGA32
R1
10k
R2
10k
 
 
6.6. Manejo de teclado matricial. 
 
 
Ing. Germán Jesús Pereira Muñoz Página 32 
 
Un teclado matricial es mucho más fácil de controlar que una pantalla como la 
anterior. Este solo testea un puerto para conocer el botón que se presionó. Para un 
teclado 4x4: 
#include<avr/io.h> 
#include<util/delay.h> 
#include "teclado.h" 
#include<avr/pgmspace.h> 
prog_char tecla[4][4]= {{'7','8','9','/'}, 
 {'4','5','6','*'}, 
 {'1','2','3','-'}, 
 {'c','0','=','+'}}; 
char leer(void){ 
 char i,j,k,m; 
 k=(1<<T_FILA_2)|(1<<T_FILA_3)|(1<<T_FILA_4);/* TAMBIÉN PODEMOS PONER 
(15<<T_COL_1);*/ 
 for(i=0;i<4;i++){ 
 T_FILAS_PORT=k; 
 retardo(); 
 m=T_COL_PIN; 
 m&=(15<<T_COL_1); 
 if(m!=(15<<T_COL_1)){ 
 for(j=0;j<4;j++){ 
 if((m&(1<<T_COL_1))==0){ 
 return pgm_read_byte(&tecla[i][j]);/*para retornar el valor, ya que return no funciona*/ 
 } 
 m>>=1; 
 }} 
 k<<=1; 
 k|=(1<<T_FILA_1);/*estas dos últimas para rotar el cero a la izq*/ 
 } 
 return 0; 
 } 
 char espera(void){ 
 char t; 
 do{ 
 t=leer(); 
 }while(t==0); 
 while(leer()!=0) 
 retardo(); 
 return t;} 
 void inicia(void){ 
 T_FILAS_PORT=0; 
 T_FILAS_DDR=(15<<T_FILA_1); 
 T_COL_DDR=0; 
 T_COL_PORT=(15<<T_COL_1); 
 } 
 
 Lo que realiza este programa es definir los botones que se usara en una matriz, 
luegolos ‘for’ testean cada botón moviendo por filas y columnas y cuando detectan un 
cero (cuando alguien presiona un botón) entonces se conoce la posición del botón y asi 
su símbolo definido anteriormente y ese se devuelve como dato. 
El programa nexo: 
#define T_FILAS_PORT PORTB 
#define T_FILAS_DDR DDRB 
#define T_FILA_1 PB2 
#define T_FILA_2 PB3 
#define T_FILA_3 PB4 
#define T_FILA_4 PB5 
#define T_COL_PORT PORTC 
#define T_COL_DDR DDRC 
#define T_COL_PIN PINC 
#define T_COL_1 PC0 
#define T_COL_2 PC1 
 
Ing. Germán Jesús Pereira Muñoz Página 33 
 
#define T_COL_3 PC2 
#define T_COL_4 PC3 
#define retardo() _delay_ms(1) 
char leer(void); 
char espera(void); 
void inicia(void); 
 
Este solo define las filas y columnas y las funciones de esperar dato, leer dato y la de 
iniciar. 
 
Luego, el programa que utiliza este sistema: 
 
#include "Lcd.h" 
#include "teclado.h" 
#include<stdio.h> 
int teclado_getchar(FILE*stream){ 
unsigned char tecla; 
 tecla=espera(); 
 if(tecla=='c'){ 
 Lcd_clear(); 
 return 0; 
 } 
 if ((tecla>='0')&&(tecla<='9')) 
 {Lcd_char(tecla); 
 return tecla; 
 } 
 Lcd_char(tecla); 
 return 0; 
 } 
FILE teclado=FDEV_SETUP_STREAM(NULL,teclado_getchar,_FDEV_SETUP_READ); 
int main(void){ 
 int f; 
 inicia(); 
 stdin=&teclado; 
 Lcd_init(); 
 while(1){ 
 Lcd_clear(); 
 scanf("%3i",&f); 
 Lcd_cmd(0xC0); 
 printf("sumado 20 es:%i",f+20); 
 delay_ms(1000); 
 } 
 } 
 
Teclado.c nos devuelve el dato en una variable llamada ‘tecla’, entonces solo 
preguntamos qué valor tiene tecla y así sabremos que botón se presionó. Una vez 
conocido el dato, nosotros podemos hacer lo que queramos con ese dato. Como por 
ejemplo del programa sumarle un número como 20. 
 
Nótese que el dato introducido es de tipo char y se trabaja como tal y si se requiere 
trabajar como entero o punto flotante, como en el programa, utilizamos un stream. 
 
 
Ing. Germán Jesús Pereira Muñoz Página 34 
 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
D
7
1
4
D
6
1
3
D
5
1
2
D
4
1
1
D
3
1
0
D
2
9
D
1
8
D
0
7
E
6
R
W
5
R
S
4
V
S
S
1
V
D
D
2
V
E
E
3
LCD1
LM016L
RV1
10
1 2 3
654
8 9
=
7
++C
ON 0
A
B
C
D
1 2 43
 
 
 
 
 
 
6.7. Manejo de interrupciones. 
 
El AVR cuenta con distintas interrupciones, como por ejemplo las más usuales, la 
temporizada y la externa. Manejarlas es muy sencillo ya que se tiene predeterminado 
una función donde se salta el programa para atender a la interrupción. Esta es: ISR 
(‘nombre’_COMP_vect) {}. Donde en ‘nombre’ colocamos el nombre de la interrupción a 
utilizar. 
Entonces para un reloj digital, utilizando la interrupción temporizada tenemos: 
 
#include <avr/io.h> 
#include "lcd.h" 
#include <avr/interrupt.h> 
volatile char h,m,s,c; 
volatile char contador; 
void timer_init (void) { 
contador=0; 
TCNT0=0; 
OCR0=74; 
TCCR0=(1<<WGM01)|(3<<CS00); 
TIMSK|=(1<<OCIE0); 
} 
ISR (TIMER0_COMP_vect) { 
if(++contador>=25) { 
contador=0; 
if(++c<100) return; 
c=0; 
if(++s<60) return; 
s=0; 
if(++m<60) return; 
m=0; 
if(++h<24) return; 
h=0; 
} 
} 
El programa realiza la cuenta de un reloj desde centésimas de segundo y se tiene el 
cálculo efectuado de la siguiente manera: 
 
Para un cristal de 12Mhz tenemos que cada 10ms seria 120000 cuentas. Pero el 
contador TNT0 solo va de 0 a 255 entonces escalamos y el valor exacto para la división 
 
Ing. Germán Jesús Pereira Muñoz Página 35 
 
es 64 con lo que tenemos 1875 que sigue siendo mayor a 255. Pero 75x25=1875 y 
entonces contamos hasta 75 y creamos un contador hasta 25 para incrementar una 
centésima de segundo. 
Los demás registros ayudan a configurar este detalle. (ver la hoja de datos) 
 
El programa principal no realiza nada más que imprimir los valores de centésima, 
segundo, minuto y hora: 
#include <avr/io.h> 
#include "lcd.h" 
#include <stdio.h> 
#include <avr/interrupt.h> 
void timer_init(void); 
extern volatile char h,m,s,c; //para compartir variables externas 
int main (void) { 
Lcd_Init(); 
timer_init(); 
sei(); 
while(1) { 
Lcd_cmd(0x80); 
printf("%02i:%02i:%02i-%02i",(unsigned int)h,(unsigned int)m,(unsigned int)s,(unsigned int)c); 
} 
} 
Utilizamos sei() para habilitar todas las interrupciones. 
 
PA0/ADC0
40
PA1/ADC1
39
PA2/ADC2
38
PA3/ADC3
37
PA4/ADC4
36
PA5/ADC5
35
PA6/ADC6
34
PB0/XCK/T0
1
PB1/T1
2
PB2/INT2/AIN0
3
PB3/OC0/AIN1
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
PA7/ADC7
33
RESET
9
XTAL1
13
XTAL2
12
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP
20
PD7/OC2
21
AVCC
30
AREF
32
U1
ATMEGA32
D
7
1
4
D
6
1
3
D
5
1
2
D
4
1
1
D
3
1
0
D
2
9
D
1
8
D
0
7
E
6
R
W
5
R
S
4
V
S
S
1
V
D
D
2
V
E
E
3
LCD1
LM016L
R1
10k
 
 
6.8. Manejo del conversor análogo digital. 
 
Una herramienta fundamental para trabajar en tiempo real con el controlador es su 
conversor análogo digital, el cual es bastante útil para el control de procesos. 
 
Un ejemplo bastante sencillo de esta herramienta es el control de temperatura 
utilizando un sensor como el LM-35. Pero para no complicarnos con el algoritmo de 
control y los parámetros, solo utilizaremos el conversor para visualizar los datos que 
manda el sensor en variación de voltaje. 
 
El programa para esto es bastante sencillo. 
 
Ing. Germán Jesús Pereira Muñoz Página 36 
 
 
#include<avr/io.h> 
#include "lcd.h" 
#include<stdio.h> 
volatile int a; 
void iniciar(void){ 
 ADMUX=0x40; 
 SFIOR=0x00; 
 ADCSRA = 0x86; 
 
 ADCSRA |= (1<<ADSC); 
 
 } 
 
int main (void){ 
 Lcd_init(); 
 iniciar(); 
 while(1){ 
 ADCSRA |=(1<<ADSC); 
 a=ADC; 
 
 Lcd_cmd(0x80); 
 printf("La temperatura:"); 
 Lcd_cmd(0xC0); 
 printf("%04i C",a);} 
 } 
El programa solo configura los registros para utilizar conversión por medio del primer 
canal, con referencia de 5V y con solo 8 bits de los 10 bits de resolución que se tiene. 
 
Nótese que el dato se guarda en el registro ADC y que la instrucción ADCSRA |= (1<<ADSC); 
indica al controlador que convierta. 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
D
7
1
4
D
6
1
3
D
5
1
2
D
4
1
1
D
3
1
0
D
2
9
D
1
8
D
0
7
E
6
R
W
5
R
S
4
V
S
S
1
V
D
D
2
V
E
E
3
LCD1
LM016L
RV1
10
6.0
3
1
VOUT
2
U2
LM35
 
 
 
 
 
 
6.9. Comunicación serial. 
 
Una herramienta muy importante para interactuar con una PC es la comunicación 
serial. Mediante esta se pueden mandar o recibir datos desde el controlador o hacia 
 
Ing. Germán Jesús Pereira Muñoz Página 37 
 
este. El microcontrolador ya tiene incluida esta comunicación y solo basta con colocar 
el dato a enviar en el buffer correspondiente. Si un dato llega también llega a un 
registro buffer. 
 
El programa habilita la comunicación serial para recepción y transmisión. 
El registro al que llega el dato es UDR y también colocamos el dato ahí para enviar. 
 
#include<avr/io.h> 
 
void usart_init(void){ 
UBRRH=0; 
UBRRL=77; 
UCSRA=0; 
UCSRB=(1<<RXEN)|(1<<TXEN); 
UCSRC=(1<<URSEL)|(3<<UCSZ0); 
} 
void usart_tx(char dato){ 
 while((UCSRA&(1<<UDRE))==0); 
 UDR=dato; 
 } 
char usart_rx(void){ 
 while((UCSRA&(1<<RXC))==0); 
 return UDR;} 
char rxready(void){ 
return(UCSRA&(1<<RXC)); 
} 
 
Después de habilitar la comunicación serial podemos ver el programa que llama a 
estas rutinas. 
#include<avr/io.h> 
#include "usart.h" 
char c; 
 
int main (void){ 
 usart_init(); 
 
 DDRC=0xff; 
 while(1){ 
 c=usart_rx(); 
 usart_tx(c); 
 
 } 
 } 
 
El programa solo inicia la usart y recibe un dato. Luego de recibir, envía. 
PB0/T0/XCK
1
PB1/T1
2
PB2/AIN0/INT2
3
PB3/AIN1/OC0
4
PB4/SS
5
PB5/MOSI
6
PB6/MISO
7
PB7/SCK
8
RESET
9
XTAL2
13
XTAL1
12
PD0/RXD
14
PD1/TXD
15
PD2/INT0
16
PD3/INT1
17
PD4/OC1B
18
PD5/OC1A
19
PD6/ICP1
20
PD7/OC2
21
PC0/SCL
22
PC1/SDA
23
PC2/TCK
24
PC3/TMS
25
PC4/TDO
26
PC5/TDI
27
PC6/TOSC1
28
PC7/TOSC2
29
PA7/ADC7
33
PA6/ADC6
34
PA5/ADC5
35
PA4/ADC4
36
PA3/ADC3
37
PA2/ADC2
38
PA1/ADC1
39
PA0/ADC0
40
AREF
32
AVCC
30
U1
ATMEGA32
RXD
RTS
TXD
CTS
 
 
Ing. Germán Jesús Pereira Muñoz Página 38 
 
 
 
 
 
 
 
 
 
 
Microcontroladores de la familia AVR 
 
Índice. 
1. Introducción a los microcontroladores. 
2. Microcontrolador AVR. 
3. Arquitectura de un microcontrolador AVR. 
4. Software de Programación (WIN-AVR). 
5. Grabadores del AVR. 
6. Programación. 
6.1. Uso de instrucciones básicas entrada/salida en displays. 
6.2. Aplicación básica. (Semáforo) 
6.3. Multiplexación de displays. 
6.4. Manejo de pantalla LCD. 
6.5. LCD graficadora. 
6.6. Manejo de teclado matricial. 
6.7. Manejo de interrupciones. 
6.8. Manejo del conversor análogo digital. 
6.9. Comunicación serial

Continuar navegando

Materiales relacionados

221 pag.
29 pag.
Sistemas Numéricos e Conversões

BUAP

User badge image

Estudiando Y Aprendendo

29 pag.
Tarea1_VLSI_TGJL - Jorge González

User badge image

Desafío México Veintitrés