Registros de puertos
Los registros de puertos permiten una manipulación de menor nivel y más rápida de los pines de entrada / salida del microcontrolador en una placa Arduino. Los chips utilizados en la placa Arduino ( ATmega8 y ATmega168 ) tienen t ienen tres puertos:
B (pin digital 8 a 13) C (clavijas de entrada analógicas) D (pines numéricos 0 a 7) Cada puerto está controlado por tres registros, que también son variables definidas en el lenguaje arduino. El registro DDR, determina si el pin es un INPUT o OUTPUT. El registro PORT controla si el pin es HIGH o LOW, y el registro PIN lee el estado de los pins INPUT establecidos para la entrada con pinMode (). Los mapas de de los chips ATmega8 y ATmega168 ATmega168muestran muestran los puertos. El chip Atmega328p más reciente sigue el pinout de la Atmega168 exactamente. Los registros DDR y PORT se pueden escribir y leer. Los registros PIN corresponden al estado de las entradas y sólo se pueden leer. PORTD mapas a Arduino digital pins 0 a 7
DDRD - Registro de dirección de datos del puerto D - lectura / escritura PORTD - Registro de datos del puerto D - lectura / escritura PIND - El Puerto D Insertos de Entrada - sólo lectura
PORTB asigna a Arduino los pines digitales 8 a 13 Los dos bits altos (6 y 7) se asignan a
los pines de cristal y no son utilizables DDRB - Registro de dirección de datos del puerto B - lectura / escritura PORTB - Registro de datos del puerto B - lectura / escritura PINB - El puerto B Botones de entrada Registro - solo lectura PORTC asigna a los pines analógicos Arduino de 0 a 5. Los pins 6 y 7 sólo son accesibles
en el Arduino Mini DDRC - Registro de dirección de datos del puerto C - lectura / escritura PORTC - Registro de datos del puerto C - lectura / escritura PINC - El Pico de entrada del puerto C Registro - solo lectura Cada bit de estos registros corresponde a un solo pin; Por ejemplo, el bit bajo de DDRB, PORTB y PINB se refiere al pin PB0 (pin digital 8). Para un mapeo completo de los números de pin de Arduino a puertos y bits, vea el diagrama de su chip: ATmega8 , ATmega168 . (Tenga en cuenta que algunos bits de un puerto se pueden utilizar para cosas distintas de i / o, tenga cuidado de no cambiar los valores de los bits de registro correspondientes a ellos.)
si vemos la imagen superior de un ATMega168 símil del 328, nos llamara la atención la cantidad de datos que nos proporciona cada pin del Micro, si vemos la pr imera designación para cada pin desde el interior hacia afuera nos toparemos con el nombre del pin y su puerto.
si vemos el pin nº2 del Atmega328 veremos que primera designación es PD0,¿que significa esto?, PD0 = Puerto D pin nº0. pin numero 0 correspondiente al puerto D del micro-controlador.
la
Cada micro-controlador tiene puertos de propósito general llamados GPI/O, o puertos de propósito general de entrada y salida, según el uControlador en cuestión, este tendrá desde un puerto a N cantidad. Un puerto consta de 8 pines, cada puerto es identificado con una letra, en algunos casos comienzan de la A y en otros la letra B, como vemos en la imagen superior nuestro ATMega168 tiene tres puertos; el puerto B que va desde el pin PB0 al PB7, el puerto C que va desde el pin PC0 al PC7 y el el puerto D que va desde el pin PD0 al PD7. A la hora de programar en compiladores de C o C+ + para Micros AVR para llamar a los p uertos o utilizarlos se les llama con la designación de cada puerto B, C y D y ahora que sabemos como se identifican estos puertos y los pines de cada uno podemos pasar a ver como manipularlos.
Para no dar un salto tan grande desde Arduino a Avr, podemos usar la misma IDE Arduino para manipular los puertos a nuestro antojo, ya que la IDE compila en la utilidad avr-gcc, podemos utilizar las mismas funciones y registros que usamos para manipular puertos a bajo nivel. bien el segundo paso sera conocer que para manipular los puertos GPI/O nosotros usaremos lo que se conoce como "registros de control de puertos" estos registros de control nos permiten operar la dirección, escritura y lecturas logicas en cada pin o puerto del microcontrolador. para manipular un puerto por completo Existe tres registros, los cuales son: DDRx, PORTx y PINx.
DDRx
(Data
Direction
Register):
Este registro lo podemos comparar en el entorno Arduino con pinMode();, ya que este registro se encarga de manipular la dirección de cada p in del puerto, esto significa q ue podemos indicar a cada pin del puerto x, que trabajen como escritura o lectura, salida o entrada, o como en Arduino pinMode(3,OUTPUT); DDRx se encarga de este trabajo, ahora la pregunta es ¿como funciona este registro?. DDRx es un registro que nos permite indicar el modo de operación de cada pin del puerto, la x es para indicar el puerto, si quisiésemos manipular el puerto B del Atmega328 DDRx seria DDRB,si comparamos con el entorno Arduino DDRx es similar a PinMo de en el cual se debe indicar el numero de pin y la m odalidad de trabajo, en DDRx debes indicar en una instrucción ambas cosas.
Ejemplo: teniendo en cuenta la imagen del ATMega328, (arriba), podremos observar los datos de los pines correspondientes a cada puerto,( PB,PC,PD etc) y el nombre del pin en la placa Arduino. podemos observar que los pines del puerto D, PD0 al PD7 son los pines de la placa Arduin o digital 0,1,2,3,4,5,6 y 7, por ende podemos usar este puerto como ejemplo para el registro DDRx. sabemos que el puerto de esos pines es el D, por ende el registro sería DDRD, cabe mencionar que el pin PD0 y PD1 son los pines del Arduino RX y TX, estos pines debemos tratar de no ocuparlos demasiado debido a que por ellos el atmega328 es programado desde la IDE Arduino, es decir estos pines deben ser siempre de entrada. entonces: DDRD = 11111100; "00" son los pines PD0 y PD 1, RX y TX en Arduino, son denom inados 0 para indicarle al registro que estos pines serán de "lectura de datos", y el resto de los pines serán de escritura o salida y por ende son designados como 1.
NºPin = 76543210; DDRD = 11111100; 1 = salida o write 0 = entrada o read; En la función de Arduino pinMode(numero,modalidad) r ealiza el mismo trabajo, pero pin a pin, indicando el numero de pin y si este es de lectura o escritura. código ejemplo void DDRD }
=
PORTx (Pin Output Register):
setup(){ 11111100;
Este registro en Arduino tendría su similar con la función digitalWrite(nºpin, estado); , este registro se encarga de escribir el estado de cada uno de los puertos al cual este contr olando, como vimos en DDRx el cual designaba la modalidad de trabajo para cada Pin, PORTx se encarga por ahora de los pines que fueron configurados como salida "1", o en Arduino pinMode(3,OUTPUT); para luego decir digitalWrite(3,HIGH);.
Como observamos en el esquema de arriba, al aplicar PORTx todos los pines que fueron configurados como salidas, en ellos PORTx escribirá un valor lógico alto "1" o un valor lógico bajo "0", (el valor 1 depende del voltaje logico del circuito los mas comunes son 5 y 3.3 donde 5 y 3.3 son "1")
Ejemplo: Siguiendo el esquema anterior donde ocupamos el puerto D, entonces: DDRD = 11111100;// pin 0 y 1 como entrada, del pin 2 al 7 como salida; PORTD = 10101000;// pin 3 , 5 y 7 tienen en su salida un valor lógico alto o 5v y el resto 0. si aplicásemos PORTD como en ejemplo, si conectamos LEDs a los pines digitales 2,3,4,5,6,7; podríamos observar que los LEDs conectados a los pines 3,5,7 estarán encendidos.
código ejemplo void DDRD
setup(){ 00000000; 10101000;
= PORTD
=
}
nota(digitalWrite() digitalWrite consume 50 ciclos de reloj aproximadamente ), (imagen ejemplo solo para referencia, no conecten el diagrama ejemplo, le faltan resistencias de protección a cada LED)
PORTx para pines de Entrada. PORTx también se ocupa cuando los pines de los puertos son configurados como entrada o de lectura con el registro DDRx, y es empleado para activar resistencias de PULLUP, ¿que significa esto?, que los pines de entrada podemos reconectarlos a un uno lógico "1", y así evitar problemas de ruido o como también detectar el cambio de 1 a 0, esto se aplica conectando a cada pin de entrada una resistencia conectada a Vcc o 5v, en Arduino y por ende cada pin con PULLUP esta conectado a 1. ejemplo: 0 1
= =
sin con
pullup. pullup.
NºPin = DDRD = 11100000; // pines 2,3,4 configurados PORTD= 00011100; //pines 2,3,4 se habilita la resistencia de pullup.
como
76543210; entrada.
La imagen de referenci a nos permite inferir como quedan habilitados los pines cuando estos son configurados como lectura y además habilitados con resistencias de PULLUP.
PINx (Pin Input Register): solo va faltando una cosa por realizar y es la lectura con los pines GPI/O, leer un estado 1 o 0, el registro PINx nos perm ite obtener el estado del puerto en general como también de un pin en especifico. para
entender
podemos
seguir
los
ejemplos
anteriores
y
usar
el
puerto
D.
byte estadoPuerto;//creamos una variable que contendrá los datos de cada pin. //en Arduino byte es la forma de referirse a la variable int8_t de las mismas características, variable de 8 bits. "como cada puerto solo posee 8 pines, usam os variables del mismo tamaño, en Ar duino byte es una variable que puede almacenar datos de hasta 8 bits, por ende se vera en forma optima el estado de cada puerto representado en cada bit" DDRD PORTD
=
00000000;//todos los = 00000000;//indicamos
pines que
del
puerto D los pines
como entrada. no tendrán pullup.
Si los pines 2,3 y 4 los conectamos a 5v y los pines 6,7 y 8 a 0v (GND), el estado de los pines en el puerto se verían de esta forma Conexión = Por
00011100; ende
si
realizamos
EstadoPuerto
una
=
lectura. PIND.
El valor de estado puerto si lo mostramos en puerto serial con "serial.println(estadoPuerto,BIN)" lo mostrado en el puerto serial serial 000111000. El
código
seria:
byte
estadoPuerto;
void DDRD = PORTD Serial.begin(9600); }
setup(){ 0B00000000; 0B00000000;
=
void estadoPuerto Serial.println(estadoPuerto,BIN); delay(1000); }
Puerto
serial
loop(){ PIND;
=
=
56d
=
111000b;
Estos son los tres grandes registros de manipulación de puertos GPI/O a grandes rasgos, estos registros pueden verse toscos a la hora de usarlos , pero se pueden complicar mas o simplificar si tenemos un correcto uso de las operaciones lógicas digitales básicas. las cuales son AND ("&"), OR ( "|" ) y NOT ("~"), y un extra , las operaciones shitfLeft y shiftRight "<< y >>" resumen: La operación And es una multiplicación lógica binaria, la cual retorna un 1 solamente cuando ambos datos multiplicados son iguales a 1, "A and B = C" =>" A & B = C". Ejemplo: And 0 0
= & &
0 1
&. = =
0; 0;
1 1
& &
0 1
= =
0; 1;
La operación Or es una suma lógica binaria , la cual retorna un 1 cuando cu alquiera de datos sumados sea 1, "A or B = C" =>" A | B = C". Or 0 0 1 1 La operación NOT "si A =
=
"|"
| | | |
0 1 0 1
nos 0"
retorna el ==>
Not ~0 ~1
; 0; 1; 1; 1;
= = = =
inverso "Not(A)=
lógico 1"
de
un ==>"~A
dato =
= = =
binario. 1". "~". 1; 0;
Las operaciones shiftLeft y shiftRight son un tipo de instrucción para manipular datos, son usadas para desplazar bits a lo lar go de un dato como por ejemplo un byte (dato compuesto de 8 bits), ahora como se usan: Supongamos que tenemos una variable del tipo byte llamada "data", data consta 8 bits de información los cuales son: "byte data = 00000000", pero nosotros dadas a circunstancias externas queremos que data en ves de valer 0 tenga un valor binario de cuatro = "00000100", pero para llevar a cabo esto tendríamos que volver a re-asignar el nuevo valor a la variable, oh aplicar operaciones básicas para cambiar el valor de "data", pero contamos con la operación "shiftLeft" la cual nos permite ingresar y desplazar un bit n espacios a la izquierda, por ende ingresamos el bit 1 y lo desplazamos hacia la izquierda para que data sea igual a 4 en ves de 0, ejemplo: shiftLeft
"<<"
Byte data = 00000000; //data = 0; data = 1<<2; //ingresamos el bit 1 desplazamos a la data
= 00000100;
lo ubicamos en 0 y lo //izquierda //data
dos =
espacios 4;
Cabe mencionar que al mover el bit hacia la derecha o a la izquierda, el vació dejado por el bit al desplazarse será ocupado por "0" y esto ocurrirá tanto si desplazamos un "0" o un "1". ejemplos: 00000001; 11111111; 01010101;
1<<5 0<<3 1<<2
= = =
para
01100000; 11110000; 10101100; shiftRight;
11111110; 00011100;
0>>5 1>>3
= =
00000011; 00010001;
Ahora que refrescamos nuestra memoria con las operaciones básicas And, Or, Not y ademas
haber conocido las de manipulación de
operaciones
"shift"
podemos comenzar puertos
comenzaremos
las
"Buenas
practicas" I/O.
con
DDRx:
Como ya sabemos DDRx nos permite configurar la modalidad de funcionamiento de los pines de un micro controlador AVR tanto como de lectura o escritura. ej: void DDRD = 0xC; =Output }
setup(){ (0xC hexadecimal = 12 binario = 00001100;// pin PD2 y PD3
Lo que acabamos de hacer es configurar el registro para que los pines 2 y 3 con un numero hexadecimal y que en binario es igual a 12, esto concuerda con los pines 2 y 3 sean de escritura , pero ¿podemos representar de otra forma esta instrucción? la respuesta es si. Ejemplos: A)-
DDRD
B)-
DDRD
C)-
DDRD
D)E)F)-
= =
|=
DDRD =
DDRD
=
y
PD3
=Output
00001100;//PD2
y
PD3
=Output
(1<<2)
|=
DDRD
0xC;//PD2
|
00000100
DDRD 00000000
(1<<3);//PD2
y
| 00001000;//PD2
| 00000100
PD3
y
PD3
| 00001000;//PD2
| 00000100
y
| 00001000;//PD2
y
=Output =Output
PD3
=Output
PD3
=Output
Todas las instrucciones anteriores tienen el mismo resultado que los pines 2 y 3 sean de escritura, la diferencia esta en la facilidad de lectura y lo optimizado de cada instrucción, por ende una instrucción mas optimizada obtiene un mejor resultado en su ejecución, como también que la instrucción sea m as clara y precisa, la que cumple con todo esto es la instrucción C debido a que sabemos de form a clara que los pines 2 y 3 serán de salida en el puerto D, pero ¿porque?. C)-
DDRD
|=
(1<<2)
|
(1<<3);
de ante mano sabemos que en 1<<2 el bit numero nº 2 equivalente a PD2 sera de salida, como también el bit nº 3, PD3, 1<<3 sera salida, ademas 1<<2 es equivalente a 00000100 y 1<<3 es equivalente a 00001000, por lo tanto la instrucción C se descompone en: DDRD
|=
también sabemos DDRD DDRD que
|=
que
x ;
es
el
00000100
operando "
igual
=
a DDRD DDRD
es
|=
" =
es
el
DDRD
| 00001000;
equivalente |
x ;
| 00000100 igual
de lo
un
auto
"OR"
que
nos
deja:
| 00001000; a:
F)-
DDRD
=
00000000
| 00000100
| 00001000;
en resumen la instrucción C es la mas precisa, clara de entender y de manipular. todo lo anterior también es aplicable a los registros PORTx y PINx. vamos a poner un ejemplo en código Arduino con instrucciones de Arduino y avr. ARDUINO void { pinMode(2,OUTPUT); } void { digitalWrite(2,HIGH); delay(500); digitalWrite(2,LOW); delay(500); }
en
setup()
loop()
Avr
void { DDRD } void {
en
Arduino: setup()
|= (1<<2); //
bit
nº2
equivalente
a
PD2
=
1,
salida. loop()
PORTD delay(500); PORTD delay(500);
|= &=
(1<<2);// ~(1<<2);//
00000100;2, 00000000;2,
HIGH LOW
}
En el código anterior en la sección "setup" podemos observar al registro DDRD configurando el pin PD2 como salida, DDRD |= (1<<2);,lo interesante aparece a la hora de manipular el pin D2, ocupamos la siguiente instrucción para escribir un 1 o HIGH, PORTD |= (1<<2); sabemos que la operación 1<<2 es igual a 0010 y provocara en el puerto que el pin nº 2 pase de 0 a 1, de LOW a HIGH, ahora ¿como podemos invertir el proceso?. si miramos con atención la instrucción que envía al pin nº 2 a un estado LOW. Si sabemos que en el estado anterior el puerto D fue configurado como, PORTD = 00000100;, la siguiente instrucción PORTD este efecto en el registro. &= ~(1<<2); tendrá void PORTD portd (or)
|=
+
= 00000100;// sumamos al = 00000100;//resultado pin 2
loop(){ (1<<2); 00000000; registro anterior (1<<2) esta en estado HIGH
delay(500); PORTD
&= ~(1<<2);//= PORTD &= (0<<2); //multiplicamos al registro la negación de (1<<2) => ~(1<<2) =
0<<2; portd = 00000100; (and)* 00000000;// and al registro anterior con ~(1<<2). ~(1<<2) = (0<<2); = 00000000;//obtenemos del And que el pin 2 vuelve a un estado LOW
delay(500); }
Podemos observar claramente como en el código anterior se produce un HIGH en el pin dos por la instrucción OR que suma en forma binaria un 1 al registro en la posición nº2, y por ende el estado lógico cambia de 0 a 1, a continuación la instrucción que viene multiplica un cero al uno anteriormente configurado en PORTD por ende al realizar el and (&) entre 1 y 0 ( 1 & 0) la resultante sera un cero y el pin pasara de tener un valor HIGH a uno LOW. Ahora vamos a revisar que sucede en el caso contrario, cuando quiero leer el estado lógico de un pin del Puerto D. En el caso que quisiéramos ver el estado lógico del pin 7 del puerto D , (pin digital 7 en Arduino).
Arduino
Avr en Arduino boolean estadoPin;
boolean estadoPin; void setup() { Serial.begin(9600); pinMode(7,INPUT); } void loop() { estadoPin = digitalRead(7); Serial.println(estadoPin, BIN); delay(500); }
void setup() { Serial.begin(9600); DDRD &= (0<<7); } void loop() { estadoPin = PIND7; Serial.println(estadoPin,BIN); delay(500); }
En el código anterior podemos ver las pequeñas diferencias entre las instrucciones en Arduino como en manipulación de registros, para obtener el estado actual de un determinado pin, ademas podemos apreciar las semejanzas entre ambos códigos a la hora de efectuarlo, se logra apreciar a simple vista que podemos preguntar con el registro PINx por un determinado pin de x puerto, "PIND7", pero las preguntas surgen a la hora de como puedo censar ese pin en un if o compararlo con el estado de otro pin. los
siguientes códigos son de
ejemplo
para
IDE
en
Arduino:
detectar
si
pin
7
es
=
1;
void setup() { Serial.begin(9600); pinMode(7,INPUT); } void loop() { byte estadoPin = digitalRead(7); if(estadoPin == HIGH){ Serial.println( "pin 7 = 1" ); delay(500); } }
detectar si pin 7 es = 1; void setup() { Serial.begin(9600); DDRD &= (0<<7); } void loop() { if(PIND & (1<<7)){ Serial.println("pin 7 = 1"); delay(500); } }
En ambos códigos la función principal es detectar el estado actual del pin PD7, en Arduino lo hacemos consultando a la instrucción "digitalRead(nº pin)" la cual nos retorna el estado del pin. en el proceso mediante manipulación de registros la instrucción if(PIND & (1<<7)) compara el estado del puerto D "PIND" y lo multiplica por el dato (1<<7), el cual es equivalente a 0 1000000, si la operación and "&" arroja un 1 el estado actual del pin PD7 es 1. IF(PIND * 1<<7); = IF(01000000 & 01000000) = 01000000; SE CUMPLE IF(PIND * 1<<7); = IF(00000000 & 01000000) = 00000000; NO SE CUMPLE
detectar si los pines 6 y 7 están en 1 void setup() { Serial.begin(9600); pinMode(7,INPUT); pinMode(6,INPUT); } void loop() { byte Pin6 = digitalRead(6); byte Pin7 = digitalRead(7);
detectar
si
los
pines
6
y
7 están en
1
void setup() { Serial.begin(9600); DDRD &= (0<<7) | (0<<6); } void loop() { if((PIND&((1<<7)|(1<<6)))){ Serial.println("pin 6 y 7 = 1" ); if(pin6&pin7 == HIGH){ delay(500); Serial.println("pin 6 } y 7 = 1"); } delay(500); } } En el siguiente caso se consulta cuando dos pines son de entrada y se envía una frase por puerto Serial solo cuando estos dos pines estén leyendo un valor logico 1, o 5v, en Arduino usamos nuevamente digitalRead() pero guardando el estado en dos variables, y
estas son multiplicadas con un & y comparadas con el valor HIGH. mediante registros podemos obtener el mismo resultado sumando (1<<7)|(1<<6) y luego compararlo mediante un & con el estado actual del puerto PIND&. if((PIND&((1<<7)|(1<<6)))) if((PIND&((1<<7)|(1<<6)))) cumple. if((PIND&((1<<7)|(1<<6)))) cumple. if((PIND&((1<<7)|(1<<6)))) cumple.
encender y apagar 2,3,4,5 void setup() { pinMode(2,OUTPUT); pinMode(3,OUTPUT); pinMode(4,OUTPUT); pinMode(5,OUTPUT); } void loop() { digitalWrite(2,HIGH); digitalWrite(3,HIGH); digitalWrite(4,HIGH); digitalWrite(5,HIGH); delay(500); digitalWrite(2,LOW); digitalWrite(3,LOW); digitalWrite(4,LOW); digitalWrite(5,LOW); delay(500);
= 01100000 & (01000000 | 00100000); se cumple. = 01000000 & (00000000 | 00100000); no se = 01000000 & (01000000 | 00000000);
no se
= 00000000 & (00000000 | 00000000);
no se
pines encender y apagar pines 2,3,4,5
void setup() { DDRD |= (1<<2)|(1<<3)|(1<<4)|(1<<5); } void loop() { PORTD |= (1<<2)|(1<<3)|(1<<4)|(1<<5) ; delay(500); PORTD &= (0<<2)&(0<<3)&(0<<4)&(0<<5); delay(500); }
}
Y para el final un simple código de encendido y apagado de pines en gran cantidad, usando digitalWrite(), y la manipulación de puertos.
https://www.youtube.com/watch?time_continue=213&v=364HVng2Dl8
El puerto B son 8 bits (pero son 6 bits si trabajan los pines PB6 y PB7 los para e l cristal de cuarzo externo), el puerto C son 7 y el puerto D son 8 bits.
Y así sería a grandes rasgos, como el micro-controlador configura los puertos y les asigna un valor:
Registro DDRX: Permite configurar un pin como entrada o salida. Si cargamos un 1 será una salida y si le cargamos un 0, será
una entrada.
Registro PINX: Es el registro donde se cargarán los datos de los pines que se hayan configurado como entradas.
Registro PORTX: Permite activar o desactivar los pines configurados como salida ( para enviar un 1 o u n 0 ) y en el caso de las entradas, si ponemos un 1, conectará la entrada a una resistencia pull-up y si ponemos un 0 , dejará la entrada sin conectar ninguna resistencia. La resistencia de 20k es interna y todos los pines la tienen. Si no vamos a usar la resistencia interna porque queremos ejecutar una acción cuando el pin de entrada esté a nivel alto, es recomendable conectar una resistencia de 10k a tierra ( pull-down ) para referenciar la salida a masa y evitar ruidos
El registro DDR, determina si el pin es una entrada o una salida (1 salida, 0 entrada). El registro PORT controla si el pin está en nivel alto (1) o en nivel bajo (0). El registro PIN permite leer el estado de un pin. (solo lectura) Un ejemplo que configura pines 0,1,2,3 como entradas digitales y los pines 4,5,6,7 como salidas digitales: DDRD = 0b11110000; y todos los pin como salidas: DDRD = 0b11111111;
Al utilizar Registros DDR tenemos la ventaja de que con solo una instrucción podemos declarar el pin como entrada o salida, sin embargo con pinMode() necesitaríamos 8 instrucciones.
DDRD = 0b11111111; // Todos los pines de PORTD son salidas. PORTD = 0b11111111; // Todos los pines de PORTD están en estado alto. DDRD = 0b11111111; // Todos los pines de PORTD son salidas. PORTD = 0b00000000; // Todos los pines de PORTD están estado bajo. DDRD = 0b00000000; // Todos los pines del PORTD son entradas char my_var = 0; // variable para guardar la información leída en PORTD my_var = PIND; // Lee PORTD y pone la información en la variable