Microcontrolador 8051
Microcontrolador 8051
1. Introducción
1.1 Reseña histórica
En 1978, Intel produjo la primera microcomputadora en un solo circuito (single chip
microcomputer). La misma contenía en un encapsulado de 40 patas un rudimentario
microprocesador orientado a manipulaciones de bits (privilegiando el manejo de variables de este
tipo frente a complejas operaciones de bytes), 27 líneas de entrada / salida, 64 bytes de memoria de
datos y 1 kbyte de memoria de programa (ROM para la versión 8048, EPROM para el 8748) y un
temporizador de 8 bits.
Esta microcomputadora estaba dirigida al mercado de los sistemas de control en los que se
suelen manejar entradas y salidas binarias y se optimizaron las operaciones booleanas entre las
mismas.
Intel y National presentaron algunas variantes al 8048 como el 8049 con 2 kbytes de ROM y
128 bytes de memoria de datos y el 8050AH con 4 kbytes de ROM y 256 bytes de memoria de
lectura / escritura.
En 1980, Intel presentó una versión mejorada del 8048 denominada 80511 al cual le incorporó
más memoria de programa y de datos, un segundo temporizador con mayor versatilidad, una puerta
serie asincrónica e importantes mejoras en su repertorio de instrucciones y arquitectura de registros.
Fue tan importante su penetración en el mercado que continúa siendo el núcleo de los
microcontroladores actuales. Podrán disponer de mayor memoria de datos o de programas (con
distintas tecnologías) o que se le agreguen más instrucciones o registros, temporizadores con
mayores funciones, pero siempre bajo la estructura básica del 8051.
Como las herramientas de desarrollo (compiladores, simuladores, etc.) pueden ser de dominio
público (gratuitos) o muy populares a moderado costo (Franklin, Keil) y siguen siendo adaptables a
las versiones actuales de microcontroladores, es que en este curso se estudiará el 8051 básico y al
concluir el capítulo se hará una presentación de las distintas alternativas comerciales actuales.
1
Comenzó a denominar a estas microcomputadoras en un solo circuito, microcontroladores.
Capítulo 9. Microcontroladores. Hoja 2
1.2 Nomenclatura.
Otro punto para tener en cuenta es la notación de las señales activas a nivel bajo (negadas).
Según un estándar actualmente difundido en la bibliografía de la materia, por ejemplo para referirnos
a la señal que indica escritura de datos externa (Write) y es activa a nivel bajo lo anotaremos “/WR” ó
“/WR” (se leerá WRITE negado).
Los microprocesadores poseen los mecanismos para arbitrar el uso de por lo menos tres
buses diferentes: bus de direcciones, bus de datos y bus de control. Gracias a estos buses, y
dependiendo de la forma de manejo de los mapas de memoria (memoria de programa, memoria de
datos, registros de entradas y salidas, etc., según algún modelo particular, (por ejemplo Von
Neumann o Harvard) el microprocesador administra el flujo de datos entre el universo de dispositivos
que pueden conectársele.
Direcciones
Memoria
M
MPU Datos emoria MM
emoria
emoria
Control
Perifericos
Para este caso simplificado suponemos que dentro del grupo de los periféricos se encuentran
controladores y dispositivos diversos (discos, controladores de acceso directo a memoria,
controladores de interrupciones, etc.).
Ahora bien, veremos que el panorama cambia para el caso de los microcontroladores, pues
estos poseen en forma integrada (dependiendo del tipo y modelo): memoria de datos, de programa,
temporizadores, puertos de entrada y salida, puertos serie, conversores A/D, D/A, etc.
Para lograr el ordenamiento del flujo de datos y sus señales de control los
microcontroladores poseen buses internos y que no son accesibles desde el exterior. En general lo
que encontraremos accesible desde el exterior serán puertos de entrada y salida, puertos serie y
paralelo, etc.
Los puertos que llamamos de “Entrada / Salida” son para propósitos generales, y pueden
configurarse a nivel de bit, es decir, se pueden usar 8 bits de salida o configurar individualmente los
bits que conforman dicho puerto como entradas y/o salidas para funciones totalmente diferentes, de
esta forma podemos vislumbrar la existencia de la que algunos tienden a denominar la filosofía del
bit.
Capítulo 9. Microcontroladores. Hoja 4
Fuentes externas
de interrupción
Control de
Interrupciones ROM RAM
Buses Internos
CPU
Puertos Puertos
de Serie Temporizadores
Pero esta “filosofía del bit” no solo implica el cambio de estado de bits pertenecientes a
puertos o posiciones de memoria, sino que también existen códigos de operación para procesar a
nivel lógico y aritmético determinados conjuntos de bits y lo que es muy importante, tomar decisiones
en base a los resultados.
Por ejemplo si un determinado bit que representa el estado de un pata de una puerta
configurada como entrada toma el valor “1” entonces una instrucción nos permitirá saltar a otra parte
del programa que pondrá en “0” (o en “1”) otro bit configurado como salida. En nemónicos:
JB ENT1, SACA_DATO ; Si el bit ENT1 vale 1, se saltará a ejecutar el tramo
...... ; de programa SACA_DATO
SACA_DATO: CLR BIT_SALIDA ; Se pone en “0” el bit definido como BIT_SALIDA
2
Por ese motivo, entre otros, se dice que algunas familias de microcontroladores incorporan en su interior un procesador
booleano
Capítulo 9. Microcontroladores. Hoja 5
Compárese la simpleza de este programa frente a lo que sería su equivalente para el 8088.
MOV DX,ENTRADA.
IN AL,DX ; Lee un byte completo de entrada
TEST AL,ENT1 ; Aisla el bit que se desea analizar por medio de una máscara
JNZ SACA_DATO ; Si el bit considerado vale “1”, salta a SACA_DATO
.....
SACA_DATO: MOV DX,SALIDA ; Apuntamos a la puerta de salida
IN AL,DX ; Se lee el estado de la puerta de salida
OR AL,BIT_SALIDA ; Se pone en “1” el único bit que se desea cambiar
OUT DX,AL ; Se lo escribe sobre la puerta de salida.
Es evidente que para la operación con variables del tipo bit, es mucho más sencillo operar
con un microcontrolador que con un microprocesador. También debe destacarse que para las
operaciones que requieran alguna complejidad matemática o lógica a nivel de bytes o palabras, es
conveniente el empleo de un microprocesador.
Los microcontroladores de esta familia poseen una CPU de 8 bits, 27 líneas de Entrada /
Salida, un Temporizador / contador (Timer/Counter) programable. Dependiendo del modelo
(8050,8049,8048) poseen respectivamente 256, 128 o 64 bytes de RAM estática. Existen en
versiones ROM, EPROM, Flash y sin ROM (Romless). Esta última es un caso particularmente
interesante y es una característica habitual de la mayoría de las familias. Alguno de los
microcontroladores, no poseen memoria para almacenamiento de código ejecutable, entonces se
reservan algunas líneas de Entrada / Salida para armar los tres buses necesarios (direcciones, datos
y control), de esta forma el programa puede ser almacenado en cualquier memoria externa (ROM,
EPROM, E2PROM, FLASH, NVRAM, etc.) y accedido por estos buses.
3
Estos dispositivos son sumamente útiles y costosos para el desarrollo de hardware y software. Consiste en un elaborado
sistema de adquisición de datos digitales con memorias estáticas de alta velocidad que muestrean en cada flanco de reloj el
estado de cada línea del microprocesador en tiempo real. Posteriormente se podrá mostrar el desensamblado del programa
real ejecutado o como mapas de bits o como contenido de memoria. También se dispondrá de comparadores de alta
velocidad que detendrán la operación (breakpoints) del microcontrolador al acceder a una determinada posición de
memoria o puerto de E/S.
También el programa compilado en una PC se podrá cargar en una memoria RAM interna del emulador, detectar fallas o
problemas en el programa y modificar manualmente algunos bytes del mismo sin necesidad de grabar una EPROM o
FLASH ganando mucho tiempo en el proceso de depuración.
Capítulo 9. Microcontroladores. Hoja 6
que también se han desarrollado infinidad de aplicaciones y herramientas, quizás con mas variedad
de integrantes en su familia que los Intel.. Esta familia cuenta con versiones ROM, EPROM,
E2PROM y flash, con una particularidad muy interesante, poseen memoria en cantidades no
convencionales (192, 768 bytes en lugar de cantidades más razonables como 64, 128 o 256 bytes).
A diferencia de los Intel se los consigue en versiones de tamaño reducido (encapsulado DIP 16 por
ejemplo), para aplicaciones donde no se requieren demasiadas líneas de Entrada / Salida.
Una de las particularidades de esta familia es que sus integrantes son autocontenidos, es
decir que se deberán utilizar exclusivamente los recursos internos ya que no disponen de buses de
expansión y por ende no pueden conectarse memorias ni de datos ni de programa externas.
Una de las características más destacables para los usuarios de Motorola es la ortogonalidad
de su repertorio de instrucciones. Esto quiere decir que no existen registros dedicados (por ejemplo
en Intel para hacer una operación de entrada / salida se debe emplear indefectiblemente el
acumulador), por lo que el manejo del repertorio de instrucciones es mucho más sencillo pues no
existen excepciones (como por ejemplo la inexistente intrucción MOV AL,byte ptr [DX]) a las reglas
generales de direccionamiento o movimiento de datos.
Existe compatibilidad de código entre los distintos integrantes de la familia, teniendo como
atractivo trascendente su muy bajo costo y disponibilidad de compiladores de libre disponibilidad así
como programadores fácilmente armables por el usuario.
4
Perro guardián o Cancerbero. Un monoestable redisparable que si no es pulsado en un tiempo predeterminado, resetea al
procesador. Se emplea para evitar que el microprocesador quede en un estado “colgado” permanente.
Capítulo 9. Microcontroladores. Hoja 7
La familia 8051 posee cuatro puertas bidireccionales de ocho bits, esto es, cualquier pata de
cualquier puerta puede configurarse indistintamente como entrada o como salida. Esto nos daría 32
líneas de Entrada / Salida para utilizar como más nos convenga, pero esto también tiene un precio,
como veremos más delante.
Si deseamos utilizar por ejemplo líneas de interrupción externas o el puerto serie, deberemos
sacrificar algunas de las 32 líneas. Dependiendo del modelo de MCS®-51 tendremos diferentes
configuraciones de memoria y cantidad de temporizadores según la siguiente nomenclatura:
3 = Sin ROM
0 : ROM
7 : EPROM 1 : 128b RAM, 4Kb ROM, 2 timers
9 : FLASH ROM 2 : 256b RAM, 8Kb ROM, 3 timers
Nada = HMOS
C = CMOS
Si por ejemplo tenemos entonces un 87C52 disponemos de 8Kb de memoria EPROM, 256
bytes de memoria RAM, 3 temporizadores y todo esto en tecnología CMOS.
5
En la práctica se emplean cristales de 11,0592 MHz para poder derivar las velocidades más populares de comunicación
serie. En efecto 11,0592 MHz/12 provee de una frecuencia de reloj de 921.600 Hz, que empleada por los temporizadores
internos y dividida por 96 permite la comunicación a 9600 bits/s.
6
y cuyas patas se indican con un número después de la puerta, así P0.4 quiere significar la pata 4 de la puerta 0
Capítulo 9. Microcontroladores. Hoja 8
Nos centraremos en la configuración DIP. En ella vemos el significado primario de las patas
en la designación más próxima al número de la pata y entre paréntesis el significado ampliado que
iremos analizando a lo largo de este capítulo.
Estos puertos no solo son diferentes en la forma de utilización a nivel funcional sino que
también a nivel circuital, ya que la configuración interna de las salidas (Drivers) es diferente entre
ellos. Mientras que por ejemplo, la puerta P0 posee una configuración de salida con un “Soft Pull-
Up”, esto es una “resistencia” programable por software que hace las veces de Pull-Up (en la práctica
se utiliza un mosfet), el resto de las puertas poseen resistores de valor fijo integrados internamente
como pull-up.
3.4.1 Puerta 0.
El 8051 permite conectar sobre sus buses externos tanto memorias de datos como de
programa o dispositivos de entrada / salida ([Link]. conversores A/D o D/A). A este modo de
funcionamiento se lo denomina de “acceso externo” (External Access, o EA).
Como el bus multiplexado deberá pasar por el estado de alta impedancia, la salida es una tri-
state estándar (ambos transistores de salida pueden estar cortados) sin ningún tipo de pull-up
interno. Por tal motivo, si se desea emplear la puerta 0 como salida para excitar algún dispositivo que
Capítulo 9. Microcontroladores. Hoja 10
no pueda tener las entradas en alta impedancia (por ejemplo un display, un decodificador, un
contador), se deberán colocar resistores de pull-up externos.
En la operación de salida, la señal Control permite que las direcciones o datos (Address /
Data) lleguen en forma complementada (inversor de por medio) a los transistores de salida, mientras
que en el caso de que se habilite la salida de la puerta 0, almacenada en el latch, sólo se podrá
activar el transistor inferior, permitiendo que al conducir el mismo se genere un “0”, mientras que si el
mismo queda cortado, solo un pull-up externo puede producir un “1”.
Basándose en esto último, es que podemos definir una pata como entrada, escribiendo
un “1” en el bit correspondiente de dicho puerto.
Con lo cual quedarán deshabilitados ambos transistores de salida y el estado de la pata será
manejado por la fuente exterior.
3.4.2 Puerta 2.
En el próximo tema 3.5 Mapas de memoria y conexión con el mundo exterior., veremos
que el 8051 dispone de 64K de capacidad de direccionamiento de memoria externa. Ello requiere 16
líneas de direcciones. Las 8 más bajas aparecerán sobre la Puerta 0 (P0) y las 8 más significativas lo
harán sobre la Puerta 2 (P2).
7
El acceso externo se habilita por medio de la pata /EA para la memoria de programa o por medio de códigos de operación
especiales que indican acceso a memorias de datos externas.
Capítulo 9. Microcontroladores. Hoja 11
Análogamente a lo planteado para la puerta 08, para programar una pata como entrada basta
con poner un “1” en el bit correspondiente.
3.4.3 Puertas 1 y 3.
Ambas puertas son idénticas entre si y están representadas por el circuito de la Fig. 8.
Vemos que aparece un resistor de pull-up que garantiza un “1” lógico cuando el transistor no
conduce.
Las señales del bus de control (/RD, /WR, interrupciones, etc.) se generarán a expensas de
líneas de puerta (fundamentalmente la puerta 3 y en algunas versiones se agrega alguna línea de la
puerta 1). Por tal motivo es que aparece una señal de comando Alternate Output Function para las
señales de control de salida (por ejemplo /WR, /RD) que suspende la operación de la pata de la
puerta y redefine su operación. Análogamente la señal de comando Alternate Input Function que
redefine las patas para señales de control de entrada (Interrupciones).
8
También válido para las puertas 1 y 3.
Capítulo 9. Microcontroladores. Hoja 12
La familia 8051 dispone de una pequeña memoria de datos interna de 128 ó 256 bytes según
el componente de la familia de que se trate. Existen versiones (Dallas, por ejemplo) que proveen
hasta 64 kbytes de memoria de datos incorporada en el circuito del microcontrolador.
Como puede observarse en la Fig. 9, existe para los dispositivos de 256 bytes de memoria
interna (8x52 y posteriores), una zona del mapa donde se superponen dos áreas, estas son: el área
alta (80h a FFh) y el área de SFR (Special Function Registers).
El mecanismo que permite acceder a una zona de memoria o a la otra es utilizando diferentes
modos de direccionamiento para referirse a los datos en estas zonas. Más adelante veremos en
detalle todo lo que tiene que ver con los modos de direccionamiento para los códigos de operación y
el acceso a la memoria de datos interna y externa, por el momento aceptemos que se puede acceder
en forma directa a las posiciones de memoria interna con los códigos de operación apropiados, por
ejemplo:
MOV P1,A
Lleva el contenido del acumulador a la puerta P1, pero la puerta P1 ocupa la posición 90H
dentro del mapa de memoria de los registros de funciones especiales. Por ello, la instrucción anterior
es equivalente (hasta en el código de operación) a:
Capítulo 9. Microcontroladores. Hoja 13
MOV 90H,A
Por ejemplo, para escribir el contenido del acumulador en la posición de memoria de datos
interna 70H, haremos:
MOV R0,#70H ; Se inicializa el registro interno R0, en forma inmediata (#) con 70H
MOV @R0,A ; Se almacena el Acumulador en la posición de memoria apuntada por R0 (@)
Si contamos con una versión tipo 8052 (256 bytes de memoria de datos), podremos escribir la
posición de memoria de datos 90H, por medio de :
MOV R0,#90H ; Se inicializa el registro interno R0, en forma inmediata (#) con 90H
MOV @R0,A ; Se almacena el Acumulador en la posición de memoria de DATOS 90H.
D0
D1
D2
D3
D4
D5
D6
D7
U2
3 2 A0
4 D0 Q0 5 A1
7 D1 Q1 6 A2
8 D2 Q2 9 A3
U1
31 39 13 D3 Q3 12 A4
EA/VP P0.0 38 14 D4 Q4 15 A5
19 P0.1 37 17 D5 Q5 16 A6
X1 P0.2 36 18 D6 Q6 19 A7
P0.3 35 D7 Q7
18 P0.4 34 1
X2 P0.5 33 11 OC
P0.6 32 G
9 P0.7
74LS373
RESET 21 A8
P2.0 22 A9
12 P2.1 23 A10
13 INT0 P2.2 24 A11
14 INT1 P2.3 25 A12
15 T0 P2.4 26 A13
T1 P2.5 27 A14
1 P2.6 28 A15
2 P1.0 P2.7
3 P1.1 17 /RD
4 P1.2 RD 16 /WR
5 P1.3 WR 29 PSEN
6 P1.4 PSEN 30
7 P1.5 ALE/P 11
8 P1.6 TXD 10
P1.7 RXD
8051
Para concluir con nuestro resumen debemos mencionar el comportamiento de tres líneas
adicionales:
/RD (Read): Esta línea, activa a nivel bajo, indica primero con su flanco descendente que el
microcontrolador esta dispuesto a recibir datos de la memoria de datos9. Y segundo, estos datos
deben estar presentes en dicho bus al momento del flanco de subida al estado inactivo de la línea
/RD, con este flanco el Microcontrolador ingresa los datos.
/WR (Write): La contrapartida de /RD. Esta línea, también activa baja, indica con su flanco de
bajada que los datos colocados por el microcontrolador en el bus multiplexado son válidos y que el o
los dispositivos que lean este dato disponen del tiempo que dura el pulso de /WR más un pequeño
tiempo adicional especificado por cada fabricante para recibir este dato.
/PSEN (Program Store Enable): Esta línea puede utilizarse de dos formas diferentes, como su
nombre lo indica hace las veces de /RD pero para la memoria de programa (ROM). Cabe destacar
que aunque se utilice acceso interno, esta línea permanece activa. Puede vérsela como una línea
más de direcciones entonces podemos tener acceso a 64Kb de datos y 64Kb de programa.
Mediante los siguientes mecanismos ejemplificados en la siguiente figura podemos ver como se
utiliza esta línea.
9
Recordemos que el microcontrolador 8051 y sus derivados se basan en una arquitectura Harvard.
Capítulo 9. Microcontroladores. Hoja 15
MCU MCU
RD_ROM# RD_RAM#
PSEN# 1 RD_ROM#
PSEN# 3
RD_RAM#
RD# RD# 2
WR_RAM# WR_RAM#
WR# WR#
/INT0 e /INT1: Estas son dos entradas para interrupciones externas que pueden utilizarse por
nivel o por flanco.
T0 y T1: Estas entradas pueden utilizarse para contar eventos externos con los registros de
los temporizadores / contadores internos.
RXD y TXD: Estos dos pines son la entrada y salida respectivamente de la UART interna del
Microcontrolador, maneja niveles TTL, por lo que es necesario adicionarle un circuito adaptador de
niveles para transformarlo en una comunicación que emplee otra norma, por ejemplo RS232 ó
RS485.
Estas ocho últimas patas (/RD, /WR, T1, T0, /INT0, /INT1, RXD Y TXD) conforman el P3.
Como vemos, al agregarle funcionalidad al sistema seguimos perdiendo líneas de puertos para
propósitos múltiples.
En particular para las versiones de microcontrolador con tres temporizadores, también deben
sacrificarse dos líneas de la puerta P1 (que hasta ahora esta totalmente libre), para la entrada T2 que
cumple las mismas funciones de T0 y T1, y la entrada T2EX para la captura y recarga del
temporizador 2 que luego veremos con más detalle.
XTAL1 y 2: Estas patas sirven para conectar un cristal oscilador externo o en su defecto, una
señal externa de reloj en la pata XTAL2 dejando el XTAL1 al potencial más negativo de alimentación
del Microcontrolador (Vss o GND). El fabricante recomienda el siguiente esquema de conexión.
27p
27p Xtal
Vcc y Vss: Aquí se conecta la fuente de alimentación del Microcontrolador, los requerimientos
de corriente y niveles de tensión dependen del tipo de dispositivo (familia lógica y versión) y cargas
conectadas, recomendamos consultar las hojas de datos de estos dispositivos.
Capítulo 9. Microcontroladores. Hoja 16
Como todo procesador el 8051 posee registros para funciones varias, la particularidad que
tiene este Microcontrolador es que estos registros forman parte de la RAM interna accesible por
programa. Por ejemplo, los registros acumuladores para operaciones generales poseen una
dirección en la zona de SFR.
Esto significa que no solo vamos a tener un código de operación para poner un acumulador
en cero, también podemos hacerlo poniendo en cero la posición de memoria correspondiente a dicho
acumulador mediante el mismo código de operación.
Recordando lo que mencionamos al comenzar este apunte sobre la “filosofía del bit”, algunos
de los registros en la zona de SFR son direccionables a nivel de bit, al igual que posiciones de
memoria baja. En el gráfico tenemos un mapa simplificado y en la tabla un detalle de los registros
del área SFR.
10
Versión para el 8051 del IP del 8088 con la particularidad de que al no haber cola interna, apunta a la próxima
instrucción a ejecutar
Capítulo 9. Microcontroladores. Hoja 17
Fig. 14. Memoria interna. Bancos de registros y zona direccionable por bits.
Como se puede apreciar en la Fig. 14, hay una zona de registros que es direccionables a
nivel de bit que está comprendida entre los bytes 20h y 2Fh (en total 16 bytes que dan 128 bits), cada
uno de los bits de esta zona se numera de 0 a 127 y pueden ser accedidos por sendos código de
operación.
La pila se encuentra dentro de la escasa memoria de datos interna, por lo cual al producirse
una interrupción y querer salvar los registros en uso se podría agotar fácilmente la disponibilidad de
memoria. Por tal motivo se implementó el cambio de banco de registros activo como forma de
salvaguardar los registros sin perder demasiado tiempo. Ello se realiza cambiando un par de bits en
el registro PSW que almacena el estado del programa11.
11
Inclusive los compiladores proveen una forma muy simple de efectuar el cambio de banco de registros activo por medio
de la sentencia “Using” así si escribimos en nuestro programa Using 2 el compilador generará las instrucciones necesarias
para cambiar los bits del PSW y activar al banco 2.
Capítulo 9. Microcontroladores. Hoja 19
Luego del Reset se halla activo el banco de registros 0 que abarca los registros R0 a R7 que
ocupan las posiciones de memoria de datos 0 a 7.
Como dichos registros se numeran de R0 a R7, existen cuatro R0, cuatro R1, etc. y al
referirnos a uno de ellos debemos tener en cuenta a que banco pertenece para seleccionarlo si es
necesario.
El resto de los registros del área de SFR serán explicados a medida que vayamos avanzando
sobre los temas con ellos relacionados a lo largo de este apunte.
Si bien las memorias se conectan sobre los mismos buses de direcciones y datos,
mencionamos que la pata /EA (External Access) es la que obliga al microcontrolador a ir a buscar el
programa a la memoria externa. En tal situación será la señal /PSEN (Program Store Enable) la que
produce la lectura. Como es memoria de programa, no habrá necesidad de ninguna señal de
escritura.
12
Gracias a este rudimentario mecanismo de cambio de contexto (Context Switching), algunas compañías de
software han desarrollado núcleos de sistema operativo (kernels) en tiempo real (RT kernels) para Microcontroladores de la
familia MCS®-51 (ver citas bibliográficas y WEBográficas al final del apunte) estos nos permiten ejecutar varias tareas
utilizando el concepto de ranuras de tiempo (Time Slicing O Time Slots), donde a cada tarea se le asigna un tiempo de
procesamiento en general configurable y el kernel se encarga de conmutar entre las tareas activas, concepto similar al de los
sistemas operativos como el UNIX y sus derivados o los basados en Windows.
Capítulo 9. Microcontroladores. Hoja 20
U1
3 2
4 D0 Q0 5
7 D1 Q1 6
U2 8 D2 Q2 9
31 39 13 D3 Q3 12 U3
EA/VP P0.0 38 14 D4 Q4 15 10 11
19 P0.1 37 17 D5 Q5 16 9 A0 O0 12
X1 P0.2 36 18 D6 Q6 19 8 A1 O1 13
P0.3 35 D7 Q7 7 A2 O2 15
18 P0.4 34 1 6 A3 O3 16
X2 P0.5 33 11 OC 5 A4 O4 17
P0.6 32 G 4 A5 O5 18
9 P0.7 3 A6 O6 19
RESET 74LS373 A7 O7
21 25
P2.0 22 24 A8
12 P2.1 23 21 A9
13 INT0 P2.2 24 23 A10
14 INT1 P2.3 25 2 A11
15 T0 P2.4 26 26 A12
T1 P2.5 27 27 A13
1 P2.6 28 1 A14
2 P1.0 P2.7 A15
3 P1.1 17 20
4 P1.2 RD 16 22 CE
5 P1.3 WR 29 OE
6 P1.4 PSEN 30
P1.5 ALE/P 27512
7 11
8 P1.6 TXD 10
P1.7 RXD
8051
En la Fig. 16 también vemos que ALE aparece en el primer estado del cada ciclo de máquina
y baja en el segundo. En ese momento en P0 se halla presente la parte baja de contador de
programa (PCL). Cuando el microcontrolador retira PCL de P0 para a alta impedancia y espera que
Capítulo 9. Microcontroladores. Hoja 21
la memoria de programa (cuya salida ha sido habilitada por /PSEN) coloque el código de operación u
operando sobre el bus de datos. Sobre P2 aparece estable la parte alta de PC.
U1
3 2
4 D0 Q0 5
7 D1 Q1 6
U2 8 D2 Q2 9
31 39 13 D3 Q3 12 U3
EA/VP P0.0 38 14 D4 Q4 15 10 11
19 P0.1 37 17 D5 Q5 16 9 A0 D0 12
X1 P0.2 36 18 D6 Q6 19 8 A1 D1 13
P0.3 35 D7 Q7 7 A2 D2 15
18 P0.4 34 1 6 A3 D3 16
X2 P0.5 33 11 OC 5 A4 D4 17
P0.6 32 G 4 A5 D5 18
9 P0.7 3 A6 D6 19
RESET 74LS373 A7 D7
21 25
P2.0 22 24 A8
12 P2.1 23 21 A9
13 INT0 P2.2 24 23 A10
14 INT1 P2.3 25 U4 2 A11
15 T0 P2.4 26 1 15 A12
T1 P2.5 27 2 A Y0 14 20
1 P2.6 28 3 B Y1 13 26 CS1
2 P1.0 P2.7 C Y2 12 27 CS2
3 P1.1 17 Vcc Y3 11 22 WE
4 P1.2 RD 16 6 Y4 10 OE
5 P1.3 WR 29 4 G1 Y5 9
P1.4 PSEN G2A Y6 6264
6 30 5 7
7 P1.5 ALE/P 11 G2B Y7
8 P1.6 TXD 10
P1.7 RXD 74LS138
8051
Para la operación con una memoria de datos externa, la configuración es muy similar a la
vista en la Fig. 15. Solamente que se emplean las señales /RD y /WR para acceder a la memoria de
datos.
En el primer ciclo de máquina todo ocurre como en el caso anterior. Para acceder a la
memoria de datos externa se deberán emplear las instrucciones adecuadas (MOVX) y se sacará en
el conjunto P2 – P0 el puntero a datos (DPTR) o en P0 se sacará el valor del puntero de 8 bits a
memoria de datos (R0 o R1 del banco de registros activos) manteniendo P2 como puerta de E/S.
Capítulo 9. Microcontroladores. Hoja 22
Fig. 18. Diagrama temporal con memoria de programa y lectura de datos externos.
Similarmente se procede para el escritura, empleándose la señal /WR en lugar de /RD y será
el microcontrolador el que escribe los datos.
Fig. 19. Diagrama temporal con memoria de programa y escritura de datos externos.
5. Modos de direccionamiento.
Tal como vimos en el caso de los microprocesadores, estos modos de direccionamiento se
refieren a la forma en que se le entregan al microprocesador los operandos para ejecutar una
instrucción y como su nombre lo indica, se clasifican por la forma en que se accede a los datos a los
que hacen referencia y como se ejecutan los código de operación.
La forma en que se codifican los código de operación depende del modo de direccionamiento
o sea de la forma en que se proveen de los operandos al microcontrolador. En esta familia de
Microcontroladores los código de operación son de 8 bits, lo que en principio nos proporcionaría 256
código de operación posibles, pero en realidad no están implementados todos, el único no
implementado o mejor dicho, no documentado es el que corresponde al byte A5h (10100101b).
Recomendamos consultar el apéndice de tablas para más detalles.
• Implícito
• Inmediato
• Directo
• Indirecto
• Por registro
• Indexado
Antes de empezar a ver en detalle estos modos aclararemos ciertos tópicos y definiciones
concernientes al ensamblador del Microcontrolador y algunos registros.
El 8051 puede acceder a 64 K memoria de datos y otros 64K de memoria de programa, por lo
que se necesitará (por lo menos de) un registro de 16 bits para acceder a todo el mapa de memoria.
Uno de los registros de 16 bits mencionado anteriormente se llama DPTR (Data Pointer) el
cual esta compuesto por dos registros de 8 bits, el DPL (los ocho bits menos significativos) y el DPH
(los ocho bits más significativos). .
Otro registro muy importante es el PSW (Program Status Word o Registro de estado del
programa o Registro de códigos de condición), direccionable a nivel de bit y que contiene ciertos
indicadores o “flags” que indican el estado de la CPU, sus bits indican lo siguiente
C AC F0 RS1 RS0 0V - P
Capítulo 9. Microcontroladores. Hoja 24
B7, C: bit de acarreo (Carry), se pone a 1 al producirse acarreo desde el bit 7 del ACC, o sea
siendo el ACC.7 = 1 se le suma 1 a este bit. Ejemplo:
ACC = 10011011
10000100
B6, AC: acarreo auxiliar, funciona igual que el anterior solo que al producirse el acarreo del
nibble inferior del ACC al superior.
B5, F0: Flag cero, es un bit de propósito general disponible para el usuario, que puede
ponerlo a cero, hacerlo valer “1” a voluntad y luego tomar decisiones en base a ello por medio de
saltos condicionados a su valor (JF0).
B4 y B3: Con estos bits se selecciona el banco de registros activo (R0 a R7) según la
siguiente tabla:
RS RS Banco
1 2
0 0 0
0 1 1
1 0 2
1 1 3
B2, OV: Desborde u overflow. Este bit es el resultado de la operación OR-EXCLUSIVO entre
el resultado del acarreo de los bits ACC.7 y ACC.6, es decir, si no se produce acarreo en ambos o si
se produce en ambos, el OV es cero. Según lo visto en Técnicas Digitales I, este bit previene el
excederse de la capacidad del acumulador, es decir que busca advertir al programador que ha
sumado dos números positivos y el resultada ha sido (absurdamente) negativo o similarmente que al
sumar dos números negativos, el resultado fue positivo.
B1 : Este al igual que F0 es un bit disponible para propósitos generales solo que no tiene un
nombre en particular, se hace referencia a el como “PSW.1”.
B0, P: Bit de paridad, indica la paridad de la cantidad de bits en uno en el acumulador incluido
el bit de paridad, es decir, si la cantidad de bits en uno en el acumulador es impar entonces el bit P =
1, se dice que este bit indica “paridad par”.
Supongamos que queremos poner el contenido del acumulador A en cero, para ello existe un
código de operación de modo implícito que realiza esta operación:
Por ejemplo también tenemos operaciones como dividir el contenido del acumulador A con el
del acumulador B (obviamente requiere que previamente se carguen los contenidos de dichos
acumuladores con los números a dividir):
Como podemos apreciar, con un simple código de operación el microcontrolador tiene toda la
información necesaria para ejecutar la instrucción.
MOV A,#3Eh ;carga el ACC con la constante (número) 3Eh, no el contenido del registro 3Eh.
Es de hacer notar la utilización del símbolo "#" indicando que lo siguiente es una constante.
De esta manera si por ejemplo deseamos “MOVer” el contenido del acumulador A al byte 3Fh
de la RAM interna, el código de operación es el siguiente:
13
MOV 3Fh,A ; mueve el contenido de A a la posición 3Fh
Es interesante notar que a este mismo modo también pertenecen algunos código de
operación que operan sobre bits en lugar de bytes. En este caso el operando que acompaña al
código de operación indica la dirección de uno de los bits direccionables (de la zona 20h a 2Fh o de
los registros con la propiedad direccionables a nivel de bit), por ejemplo:
De la misma forma se puede operar con los acumuladores y registros direccionables por bits,
por ejemplo:
13
Nótese que el ensamblador del microcontrolador requiere la especificación de la base de numeración (h en este caso) para
el operando13. Se puede especificar en binario (00111111b) o en decimal (63d).
Capítulo 9. Microcontroladores. Hoja 26
00h 2Fh R0
01h R1
02h R2
03h R3
2Fh
El símbolo "@" (at..., que en inglés significa “en el sitio...” o “en el lugar...”) nos indica que el
dato es "apuntado" por el registro R0 y no "contenido" en él.
Esta es una instrucción muy poderosa que nos permite armar el equivalente de lazos al estilo
"FOR..NEXT", como analizaremos posteriormente.
Lo que nos interesa ahora es notar la codificación de esta instrucción. Analicemos los dos
bytes que componen esta instrucción:
11011xxx aaaaaaaa
Los primeros cinco bits del primer byte (1 1 0 1 1) identifican al código de operación del "DJNZ
Rx,Dirección", los bits marcados como xxx codifican el número de registro utilizado en esta
instrucción y el segundo byte indican una dirección relativa (en complemento a 2) a la de la
instrucción en curso.
Capítulo 9. Microcontroladores. Hoja 27
Para el manejo de estas tablas son necesarios dos elementos, una dirección base de la tabla
y tamaño definido de la tabla.
El primer elemento es necesario como punto de referencia y será apuntado por un registro ad
hoc, si pretendemos que nuestra tabla se ubique en cualquier punto de nuestra memoria de
programa y si recordamos que dicha memoria puede ser de hasta 64 Kb concluimos entonces que es
necesaria la utilización de un registro de 16 bits para ubicar (apuntar) el comienzo de nuestra tabla.
De aquí se desprende que los únicos registros disponibles para tal fin son el Puntero a datos (DPTR)
y el Contador de Programa (PC).
El segundo elemento para manejar la tabla es un índice para recorrerla desde la dirección
base (apuntada por alguno de los registros de 16 bits) hasta su tamaño máximo, aquí la posibilidad
es un límite de 256 bytes, de esta forma utilizando algún registro de 8 bits (el ACC por ejemplo) nos
desplazamos a lo largo de la tabla.
Para el manejo de estas tablas entonces, existen dos sentencias del ensamblador que nos
permiten utilizar el ACC y el DPTR o el PC.
Las instrucciones del 8x51 pueden codificarse en instrucciones de hasta tres bytes. El primer
byte siempre es el código de operación, los siguientes pueden existir o no dependiendo precisamente
del modo de direccionamiento que involucre el código de operación de que se trate.
También de esto dependerá el significado de los bytes y hasta los bits que acompañan y
conforman el nemónico en cuestión. Luego veremos los diagramas temporales (tempogramas) que
describen el comportamiento de las señales que componen los buses de control, datos y direcciones
cuando la CPU del microcontrolador efectúa en la memoria de programas la búsqueda de código de
Operación para ejecutar (Op Code Fetch) y el acceso a memoria externa de datos.
Utilizaremos las siguientes convenciones para especificar la sintaxis de las instrucciones del
ensamblador:
Comenzamos entonces con las instrucciones para el movimiento (MOV) de datos entre
registros. El propósito de estas instrucciones es permitir la asignación de valores a registros o
modificar el contenido de los mismos intercambiando si es necesario con el contenido de otros
registros. Esto se puede llevar a cabo con instrucciones que utilizan direccionamiento directo,
indirecto, por registro, inmediato e indexado. Así, tenemos entonces el ...
Ahora veremos como se componen los operandos que representan a "x" e "y" en la instrucción
MOV ya que las variantes son muchas.
Un registro cualquiera (cualquier byte de la RAM interna de 00h a 7Fh y cualquier registro del
área SFR) puede hacer las veces de operando destino, y como operando fuente una constante.
MOV P1, #00000001b ; P1 ← 01h pone en "1" el bit 0 del port 1 y los demás en
cero.
Y ...
También podemos utilizar como operando fuente a otro registro, es decir, el contenido de un
registro puede copiarse en otro utilizando la instrucción MOV, veamos:
De la misma forma:
MOV 1, #07h ;al igual que antes carga R1 del banco 0 con 07h
Si bien el resultado es el mismo, no lo son los código de Operación de "MOV A, R1" y "MOV
0E0h, 1". Recomendamos consultar los apéndices con las tablas de los código de Operación.
Continuando con las variantes del MOV, ahora veremos como acceder en forma indirecta
mediante un registro.
La idea es utilizar a un registro en particular (R0 o R1 únicamente pueden ser estos registros)
para apuntar a una posición de memoria, la cual puede hacer las veces de fuente o destino de los
datos. El inconveniente (o ventaja, como veremos luego) es que previamente se debe cargar al
registro que servirá de apuntador (R0 o R1), luego se puede hacer el MOV en cuestión, veamos:
Luego de esta secuencia de instrucciones el contenido del acumulador B pasa a ser 8 (no 3Fh),
ya que el modo de direccionamiento utilizado es el indirecto (recordar el significado de "@"...).
Como dijimos anteriormente, el registro apuntador puede utilizarse como fuente o destino, de
esta forma es válido escribir (suponiendo que no se modificó el contenido de R0):
MOV @R0, #0E1h ; (R0) ← E1h , coloca E1h en la posición apuntada por R0.
Este mismo modo puede utilizarse para acceder a memoria externa de datos, esto se hace
utilizando MOVX (por MOVe eXternal) en lugar de MOV. Sin embargo el único registro que puede
operar como fuente o destino directo de acceso a memoria externa es el ACC. Como registros de
indirección se pueden utilizar R0, R1 y el DPTR (compuesto como ya vimos por DPL y DPH). Esto
permite acceder a dispositivos conectados al bus multiplexado del microcontrolador. La sintaxis de
estas instrucciones es la siguiente:
XCH A, @Ri ; A ((Ri)) y (Ri) (A), el lugar apuntado por Ri intercambia contenido
; con ACC.
DPTR
T00
T01
T02
......
......
DPTR + A
T45
......
......
TFF
Fig. 21. Uso del DPTR para acceder a una tabla de búsqueda.
Así, supongamos que el valor obtenido a la salida del conversor ha sido 45H. Ese valor se
hallará en el acumulador y se inicializará DPTR al comienzo de la tabla. Al ejecutar MOVC A,@A
+ DPTR se trasladará al acumulador el valor T45 que corresponde a la temperatura estimada para
esa tensión de salida de la termocupla.
Esta instrucción opera en forma similar, salvo que el contador de programa es empleado
como puntero y la tabla es accedida normalmente a través de una subrutina.
........
CONVERSION_A_TEMP:
TABLA: DB T00,T01,T02,.....,T45,.....TFF
6.1.4 Pila
Hasta aquí hemos visto el manejo de datos en memoria con el grupo MOV de instrucciones, sin
embargo, no es el único tipo de instrucciones para tal fin.
En este grupo están, las instrucciones para el intercambio entre registros y el manejo de la pila
(Stack).
Para entender el funcionamiento del STACK pensemos que se trata de un tubo cilíndrico para
guardar monedas, cada moneda representa el contenido de un registro que desea guardarse en él.
Al comenzar, con el tubo (STACK) vacío, si guardamos una moneda, el lugar disponible habrá
disminuido obviamente, al colocar otra moneda, la única forma de recuperar la primera es quitando la
que introdujimos en segundo lugar. Este primitivo método de almacenamiento hace que tengamos
inmediatamente disponible el último dato ingresado, el concepto de este mecanismo se conoce como
método de pila LIFO (Last In, First Out), el primero en ingresar a la pila será el último en salir.
Dicha pila es una zona de la memoria RAM interna y apuntada por un registro especial
reservado exclusivamente para esta tarea (llamado Stack Pointer o SP), luego de producirse un reset
del microcontrolador, por el motivo que fuese, el registro SP "amanece" con el número 07h, es decir,
apunta a la 8ª posición de RAM interna, que coincide con el registro R7 del banco 0 de registros.
4. La pila emplea la memoria interna que podrá tener 128 ó 256 bytes como máximo. Por
ello es que se debe ser muy cuidadoso en el uso de la pila a fin de no sobrescribir
variables.
1. Modificación directa del registro SP. El registro SP puede cargarse directamente con
una instrucción MOV, de esta forma podemos hacer que apunte a la zona de memoria
que queremos utilizar como STACK
Es de hacer notar que se puede hacer PUSH o POP con cualquier posición de memoria
en forma directa, es más, se puede hacer un PUSH del mismo SP.
Esto es útil cuando se desea utilizar el DPTR y no perder el contenido actual, entonces
lo guardamos momentáneamente en el STACK y luego lo recuperamos con:
POP DPH ; coloca en DPH lo que se encuentra en el tope del
STACK,
; decrementa SP.
POP DPL ; ídem anterior(recordar que se decrementó), vuelve a
Capítulo 9. Microcontroladores. Hoja 33
; decrementar SP.
Nótese que para retirar datos del STACK hay que hacerlo en el orden inverso a como
fueron ingresados. Es particularmente crítico el manejo del STACK y una de las causas
más frecuentes de errores de funcionamiento del software, se debe tener cuidado
extremo al colocar y retirar datos del STACK.
Las operaciones de suma y resta permiten utilizar los modos de direccionamiento por registro,
directo, indirecto e inmediato para uno de los operandos, el otro operando es siempre el ACC y
además este último es quién recibe el resultado e la operación en todos los casos.
En particular las operaciones de adición pueden hacerse incluyendo el bit de acarreo (suma
con acarreo) o no, mientras que en la sustracción siempre14 se utiliza el acarreo de resta (borrow,
que no es otra cosa que el complemento del acarreo), de esta forma tenemos cuatro modos de
direccionamiento, dos operaciones y una de ellas con y sin acarreo. Total: doce variantes.
ADD A, Rn ; suma el contenido del ACC con el contenido del registro Rn.
14
Esto quiere decir que NO existe la resta sin borrow.
Capítulo 9. Microcontroladores. Hoja 34
ADDC A, Rn ; suma el CARRY, el contenido del ACC y el contenido del registro Rn.
6.2.3 Sustracción:
La sintaxis de las variantes de esta instrucción es:
Entonces tenemos:
15
Justamente una de las ventajas que presentan algunos de los “8051 de segunda generación” es la posibilidad de
decrementar el DPTR
Capítulo 9. Microcontroladores. Hoja 35
Y las de decremento:
Recordemos que la Unidad Aritmética – Lógica sólo sabe operar con números binarios. Hasta
aquí el resultado de la suma es 4Bh pero para que sea correcto en BCD debería ser 51h, esta
contingencia se resuelve con:
16
Como ejemplo, presentamos un pequeño programa que realiza el decremento del DPTR
DEC DPL ; Se decrementa la parte menos significativa del DPTR
MOV R7,DPL ; Se copia a R7 utilizado como registro auxiliar
CJNE R7,#0FFH,NO_DEC ; Si pasó de 00 a FF se debe decrementar la parte alta. Si no continua.
DEC DPH ; Se decrementa.
NO_DEC: .......
Capítulo 9. Microcontroladores. Hoja 36
Para comenzar, las operaciones principales con bits constan de asignarles un estado, "0" o "1",
de esta forma simplemente con dos instrucciones podemos cambiar (fijar) el estado de cualquier bit
que permita direccionamiento (recomendamos consultar las tablas del capítulo 2).
Debe notarse que, estas instrucciones si bien son simples, constan de un código de operación
y un operando (un byte adicional) para indicar el bit en cuestión.
Intentaremos aclarar un poco más estos conceptos con los siguientes ejemplos:
Este mismo grupo de instrucciones existe para un bit privilegiado, este es el bit de acarreo que
cumplirá las veces de acumulador de un bit.
SETB C ; C = 1.
CLR C ; C = 0.
Estas dos instrucciones son de un solo byte. Podemos verificar con las siguientes
instrucciones los privilegios de C y que hace las veces de acumulador pero en operaciones a nivel de
bit:
Vemos que se comporta como un equivalente a los MOV A,... y viceversa para las operaciones
con registros.
Las operaciones que se pueden realizar son las fundamentales dentro del álgebra de Boole.
Tendremos entonces la posibilidad de utilizar la adición (OR), multiplicación (AND), negación o
complemento (NOT), OR exclusivo (XOR). A diferencia de las operaciones aritméticas no se utiliza
únicamente el acumulador, esto nos permite realizar una operación y obtener el resultado
directamente en una variable que no sea el acumulador. Las operaciones posibles con esta
comodidad son AND, OR y XOR:
ANL A, Rn
ANL A, byte
ANL A, @Ri
ANL A, #dato
ANL byte, A
En las cuatro primeras podemos ver que el resultado de la operación queda en el acumulador y
en las dos últimas el resultado queda en el byte indicado.
Capítulo 9. Microcontroladores. Hoja 37
ORL A, byte
ORL A, @Ri
ORL A, #dato
ORL byte, A
XRL A, Rn
XRL A, byte
XRL A, @Ri
XRL A, #dato
XRL byte, A
A nivel de bit (variable booleana) podemos realizar también operaciones por el estilo, tenemos:
CPL C ;complementa el C.
Podemos notar aquí lo conveniente de la notación "/bit", indica que al realizarse la operación,
se utiliza el complemento del bit especificado, ahorrándonos un paso intermedio.
1 0 1 1 0 0 1 0
0 1 0 1 1 0 0 1
Como podemos observar, al rotar, el bit menos significativo pasa a ocupar el lugar del más
significativo. En un desplazamiento, el bit menos significativo " cae al vacío ", se pierde, y el más
significativo se completa con "0". Sin embargo, en estos MCU no existe la operación de
desplazamiento, pero ésta puede hacerse utilizando el bit C, veamos.
C 1 0 1 1 0 0 1 0
0 C 1 0 1 1 0 0 1
CLR C ;pone el C = 0
Básicamente tenemos: los saltos o desvíos incondicionales, en los que se fuerza a un salto
incondicionalmente, porque sí, y los desvíos condicionales, en los que dada una cierta condición,
el programa tomará uno u otro camino en caso de cumplirse o no ésta condición. A su vez tenemos
dentro de los incondicionales el salto a subrutinas, la diferencia es que en la subrutina se "recuerda"
el punto de retorno (para esto se usa la pila). Veamos entonces como primer punto los llamados a
subrutinas.
b) Se incrementa el SP.
d) Se incrementa el SP.
f) Los 11 bits menos significativos del contador de programa se cargan con la dirección
de la subrutina a la que se está llamando.
b) Se incrementa el SP.
d) Se incrementa el SP.
a) La parte alta del contador de programa se carga con el primer byte disponible del
stack.
b) Se decrementa el SP.
c) La parte baja del contador de programa se carga con el primer byte disponible del
stack (recordar que luego de retirar uno en el punto a), el SP se decrementó en el
punto b) apuntando a un nuevo byte).
d) Se decrementa el SP.
AJMP dirección 11 bits ;al igual que el ACALL salto dentro de los 2 Kb.
SJMP relativo 8 bits ;salta dentro de la cercanía de los +127 /- 128 bytes.
Existe un tipo especial de JMP que permite saltar a ejecutar código relativo a un
desplazamiento indicado en el ACC:
JMP @A+DPTR ;salta a la posición apuntada por DPTR más el desplazamiento indicado
en ;el Acumulador.
Mecanismo útil para generar una opción del tipo DO CASE de funciones de un menú principal,
por ejemplo:
.........
TBLA_DE_SALTO:
AJMP FUNCION_3
.........
JZ relativo 8 bits ; salta al igual que un SJMP si el contenido del ACC = 0. Los 8 bits son
; interpretados en complemento a 2
JBC bit, rel 8 bits ;ídem si el bit indicado es = 1 y luego pone dicho bit = 0.
6.6.5 Comparaciones
Además de estas instrucciones de saltos condicionales, también existe un conjunto muy
interesante para realizar comparaciones y decrementos automáticos, estos grupos son los CJNE
(Compare and Jump if Not Equal, comparar y saltar si no es igual) y DJNZ (Decrement and Jump if
Not Zero, decrementar y saltar si no es cero). La pregunta que surge inmediatamente es qué cosa
no es igual a qué otra (en el CJNE) y el decremento de qué registro no es igual a cero (en el DJNZ).
Pues bien, en los CJNE se puede comparar:
CJNE A, byte, rel 8 bits ;compara y salta si no son iguales A y la posición de memoria
de
;datos "byte".
CJNE A, #dato, rel 8 bits ;ídem con el contenido del ACC y el "dato" inmediato.
CJNE @Ri, #dato, rel 8bits ;ídem entre el contenido de lo apuntado por Ri y el "dato"
inmediato.
El caso de los DJNZ es más simple, nos permite decrementar automáticamente el contenido de
un registro específico y en caso de no llega a cero saltar a la posición relativa de 8 bits indicada
(similar al LOOP del 8088) Los código de Operación existentes a tal fin son:
DJNZ Rn, rel 8 bits ;decrementa en una unidad el contenido de Rn, si no llega a cero,
DJNZ byte, rel 8 bits ;ídem con la posición de memoria RAM "byte".
6.7 Misceláneos.
Si bien el fabricante y algunos autores colocan dentro de otros grupos a los código de
Operación que veremos a continuación, nosotros preferimos agruparlos dentro de misceláneos ya
que rigurosamente hablando no forman parte de ninguno de los grupos descritos hasta el momento,
son tres instrucciones y su sintaxis y función son las siguientes:
CLR A ; ACC = 0.
7. Aplicaciones
Sin entrar en complicadas codificaciones expondremos unos ejemplos de aplicación pero que
trataremos de analizar a fondo para fijar lo mejor posible los conceptos hasta el momento estudiados.
Como primer problema para familiarizarnos con los códigos de operación de estos
microcontroladores y la interacción con dispositivos de accionamiento externo planteamos el
siguiente:
Capítulo 9. Microcontroladores. Hoja 43
Se desea accionar unos contactores (relays accionados por tensión alterna para altas
potencias) que controlarán el accionamiento de los resistores de calentamiento del horno (varios
kilovatios) y ventiladores de circulación de aire caliente. Debe tenerse en cuenta que se trata de un
recinto de dimensiones importantes y posee una gran inercia térmica.
Antes de encarar la resolución debe tenerse en cuenta que este no pretende ser un ejemplo
de la vida real ni mucho menos, si bien veremos como resolverlo teniendo en cuenta detalles como si
lo fuera.
1. Electrónica digital.
Es muy importante esta distinción ya que en los ambientes industriales se debe tener mucho
cuidado con los accionamientos de potencia y las aislaciones ópticas o galvánicas para evitar
interferencias en la electrónica digital de control.
En lo que respecta al diseño digital, por el momento contaremos con que disponemos
de la señal de temperatura en el bit 0 del puerto 1 (P1.0), el accionamiento del horno en el bit P1.1 y
el de los ventiladores en el P1.2.
3. Configurar los puertos del microcontrolador como entradas o salidas según enunciado.
4. Inicializar variables que necesiten un valor determinado antes del desarrollo del programa
principal.
Por consiguiente para nuestro ejemplo, como no utilizaremos los bancos de registros 1, 2 y 3,
solamente el 0, el SP puede quedar en su posición por defecto.
;**************************************************************************
;* PROGRAMA : [Link]
;* COMPILADOR : FRANKLIN A51
;* PLATAFORMA : MCU 8751
;* PROPOSITO : CONTROL DE HORNO ELECTRICO CON VENTILACION, SE UTILIZAN
;* BITS DEL PUERTO P1 COMO ENTRADAS Y SALIDAS
;**************************************************************************
zonda_t EQU P1.0 ;aquí definimos nombres amigables para los bits
calienta EQU P1.1 ;de P1
ventila EQU P1.2
En segundo término, para configurar un puerto como entrada basta con poner el bit
correspondiente en "1", de esta forma nuestro programa lucirá más o menos así:
Hasta aquí la inicialización de nuestro problema, ahora queda ver como es el comportamiento
del lazo principal de control.
De esta forma tenemos que un ciclo de máquina consume 12 períodos de reloj, si este es de
12 MHz, el ciclo de máquina tiene entonces una duración de 1 Seg.
Capítulo 9. Microcontroladores. Hoja 45
Por consiguiente, basta con escribir un fragmento de código que ejecute o "consuma" 60
millones de ciclos de máquina y allí tenemos nuestro minuto.
Volvemos a remarcar aquí que este es un método bastante rudimentario como tal, pero el
objetivo es notar lo fácil que es implementar lazos repetitivos con unos pocos códigos de operación.
Continuando entonces con el desarrollo, el código para verificar el estado del horno puede
ser:
;***********************************************************************
;***********************************************************************
17
En este ejemplo aparecerá el símbolo $. Éste es el llamado contador de ubicación o location counter. El mismo equivale a
decir “aca” y debe interpretarse como:
JMP $ Equivale a ACA: JMP ACA.
Capítulo 9. Microcontroladores. Hoja 46
;**************************************************************************
un_minuto: ;<--- Rutina
; PROPOSITO : Genera un retardo de aproximadamente un minuto
; REGISTROS : utiliza R4, 5, 6 y 7
; REQUIERE : nada
; DEVUELVE : nada
; OBS. :
;**************************************************************************
PUSH R4
PUSH R5
PUSH R6 ;salvamos en la pila el contenido de los registros
PUSH R7 ;a utilizar
1. Es de muy buena costumbre comentar lo más posible las líneas de código que se van
escribiendo, en casi la totalidad de los compiladores esto está permitido anteponiendo al
comentario un punto y coma ";" como puede notarse en el programa de ejemplo.
2. Por lo general se suelen escribir por separado (antes o después) las subrutinas del
programa principal por una cuestión de claridad e interpretación, al igual que en el reciente
ejemplo de aplicación. Es recomendable (aunque no excluyente) escribirlas antes así el
compilador al barrer secuencialmente el archivo fuente, al momento de ser invocadas, ya
analizó las subrutinas. Simplemente es más rápida la compilación.
4. Otro detalle interesante es la utilización de la directiva "EQU", que nos permite asignarle
nombres personalizados a variables, posiciones de memoria en general y bits en particular
Capítulo 9. Microcontroladores. Hoja 47
que deseemos nombrar con algún nombre en especial. Esto facilita la interpretación y el
posterior mantenimiento del programa.
5. En la práctica se buscará algún método más efectivo para computar tiempos, pues es un
desperdicio total del poderío del microcontrolador emplearlo exclusivamente para hacer una
demora ejecutando lazos.
Por último tenemos la directiva "ORG" para indicar el nuevo origen de ensamblado del
programa, al encontrar esto, el compilador comienza a ensamblar código a partir de la posición
especificada. De esta forma es posible ubicar porciones de código o tablas de datos en la zona del
mapa de memoria de programa que se quiera.
220 v +12v
Relay
Vent
Contactor
A continuación, queda definir cómo será la conexión de los dispositivos que debe controlar el
MPU. Como mencionamos anteriormente, para los accionamientos de potencia se utilizan
contactores, a su vez los contactores deben ser accionados con relays y estos con transistores.
Para mejorar aún más la separación de los circuitos de potencia y digitales se puede recurrir a
la aislación óptica:
Capítulo 9. Microcontroladores. Hoja 48
+12v
Relay
Vcc
Debe tenerse en cuenta para este caso en particular que el accionamiento se realiza con un
"0" y no con un "1", para que sea como el circuito anterior se debe colocar un optoaislador PNP.
En principio puede parecer exagerado extremar las precauciones, pero nunca están de más a
la hora de diseñar un sistema sólido y confiable ante interferencias que pueden ingresar por las
fuentes de alimentación de los relevadores y contactores.
Para finalizar, veremos la forma de realizar una entrada desde un dispositivo externo.
Para el caso del sensor de temperatura supongamos que el mismo entrega a su salida "un
contacto seco", esto es el equivalente a un interruptor, supongamos que por debajo de 80 grados
esta abierto y por arriba de 80 está cerrado, de esta manera lo conectaríamos así:
contacto seco
Vcc
+12v
Capítulo 9. Microcontroladores. Hoja 49
Podemos ver como al cerrarse el contacto el diodo del optoaislador produce el corte del
transistor PNP el MCU "ve" un "0" en el pin del port P1.0. El resto de los componentes hacen la
protección contra sobretensiones y permiten un "CLAMPING" de la malla de entrada a un valor
limitado de tensión.
• Instrucciones de iteración.
Se desea implementar un sistema que mediante una tabla de conversión de 256 valores
transforma un dato de 8 bits en otro, también de 8 bits. Se dispone entonces de 8 líneas de entrada
y 8 de salida, las mismas deben implementarse sobre un bus bidireccional sobre el puerto P1.
El programa debe leer permanentemente el puerto P1, realizar la conversión del dato
mediante la tabla mencionada y volcar el dato convertido sobre el mismo puerto. Se cuenta para el
desarrollo con un 80c31 y una memoria EPROM 27c64.
En primer término y a diferencia del problema anterior debemos hacer uso de las capacidades
del bus multiplexado para acceso a memoria de programas externa.
A modo de ejercicio implementaremos un bis bidireccional de 8 bits para leer y escribir sendas
líneas de I/O utilizando el P1; como líneas de control para este bus haremos uso de líneas
adicionales que tomaremos del P3.
U?
2 19
3 D1 Q1 18
4 D2 Q2 17
5 D3 Q3 16
6 D4 Q4 15
7 D5 Q5 14
U? 8 D6 Q6 13
U? 27p D7 Q7
31 39 9 12
19 2 EA/VP P0.0 38 D8 Q8 U?
18 Q1 D1 3 19 P0.1 37 11 11 10
17 Q2 D2 4 X1 P0.2 36 1 C 12 O0 A0 9
Q3 D3 27p 12MHz P0.3 OC O1 A1
16 5 35 13 8
15 Q4 D4 6 18 P0.4 34 15 O2 A2 7
Q5 D5 X2 P0.5 74HCT573 O3 A3
14 7 33 16 6
13 Q6 D6 8 P0.6 32 17 O4 A4 5
12 Q7 D7 9 9 P0.7 18 O5 A5 4
Q8 D8 RESET 21 19 O6 A6 3
11 P2.0 22 O7 A7 25
C 1 12 P2.1 23 A8 24
OC Vcc 13 INT0 P2.2 24 A9 21
14 INT1 P2.3 25 A10 23
74HC573 T0 P2.4 A11
15 26 2
U? T1 P2.5 27 A12
2 18 1 P2.6 28 20 Vcc
4 1A1 1Y1 16 2 P1.0 P2.7 CE 22
6 1A2 1Y2 14 3 P1.1 17 OE 27
8 1A3 1Y3 12 4 P1.2 RD 16 PGM 1
11 1A4 1Y4 9 5 P1.3 WR 29 VPP
13 2A1 2Y1 7 6 P1.4 PSEN 30
2A2 2Y2 P1.5 ALE/P 27C64
15 5 7 11
17 2A3 2Y3 3 8 P1.6 TXD 10
2A4 2Y4 P1.7 RXD
1
1G 8051
19
2G
74HCT244
Capítulo 9. Microcontroladores. Hoja 51
Con respecto al bus bidireccional sobre el puerto P1, notamos la existencia de dos
dispositivos, un LATCH de 8 bits 74hc573 para la salida, y un óctuple buffer de 3 estados para la
entrada. La idea es, mientras está habilitado uno de ellos, el otro permanece inactivo. Desde el
punto de vista del 573 de salida, cuando retiene un dato, el buffer de entrada debe estar en tercer
estado (alta impedancia). Por otro lado, cuando se está leyendo el buffer, no se debe producir el
“latcheo” sobre el 573, sino se almacenará el dato entrante en forma directa.
Para llevar a cabo la señalización correspondiente utilizaremos dos líneas del P3, una para
controlar la retención de datos en el 573 y otra para poner la salida del buffer de entrada en alta
impedancia a voluntad.
El Programa:
La inicialización del programa es similar a la del ejemplo de aplicación 1 que viéramos con
anterioridad, tenemos entonces:
;**************************************************************************
;*
;* PROGRAMA : [Link]
;* COMPILADOR : FRANKLIN A51
;* PLATAFORMA : MCU 80c31
;* PROPOSITO : CONVERSION DE DATOS DE 8 BITS POR PUERTO BIDIRECCIONAL
;*
;**************************************************************************
Inicio:
• Deshabilitar el 573.
• Habilitar el 244.
• Deshabilitar el 244.
• Deshabilitar el 573.
• Volver a comenzar.
Entonces:
Loop:
SETB out ;
;**************************************************************************
La mayor dificultad , por llamarla de alguna manera y ya veremos por qué, reside en el
manejo de una tabla. Notemos antes un detalle importante, el dato a convertir se encuentra en el
ACC antes de llamar a la rutina de conversión, cosa que aprovecharemos para utilizar el OPCODE
“MOVC A,@A+DPTR”:
Capítulo 9. Microcontroladores. Hoja 53
;**************************************************************************
; REGISTROS : ACC
; OBS. :
;**************************************************************************
;**************************************************************************
Tabla:
DB dato0,dato1,dato2, ... ,
DB ....
DB dato253,dato254,dato255
;**************************************************************************
Cada uno de los dos temporizadores comunes a todos los microcontroladores operan y cada
uno se programa mediante cuatro registros especiales18. Estos registros forman parte del área de
registros de funciones especiales y solo uno de ellos es direccionable a nivel de bit.
A continuación veremos un diagrama que muestra la interrelación entre las patas de conexión
del puerto P3 con propósito de contar eventos externos, los registros dedicados de los
temporizadores y circuitería especial a los efectos del funcionamiento de los temporizadores.
MCS-51
Divisor
Oscilador x 12
Registros
TL0
TL0 TH0
TL1
Entrada T0
Registro TMOD
G C/T#
Registro TCON
TF0 TR0
1 2 1
3 1
2 3
2
INT0#
18
El tercer temporizador, exclusivo del 8052 y sus derivados tiene características particulares que estudiaremos en el punto
5.c
Capítulo 9. Microcontroladores. Hoja 55
Como veremos próximamente, los registros TL0 y TH0 pueden programarse para
comportarse de diferentes modos. En principio lo que debemos tener muy presente es que al recibir
transiciones de señal por la línea de entrada a TL0 representada en la Fig. 23, se incrementa el valor
de la cuenta de dichos registros (luego veremos las variantes). Al producirse un desborde de la
cuenta de estos registros, se ve afectado el bit TF0 del registro TCON indicando el fin de la cuenta y
que puede servir tanto para manejar el dispositivo por programa (lectura de registro de control
TCON) o bien por interrupción.
En realidad, los registros TCON y TMOD poseen un conjunto idéntico de bits TF, TR, G y
C/T# para cada uno de los temporizadores. Nuestro ejemplo es con el temporizador 0 simplemente
porque se debía elegir uno de los dos ya que ambos temporizadores se comportan exactamente
igual. Los registros de conteo del temporizador 1 se denominarán TL1 y TH1.
Sin embargo no es suficiente con que se den las condiciones nombradas. También deben
darse las condiciones para que la compuerta AND active el multiplexor la llave que conecta con los
registros de cuenta. Es indispensable que el bit TR (TIMER RUN) se encuentre en "1", luego basta
con que o bien esté en "1" la entrada INT0# o esté en cero el bit G (GATE).
De todo esto se desprende que si por ejemplo debemos contar con una resolución del orden
de los milisegundos, colocando un cristal de 12MHz y programando al bit C/T# = 0, tendremos una
resolución de cuenta del orden del µseg.
Si en cambio la fuente de reloj (base de tiempo) debe ser externa, utilizamos la entrada T019.
Por supuesto que existen muchas variantes que estudiaremos con más detalle en los ejemplos de
aplicación del capítulo correspondiente.
19
Debe destacarse que en caso de no utilizarse la opción de contar eventos externos, las patas T0 y T1 de la puerta 3
quedan disponibles para su uso normal.
Capítulo 9. Microcontroladores. Hoja 56
Como podemos ver, el registro se divide en dos, una mitad controla al temporizador 0 y la otra
al temporizador 1. Resta ver el significado de los bits M (modo), las combinaciones de estos bits
significan (para cualquiera de los temporizadores):
• Modo 1 (0,1): Idem anterior pero utilizando los 16 bits del par TH-TL.
• Modo 2 (1,0): En este modo se utilizan para la cuenta los 8 bits del registro TL. El registro
TH se utiliza para mantener en el un valor de recarga para TL, es decir, una vez agotada
la cuenta de TL, éste se recarga con el contenido de TH y así sucesivamente. Es de
notarse que al acabarse la cuenta de TL, se activa el bit TF correspondiente y se lo
20
Este modo de 13 bits se planteó por compatibilidad con el 8048, predecesor en la familia de microcontroladores de Intel.
Capítulo 9. Microcontroladores. Hoja 57
• Modo 3 (1,1): Este es un modo mixto, aquí tenemos que TL0 y TH0 se comportan en
forma independiente (como si se trataran de dos temporizadores separados). TL0
funciona como en los modos 0 y 1 pero contando 8 bits y actúa sobre el indicador TF0.
TH0 lo hace controlado por los bits de control del temporizador 1 y por ende actúa sobre
TF1 y solo como temporizador. El temporizador 1 queda bloqueado como tal y solo se lo
puede utilizar como contador pero sin interactuar con ninguno de los dos flags TF0 ó TF1.
Simplemente mostramos la ubicación de los bits 4 a 7 que son los que por ahora nos
interesan, los restantes los veremos al estudiar las interrupciones.
• Captura de cuenta. Suele ser frecuente querer determinar el momento en el que ocurre
un evento. Un método sería estar leyendo permanentemente un bit de una puerta de
entrada y en el momento que se activa la señal, leer un temporizador. Lamentablemente
ese método emplea mucho tiempo de CPU. El temporizador 2 del 8052 incorpora un
nuevo modo de trabajo que al producirse ese hecho externo se retiene el estado de ese
Capítulo 9. Microcontroladores. Hoja 58
Para llevar a cabo lo arriba enunciado, dispone de cinco registros y dos patas del puerto P1.
Los registros involucrados comprenden:
• TF2 (Timer 2 overflow flag): Puesta a "1" por desborde del temporizador 2, debe
resetearse por software. No se pone a "1" cuando RCLK o TCLK son "1".
• EXF2 (Timer 2 external flag): Se pone en "1" cuando se produce una captura o recarga
por una transición a estado bajo en T2EX y el bit EXEN2 está en "1". Cuando la
Capítulo 9. Microcontroladores. Hoja 59
• RCLK (Receive clock flag): Al estar en "1" fuerza la utilización de los pulsos de desborde
del temporizador 2 como reloj de recepción del puerto serie en los modos 1 y 3 (se verá
más adelante).
• TCLK (Transmit clock flag): Idem anterior pero con la transmisión del puerto serie.
• EXEN2 (Timer 2 external enable flag): Si está en 1, permite la captura o recarga del
temporizador 2 en las transiciones a nivel bajo de T2EX si es que el temporizador 2 no
está siendo utilizado como reloj del puerto serie. Puesto a "0" se ignoran los pulsos y
transiciones de T2EX.
• C/T2 (Timer or counter select): Igual a "0" funciona como contador interno divisor por 12
de la frecuencia de cristal. Igual a "1" es contador de eventos externos (por flancos
descendentes).
• CP/RL2 (Capture/Reload flag): Cuando está en "1", se producen capturas del valor de
cuenta sobre los registros de captura con los flancos descendentes en T2Exsi EXEN2 = 1.
Cuando está en "0" se producen autorrecargas con el desborde del temporizador 2 o
transiciones negativas en T2EX cuando EXEN2 = 1. Si RCLK o TCLK son "1" este bit es
ignorado y el temporizador se autorrecarga solo con los desbordes.
La programación del bit DCEN se hace sobre el registro T2MOD que describimos a
continuación:
Existe un modo en el que se puede programar una señal rectangular de salida con un ciclo de
actividad del 50% por el pin P1.0. Este pin , en lugar de ser una I/O común, además puede
comportarse de forma de proporcionar una frecuencia de salida programable desde 61 Hz hasta 4
MHz (con cristales externos de 16 MHz). Para configurarlo en este modo se deben cumplir las
siguientes condiciones en estos bits: C/T2=0, T2OE=1, TR2=1.
Indirect addressing mode in MCS®-51 microcontrollers is used when a more flexible and data-driven access to memory locations is required. This mode involves using a register (e.g., R0 or R1) to hold the memory address of the data rather than the data itself. The execution of indirect addressing uses the '@' symbol to denote the pointed data. For instance, to access the content of memory location 2Fh, R0 is loaded with 2Fh, and then the instruction 'MOV A,@R0' is executed, which moves the data from address 2Fh into the accumulator. This allows for dynamic adjustments to memory access without changing the operand code .
The 74LS373 is crucial in microcontroller circuits with expanded external memory access as it functions as an address latch. Because the ALE signal only briefly presents the address on the multiplexed address/data bus, the 74LS373 retains the address during this period. This retention allows the other portions of the circuit to access this address while data flows over the same bus without overwriting the address information. Thus, it simplifies and stabilizes the transition between address and data phases in a circuit .
When executing a program stored in external memory, the microcontroller releases the ALE signal to demultiplex the address bus, followed by enabling the /PSEN signal to read instructions from the external memory. Each machine cycle involves six states where the ALE indicates the availability of the address, and /PSEN controls the data bus’s tristate output to read the instructions. This process involves fetching the low and high bytes of the address into ports P0 and P2, respectively, while the state machine ensures the execution of the fetched operation code .
The ALE (Address Latch Enable) signal in a microcontroller circuit with external memory interface is used to demultiplex the lower byte of the address from the data bus. This is necessary because the address and data share the same bus, and the ALE signal indicates when the address is valid so that it can be latched into an external latch like the 74LS373 or 74LS573. This mechanism allows the microcontroller to communicate effectively with external memory components .
Context switching in MCS®-51 family microcontrollers is implemented through a rudimentary mechanism that allows for real-time operating systems (RTOS) to switch between different tasks. This involves partitioning the processor's time into slots (time slicing) and assigning each active task a specific time slot. When the specific time allocated for a task elapses, a context switch occurs, enabling the microcontroller to save the current task state and load a new task to be processed. This method is similar to that used in full-fledged operating systems like UNIX or Windows and allows for efficient multitasking .
The /RD (Read) and /WR (Write) signals are vital in memory operations involving external data memory for microcontrollers. The /RD signal indicates the microcontroller is ready to read data from the memory, starting with a low-level trigger and requiring data to be present on the bus when it transitions back to high. The /WR signal, on the other hand, initiates writing data to the memory. It goes low to signal that valid data is on the bus, and devices can capture this data during the time it remains low, alongside a specified additional period dictated by device timing requirements .
The implementation of time slices in MCS®-51 microcontrollers enhances task management in real-time kernels by allowing each task to operate in a dedicated time frame. This efficient scheduling ensures that the microcontroller handles multiple tasks without conflicts or resource wastage. By segmenting execution times into slots, the kernel switches between tasks preemptively, reducing delay and increasing system responsiveness, similar to full-scale operating systems. This capability of allotting and switching time slices empowers the microcontroller to manage complex functions in real-time systems .
The Parity Bit (P) in microcontrollers is significant as it serves as a basic form of error detection. It indicates the parity of the number of '1' bits in the accumulator, including itself. If the count of '1' bits is odd, the parity bit is set to '1', indicating even parity. This feature impacts processing by providing a quick check mechanism that enhances data integrity during operations, alerting the system to potential bit errors that occurred during transmission or processing, though it cannot correct such errors .
The microcontroller accesses external program memory when the /EA (External Access) pin is connected to '0' or when the program counter exceeds the internal memory limit of 1FFFH. In such cases, the /PSEN (Program Store Enable) signal facilitates reading from the external program memory. The microcontroller enables /PSEN to allow the external program memory’s tristate output to drive the data bus, effectively reading and executing instructions from there .
Direct addressing mode facilitates operations involving Special Function Registers (SFR) by allowing the microcontroller to manipulate specific registers through direct memory access without intermediary steps. In this mode, the command consists of an operation code followed by an operand holding the register address. For instance, to modify SFRs, which are mapped from addresses 128 to 255, direct addressing directly specifies the register, enabling immediate execution of tasks such as data movement between the accumulator and the SFRs, optimizing control over microcontroller functionalities .