4. Salidas y entradas digitales.

placs

En este tutorial vamos a ver para qué sirven y cómo se usan los puertos de entrada/salida digitales de un microcontrolador, realizaremos un pequeño ejemplo práctico con un ATtiny841 donde poder ver el funcionamientos de los pines digitales.

Entre las diversas funciones que puede realizar un pin del microcontrolador la más común es que se pueda configurar como pin de entrada/salida digital, para este tutorial vamos a usar una placa con un ATtiny841.

En el datasheet del ATtiny841 podemos identificar los pines digitales del microcontrolador bajo el nombre PAx y PBx.

PAx, PBx: pines de entrada/salida digitales. ATtiny841.

PAx, PBx: pines de entrada/salida digitales. ATtiny841.

Los nombres en cada pin identifican el hardware interno del microcontrolador al que se puede conectar el pin, y por tanto la función que el microcontrolador puede realizar con cada uno de sus pines. Los pines de entrada salida digital se identifican bajo la etiqueta PAx y PBx, por lo que en este caso todos los pines del microcontrolador excepto los de alimentación se pueden usar como pines digitales.

Si conectamos un cristal externo para generar la señal de reloj en los pines PB0 y PB1 (XTAL1 y XTAL2), esos pines se usarán para dicha tarea y por tanto no podremos usarlos como entradas o salidas digitales.

Lo mismo pasa con el pin B3 donde encontramos el RESET, el microcontrolador necesita que el pin B3 esté asignado a la función de RESET para poder ser programado mediante su conector de programación, por lo que en este caso este pin no lo podremos usar para ninguna otra tarea.

PA4, PA5 y PA6 se usan para programar el microcontrolador, por lo que si conectamos electrónica a estos pines habrá que ver si esa electrónica puede interferir con la programación del microcontrolador como se comenta en este tutorial.

ecTiny841 con ATtiny841.

ecTiny841 con ATtiny841.

ecTiny841 tiene 11 pines de entrada/salida (B3 se usa para el RESET). Si usamos un cristal externo para generar la señal de reloj tendrémos 9 pines digitales disponibles y B0, B1 y B3 no se podrán conectar a nada.

¿Cómo funciona un pin digital de salida?

Un pin de salida podemos ponerlo a “1 lógico” o a “0 lógico”, cuando esté a uno el pin tendrá un valor de tensión cercano al de la alimentación del microcontrolador (5V, 3.3V, etc..). Y cuando esté a cero el pin tendrá un valor de tensión positivo cercano a 0V.

Por lo que el pin de salida puede suministrar corriente o absorberla. Por ejemplo, en la imagen siguiente se configuran los pines PA7 y PA6 como salidas digitales, pudiendo tomar estos los valores de 1 lógico y 0 lógico.

PA7 y PA6 configurados como salidas digitales.

PA7 y PA6 configurados como salidas digitales.

Cuando PA7 se ponga a 1 lógico la tensión en este pin es cercana a Vcc, por lo que no circula conrriente por R1 y el Led1, ya que no hay una diferencia de potencial suficiente para ello entre sus extremos. Cuando PA7 se ponga a 0 lógico el pin tendrá un valor de tensión cercano a GND, por lo que por la rama circulará una corriente de (Vcc – Vled)/R1 iluminando el diodo led. Cuando se ilumina el led el pin PA7 está absorbiendo esa corriente.

De igual forma cuando PA6 se ponga a 0 lógico tendrá una tensión cercana a GND, ahora el Led2 está conectado a GND por lo que en este caso no hay una diferencia de potencial suficiente entre los extremos de R2 y del Led2 para que éste se encienda. Si PA6 se pone a 1 lógico estará a un valor de tensión cercano a Vcc, ahora la tensión entre los extremos de R2 y del Led2 es de aproximadamente Vcc como por la rama circula una corriente de (Vcc – Vled)/R2 iluminando el led. En este caso el pin PA6 está suministrando esa corriente.

