el registro port en arduino

17
El Registro PORT (Puerto) en Arduino En este post veremos qué son los PORT y cómo se utilizan. Al final lograremos manipular en bajo nivel y de manera más rápida los contactos de entrada/salida del microcontrolador de las placas Arduino. Los pines usados en la placa Arduino (el ATmega8 y el ATmega168) poseen tres puertos (El ATmega328 (Arduino Uno) usa la misma configuración de pines que el ATmega168): B (pines digitales del 8 al 13) C (entradas analógicas) D (pines digitales del 0 al 7) Puertos que posee el Arduino ATmega328 Para más detalles sobre cómo están mapeados los números de los pines de Arduino a los puertos y los bits observa la siguiente imagen:

Upload: juan

Post on 01-Feb-2016

64 views

Category:

Documents


0 download

DESCRIPTION

Registro PORT en Arduino

TRANSCRIPT

Page 1: El Registro PORT en Arduino

El Registro PORT (Puerto) en Arduino

En este post veremos qué son los PORT y cómo se utilizan. Al final lograremos manipular en bajo nivel y de manera más rápida los contactos de entrada/salida del microcontrolador de las placas Arduino.

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

B (pines digitales del 8 al 13)

C (entradas analógicas)

D (pines digitales del 0 al 7)

Puertos que posee el Arduino ATmega328

Para más detalles sobre cómo están mapeados los números de los pines de Arduino a los puertos y los bits observa la siguiente imagen:

Page 2: El Registro PORT en Arduino

Números de los pines de Arduino a los puertos y los bits.

El Arduino Mega presenta varios puertos B,C,D,E,F, etc.

Cada puerto es controlado por tres registros, los cuales también están definidos como variables en el lenguaje del Arduino.

El registro DDR, determina si el pin es una entrada o una salida.

El registro PORT controla si el pin está en nivel alto o en nivel bajo.

El registro PIN permite leer el estado de un pin que se ha configurado con entrada usando la función pinMode().

“Los registros DDR y PORT pueden ser ambos, escritos y leídos. El registro PIN corresponde al estado de las entradas así que solo puede ser leído.” El PORTD mapea los pines digitales del 0 al 7

DDRD – El registro de configuración 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) están 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 configuración 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

Page 3: El Registro PORT en Arduino

El PORTC mapea los pines de entrada analógica del 0 al 5.

DDRC – El registro de configuración 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 hacíamos de la siguiente manera:

void setup(){

Serial.begin(9600);

pinMode(2,INPUT);

pinMode(3,OUTPUT);

}

Entonces si quisiéramos declarar 7 pines (desde el digital 0 al digital 7), tendríamos que repetir pinMode 7 veces. Al igual que escribir tendríamos 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 instrucción podemos declarar el pin como entrada o salida y también podemos escribir, si lo queremos, como estado HIGH o LOW.

Para controlar cualquiera de los tres puertos, debemos usar dos instrucciones.

Primera instrucción: 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:

“Los pines 0 y 1 son la transmisión serial (RX y TX respectivamente). Si utilizas “Serial.begin” en alguno de esos 2 puertos; no tendrás comunicación serial. Cuidado con eso.”

Ejemplo:

1 = OUTPUT 0 = INPUT

DDRX = B11111110;

donde X es la letra del puerto a usar (BCD). Sin embargo si queremos tener comunicación Serial tendríamos que hacer esto:

DDRX = DDRX | B11111100;

