• No se han encontrado resultados

Gestión de puertos de entrada salida digitales

N/A
N/A
Protected

Academic year: 2022

Share "Gestión de puertos de entrada salida digitales"

Copied!
11
0
0

Texto completo

(1)

Gestión de puertos de entrada salida digitales

Nos centraremos en los módulos IO de los procesadores AVR. Otros procesadores tienen ligeras diferencias, pero en general son muy similares.

Descripción general:

El módulo IO presenta la siguiente disposición

Un registro DDRx (Data Direction Register)

determina si el puerto es de entrada o de salida

Un registro PORTx (Pin Output Register) determina si el puerto presenta un valor alto o bajo cuando el puerto es de salida, y además controla si hay un pull-up cuando es de entrada Un registro PINx (Pin Input Register) nos permite leer el estado del puerto.

(x = A,B,C…)

Si queremos efectuar una salida por un pin determinado debemos poner primero ese pin como salida y escribir el valor deseado en él.

Si queremos que un pin actúe como entrada, primero lo configuramos como entrada (bien en alta impedancia o con pull-up interno) y después leemos el estado del pin.

Para efectuar estas acciones tenemos dos opciones:

(2)

Con funciones de biblioteca

Accediendo directamente a los registros en el espacio de direccionamiento

Si necesitamos ahorrar espacio, queremos hacer cambios a alta velocidad o cambiar varios pines simultáneamente usaremos esta alternativa.

El pin 12 se correponde con el bit 4 del puerto B

El DDRB es el registro en el que tenemos que que configurar el bit 4 como salida (a 1) → DDRB | 0b00010000

o como entrada (a 0) → DDRB & 0b111101111 DDRB bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

PORTB bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

PINB bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0

Después si es salida tenemos que escribir en el bit 4 de PORTB el valor deseado (1 para alto, 0 para bajo)

En el caso de que fuese entrada tendríamos que decidir si queremos pull-up en ese pin (escribiendo un 1 o un 0 en PORTB) y leer el bit 4 de PINB

// escribiendo en un pin

void escribe_pin(int pin, int estado) {

pinMode(pin, OUTPUT);

digitalWrite(pin , estado);

}

// leyendo un pin int lee_pin(int pin) {

// pinMode(pin, INPUT_PULLUP);

pinMode(pin, INPUT);

return digitalRead(pin);

}

// escribiendo en un pin en el port b

void escribe_pin_portb(int bit, int estado) {

DDRB = DDRB | (1 < bit);

PORTB = (estado)? (PORTB | (1 < bit)) : (PORTB & ~(1 < bit));

}

// leyendo un pin del port b int lee_pin(int pin) {

DDRB = DDRB & ~(1 < bit);

// PORTB = PORTB | (1 < bit); // con pull-up PORTB = PORTB & ~(1 < bit); // sin pull-up return (PINB & (1 < bit));

}

(3)

Comparando los dos métodos:

Vamos a comparar dos programas en el arduino, ambos haciendo parpadear un pin a máxima velocidad. Uno será implementado con funciones de biblioteca y otro directamente.

Comprobaremos la máxima frecuencia alcanzada y el tamaño del programa

Frecuencia de blink: 66.666 Kh → período 15µs Tamaño: 722 bytes

Frecuencia de blink: 833.333 Kh → período 1.2µs Tamaño: 438 bytes

/*

blink un led a través de funciones de biblioteca

*/

void setup()

{ pinMode(12, OUTPUT);

}

void loop() {

while(1) {

digitalWrite(12 , HIGH);

digitalWrite(12 , LOW);

} }

/*

manejo de un port directamente

*/