No hay diferencia entre suministrar o absorber corriente por el pin, toda la corriente la acaba suministrando la batería/aimentación, pero solo ver el concepto para mostrar que la carga conectada al pin la podemos activar poniendo el pin a 1 ó a 0, dependiendo de si ésta está conectada a Vcc o GND.

La corriente que puede suministrar o absorber un pin del microcontrolador es muy limitada, si leemos el datasheet (página 246) nos dice que la corriente suministrada o absorbida por un pin no puede ser mayor de 40 mA (en muchos pines no recomienda pasar de 10 mA a 5V), y la suma de la corriente suministrada o absorbida por todos los pines no puede ser mayor de 100 mA. Si pasa una corriente mayor de esos 40/100 mA el microcontrolador se acabará calentando y dañando.

Por lo que un pin del microcontrolador lo podremos usar para activar cargas que requieran muy poca corriente, si necesitamos activar una carga que requiere más corriente tendremos que usar un driver/transistor entre el pin y la carga.

Un circuito típico puede ser el siguiente:

Circuito para carga que requiere más corriente que la del pin del microcontrolador puede dar.

Circuito para carga que requiere más corriente que la del pin del microcontrolador puede dar.

Si necesitamos más corriente para activar una carga que la que el pin del microcontrolador puede dar podemos montar el circuito anterior con un Mosfet. Comentar el uso de la resistencia R1 y R2.

Aunque la corriente de puerta de un Mosfet sea muy muy pequeña, el Mosfet tiene una capacitancia parásita entre puerta y fuente, por lo que cuando el pin del microcontrolador cambia su estado (1 ó 0) y conmuta el Mosfet, debemos cargar o descargar este condensador parásito produciendose un pulso de corriente grande, por lo que para proteger el pin del microcontrolador es adecuado añadir una resistencia en serie con la puerta del Mosfet que limite ese pulso de corriente.

Cuando el microcontrolador se resetea o se inicia por primera vez todos los puertos están configurados como entradas hasta que el microcontrolador ejecute la parte del código que los configure como salidas, código que el microcontrolador tardará unos milisegundos en ejecutar. Durante ese tiempo que el pin está configurado como entrada la puerta del Mosfet tendrá un estado indefinido de tensión, pudiendo encender y apagar la carga, por lo que si queremos evitar ese estado indefinido y que la carga esté apagada hay que poner una resistencia de pull-down R2, como se muestra en la imagen.

¿Cómo funciona un pin digital de entrada?

Un pin de entrada solo mira si el valor de tensión que le llega es cercano a Vcc o a GND, si es cercano a Vcc leerá un 1 lógico y si es cercano a GND un 0 lógico. Si el valor de tensión está en una zona intermedia o al aire (pin sin conectar) puede dar como resultado de la lectura cualquiera de los dos valores lógicos, o incluso oscilar leyendo entre 1 y 0 e incrementando el consumo de corriente del microcontrolador.

Los pines del microcontrolador que no se usan se configuran como salida o se configuran como entrada activando la resistencia interna de pull-up del pin, proporcionando la resistencia un estado de tensión definido en la entrada y evitando que ésta oscile incrementando el consumo de corriente del microcontrolador.

Un uso habitual de los pines de entrada es conectar pulsadores para comunicarnos con el microcontrolador como se muestra en la imagen:

Circuito típico para conectar un pulsador a una entrada digital.

Circuito para conectar un pulsador a una entrada digital.

Si configuramos PA7 como pin digital de entrada por él no sale ni entra corriente, su impedancia de entrada es muy alta. Si el pulsador está abierto el pin de entrada está al valor de tensión de Vcc (si no circula corriente por la rama no puede haber caída de tensión en las resistencias) y lee un uno lógico, si el pulsador está cerrado el pin está conectado a tierra a través de R2 y lee un cero lógico.