void setup(){Serial.begin(9600);pinMode(2,INPUT);pinMode(3,OUTPUT);

Page 4: El Registro PORT en Arduino

Esta manera es más 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 más referencia de los operadores bit a bit tipo AND, visita el siguiente post de Arduino:

Tutorial de operaciones con bits .

Tenemos el siguiente código:

B11111110;

Colocamos B, porque el número a marcar es Binario. Para ampliar más 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.

Número a marcar

1 1 1 1 1 1 1 0

Ubicación del pin en Arduino.

7 6 5 4 3 2 1 0

Segunda instrucción: es la escritura del puerto (esta variará en función del programa que estemos desarrollando). Lo localizamos en la función void loop().

Ejemplo:

1 =HIGH 0 = LOW

PORTX= B11111110;

En este ejemplo los pines del 1 al 7 permanecerán 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 más alumbren los impares y durante dos más todos se apaguen. Veamos:

Configuración Utilizada:

Page 5: El Registro PORT en Arduino

Configuración Utilizada para este proyecto.

Abrimos Arduino IDE y escribimos el siguiente código:

int contador=0;//declaramos la variable interna como contador.

void setup(){

DDRD= B11111100;//Utilizamos D porque es del 0 al 7 y B porque es Binario.

//En este ejemplo no utilicé los pines 0 y 1 del Arduino.

/*Esta instrucción es lo mismo que hacer esto:

pinMode(2,OUTPUT);

pinMode(3,OUTPUT);

pinMode(4,OUTPUT);

pinMode(5,OUTPUT);

pinMode(6,OUTPUT);

pinMode(7,OUTPUT); */

}

void loop(){

int contador=0;//declaramos la v

void setup(){ DDRD= B11111100;//Utilizamos

Page 6: El Registro PORT en Arduino

for (contador=0; contador <3; contador ++){//establecemos contador menor que 3 porque solo son 3 casos.

switch(contador){

case 0:

PORTD= B11111111;//aquí encenderemos todos los leds

delay(2000);

case 1:

PORTD= B10101010;//aquí encenderemos solo los impares.

delay(2000);

case 2:

PORTD= B00000000;//aquí los apagaremos todos.

delay(2000);

}

}

}

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 código es mucho más difícil de depurar y mantener, y es mucho más difícil de entender para la gente. Solo lleva algunos microsegundos al procesador ejecutar código, pero podría llevarte horas descubrir por qué no funciona y arreglarlo.

Es mucho más fácil causar mal funcionamiento no intencionado usando el acceso directo a un puerto. Observa como la línea DDRD = B11111110, de arriba, menciona que el pin 0 se debe dejar como una entrada. El pin 0 la línea de recepción (RX) en el puerto serial.Podría ser muy fácil 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 rápido, en fracciones de microsegundos. Las funciones digitalRead() y digitalWrite() son cada una cerca de una docena de líneas de código, lo cual al ser compilado se convierte en unas cuantas instrucciones máquina.

Cada instrucción 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.

Page 7: El Registro PORT en Arduino

Algunas veces necesitamos configurar muchos pines exactamente al mismo tiempo. Por lo que usar las funciones digitalWrite (10,HIGH), seguida de la función digitalWrite (11,HIGH), causará que el pin 10 se ponga en nivel alto varios microsegundos después 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 estás quedando sin memoria para tu aplicación, puedes usar estos trucos para hacer que tu código sea más pequeño. Usando este método se necesitan muchos menos bytes de código compilado que si se hace un bucle para que se vaya cambiando cada pin uno por uno.

Mapear Puertos de Arduino

julio 31, 2015 Gustavo Circelli Arduino, Electrónica, Programación, Proyectos con Arduino

Mapear puertos en Arduino es una técnica muy utilizada en programación para facilitar la generación posterior de código.

En algunas situaciones requerimos la posibilidad de mapear una cierta cantidad de bits en forma directa a los puertos de Arduino o de un microcontrolador en general, por ejemplo modificar el estado de una cierta cantidad de salidas digitales a partir de algún método, que nos permita “enlazar” ( lo que llamo mapear) un bit de una variable de código con un bit físico de los puertos.

Supongamos que necesitamos controlar 12 pines digitales de salida de nuestro Arduino , para lo cual ya conocemos que deberíamos utilizar digitalWrite(pin, LOW/HIGH) para cada uno de los pines de salida.

Arduino simboliza los pines digitales desde el 0 al 13 , lo que nos facilita la construcción de Sketches “hardcodeando” el código. Hardcodear significa hacer referencia en el código en forma directa a un pin físico específico de nuestro Hardware. Cuando hacemos digitalWrite(3,HIGH) estamos hardcodeando, es decir refiriéndonos al pin físico 3 de nuestro Arduino Uno y mas precisamente el bit PD3 del PORTD.

Mapeo de los pines en el Atmega328

El micro Atmega328 o 168 que utiliza Arduino poseen los siguientes puertos con la consiguiente simbología en lo que respecta al Hardware de este chip:

Page 8: El Registro PORT en Arduino

Hardware Chip Atmega328, mapear ports

Ciertos aspectos de lo que se verá en esta entrega ya fue tratado en otra entrada dentro de este mismo sitio. Para más información consultar: “El registro PORT (puerto) Arduino”

Si consideramos solo los pines digitales del 0 al 13, podemos extraer la siguiente tabla de relaciones entre los pines digitales de Arduino y su mapeo en los PORTS físicos del Atmega328:

PortD y PortB de Arduino

Donde PDx y PBx son los puertos D y B del Atmega que indican los bits asociados a cada Puerto. Por ejemplo PD0 es el bit0 del PuertoD.

Planteo

Imaginen ahora que poseo conectados 12 Leds a cada pin con excepción de los pines 0 y 1 que se utilizan para el Serial del monitor de Arduino, y los dispongo de la siguiente manera:

Mapear el Problema presentado

Page 9: El Registro PORT en Arduino

Sabemos que en Arduino el mapeo previo de los pines es el siguiente:

dp0: rx, dp1: tx, dp2 PD2 , dp3 PD3, dp4:PD4, dp5 PD5, dp6 PD6, dp7 PD7, dp8 PB0, dp9 PB1, dp10 PB2, dp11 PB3

dp12 PB4, dp13(led13 Arduino) PB5.

Hay un libro muy interesante que trata el Hardware interno del Atmega pero muy entendible donde trata además el acceso a este Hardware con el lenguaje del skecth de Arduino lo que lo hace muy útil. Introduction to Embedded Systems: Using ANSI C and the Arduino Development Environment (Synthesis Lectures on Digital Circuits and Systems).

Se me ocurrieron Leds para facilitar la explicación, pero podrían ser cualquier tipo de otros dispositivos digitales.

Mi deseo es crear una función que podamos utilizar en nuestros sketches y que me permita modificar el estado de los 12 Leds sin tener que recurrir a las ya conocidas digitalWrite().

La función o Método de mapeo

Mi función podría ser algo como esto escribirLed(0b1111111111110000);

Como podrán notar la indicación “0b” indica que es un numero en formato binario y cada bit representa el estado de cada led, en este caso todos ON=1.

El formato del método es el siguiente:

escribirLed(0bLed1 Led2 Led3 Led4 Led5 Led6 Led7 Led8 Led9 Led10 Led11 Led12); donde Ledx representa el Número led cuyo valor puede ser “1” ON o “0” OFF

Implementación

Como primera medida el valor a pasar es un tipo de dato de 2 Bytes, entonces en Arduino podríamos utilizar el unsigned int , de manera que el prototipo de mi método sería:

void escribirLed( unsigned int leds)

{ ………………. }

El método deberá ir tomando cada bit de la posición del valor pasado como variable Leds a la función y mapear directamente su valor al Port que corresponda y en la posición que corresponda. Por ejemplo si Leds =0b100………….., el Led1 está en ON y por lo tanto dicho “1” deberá ser escrito en el PortD en el tercer bit = PD2.

Si observamos el PortD es un Byte (8bits), al igual que el PortB, aunque este último solo usa 5 bits. En la siguiente tabla vamos a representar la variable leds (16 bits), el PORTD(8bits), PORTB(5 bits) y una variable auxiliar (aux) tipo byte de 8 bits

Se puede apreciar el MSB o byte más significativo que es la parte alta o Nible Alto de la variable leds y el LSB o byte menos significativo o Nible Bajo de la variable leds. El MSB de los PortD/B no existen.

void escribirLed( unsigned int le

{ ………………. }

Page 10: El Registro PORT en Arduino

Relación de mapeo entre Variables de código y Ports Físicos

¿Cómo hacemos entonces por ejemplo para llevar el BIT15 de la variable leds que representa el LED1 , al BIT2=PD2 del PuertoD ?,….. en eso se resume lo que va a hacer nuestro método, no solo con LED1 sino con el resto. Para esto hay que ir analizando pin a pin.

Vamos a utilizar las funciones que provee implementadas en el compilador de Arduino y que son las de desplazamiento de bits.

Crearemos una variable local, dentro del método, evitando el uso de Globales y optimizar memoria, ya que las Locales se destruirán al no ejecutarse el método y se crearán temporalmente dentro de nuestro método de mapear.

Vamos a explicar para el caso del LED1. Creamos la variable byte Led1; y otra variable auxiliar del tipo byte aux,.

aux= leds>>8; es decir que en aux ahora se guardaran los bits resultado de desplazar a la derecha 8 posiciones a la variable leds, esto es el BMSB ( Byte más significativo de leds) y que contiene desde el LED1 al LED8.

variable byte aux para mapear PORTD

Si observamos ahora, LED1 en aux y LED1 en PortD, hay 5 espacios a la derecha, de esta manera hacemos:

led1= (aux >>5) & 0b00000100; logrando que led1 ya creada, tenga el estado solo del LED1, esto es 0 o 1. La operación AND con 0b00001000, permite descartar y asegurarse que solo el bit2 de aux tenga el valor del Led1. La operación AND se realiza bit a bit, de manera que 1.0=0 y 1.1=0.

De la misma manera observamos que :

Page 11: El Registro PORT en Arduino

led2=(aux>>3) & 0b00001000; desplazo 3 a la derecha

led3=(aux>>1) & 0b00010000; desplazo 1 a la derecha

led4= (aux<<1) & 0b00100000; desplazo 1 a la izquierda

led5=(aux <<3) & 0b01000000; desplazo 3 a la izquierda

led6=(aux<<5) & 0b10000000; 5 a la izquierda

Hasta aquí tendremos logrado casi el mapeo en el PortD , cada variable ledx posee el estado de cada led, solo resta mapearlos en el puerto. Para esto creamos otra variable byte puertoD=0; y hacemos lo siguiente

puertoD=puertoD | led1|led2|led3|led4|led5|led6; puertoD se armará realizando las sucesivas operaciones OR bit a bit . Para ilustrar el caso supongamos que cada variable tenga seteado su bit

led1=00000100, led2=00001000,led3=00010000,led4=00100000,led5=01000000 y led6=10000000

Haciendo OR bit a bit portD=0B111111xx , el xx que queda son los bit de rx y tx y que no debemos afectar su valor. Antes de escribir directamente al PortD debemos cuidar de no afectar el valor de rx, tx. Para esto creamos una variable byte d; que va a leer los bits del PortD , de la siguiente manera:

d= PIND; esta es una macro con acceso directo solo a la lectura del PortD. En el tutorial mencionado antes “El registro PORT (puerto) Arduino” , se describe muy bien. Para resumir, cada puerto del Arduino B,C y D, existen macros para su acceso directo de la forma:

DDRx lectura escritura del registro de dirección (entrada o salida digital) del Port x

PORTx lectura escritura sobre el puerto x

PINx lectura de los pines del puerto x.

En mi caso utilicé PINx para la lectura del PortD y así ver en qué estado están los pines rx y tx.

Lo que hacemos ahora finalmente es

d=d&0b00000011; nos interesa rescatar solo los pines 0 y 1, su estado, AND 00000011

puertoD=puertoD|d; Hacemos OR bit a bit entre puertoD y d y guardamos en la misma variable , ahora está completo con el estado de los Leds y el estado real de rx/tx sin afectar

PORTD = puertoD; / escritura final al puerto D.

Resta ahora poder mapear los leds restantes del 7 al 12 sobre el PortB de PB0 a PB5.

Si volvemos a ver la tabla con la variable leds, PortD y PortB, y la variable aux que teníamos como resultado de leds>>8 , el LED7 deberá ir al PortB PB0 y el LED8 al PB1.

Para lograr esto hacemos:

led7=(aux>>1) & 0b00000001;

led8=(aux<<1) & 0b00000010;

Page 12: El Registro PORT en Arduino

Con lo cual ya nos quedan formateadas las variables led7 y 8 con la posición, al igual que los leds del 1 al 6 ya analizados.

El LED9 ya no lo podemos recuperar de la variable aux ya que posee 8 bits y el LED9 está en una novena posición que no existe, por este motivo hacemos:

Aux=leds; y aquí viene lo divertido, al igualar un byte a un int , la información se recorta ya que aux solo tomará el LSB de la variable leds, y de esta manera el nuevo aux queda como sigue:

Variable byte aux para mapear PORTB

Luego.

led9=(aux>>5) & 0b00000100;

led10=(aux>>3) & 0b00001000;

led11=(aux>>1) & 0b00010000;

led12=(aux<<1) & 0b00100000;

Creamos otra variable byte puertoD y la armamos con la OR entre lo leds que faltan.

puertoB=puertoB|led7|led8|led9|led10|led11|led12;

y finalmente enviamos la información al PuertoB

PORTB = puertoB & 0b00111111; con la máscara de esos 6 bits que nos interesan

Con esta explicación ya podrán seguir el código completo, para lo cual les dejo el mismo para este del método. Este método es muy útil, por ejemplo lo estoy usando para hacer un cubo de leds de 3×3.

Esta es una manera de hacerlo, puesto que en lugar de desplazar previamente aux , 8 posiciones a la derecha, quizás otro método podría ser lograr desplazamientos acordes al bit a mapear, por ejemplo si tomo LED1 ,y desplazo 13 posiciones a la derecha la variable leds y lo guardo directamente en la variable led1 ya estaríamos en la posición:

led1=(leds>>13) & 0b00000100, ya estaría.

led2=(leds>>11) & 0b00001000; led3=(leds>>9) & 0b00010000; led4=(leds>>7) & 0b00100000;

led5=(leds>>5) & 0b01000000; led6=(leds>>3) & 0b10000000; led7=(leds>>9) & 0b00000001;

led8=(leds>>7) & 0b00000010; led9=(leds>>5) & 0b00000100; led10=(leds>>3) & 0b00001000;

led11=(leds>>1) & 0b00010000 y led12=(leds<<1) & 0b00100000.

Page 13: El Registro PORT en Arduino

Como ven este otro enfoque es mas sintético e incluso se podría optimizar usando vectores ya que los desplazamientos son impares, entonces se podría usar una variable índice que incrementara en forma impar, pero esa es otra historia. La idea aunque parece rebuscada fue la mejor manera de explicar que encontré para mostrar el tema de las posiciones en variables y puertos de diferente tamaño.

void escribirLed2( unsigned int leds)

{

/*

Esta funcion mapea los pines del 2 al 10 que son Led 1 al 10 en los puertos

PORTD y PORTD

PORTD =dp7,dp6,dp5,dp4,dp3,dp2,tx,rx

=L6, L5, L4,L3, L2, L1, x, x

PORTB = x,x,dp13, dp12, dp11 , dp10,dp9, dp8

= x,x,L12, L11, L10, L9 , L8 , L7

El parametro a pasar de lafuncion es el estado de los 9 bits

0bL1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11,L12,x,x,x,x es in int 16 bits

*/

byte led1,led2,led3,led4,led5,led6,led7,led8,led9,led10,led11,led12;

// variables que van a mapear el estado en los puertos D y B

byte puertoD=0;

byte puertoB=0;

byte aux;

byte d; // la lectura del byte del puerto D

aux=leds>>8; // BLSB del int tiene la info de los leds 2 al 8

led1= (aux >>5) & 0b00000100;

led2=(aux>>3) & 0b00001000;

led3=(aux>>1) & 0b00010000;

void escribirLed2( unsigned int{ /*

Page 14: El Registro PORT en Arduino

led4= (aux<<1) & 0b00100000;

led5=(aux <<3) & 0b01000000;

led6=(aux<<5) & 0b10000000;

//Serial.println(aux,BIN);

led7=(aux>>1) & 0b00000001;

led8=(aux<<1) & 0b00000010;

// hasta aqui se mapearon del led 1 al 8 donde se desplazo el MSB de leds al LSB de aux

// nuevo

aux=leds; // aux tiene el LSB de leds : L9,L10,L11,L12,x,x,x,x

led9=(aux>>5) & 0b00000100;

led10=(aux>>3) & 0b00001000;

led11=(aux>>1) & 0b00010000;

led12=(aux<<1) & 0b00100000;

puertoD=puertoD | led1|led2|led3|led4|led5|led6;

puertoB=puertoB|led7|led8|led9|led10|led11|led12;

d=PIND; // d lee el puertoD esto es para no afectar la escritura de rx/tx bit 1 y 0

d=d&0b00000011;

puertoD=puertoD|d;

PORTD = puertoD; // escritura al puerto D

PORTB = puertoB & 0b00111111;