Está en la página 1de 32

Conocimientos Bsicos del Microcontrolador ATmega 328.

Puertos de Entrada/Salida de propsito general.


Los pines usados en la placa Arduino (el ATmega8 y el ATmega168) poseen tres puertos (El
ATmega328 (Arduino Uno) usa la misma configuracin de pines que el ATmega168):

B (pines digitales del 8 al 13)

C (entradas analgicas)

D (pines digitales del 0 al 7)

Puertos de Entrada y Salida (I/O Ports). LA siguiente figura muestra el circuito


equivalente de un pin de entrada y salida.

Todos los registros y los bits de referencia se escriben en forma general. Los
registros indicados en la siguiente descripcin nos permiten emplearlos para un
uso especfico en funcin de la operacin deseada por el usuario. A
continuacin se muestra el esquemtico del Puerto como entrada y salida de
propsito general.

A continuacin se muestra la siguiente tabla que describe su uso para la


configuracin de los puertos y pines del microcontrolador.

Debemos de recordar que las terminales de los diferentes puertos tienen


funciones adicionales tal y como se muestra a continuacin:

A continuacin se describen los registros correspondientes.


PUERTO B.

PUERTO C.

PUERTO D.

El Arduino Uno que contine al microcontrolador ATmega328P presenta los


puertos B,C y D.
Cada puerto es controlado por tres registros, los cuales tambin estn definidos
como variables en el lenguaje del Arduino.
El registro DDRx (Registro de Direccin de Datos del Puerto x), determina si el
pin es una entrada o una salida.
El registro PORTx (Registro de Datos del puerto x), controla si el pin est en
nivel alto o en nivel bajo.
El registro PINx (Registro de configuracin de PIN del puerto x), permite leer el
estado de un pin que se ha configurado con entrada usando la funcin
pinMode().
Los registros DDR y PORT pueden ser ambos, escritos y ledos. El registro PIN
corresponde al estado de las entradas as que solo puede ser
ledo. El PORTD mapea los pines digitales del 0 al 7
DDRD El registro de configuracin del modo de los pines del puerto D
lectura/escritura
PORTD Registro de datos del puerto D lectura/escritura
PIND Registro de pines de entrada solo lectura
El PORTB mapea los pines digitales del 8 al 13. Se debe recordar que Los bits
altos (6 & 7) estn mapeados a los pines del cristal de cuarzo y no pueden ser
usados. Estos son solamente accesibles en el Arduino Mini.
DDRB El registro de configuracin del modo de los pines del puerto B
lectura/escritura
PORTB Registro de datos del puerto D lectura/escritura
PINB Registro de pines de entrada solo lectura
El PORTC mapea los pines de entrada analgica del 0 al 5.
DDRC El registro de configuracin del modo de los pines del puerto B
lectura/escritura
PORTC Registro de datos del puerto D lectura/escritura

PINC Registro de pines de entrada solo lectura


Cada bit de estos registros corresponden con un solo pin; por ejemplo el bit
menos significativo de los registros DDRB, PORTB, y PINB hace referencia al
pin PB0 (pin digital 8)
Normalmente antes para declarar un pin lo hacamos de la siguiente manera:
Una de las caractersticas de la programacin de microcontroladores es el
manejo de operaciones orientadas a bits. Esto se usa debido a que cada uno de
los bits de los registros de E/S del microcontrolador tienen un significado
especfico, como por ejemplo la habilitacin de una caracterstica.
Existen diferentes formas de acceder a los bits de un registro, entre ellas la de
realizar enmascaramientos con operaciones or y and. A continuacin se
muestra un ejemplo de ello:
PORTB |= (1<<PB2); //Coloca un 1 en el bit 2 del Puerto B
PORTB &= ~(1<<PB2); //Coloca un 0 en el bit 2 del Puerto B
En la primer lnea de cdigo se realiza la operacin or entre el registro y un
valor dado por un 1 desplazado PB2 veces. Esto es 1<<2 lo cual corresponde a
"00000100" en binario. Al realizar la suma lgica de un 1 en la posicin 2,
independiente del valor en que se encuentre, este bit se coloca en 1 y el resto
de bits no se ven afectados porque el valor que se les est sumando es cero.
En la segunda lnea de cdigo se realiza la operacin and entre el registro y el
valor negado de la operacin de un 1 desplazado PA2 veces. Esto es ~(1<<2)
lo cual corresponde a "11111011" en binario. Al realizar la and con este valor
solo el bit 2 es colocado en cero y el resto de bits queda en su valor original.
Macros
Una forma simple de trabajar con estas operaciones orientadas a bits es
creando macros que permitan realizar esta tarea de forma ms simple:
1
2

#define sbi(PORT,bit) PORT |= (1<<bit)


#define cbi(PORT,bit) PORT &= ~(1<<bit)

La forma de usarlo sera la siguiente:


1
2

sbi(PORTA,1) //Coloca el bit 1 del puerto A en 1