Cuando pulsamos un pulsador éste no se abre o se cierra de forma instantánea, sino que por su naturaleza física hay unos rebotes, es decir el pulsador se abre y se cierra muchas veces en un corto periodo de tiempo, tras ese tiempo llega a su estado final cerrado o abierto. Para evitar que el microcontrolador interprete los rebotes del pulsador como varias pulsaciones se suele añadir un filtro con un condensador como se muestra en la imagen.

La tensión máxima que podemos aplicar a un pin digital de entrada está comprendida entre -0.5 V y Vcc + 0.5 V, si aplicamos un nivel de tensión mayor podemos dañar el microcontrolador.

Cuando conectamos los pines de entrada y salida digitales del microcontrolador a otros circuitos integrados digitales, hay que mirar la compatibilidad entre sus niveles de tensión para reconocer el 1 y el 0 lógico. Toda esta información de los niveles de tensión a partir de los que un integrado interpreta un 1 ó 0 lógico lo encontraremos en su datasheet.

Si tenemos un microcontrolador funcionando a 5 V y lo conectamos a un circuito digital de 3.3V, cuando el microcontrolador ponga un 1 lógico en su pin de salida estará poniendo un valor cercarno a 5V. Por tanto no lo podremos conectar directamente al pin de entrada del circuito de 3.3V sin adaptar los niveles de tensión antes mediante electrónica adicional.

Programación: uso de los pines de entrada y salida.

Si leemos el tutorial anterior podemos ver cómo se configura y controla el hardware del microcontrolador que encontramos mapeado en su memoria SRAM.

Cuando se inicia el microcontrolador o se resetea, y antes de que éste ejecute ninguna instrucción del programa, todos los pines que no se configuran mediante los fuses (cristal, reset, etc..) están configurados como pines de entrada digitales.

Para configurar y usar los puertos digitales del microcontrolador (puerto A y puerto B) donde se encuentran los pines de entrada y salida digitales, debemos manipular en nuestro programa los registros asociados a los puertos que se detallan en la página 71 del datasheet.

Por lo general configuraremos 4 registros asociados a cada puerto:

Registro que configura los pines como entrada o salida.

Registro que configura los pines como entrada o salida.

DDRx: escribiendo un 1 en sus bits configuramos el pin asociado a cada bit como una salida, dejando a 0 el bit el pin es una entrada. Por ejemplo si queremos configurar como salida el pin PA7 escribiremos un 1 en el bit 7 (DDA7) de DDRA.

Para escribir en un bit o grupos de bits de un registro lo hacemos como se explicó en el tutorial anterior con los operadores &, | y una máscara.

Pone a 1 ó 0 el pin de salida.

Pone a 1 ó 0 el pin de salida.

PORTx: escribiendo 1 ó 0 en sus bits ponemos a 1 ó a 0 el pin de salida asociado a cada bit. Por ejemplo si hemos configurado PA7 y queremos ponerlo a 1, escribiremos un 1 en el bit 7 (PORTA7) de PORTA.

En PINA leemos el valor de los pines de entrada.

En PINA leemos el valor de los pines de entrada.

PINx: en este registro podemos leer el estado al que se encuentran los pines que tenemos configurados como entradas digitales. Por ejemplo si PA5 lo tenemos conectado a un pulsador, y ese pulsador pone a tierra el pin PA5, en el bit 5 (PINA5) de PINA podremos leer un 0. Por el contrario si el pulsador pone a Vcc dicho pin en PINA5 podremos leer un 1 lógico.

Para leer el bit de un registro usamos el operador & con una máscara con la poisción del bit a leer. Por ejemplo si tenemos conectado un pulsador al pin A5 configurado como entrada y queremos leerlo lo haremos con la sigueinte línea de código.

if(PINA & (1<<PINA5)) // Si A5 está a 1 entra en el if.
{	
}
PUEA activa o desactiva las resistencias de pull-up de cada pin.

PUEA activa o desactiva las resistencias de pull-up de cada pin.

