Sei sulla pagina 1di 5

Manipulacin directa de puertos. Usando los puertos digitales.

(Segunda traduccin al castellano de los artculos escritos aqui:


http://hekilledmywire.wordpress.com/2011/02/23/)
Hola amigos lectores, hoy voy a escribir un poco acerca de los pines digitales y cmo
leer y escribir valores digitales para esto, es un tema sencillo pero de gran importancia, porque
casi todo lo que nuestros microcontroladores hacen es utilizar sus entradas y salidas para
hablar con leds, los drivers del motores, LCD, registros de desplazamiento, para leer los datos
de los sensores digitales y un monton de otros usos, as que vamos a empezar a leer acerca de
cmo hacerlo.
Nuestro microcontrolador, el ATmega328p tiene registros, estos registros estn
relacionados con los puertos de entrada/salida, cada puerto tiene un nombre especfico y sus
registros asociados, de hecho, nuestro Atmega tiene el puerto B, C y D, y cada puerto un
diferente nmero de pines (Esta es una restriccin del paquete de 28 pines PDIP y no desde el
microcontrolador, ya que un PDIP 40 pines, por ejemplo, tiene 4 puertos con los 8 bits cada
uno), el nico puerto que tiene el total de sus 8 pines de entradas/salidas es PORTD.
Como usted ya sabe, cada pin puede tener mltiples funciones, como la generacin de
PWM, o las capacidades de ADC, los pines 6 y 7 del PORTB son los pines de entrada para el
oscilador de cristal, y pin 6 del PORTC le corresponde al botn de reinicio.
En esta imagen se puede ver todas las funciones alternativas que cada pin puede tener
en el chip en cuestin que es el ATmega328P.

Y aqu est la asignacin entre los nombres de los puertos Arduino y su verdadero
nombre:

Entonces, cmo podemos interactuar con nuestros pines digitales?


Bueno, para empezar hay un registro dedicado para cada puerto que define si cada pin
es una entrada o una salida, que es el registro de DDRX, donde x es la letra del puerto que
queremos configurar, en el caso de la Arduino hay DDRB, DDRC y DDRD. Como toda variable
lgica, cada bit en los registros DDRX puede ser 1 0, es que poner un bit especficos de DDRX
a 1 configura el pin como salida y ponerla a 0 configura el pin como una entrada, vamos a ver
un pequeo ejemplo que configura pines 0,1,2,3 como entradas digitales y los pines 4,5,6,7
como salidas digitales:
DDRD = 0b11110000;

Y quiero todos los pin como salidas:


DDRD = 0b11111111;

Y si usted necesita todas las salidas? Comprubalo por ti mismo, o espera unos das que
voy a liberar mi tutorial de de manipulacin de bits.
Debe haber algn tipo de atencin cuando se utiliza PORTD y Serial / USART porque los
pines 0 y 1 del PORTD son los utilizados por la USART y si pones estos dos como entradas o
salidas, la USART sera incapaz de leer o escribir datos en los pines.
Ya podemos decir al Atmega cmo sern utilizados sus pines, pero queremos saber
cmo leer y escribir datos en dichos pines, de modo que para escribir datos en un determinado
puerto, se utiliza el registro PORTx, ste es fcil de recordar, donde x es el nombre del puerto,
y despus de la configuracin de un pin como salida es slo una cuestin de poner 0 o 1 en el
registro PORTx para controlar que el pin de este en estado alta o baja, respectivamente, vamos
a ver algo de cdigo para esto:
DDRD = 0b11111111;
PORTD = 0b11111111;

// Todos los pines de PORTD son salidas.


// Todos los pines de PORTD estn en estado alto.

DDRD = 0b11111111;
PORTD = 0b00000000;

// Todos los pines de PORTD son salidas.


// Todos los pines de PORTD estn estado bajo.

Y qu tal un patrn de encendido, apagado, encendido, ..?


DDRD = 0b11111111;
PORTD = 0b10101010;

// Todos los pines de PORTD son salidas.


// Un patrn de encendido, apagado, encendido, etc.

Ahora, lo nico que queda es leer en el pin para poder leer los datos de los sensores o
incluso cuando se pulsa un botn, para leer el estado de un pin digital configurado como
entrada, vamos a utilizar un tercer registro llamado PINX, donde de nuevo x es el nombre del
puerto donde se encuentra el pin, as que primero con DDRX decimos a la micro-controlador
que queremos algunos pines como entradas digitales, y luego usando PINX leemos sus valores,
parece fcil, por lo que permite profundizar en el cdigo:
DDRD = 0b00000000;
char my_var = 0;
my_var = PIND;

// Todos los pines del PORTD son entradas


// Creamos una variable para guardar la informacin leda en PORTD
// Lee PORTD y pone la informacin en la variable

Es tan fcil como puede ser, y en comparacin con el digitalWrite de Arduino y las
funciones de leer, con acceso directo al puerto puede ahorrar espacio en el la memoria flash y
tambin puede ganar mucha velocidad, porque las funciones Arduino puede tomar ms de 40
ciclos de reloj para leer o escribir un solo bit en un puerto y al mismo tiempo para leer otra de
un solo bit el cdigo es bastante complejo con un montn de lneas que ocupan por lo menos
unos 40 bytes, que podra ser un pequeo ahorro en flash, pero es un gran paso para acelerar
cualquier programa, pero estos son fciles de usar por las personas que no entienden mucho
acerca de la programacin/microcontroladores, por lo que cada aplicacin tiene sus ventajas y
desventajas, pero le permite continuar.
Es un poco raro que usted necesite leer o escribir en un puerto completo en cada
momento, por ejemplo, si usted quiere encender un LED, o leer un botn slo tendr que
utilizar un pin, y escribir todos los bits uno a uno cada vez que queremos cambiar un valor en
un puerto, es una tarea aburrida, pero librera C de AVR tiene algunas pocas palabras definidas
como Px0..7, donde x es de nuevo el puerto que desea utilizar y 0..7 es el valor del pin
individual de dicho puerto, por lo que para iluminar un LED debemos hacer algo como esto:
DDRD = (1<<PD2);
PORTD = (1<<PD2);

// Configura el pin 2 de PORTD como salida.


// El pin 2 de PORTD tiene ahora un valor lgico 1.

O para leer el estado de un botn:


DDRD = 0b11111101;

/*
*
char my_var = 0;
/*
*
my_var = (PIND & (1<<PD1)); /*

Configura el pin 1 de PORTD como estrada y el resto como


salida. */
Crea una variable para guardar la informacin leda
en PORTD. */
Le el pin 1 de PORTD y lo coloca en la variable. */

Tambin puede utilizar la macro Px0..7 varias veces en una misma instruccin, por
ejemplo, en este cdigo, se ejectutara algo de cdigo slo si se pulsa dos botones al mismo
tiempo:
DDRD = 0b11111100;
// Los pines 0 y 1 de PORTD son entradas, y el resto salidas.
if(PIND & ((1<<PD0) | (1<<PD1))) {
/* Algn cdigo dentro del if() que se ejecutara solo si los dos botones se
* encuentran activados. */
}

Creo que vas a encontrar el punto, pero el tutorial de manipulacin de bits ayudar un
poco sobre esto temas.
Hay todava algunas cosas ms que podemos hacer con nuestra entrada y salida de los
pines/puertos que sera muy til para usar las interfaces I2C y por ejemplo, para utilizar los
botones, estoy hablando de los pull-ups que nuestros microcontroladores tienen en su interior y
te mostrar cmo se pueden habilitar y por qu debes utilizarlos cuando se utilizan los
pulsadores.
Cuando usted tiene un botn que puede tener dos estados, uno es desconectado, y
cuando usted lo presiona har una conexin entre los pines del microcontrolador y permite por
ejemplo, conectarse a tierra, pero cuando se desconecta, no hay nada que fuerce un valor
estable en el pin de entrada, para el ojo inexperto es aceptable suponer que el pin va a leer un
1, porque cuando se pulsa el botn lee 0, pero la realidad es que el pin puede leer 1 0 ya que
el pin es muy sensible al ruido electromagntico, como una pequea antena, por lo que puede
resolver este problema de dos maneras similares, una es para conectar una resistencia de
10Kohms o ms entre el Vcc (+5 v) y el pin de entrada, o slo tienes que ahorrar algunas

monedas de un centavo con el uso de los pull-ups que nuestros micro-controlador que tienen
integrados, tambin hace que nuestros circuitos un poco ms simple y esto tambin es una
buena idea.
Para habilitar las resistencias pull-ups tenemos que hacer algo que puede resultar un
poco extrao a primera vista, porque no existe un registro dedicado para activar o desactivar el
pull-ups, estos son activados o desactivados escribiendo, 1 o 0 respectivamente en el registro
PORTx cuando el registro DDRX se configuran como entradas, vamos a ver algo de cdigo para
clarificar esto:
DDRD = 0b00000000;
PORTD = 0b00001111;
char my_var = 0;
my_var = PIND;

//
/*
*
//
//

Todos los pines de PORTD son entradas.


Habilito las Pull-ups de los pines 0,1,2 y 3 y lo deshabilito
en los pines 4,5,6 y 7.
Creo una variable para guardar la informacin leda en PORTD.
Leo PORTD y coloco la informacin en la variable.

Si ejecuta este cdigo, sin tener nada conectado a PORTD, los cuatro bits mas altos de
la variable my_var puede ser 0 1, cualquier combinacin posible de ellos porque son
flotantes (actan como pequeas antenas), pero los cuatro bits ms bajos leer todos un 1
debido a que el pull-ups imponen una seal de 5V dbil que se lee como un valor lgico 1.
En un sentido bsico esto es todo lo que necesita saber para dominar la manipulacin
directa de los puertos, pero el tutorial de manipulacin de bits le ensear cosas mas
ingeniosas como las mscara de bits, las operaciones AND, OR, NOT y XOR y cmo configurar y
limpiar los bits en un registro y algunos buenos trucos con los operaciones de desplazamiento
derecho e izquierdo, todas cosas bueno a saber, ya que puede acelerar su programa y son muy
tiles cuando se utilizan los puertos digitales.
Permitanme ahora hacer un pequeo programa de prueba que pueda hacer uso de las
entradas y salidas digitales, as como el pull-ups, porque este tutorial a sido muy terico y es
agradable llegar al final haciendo parpadear algunos leds!.
Este es el pseudo-cdigo de nuestro programa, su intencin es la de leer un interruptor
y cada vez que el interruptor es ledo, se conmutara el estado de un LED, por lo que cuando se
presione el interruptor el LED se iluminara, pulse de nuevo y el led se apagara, y de nuevo
desde el principio, esto se podra hacer usando varias condicionales if(), pero se puede hacer
en una sola lnea usando el poderoso operador XOR(^)
Main {
Configuracin del puerto, en este cdigo de ejemplo usar PORTD.
bucle infinito {
Leo el valor del botn
Si el led esta encendido y el botn ==1: Apaga el led.
Si el led esta apagado y el botn==1 enciende el led.
}
}

Slo una cosa ms (s que siempre voy un poco off-topic, pero este es importante)
cuando se utiliza un botn conectado a una entrada digital, debemos ser conscientes de que
un botn no da una buena y transicin limpia entre 0 a 1 o de 1 a 0, pero en su lugar la seal
puede tener problemas de rebote, esto es debido a las propiedades mecnicas del botn y no
un defecto de diseo. Como el botn tiene una pequeo conector en el interior, y cuando
presiona el conector se cierra y se cierra el circuito entre su entrada y salida, pero esta pata
suele a oscilar un poco hasta que quede firme en su parada, lo que debemos tener cuidado de
esto, y como siempre hay dos maneras, mediante un condensador pequeo cerca de la botn
para el rebote del valor, o que esta eliminacin de rebotes en el mismo cdigo, que es ms
fcil de hacer cuando tenemos un montn de botones y de nuevo es ms barato que la adicin
de una gran cantidad de componentes a nuestro circuito.
Si buscas en Google el termino button debouncing encontrara una gran variedad de
formas para evitar el rebote de los botones usando cdigo, la forma que voy a utilizar aqu es la
ms simple de todos, es slo insertar un pequeo retraso entre las consecutivas lecturas de un
botn, esto es de por supuesto, un mtodo de bloqueo, porque nuestro microcontrolador se
detendr por algunos milisegundos, hay otras formas mas inteligentes que usar
temporizadores, pero para proyectos de 2 3 botones que no requieren una sincronizacin
super precisa se trata de una mtodo de uso comn.
Para hacer este retraso voy a utilizar el incorporado en las rutinas de retraso
proporcionado por AVR lib-c.

As que vamos a empezar a programar, todo el cdigo es muy sencillo si usted entendi
todo lo que fue escrito anteriormente.
#include <avr/io.h>

/* Esta cabecera incluye las definiciones para todas las


* direcciones do los registros y otras cosas, casi siempre
* deber incluirse. */

#define F_CPU 16000000UL

/* F_CPU le dice al compilador que usas un cristal de 16Mhz


* y asi puede generar los delay exactos, entonces debe ser
* declarado. */

#include <util/delay.h>

/* Contiene las funciones de delay que pueden generar delays


* exactos de ms o uS. */

uint8_t readButton(void);

// Declaracin de la funcin readButton.

int main(void) {
DDRD &= ~(1<<PD2);
PORTD |= (1<<PD2);
DDRB |= (1<<PB5);

//
//
/*
*
*

Configura el pin 2 del PORTD como una entrada.


Activa el pull-ups en el pin 2 del PORTD.
Configura el pin 5 del PORTB como salida, este es el pin
digital 13 en la placa Arduino que tiene el led
integrado. */

while(1) {
// Infinite loop
if(readButton()==1) {
// Verifica el estado del botn.
PORTB ^=(1<<PB5);
/* Esta es la linea de cdigo que mencione
* anteriormente para intercambiar el estado
* del led. */
}
_delay_ms(250);
// Delay entre consecutivas lecturas.
}
}
uint8_t readButton(void) {
if((PIND & (1<<PD2)) ==
_delay_ms(25);
}
if((PIND & (1<<PD2)) ==
Return 1; // Si
}
else {
return 0; // Si
}
}

0) { // Si el botn esta presionado.


// Retardo de entrada para el valor ledo.
0) { // Verifica que la la lectura sea correcta.
todava es 0 es porque si tenamos pulsado el botn
el valor cambio la lectura es incorrecta.

Creo que no hay necesidad de mostrar cmo crear el proyecto en AvrStudio en este
punto, pero si usted tiene cualquier duda consulte los otros tutoriales o deje un comentario, y
no se olvide de presionar el botn de reinicio cuando se quiere subir su cdigo.
Tampoco tiene un esquema del circuito, pero puedo conseguir uno si alguien lo solicite,
o cuando tengo el tiempo para hacer uno, el LED usado en es el LED incorporado que Arduino
como en el pin digital 13, y el botn es tambin es fcil, se conecta una de las patas del botn
situado a la pin PORTD2, que es el pin digital 2 de arduino y la otra pierna del interruptor debe
conectarse a masa.
Hay aqu un pequeo video que muestra que este cdigo en accin, el led no siempre
cambia, pero esto es "normal" porque mi botn como ya se ha sufrido un poco en su vida y
tambin puede pulsar el botn cuando el retraso de los 250 ms se est ejecutando, pero
funciona y aqu est la prueba:
http://www.youtube.com/watch?feature=player_embedded&v=kuIz0Edlrn0
He hecho algunas correcciones al texto, y aqu est el cdigo en un proyecto de AVR
Studio listo para compilar y cargar:
http://code.google.com/p/avr-tutorials/downloads/detail?name=port.zip
Gracias por leer y no te olvides de comentar cualquier cosa que desee, y una buena
programacin!

Potrebbero piacerti anche