cbi(PORTC,6) //Coloca el bit 6 del puerto C en 0

Otros macros disponibles en las libreras del AVR Libc se pueden encontrar en:
http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html

Otra forma de llevar a cabo la elaboracin de un programa con Arduino.cc es


de la forma siguiente:

Entonces si quisiramos declarar 7 pines (desde el digital 0 al digital 7),


tendramos que repetir pinMode 7 veces. Al igual que escribir tendramos que
poner digitalWrite(pin,estado) pin 0 estado alto, pin 1 estado bajo, etc.
Al utilizar Registros PORT (Puerto) tenemos la ventaja de que con solo una
instruccin podemos declarar el pin como entrada o salida y tambin podemos
escribir, si lo queremos, como estado HIGH o LOW.
Para controlar cualquiera de los tres puertos, debemos usar dos instrucciones.
Primera instruccin: se debe declarar en la estructura void setup() y nos sirve
para declarar si el pin se va a usar como INPUT o como OUTPUT.
Dato Importante:

Ejemplo:
1 = OUTPUT

0 = INPUT

DDRX = B11111110;
donde X es la letra del puerto a usar (BCD). Sin embargo si queremos tener
comunicacin Serial tendramos que hacer esto:
DDRX = DDRX | B11111100;
Esta manera es ms segura ya que configura los pines del 2 al 7 como salidas
sin cambiar los pines 0 y 1, que como mencionamos son RX y TX Para tener
ms referencia de los operadores bit a bit tipo AND, visita el siguiente post de
Arduino:
Tutorial de operaciones con bits.
Tenemos el siguiente cdigo:
B11111110;
Colocamos B, porque el nmero a marcar es Binario. Para ampliar ms sobre
los tipos de datos en Arduino visitemos el siguiente blog:
Tipos de Datos en Arduino.
Para saber el estado que le daremos al pin siempre lo pondremos escribiendo
de derecha a izquierda.
Nmero
a
marcar
1
1
1
1
1
1
1
0
Ubicacin 7
6
5
4
3
2
1
0

del pin en
Arduino.
Segunda instruccin: es la escritura del puerto (esta variar en funcin
del programa que estemos desarrollando). Lo localizamos en la
funcin void loop().
Ejemplo:
1 =HIGH
PORTX= B11111110;

0 = LOW

En este ejemplo los pines del 1 al 7 permanecern encendidos (HIGH) y el pin 0


LOW. Veamos un ejemplo aplicando estos conceptos:
En este ejemplo lograremos que durante dos segundos todos los leds
enciendan, durante dos segundos ms alumbren los impares y durante dos
ms todos se apaguen. Veamos:
Configuracin Utilizada:

Configuracin Utilizada para este proyecto.

Abrimos Arduino IDE y escribimos el siguiente cdigo:

Para ver otro ejemplo utilizando Registro PORT (Puerto), visitemos el siguiente
post:
Control de Display 7 Segmentos mediante Pulsadores.
Ventajas y Desventajas que nos ofrece al utilizar el Registro PORT (Puerto):
Desventajas:
El cdigo es mucho ms difcil de depurar y mantener, y es mucho ms
difcil de entender para la gente. Solo lleva algunos microsegundos al
procesador ejecutar cdigo, pero podra llevarte horas descubrir por qu
no funciona y arreglarlo.
Es mucho ms fcil causar mal funcionamiento no intencionado usando
el acceso directo a un puerto. Observa como la lnea DDRD =
B11111110, de arriba, menciona que el pin 0 se debe dejar como una
entrada. El pin 0 la lnea de recepcin (RX) en el puerto serial. Podra ser
muy fcil causar que tu puerto serial deje de funcionar por cambiar el
pin 0 a una salida.
Ventajas:
Puede que puedas cambiar los pines de estado muy rpido, en
fracciones de microsegundos. Las funciones digitalRead() y digitalWrite()
son cada una cerca de una docena de lneas de cdigo, lo cual al ser
compilado se convierte en unas cuantas instrucciones mquina.
Cada instruccin maquina necesita un ciclo de reloj a 16MHz, lo cual puede
sumar mucho tiempo en aplicaciones muy dependientes del tiempo. El Registro
PORT (Puerto) puede hacer el mismo trabajo en muchos menos ciclos de
trabajo.

Algunas veces necesitamos configurar muchos pines exactamente al


mismo tiempo. Por lo que usar las funciones digitalWrite (10,HIGH),
seguida de la funcin digitalWrite (11,HIGH), causar que el pin 10 se
ponga en nivel alto varios microsegundos despus que el pin 11, lo cual
puede confundir circuitos digitales conectados al Arduino, cuyo
funcionamiento dependa del tiempo preciso del cambio de esos bits.
Si te ests quedando sin memoria para tu aplicacin, puedes usar estos
trucos para hacer que tu cdigo sea ms pequeo. Usando este mtodo
se necesitan muchos menos bytes de cdigo compilado que si se hace
un bucle para que se vaya cambiando cada pin uno por uno.

Reset.
El pin 1 del ATMega328P es el pin de RESET del cual podemos mencionar que
es una interrupcin de alta prioridad, este debe tener un estado Alto (HIGH)
para evitar que el microcontrolador se reinicie, para ello ponemos una
resistencia de 10k entre el pin 1 y positivo (5v). Si no ponemos la resistencia el
microcontrolador funcionar igualmente, pero para evitar posibles reseteos
aleatorios es mejor asegurar el nivel lgico por lo que hay que ponerla.
Para poder resetear el microcontrolador cuando uno desee se pone un pulsador
(PUSH BOTTON) conectado entre el pin 1, justo antes de la resistencia de 10k,
y negativo (GND). As cuando lo pulsemos el microcontrolador detectar el
estado bajo (LOW) en el pin 1 (RESET) y se reiniciar.
Verificar que el pin 7 est conectado a VCC del ATMega328P o sea a positivo
(5v) y el pin 8 a negativo (GND), con estas 2 conexiones se alimenta al
microcontrolador. El pin 22 tambin va conectado a negativo (GND) y los pines
20 (AVCC) y 21 (AREF), que son los voltajes de referencia.
Tambin podemos ver que el pin 9 (TOSC1) y el pin 10 (TOSC2) se tiene
conectador el oscilador de 16MHz, el oscilador no tiene polaridad, as que se
puede colocar como uno desee. En cada extremo del oscilador se tiene
conectado un condensador de 22pF a negativo (GND) para darle estabilidad
al microcontrolador, en circuitos bsicos estos condensadores de 22pF no son
necesarios, pero si es recomendable ponerlos.
Se pueden tener diferentes maneras de dar un RESET al microcontrolador, la
anterior mencionada se conoce como RESET externo, pero tambin se puede
tener RESET interno ya sea por programa o cuando uno de los mdulos se
RESETEA porque as est configurado. Los casos pueden ser:

RESET por Energa.


RESET Externo.
Por Deteccin de Bajada de tensin.
RESET por el Sistema de Perro guardin (Watchdog System Reset).
Voltaje de Referencia Interno.
Watchdog Timer.

Cmo se realiza un restablecimiento de software del AVR?


La forma cannica de realizar un restablecimiento de software de la no-XMEGA
AVR es utilizar el temporizador de vigilancia. Activar el temporizador de
vigilancia del tiempo lmite establecido ms corto, y luego ir a una, no hacer
nada bucle infinito. El organismo de control se restablecer el procesador.
XMEGA partes tienen un bit especfico RST_SWRST_bm en el RST.CTRL registro,
que genera un restablecimiento de hardware. RST_SWRST_bm est protegido
por el sistema de configuracin de Proteccin XMEGA Cambio.
La razn por la que utiliza el temporizador de vigilancia o RST_SWRST_bm es
preferible a saltar al vector de reset, es que cuando el organismo de control
o RST_SWRST_bm restablece el AVR, los registros se restablecern a sus
ajustes conocidos, por defecto. Mientras que saltar al vector de reset dejar a
los registros en su estado anterior, que por lo general no es una buena idea.
PRECAUCIN! Mayor RAV tendrn el temporizador de vigilancia deshabilitado
en un reinicio. Por estas RAV mayores, haciendo un reinicio por software que
permite al organismo de control es fcil, ya que el organismo de control y luego
se desactivar despus del reinicio. En los nuevos RAV, una vez que el
organismo de control est habilitado, entonces se queda habilitado, incluso
despus de un reinicio! Por estas nuevas RAV una funcin necesita ser
aadido a la seccin .init3 (es decir, durante el cdigo de inicio, antes de main
()) para desactivar el organismo de control con suficiente antelacin para que
no se reinicia continuamente el AVR.
Aqu hay un cdigo de ejemplo que crea una macro que se puede llamar para
realizar un restablecimiento automtico:

Para RAV ms nuevos (como el atmega1281) tambin aadir esta funcin a su


cdigo a continuacin, desactivar el organismo de control despus de un
reinicio (por ejemplo, despus de un arranque en caliente):

Por lo que al hacer un reset se puede realizar tambin de la siguiente forma:

El cdigo anterior, normalmente puede tener problemas por lo que el siguiente


cdigo se ha cargado en diferentes microcontroladores y no se ha presentado
tanto errores:
#include <avr/wdt.h>
void setup() {

wdt_disable();
Serial.begin(9600);
Serial.println("Setup..");
Serial.println("Wait 5 sec..");
delay(5000);
wdt_enable (WDTO_8S);
Serial.println("Watchdog enabled.");

// deshabilito el watchdog

// espero 5 segundos, para dar tiempo a cargar un nuevo sketch.


// habilito el watchdog cada 8 segundos

}
int timer = 0;
void loop(){
// Cada 1 segundo se incrementara un contador (que sera mostrado por el monitor serial) y parpadeara el led
if(!(millis()%1000)){
timer++;
Serial.println(timer);
digitalWrite(13, digitalRead(13)==1?0:1); delay(1);
}
// wdt_reset();
// si se llamara a esta funcion todo andaria correctamente, ya que se resetearia el
// watchdog.
// Est remarcada para que se resetee el arduino a los 8 segundos y veamos que
// pasa.
}

Comportamiento esperado:
Se deberian ver por monitor serial los valores del 1 al 9, un reset del arduino y
otra vez los valores, esto en un bucle infinito.
Comportamiento en arduinos que no funcionan correctamente:
Se ve por monitor serial los valores del 1 al 9 y luego se cuelga el arduino
parpadenado todos los leds como loco.

Interrupciones
Las interrupciones son eventos especiales que se generan interna o
externamente en el microcontrolador que pausa momentneamente la
ejecucin del programa para realizar una tarea breve, y despus volver al
punto donde se qued ejecutando el programa. Esto sirve para que el
microcontrolador no est siempre trabajando, por ejemplo, en checar el estado
de una entrada constantemente, y solo poner atencin cuando el evento
deseado ocurre.
En esta entrada se tratar de explicar cmo funciona el sistema de
interrupciones de los AVRs, los vectores y registros que lo componen tanto
como su programacin en C con el compilador avr-gcc y avr-libc.
Imaginen que estn en la mesa de su casa tomando el desayuno, de repente el
telfono suena, te levantas, atiendes la llamada, cuelgas y vuelves a la mesa a
desayunar, esa sera una analoga de lo que es una interrupcin.
Lo mismo pasa con los microcontroladores y microprocesadores, tienen un
mtodo de que otros dispositivos externos (o internos) al micro, puedan
mandarle una alerta sobre algo, y este atienda la alerta y contine su proceso.
Los microcontroladores AVR tienen no tiene interrupciones por software, lo que
es til para tareas especiales de sistemas operativos, como intercambio de

procesos, o manejo de excepciones, aqu nos concentraremos solamente en las


interrupciones por Hardware.
Las interrupciones se organizan en vectores de interrupcin con prioridad.
Cada modelo de AVR tiene un conjunto diferente de fuentes de interrupcin,
podemos averiguar cules son las fuentes que pueden interrumpir al
microcontrolador viendo su datasheet. Los vectores de interrupcin no son ms
que direcciones especficas de la memoria de programa en la que se encuentra
una instruccin de salto al fragmento de cdigo que se debe ejecutar acorde a
la fuente que lo interrumpe.
En el AVR tenemos diferentes fuentes de interrupciones, cada una teniendo su
espacio de memoria de programa para el vector de interrupcin, en esta
direccin se indicar cual es la direccin de salto donde se encuentra el cdigo
que se debe ejecutar para esa interrupcin. El contador de programa se carga
con la direccin del vector de interrupcin una vez que sta sucede y ah
tendremos una instruccin de salto a la direccin de memoria donde est el
cdigo que atiende a la interrupcin.
Se hablar de las interrupciones del ATmega328P pero es muy fcil escribir
cdigo para otro microcontrolador, basta echar un vistazo a su datasheet para
conocer los vectores y fuentes de interrupcin.
En la parte ms baja de la memoria de programa estn estos vectores de
interrupcin, si dos interrupciones ocurren al mismo tiempo la interrupcin con
una posicin de memoria ms baja se ejecuta primero, estos vectores tambin
pueden ser ubicados en la "Boot flash section" en el caso de que usemos un
boot loader.
Tabla de Interrupciones y RESET para el ATmega 328P.

Todas las interrupciones tienen su bit en el registro correspondiente para ser


habilitadas o deshabilitadas, tambin hay un bit (Global Interrupt Enable) para
habilitar o deshabilitar todas las interrupciones en el registro de Status (SREG).
Hay dos tipos de interrupciones las que se disparan por un evento que cuando

tiene lugar activa un bit (flag) en un registro del microcontrolador y cuando se


habilita salta y las que se disparan mientras la condicin de la interrupcin est
presente, no tienen que tener ningna bandera (flag) como en el caso anterior,
si la condicin de la interrupcin desaparece antes de que sta est habilitada
no
se
dispara.
El registro de Status no es salvado en el registro stack cuando se activa la
interrupcin, se debe salvar y restaurar por software, el registro que va al
stack, ste se encuentra en la SRAM (el programa empieza por un lado y el
stack por el otro,) es el contador de programa, y se restaura cuando se sale de
la interrupcin para que el micro vuelva al cdigo que estaba ejecutando.
Registros interrupciones.
Status.

Bit 1 - IVSEL (Interrupt Vector Select): sita los registros de interrupcin en


memoria, cuando:
0 se colocan al principio de la Flash,
1 se coloca al principio de la seccin del bootloader.
Para nuestro caso va a ser cero.
Bit 0 - IVCE (Interrupt Vector Change Enable): sirve para mover los vectores de
interrupcin junto con el bit anterior.

El bit 7 es Global Interrupt Enable, que habilita o deshabilita todas las


interrupciones.
Interrupciones externas y sus registros.
Estas interrupciones se pueden disparar desde el exterior del micro cambiando
el nivel de tensin de uno de sus pines, todas tienen asociadas una o varias
patillas del microcontrolador y son las que usaremos para los botones.
Las interrupciones externas son disparados por los pines INT7:0 y PCINT23:0
incluso aunque estn configurados como salida, lo que permite disparar
interrupciones por software. Hay 10 vectores de interrupcin (con el salto a la
direccin de programa donde se encuentra el cdigo que atiende a la

interrupcin) externa en la memoria, 7 de ellos (los de external interrupt


request) se corresponden con las interrupciones asociadas a los pines del
registro EIMSK, un vector/interrupcin por cada bit del registro. Los otros 3
vectores (Pin Change Interrupt Request 0, 1 y 2) se corresponden con las
interrupciones asociadas a los pines de los registros PCMSK2, PCMSK1 y
PCSMK0, una interrupcin por cada registro, es decir los 8 bits/pines del
registro pueden hacer saltar la misma interrupcin, luego habr que
determinar en la rutina de atencin a la interrupcin cual de esos bits dentro
del registro es el responsable, leyendo su entrada uno a uno.
Pin Change Interrupt. Interrupciones que se disparan cuando cambia el estado
del pin asociado a cada interrupcin, no necesitan la presencia de la seal de
reloj en el micro.

PCIE2 (Pin Change Interrupt Enable 2): habilita las interrupciones de los pines
entre PCINT23:16, los pines son habilitados uno a uno en el registro PCMSK2.
Cuando una de estas interrupciones ocurre se activa la bandera (flag)
correspondiente y se salta al vector de interrupcin $0016 si todo lo necesario
est habilitado.
PCIE1: lo mismo con PCINT15:8, PCI1, PCMSK1. $0014.
PCIE0: PCINT7:0, PCI0, PCMSK0. $0012.

Estos bits son los flags de las interrupciones habilitadas en el registro anterior,
cuando una interrupcin ocurre se pone a 1 el bit de este registro que se
corresponde con la interrupcin para indicar al microcontrolador que la
interrupcin ha tenido lugar. EL flag es limpiado cuando la rutina de
interrupcin ha sido ejecutada. Para limpiar un flag debemos escribir un uno
lgico en el bit.

La mscara para PCIE2. Si uno de estos bits est activado y el bit PCIE2 del
registro PCICR est activado, la interrupcin est habilitada en el pin de
entrada/salida correspondiente, y cuando hay un cambio en ste se dispara la
interrupcin de PCIE2.
De la misma forma con PCIE1 y PCIE0:

External Interrupt.
En este registro cada bit tiene su vector de interrupcin, no es como el caso
anterior que todos los bits del registro hacan saltar la misma interrupcin.

Los bits habilitan la interrupcin externa del pin correspondiente.

Los flags de las interrupciones asociadas al registro anterior.

En estos registros se configura el tipo de evento que dispara las interrupcines


externas asociadas a EIMSK segn la tabla de debajo, por ejemplo podemos
configurar que la interrupcin suceda cuando en el pin tenemos un flanco de
bajado o de subida, cualquiera de los dos, o cuando tenemos un nivel bajo en
el pin. Los flancos de subida y bajada de las interrupciones de EICRB necesitan
la presencia de reloj en el microcontrolador, mientras que las de EICRA son
asncronas. Al cambiar la configuracin hay que deshabilitar el bit
correspondiente en EIMSK para evitar que la interrupcin salte como
consecuencia de este cambio.

En AVR GCC una rutina de atencin a la interrupcin se declara de la siguiente


forma:
ISR(NOMBRE_vect)

{
//code
}
Donde NOMBRE_vect identifica el vector de interrupcin, puede tener ms
argumentos adems del nombre segn lo que queramos que haga la ISR, las
opciones
estn
en
el
link
anterior.
Debomos aadir el #include <avr/interrupt.h> al programa.
Con sei() y cli() de avr/interrupts.h habilitamos y deshabilitamos todas las
interrupciones poniendo a 1 0 el bit de Global Interrupt Enable. Al entrar en
una ISR se deshabilita el bit GIE a no ser que indiquemos lo contrario y al salir
se habilita.
En nuestro programa tenemos 3 botones, y por tanto deberemos configurar
una interrupcin para cada uno si usamos las interrupciones externas o
tambin podemos seleccionar una misma interrupcin para los tres utilizando
las Pin On Change Interrupt, luego tendremos que leer los botones uno a uno
para determinar cul ha sido pulsado. En este caso se van a usar las
interrupciones
externas.
Cuando se pulsa un botn no se pasa de uno a cero o viceversa en el
momento, si no que tenemos rebotes, podemos tener una seal compuesta por
varios pulsos de 1 y 0 que pueden hacer saltar a la interrupcin varias veces, lo
mismo al soltar el botn que mantenemos pulsado. Para ello lo mejor es aadir
electrnica adicional para eliminar los rebotes, como puede ser un filtro RC o
varios circuitos que se pueden encontrar buscando un poco diseados para tal
fin. En este caso no hay electrnica adicional, por lo que la nica opcin que
tenemos es eliminarlos por software, para ello se puede hacer un delay de 3040 ms en la ISR de tal forma que cuando salgamos de la interrupcin y sta
est otra vez lista para ser disparada los rebotes ya han desaparecido. En los
PCBs finales habr electrnica adicional en los botones para eliminar los
posibles rebotes, no cuesta nada hacerlo y facilitamos la programacin, y
cuando tenemos un programa complejo con varias interrupciones y muchos
tiempos cuanto ms facilitemos el programa mejor.
Para comprobar el funcionamiento de lo anterior un ejemplo sencillo:
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/interrupt.h>
#include "lcd.h"
//Botones del LCD, interrupciones externas.
//#define boton1 PORTD2 //INT0
//#define boton2 PORTD3 //INT1

void botones_inicializar();

//Inicializar Hard y configurar interrupciones.

int main(void)
{
lcd_inicializar();
botones_inicializar();
char linea1[]="Proyecto FireFly";
char linea2[]="Pulse un boton para";
char linea3[]="comenzar";
posicionar_cursor(1,3);
lcd_escribir(linea1);
posicionar_cursor(3,1);
lcd_escribir(linea2);
posicionar_cursor(4,7);
lcd_escribir(linea3);
while(1)
{
}
}
void botones_inicializar()
//Configurar registros del micro.
{
//Tipo de evento que dispar la interrupcin, 10, flanco de bajada.
EICRA &= ~(1<<ISC20); //INT02
EICRA |= (1<<ISC21);
EICRA &= ~(1<<ISC30); //INT1
EICRA |= (1<<ISC31);
EIMSK |= ((1<<INT0)|(1<<INT1));
//Habilitar interrupciones externas 0 y 1.
sei();
//Habilitar todas las interrupciones
}
//ISRs. Rutinas de atencin a las interrupciones.
ISR(INT0_vect)
{
char linea[]="Pulsado boton 1";
lcd_comando(0x01);
//Limpiar pantalla LCD.
posicionar_cursor(1,3);
lcd_escribir(linea);
// _delay_ms(40); //Rebotes
}
ISR(INT1_vect)
{
char linea[]="Pulsado boton 2";
lcd_comando(0x01);
//Limpiar pantalla LCD.
posicionar_cursor(2,3);
lcd_escribir(linea);
// _delay_ms(40); //Rebotes

El programa tiene un #include "lcd.h" en el que estn las funciones necesarias


y #defines para el control del LCD. Lo primero que debemos hacer es
configurar el hardware del pic, de esto se encarga lcd_inicializar(); que
configura los puertos como entrada/salida que necesita para funcionar, para los
botones no tenemos que tocar ningn puerto ya que por defecto estn todos
configurados como entradas, lo que si tenemos que hacer es configurar los
registros del microcontrolador para habilitar las interrupciones externas. En los
bits de ECRIA y EICRB seleccionamos que las interrupciones externas 0 y 1 (las
que vamos a usar) se disparan cuando tenemos un flanco de bajada en la
entrada, es decir cuando se pulsa el botn pasando de 1 a 0, si no
configuramos estos bits por defecto estaran en 00, la interrupcin se
disparara cuando tengamos un nivel bajo en el pin y de esta forma estaramos
entrando repetidamente a la interrupcin mientras mantuvieramos el botn
pulsado. Luego se habiltan las interrupciones en EIMSK poniendo a 1 el bit de
las que queramos que estn activas y por ltimo se habilitan todas las
interrupciones del micro con las instruccin sei();, que pone a 1 el bit 7 (GIE) de
SREG.
En el programa principal se escribe la pantalla de presentacin en el LCD y
pasa a un bcle infinito, del que slo se puede salir mediante una interrupcin
externa disparada por los botones, cuando ocurre la interrupcin saltamos a su
ISR (aqu definimos su cdigo) y sta se encarga de limpiar la pantalla y
escribir un mensaje en funcin del botn pulsado y cuando finaliza vuelve al
bucle principal. Un ejemplo sencillo que sirve para comprobar que las
interrupciones se estn produciendo de forma correcta, en este caso no
estamos haciendo nada fuera de las interrupciones pero en un programa
normal debemos salvar todos los registros que se puedan modificar dentro de
la ISR y usemos fuera de ella para luego restaurarlos y que el micro siga con su
ejecucin de instrucciones de forma correcta.
Hasta aqu con las interrupciones que an queda mucho ms de ellas...

Reloj Configuracin.
Se tienen diferentes fuentes para generar la seal de reloj en el MCU y a la vez,
la seales de reloj se distribuyen por los diferentes mdulos.

La unidad de control del reloj e encarga de generar diferentes seales de reloj


y distribuirlas a los diferentes mdulos, las diferentes seales de reloj son:
clkCPU: Ruteado al ncleo AVR, incluyendo al archivo de registros, registro de
Estado, Memoria de datos, apuntador de pila, etc.
clkFLASH: Seal de reloj suministrada a las memorias FLASH y EEPROM.
clkADC: Reloj dedicado al ADC, el ADC trabaja a una frecuencia menor que la
CPU con el objetivo de reducir el ruido generado por interferencia digital y
mejorar las conversiones.
clkI/O: Es la seal de reloj utilizada por los principales mdulos de recursos:
Temporizadores, interfaz SPI y USART. Adems de ser requerido por el mdulo
de interrupciones externas.
clkASY: Esta seal de reloj es asncrona con respeto al resto del sistema y es
empleada para sincronizar al temporizador 2, el mdulo que genera esta seal
est optimizado para manejarse con un cristal externo de 32.768 kHz.
Frecuencia que permite usar al temporizador como un contador de tiempo real,
an cuando el dispositivo est en algn modo de reposo.
La fuente de reloj se selecciona con 4 bits de configuracin internos (no
corresponden a algn registro). Se tienen las opciones:

Estos bits se deben definir durante el proceso de programacin del


microcontrolador.
Existen otro par de bits (SUT, Set up Time), que junto con los bits CKSEL,
definen un retardo inicial en la operacin del oscilador, despus de un reset;

Cristal de Baja Frecuencia Externo.


Para usar un cristal de 32.768 Khz, el cual proporciona la base para un
contador de tiempo real, debe usarse CKSEL = 1001.
Programando CKOPT se utilizan los capacitores internos. En caso contrario se
recomiendan capacitores con un valor nominal de 36 pF.
Los tiempos de establecimiento para esta opcin son:

Oscilador RC Externo.
La frecuencia se determina como f = 1/(3RC).
C debe ser al menos de 22 pF.

Los tiempos de establecimiento bajo estas opciones son:

Oscilador RC Calibrado Interno.


Se considera un voltaje de 5.0 volts y una temperatura de 25 C.

Sus tiempos de arranque:

Despus de algn modo de bajo consumo, el retraso es de 6 ciclos de reloj.


Sistema de Reloj (ATMega328p).
Similar a los otros dispositivos, la seleccin se realiza con los fusibles CKSEL,
teniendo las siguientes opciones:

El ATMega328 incorpora al fusible CLKDIV8, para dividir la frecuencia del


oscilador entre 8.
Tambin se cuenta con los bits SUT (Set up Time) para el retardo despus de
un reset.
El dispositivo se vende con CKSEL = 0010, SUT = 10, CKDIV8 = 0 (0
es programado). Por lo que inicia trabajando a 1 MHz, con un retardo inicial de
14CK + 65ms.
Ejemplo de Generacion de 1 segundo de retardo en teimpo usando el TIMER
sobre el Arduino Uno con ATMega328P.

Calculando los ciclos necesarios para 1 segundo.


Frecuencia de Reloj del ATMega328P = 16MHz.
Frecuencia de Reloj con preescaler CLK/1024= 16MHz/1024 = 15625 Hz.
Periodo del Reloj con el Preescaler CLK/1024 = (15625 Hz)^-1=6.4*10^-5seg.
Ciclos necesarios para 1 seg =15625-1=15624 ciclos =0x3D08.
El cdigo es el siguiente:
OCR1AH = 0x3D;
// Cargar el byte alto de 15624 en el registro de comparacin de salida
OCR1AL = 0x08;
// Cargar el byte bajo de 15624 en el registro de comparacin de salida
TCCR1A = 0b00000000;
TCCR1B = 0b00001101;
// Poner en el modo CTC y el preescaler de CLK/1024
while(1)
{
While ((TIFR1 & (1<<OCF1A)) == 0); // Si OCF1A es puesto a 1 (TCNT1 = OCR1A), se rompe
TIFR1 = (1<<OCF1A);
}

Temporizacin
Introduccin

// Limpiar el valor OCF1A para el siguiente retardo de tiempo.

Los AVR tienen una gama amplia de recursos internos.


Se mencionaron ya las interrupciones externas, interrupciones por cambios
en los pines I/O (ATMega328) y se vern los temporizadores.
Las seales PWM se generan con algunos de los modos de operacin de los
temporizadores.
Todos los recursos son manejados por medio de los Registros I/O o Registros
I/O Extendidos (ATMega328).
Los Registros I/O Extendidos no pueden ser accesados con instrucciones IN o
OUT. Para su acceso se utiliza STS (almacenamiento directo) para escribirlos
o LDS (carga directa) para leerlos.
En lenguaje C slo se hace la asignacin al registro y al ensamblar se genera
el cdigo que mejor convenga.
TIMERs/Contadores.

Una labor habitual de los controladores suele ser la determinacin de


intervalos de tiempo concretos.
Esto se hace a travs de un elemento denominado Temporizador (Timer).
Un temporizador bsicamente es un registro de n-bits que se incrementa de
manera automtica en cada ciclo de instruccin o cuando ocurre un evento
externo.
Si la fuente de temporizacin es externa, se le conoce como contador de
eventos.
El registro puede ser pre-cargado para iniciar su conteo a partir de un valor
determinado.
Cuando ocurre un desbordamiento en el registro (una transicin de 1s a 0s)
se genera alguna sealizacin.
En el caso de los microcontroladores AVR, pueden generarse otros tipos de
eventos relacionados con el temporizador.
El Microcontrolador ATMega328P tiene tres temporizadores y manejan tres
tipos de eventos. Los eventos quedan registrados en el registro TIFR (Timer
Interrupt Flag Registrer).

Eventos en los Temporizadores. (Sobreflujo o desbordamiento).


El evento ocurre cuando el timer alcanza su mximo valor (MAXVAL) y se
reinicia en cero.
Se muestra como con la puesta en alto la bandera TOVn (Timer/Counter
Overflow).

TCNTn puede ser pre-cargado con un valor entre 0 y MAXVAL para que la
bandera TOVn se genere a intervalos de tiempo.
Eventos en los Temporizadores. (Coincidencia de comparacin [Compare
match]).
Un registro puede ser cargado con un valor entre 0 y MAXVAL (OCRn - Output
Compare Register) y es comparado con el valor del timer en cada ciclo de reloj,
una coincidencia produce un evento. El evento se indica con la bandera OCFn
(Output Compare Flag).

Se puede configurar al temporizador para que se reinicie despus de una


coincidencia y algunas terminales relacionadas pueden ajustarse, limpiarse o
conmutarse automticamente en cada evento.
Eventos en los Temporizadores. (Captura de entrada [Input Capture]).
Con el Temporizador 1 es posible capturar eventos externos, un cambio en la
terminal ICPI provocar que el timer sea ledo y almacenado en el registro ICRn
(Input Capture Register). Indicando el evento en la bandera ICFn (Input Capture
Flag), pueden ser tiles para medir el ancho de pulsos externos.

Respuesta a los eventos de los temporizadores.


Existen 3 formas de detectar eventos y actuar ante ellos.
Sondeo (polling).

El programa principal evala de manera frecuente el estado de las


banderas. Requiere un sobre manejo de instrucciones e implica tiempo
de procesamiento.
Ejemplo: Se espera un evento por desbordamiento del Timer 0.

Bajo este esquema, las banderas se deben limpiar por software,


reescribindoles un 1 lgico.
Uso de Interrupciones.
Para que los eventos de los temporizadores generen interrupciones,
stas se deben habilitar en el registro TIMSK (Timer Interrupt Mask
Register), adems del habilitador global de interrupciones (bit 1 en
SREG).
El programa principal se ejecutar de manera normal y cuando ocurra un
evento se dar paso a la ISR correspondiente.
Ejemplo: Configurar el timer 2 para que pueda detectar eventos de
comparacin.

En este caso, las banderas son limpiadas automticamente por hardware.


Reaccin automtica sobre eventos.

Los timers 1 y 2 (y el timer 0 en el ATMega-16) soportan la posibilidad de


reaccionar slo con hardware ante eventos de comparacin. Algunas
terminales relacionadas pueden ponerse en alto, en bajo o conmutarse
de manera automtica.
La atencin a eventos se realiza de manera paralela a la ejecucin del
programa principal.
Pre-escalador.
A la entrada de los temporizadores se incluye un pre-escalador, bsicamente
es un divisor de frecuencia con diferentes salidas conectado a un multiplexor.
Los temporizadores 0 y 1 comparten al pre-escalador, pero con seleccin
independiente.

El temporizadores 2 tiene un pre-escalador independiente, con la posibilidad de


ser manejado por un oscilador externo.

Temporizacin externa
Los temporizadores 0 y 1 permiten el manejo de una seal externa para su
incremento (T0 T1). En estos casos se les denomina contadores de eventos.
Se puede configurar para detectar flancos de bajada o subida.
Los eventos externos se sincronizan con la seal de reloj interna, por lo que no
pueden tener una frecuencia mxima de f CLKIO/2.

PWM (Modulacin por Ancho de Pulso).

ADC Convertidor (Analgico-Digital).

ACMP (Comparacin Analgica).

SCI (Serial Communication Interface: Comunicaciones Seriales Asncronas).

SPI y IIC (Serial Peropherical Interface: Comunicaciones Seriales Sncronas e


Inter_Integrated Circuit).