PUEx: poniendo a 1 los bits de este registro activamos la resistencia de pull-up del pin del microcontrolador asociaco a cada bit del registro. La resistencia de pull-up la activaremos cuando nos ahorre tener que colocar una externa. O cuando dejemos un pin del mircocontrolador configurado como entrada y sin conectar, la resistencia de pull-up fijará el valor de la entrada de este pin evitando que oscile e incremente el consumo de corriente del microcontrolador.

Las posibles combinaciones de la configuración de un pin digital de entrada/salida en función de los registros anteriores las encontramos resumidas en la siguiente tabla.

Posibles configuraciones de un pin digital de entrada/salida.

Posibles configuraciones de un pin digital de entrada/salida.

Ejemplo práctico.

Para probar todo lo anterior vamos a hacer un primer ejemplo simple realizando el siguiente montaje en la protoboard:

  • Se conecta el terminal positivo de 4 diodos leds a los pines A0, A1, A2, A3 de ecTiny841 y el negativo del diodo a tierra. Estos pines los configuraremos como salidas digitales para encender y apagar los diodos leds, el diodo led se encenderá cuando pongamos a 1 lógico la salida digital.
  • Se conecta un pulsador a A7, este pin se va a usar como entrada digital y lo leeremos para conocer el estado del pulsador y encender y apagar los diodos leds. Por defecto los pines del microcontrolador se inician configurados como entradas digitales.

El montaje en la protoboard queda así usando las placas de eleCrab.

Montaje en protoboard. ecTiny841 + ecLeds + ecSwitch + ecPower.

Montaje en protoboard. ecTiny841 + ecLeds + ecSwitch + ecPower.

En lugar de usar el cable verde para conectar el pulsador con el pin del microcontrolador configurado como entrada, puede ser adecuado usar una resistencia (1K ohmio) en su lugar cuando empezamos a programar. De esta forma si por error configuramos A7 como salida en lugar de como entrada la resistencia evitará el cortocircuito que tendrá lugar si el pin se pone a 0 y el circuito del interruptor lo pone a 1, o al revés, evitando que la electrónica del microcontrolador o del circuito que conectamos al pin de entrada se dañe.

Una vez que tenemos el montaje de la electrónica en la protoboard tenemos que hacer el programa del microcontrolador. Para crear el proyecto en Atmel Studio, escribir el código y programar el microcontrolador seguimos los pasos de este tutorial.

Una vez creado el proyecto introducimos el siguiente código, lo compilamos y grabamos en el microcontrolador:

#include <avr/io.h>
#define F_CPU 8000000UL	//clock frequency
//#include <util/delay.h>
 
#define button PINA7
#define led0   PORTA0
#define led1   PORTA1
#define led2   PORTA2
#define led3   PORTA3
 
int main(void)
{
    //A0, A1, A2, A3 outputs 
    DDRA |= ((1<<DDRA0) | (1<<DDRA1) | (1<<DDRA2) | (1<<DDRA3));
 
    while(1)
    {
	if(PINA & (1<<button)) //check if the button is pressed.
	{	
		PORTA |= ((1<<led0) | (1<<led3));	//set: A0, A3
	        PORTA &= ~((1<<led1) | (1<<led2));	//clear: A1, A2
	}
	else
	{
		PORTA = 0x06;	//set: A2, A1 clear: A0, A3	
	}
    }
}

Como compilador usamos el avr-gcc que se instala con el entorno de Atmel, y también se instalan una serie de librerías, AVR Libc, que utilizaremos prácticamente en todos nuestros proyectos, por lo que viene bien tener a mano su manual de usuario.

Siempre que hacemos un programa para el microcontrolador vamos a poner un #include con <avr/io.h>, añadiendo las librerías necesarias donde están definidos todos los nombres de las posiciones de memoria (registros) del hardware del microcontrolador, así como la posición del nombre de los bits de los registros.

A continuación de los includes declaramos las funciones, variables globales, defines, etc.. como en cualquier programa en C, y llegamos al main de todo programa en C.