void setup() {

// pinMode(12, OUTPUT);

DDRB =DDRB | 0x10; // pin12=PB4 0x10= 0b00010000 = 16 // mejor así

// DDRB |= 0x10 // o todavía mejor // DDRB |= (1 << 4) }

void loop() {

while(1) {

// digitalWrite(12 , HIGH);

PORTB |= 0x10;

// mejor asi

// PORTB |= (1 << 4) // digitalWrite(12 , LOW);

PORTB = PORTB & 0b11101111;

// o también PORTB &= (~(1 << 4)) }

}

(4)

Uso de interrupciones para gestionar puertos

Volvamos a examinar el código ya visto para manejar un led a través de un pulsador usando interrupciones (aquí hacemos uso de las interrupciones externas disponibles para los pines 2 y 3)

int ledPin = 9;

int buttonPin = 2;

void pulsador_cambio_estado() {

// si esta variable fuese global y accesible desde otro lado habría que declararla volatile int estado = !digitalRead(buttonPin);

digitalWrite(ledPin, estado);

}

void setup() {

pinMode(ledPin, OUTPUT);

pinMode(buttonPin, INPUT_PULLUP);

// attachInterrupt(digitalPinToInterrupt(buttonPin), pulsador_cambio_estado, CHANGE);

attachInterrupt(0, pulsador_cambio_estado, CHANGE);

}

void loop() {

// hacer otras cosas...

delay(3000);

// ...

}

(5)

Interrupciones mediante acceso a registros

Veamos como implementar este mismo código pero accediendo directamente a los registros. El procedimiento era:

• Escribimos el código de la interrupción denominando adecuadamente la función según el tipo de interrupción (en este caso ISR(INT0_vect))

• En el setup configurar todos los periféricos como deseemos (entradas, salidas, …)

Si es necesario dehabilitamos de forma general las interrupciones: cli()

• Habilitamos el bit de interrupción correspondiente a los eventos que deseamos interceptar

• Borramos cualquier aviso de interrupción pendiente. (puede no ser necesario, ya que se borra automáticamente cuando se procesa la interrupción)

• Habilitamos el bit general de interrupción Apliquemos la secuencia:

Para detectar interrupciones externas en el pin 2 o 3 tenemos que trabajar con los siguientes registros del procesador:

EICRA (External Interrupt Control Register A) En este registro seleccionamos el tipo de interrupción deseada (LOW, CHANGE, FALLING, RISING) según la siguiente tabla

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

EICRA - - - - ISC11 ISC10 ISC01 ISC00

ISCx1 ISCx0 DESCRIPTION

0 0 Low

0 1 change

1 0 falling

1 1 rising

EIMSK (External Interrupt Mask Register) Para habilitar / deshabilitar la interrupción externa INT0 o INT1

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

EIMSK - - - INT1 INT0

EIFR(External Interrupt Flag Register) queda registrada que interrupción se disparó (la 0 o la 1)

7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 bit

EIFR - - - INTF1 INTF0

Primero deshabilitamos de forma general las interrupciones (En este caso no sería neceario y se hace sólo por motivos didácticos) con cli()

(6)

Debemos poner EICRA a “----01--” (- significa dejar el bit como está)

EICRA &= ~3; // clear existing flags

EICRA |= 1; // set wanted flags (change level interrupt)

Después habilitar la interrupción externa en pin 2 poniendo “---1” en EIMSKy dejando el resto como está

EIMSK |= 1; // enable it

Por si acaso borramos el flag de que se disparó la interrupción (por si estaba marcado de antes). Para ello ponemos a 1 el bit 0 de EIFR

EIFR |= 1

Ahora ya podemos habilitar de forma general las interrupciones con sei() El código nos quedaría de esta forma ISR(INT0_vect)

/*

Ejemplo de interrupciones externas en el pin 2 Usando la interrupción INT0

*/

int ledPin = 9;

int buttonPin = 2;

ISR(INT0_vect) {

// si esta variable fuese global y accesible desde otro lado habría que declararla volatile int estado = !digitalRead(buttonPin);

digitalWrite(ledPin, estado);

}

void setup() {

pinMode(ledPin, OUTPUT);

pinMode(buttonPin, INPUT_PULLUP);

// des activar el flag general de interrupciones cli();

// queremos que se active interrupción cuando cambie el pin 2 EICRA &= (0b11111100);

EICRA |= 1;

// limpiamos el flag de aviso de cambio en en pin 2 // en este caso no sería imprescindible.

EIFR |= 0b00000001;

// activamos flag de cambio en el pin 2 EIMSK |= 0b00000001;

// activar el flag general de interrupciones sei();

}

void loop() {

// hacer otras cosas...

delay(3000);

// ...

}

(7)

Interrupciones generales del puerto por cambio de nivel

En el arduino tenemos disponibles además de las interrupciones externas en el pin 2 y 3,

interrupciones por cambio de nivel en cualquier puerto. Estas interrupciones sólo son accesibles a través de acceso directo a registros.

Se pueden configurar las interrupciones PCINT0 (puerto B), PCINT1 (puerto C), PCINT2(puerto D)

Vamos a implementarlos en este montaje:

Es muy parecido al anterior ejemplo, pero en este caso el pin que tendría que disparar la interrupción es el 7, que se corresponde con el puerto D bit 7 (D7)

Veamos los resgistros implicados:

PCICR Pin Change Interrupt Control Register

Es donde podemos habilitar las interrupciones por cambio de estado de un pin en un puerto. (PCIE0 para el puerto B, PCIE1 para el puerto C y PCIE2 para el puerto D)

Para nuestro ejemplo debemos poner a 1 el bit PCIE2 PCICR |= 0b00000100;

y la interrupción asociada debería llamarse:

(8)

ISR(PCINT2_vect)

Llegados aquí tenemos activadas las interrupciones por cambio en el puerto D. Ahora tenemos que decir que pines concretos del puerto D pueden disparar la interrupción. Esto lo hacemos en el registro PCMSK2 (para el puerto B tenemos el PCMSK0 y para el C el PCMSK1)

En nuestro caso nos interesa el D7 que se corresponde con el PCINT23 PCMSK2 |= 0b10000000;

Si hubiese mas pines que no interesasen los trataríamos de la misma manera.

Cuando se dispara la interrupción se marcará en PCIFR el bit que corresponde al puerto D

En nuestro caso comprobaríamos que está a 1 el bit PCIF2

El pin concreto que provocó la interrupción no tenemos forma de saberlo, solo podemos ir leyendo todos los pines y actuar en consecuencia.

(9)

int ledPin = 9;

int buttonPin = 7;

// pin7 es D7

// pin change interrupt for D0 to D7 ISR(PCINT2_vect)

{

// si esta variable fuese global y accesible desde otro lado habría que declararla volatile int estado = !digitalRead(buttonPin);

digitalWrite(ledPin, estado);

}

void setup() {

pinMode(ledPin, OUTPUT);

pinMode(buttonPin, INPUT_PULLUP);

// des activar el flag general de interrupciones cli();

// activamos flag de cambio en el puerto D PCICR |= 0b00000100;

// queremos que se active interrupción cuando cambie el pin D7 PCMSK2 |= 0b10000000;

// limpiamos el flag de aviso de cambio en en port D PCIFR |= 0b00000100;

// activar el flag general de interrupciones sei();

}

void loop() {

// hacer otras cosas...

delay(3000);

// ...

}

(10)

El problema RMW (Read-Modify-Write)

En algunos microcontroladores existe una situación peligrosa, aunque raramente se produce, en la que el resultado de escribir en un puerto es distinto del esperado. En el siguiente ejemplo

supongamos un condensador de gran capacidad puesto en el pin 7 del port B y veamos lo que pasa al escribir un 1 en ese pin e inmediatamente hacer una lectura para una nueva escritura en otro bit PORTB |= (1<<7);

PORTB |= 1;

(11)

Este problema se presenta en algunos microcontroladores PIC y tiene dos soluciones:

• Un pequeño timeout entre las dos escrituras

• Uso de latches internos para leer el estado en vez de hacerlo directamente del port

Este problema no se da en los procesadores AVR ya que implementan la última solución apuntada (son registros distintos para leer y escribir: PINx y PORTx)

Hay otra circunstancia que genera un problema muy parecido: El cambio del valor del pin en una interrupción.

Imaginemos que queremos ejecutar la instrucción PORTB |= 1;

Esta instrucción normalmente no es atómica y para ejecutarse en ensamblador se divide en tres instrucciones:

1. leer el puerto a registro interno de trabajo 2. poner a 1 el bit 0 del registro interno 3. escribir el registro interno en el puerto.

Si tenemos una interrupción que también altere el mismo puerto (aunque sea otro bit) y la

interrupción se dispara después de la fase 1 y antes de la fase 3 el valor que tenemos en el registro de trabajo no se corresponde con el valor real del puerto del puerto.

Esto se pone de manifiesto en la siguiente tabla

Sentencia Acción Registro interno PortB

PORTB |= 1; xxxxxxxx 10000000

Leer portb a

RI 10000000 10000000

Set bit 0 en RI 10000001 10000000 Interrupción y

efectuamos PORTB |= 2;

10000010

Escribir RI 10000001 10000001

Siguiente sentencia …. …. ...

Realmente esta es una situación muy improbable y normalmente sólo la tendremos en cuenta en sistemas críticos. Una solución pasa por inhibir las interrupciones cuando manipulamos el puerto.

Referencias

Documento similar

Allí donde los la separación de poderes es más amplia, como en un sistema pre- sidencialista, donde el Jefe de Estado y de Gobierno no tiene que responder política- mente de sus

Un método de estudio aparte de ser una herramienta muy útil al momento de estudiar también nos ayuda a agilizar nuestra mente y tener una buena memoria para futuro?. Palabras

Se dice que la Administración no está obligada a seguir sus pre- cedentes y puede, por tanto, conculcar legítimamente los principios de igualdad, seguridad jurídica y buena fe,

La invalidez en el MMPI por no respuestas no se considera criterio positivo (sólo se puede considerar tal posibilidad en caso de daño neurológico que justifique tal estilo

1. LAS GARANTÍAS CONSTITUCIONALES.—2. C) La reforma constitucional de 1994. D) Las tres etapas del amparo argentino. F) Las vías previas al amparo. H) La acción es judicial en

1) El de cámaras. De estos dos hornos, el más económico es el de cá- maras, sin embargo tiene un enorme inconveniente que es la - gran cantidad de mano de obra que se necesita para

Las señales con las que trabaja este puerto serie son digitales, de +12V (0 lógico) y -12V (1 lógico), para la entrada y salida de datos, y a la inversa en las señales de control.

Las lecturas de francobordo/calado se toman para establecer la posición de la flotación y determinar a su vez el desplazamiento del buque en el momento de realizar la prueba