Haciendo la comparación para el que haya empezado con Arduino, lo que hay antes del bucle while() sería equivalente a la parte del programa del void setup(){} del entorno Arduino. Esta parte del código que pongamos antes del while() sólo se va a ejecutar una vez.

En esta parte escribimos o llamamos al código que se va a ejecutar una sola vez al inicio del programa en el microcontrolador, como puede ser la configuración de su hardware y pines de salida. En este caso escribimos la siguiente línea:

DDRA |= ((1<<DDRA0) | (1<<DDRA1) | (1<<DDRA2) | (1<<DDRA3));

Que se encarga de configurar los pines donde conectamos los leds como salidas digitales para poder encenderlos y apagarlos. Sobre el registro DDRA hacemos una operación OR con la máscara que creamos (00001111), poniendo a uno los 4 bits de menor peso y dejando el resto como estaban antes de hacer la operación.

El pin al que conectamos el pulsador no hay que configurarlo como entrada, ya lo está, cuando el microcontrolador se inicia sus pines por defecto están configurados como entradas.

El bucle while(1) sería equivalente al void loop(){} del entorno de Arduino, lo que metamos dentro de este bucle se va a ejecutar de forma repetitiva, ya que la condición del bucle (“1″) siempre es cierta. En los programas de los microcontroladores es necesario este tipo de bucles, debido a que ejecutan una instrucción detrás de otra, por lo que hay que poner uno de estos bucles para evitar que se llegue al final de la memoria produciendose un error en el microcontrolador.

Dentro del bucle while que se repite de manera infinita vamos a leer el estado del pulsador con las siguientes líneas:

if(PINA & (1<<button)) //check if the button is pressed.
{	
	PORTA |= ((1<<led0) | (1<<led3));	//set: A0, A3
	PORTA &= ~((1<<led1) | (1<<led2));	//clear: A1, A2
}
else
{
	PORTA = 0x06;	//set: A2, A1 clear: A0, A3	
}

El pulsador conectado al pin A7 pone un 1 ó un 0 lógico en el bit 7 del registro PINA cuando lo pulsamos o lo soltamos. Si está pulsado el bit 7 de PINA estará a 1, si está sin pulsar el bit 7 estará a 0,

Haciendo la operación & con PINA y la máscara que creamos (10000000) dejamos el bit 7 de PINA con su valor original, y el resto de bits de PINA los ponemos a 0 independientemente de su valor inicial.

Si el bit 7 vale 1 cuando hacemos la operación & la condición del if es distinta de cero y por tanto se ejecuta su contenido. Si el bit 7 vale 0 cuando hacemos la operación & la condición del if es 0 y por tanto no se ejecuta su contenido y pasamos a ejecutar el contenido del else.

En el contenido del if encendemos y apagamos los leds con los operadores & y | como se explicó en el tutorial anterior sin modificar el resto de bits del puerto. Y en el contenido del else hacemos lo mismo pero mostramos otra forma de hacerlo, escribimos un valor directamente en el puerto PORTA modificando todos los bits del puerto.

En el siguiente vídeo se puede ver el ejemplo en funcionamiento, cuando pulsamos el interruptor se encienden los leds conectados a A0 y A3, y cuando lo soltamos se encienden los leds conectados a A1 y A2.

En el pin B3 tenemos la señal de RESET, en el vídeo se puede ver como cuando llevamos este pin a 0 el microcontrolador se resetea.

Pues esto ha sido el primer tutorial sobre los puertos digitales del microcontrolador, escribiendo y leyendo sus registros asociados podemos ponerlos a 1 ó 0 lógico cuando los configuramos como salidas y leer su valor cuando los configuramos como entradas.

En próximos tutoriales se tratará el tema de las interrupciones, que nos permiten detectar cuando cambia el estado de un pin de entrada digital sin necesidad de que el microcontrolador tenga que estar leyendo el registro PINx del puerto continuamente.

Enlaces relacionados: