Archivo de la categoría: Salidas Digitales

Entradas y Salidas en Arduino

Arduino dispone de una serie de entradas y salidas digitales y analógicas programables que es la base de manejo de Arduino. Es fundamental conocerlas para empezar a programar Arduino.

En Arduino UNO la disposición de los pines de entrada y salida son:

En el Wemos D1 Mini la disposición de los pines de entrada y salida son:

En todas las placas los pines son multifunción o multipropósito, es decir en función de la configuración tienen una funcionalidad u otra.

Pines Digitales

Una señal digital es un tipo de señal generada por algún tipo de fenómeno electromagnético en que cada signo que codifica el contenido de la misma puede ser analizado en término de algunas magnitudes que representan valores discretos, en lugar de valores dentro de un cierto rango. Por ejemplo, el interruptor de la luz sólo puede tomar dos valores o estados: abierto o cerrado, o la misma lámpara: encendida o apagada.

En arduino y todas las placas compatibles los pines digitales se describen y tienen la propiedades siguientes:  http://arduino.cc/en/Tutorial/DigitalPins

En arduino y placas compatibles para tratar las entradas y salidas digitales usamos las siguientes funciones:

En la imagen siguiente se muestra el estado por defecto de una I/O digital en un microcontrolador de Arduino. Se ha simplificado con interruptores la compleja electrónica que hay dentro. Por defecto los digital I/O pins están configurados como inputs en un estado de alta impedancia (equivalente a una resistencia de 100 Mohms en frente del pin), es decir, SW3 a ON y no hace falta llamar a la función pinMode() aunque es recomendable para aclarar el código.

  • PinMode(x, INPUT) –> SW3 = ON (resto a OFF). Los valores leídos serán aleatorios si el pin de Arduino está al aire. El pin está en un estado de alta impedancia (resistencia de 100 Mohms).
  • PinMode(x,INPUT_PULLUP) –> SW3 = ON & SW4 = ON (resto a OFF). Los valores leídos sin nada conectado al pin es HIGH. La Resistencia R1 tiene un valor dependiendo del microcontrolador, pero tiene un valor entre 20kOhm y 150kOhm.
  • PinMode(x, OUTPUT) & digitalWrite(x,HIGH) –> SW2 = ON & SW1 = +5V (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una resistencia adecuada a la salida el pin para no superar los 40mA (source) máximos admitidos
  • PinMode(x, OUTPUT) & digitalWrite(x,LOW) –> SW2 = ON & SW1 = GND (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una adecuada para no superar los 40mA (sink) máximos admitidos

Pines Analógicos

Una señal eléctrica analógica es aquella en la que los valores de la tensión o voltaje varían constantemente y pueden tomar cualquier valor. En el caso de la corriente alterna, la señal analógica incrementa su valor con signo eléctrico positivo (+) durante medio ciclo y disminuye a continuación con signo eléctrico negativo (–) en el medio ciclo siguiente.

La señal digital obtenida de una analógica tiene dos propiedades fundamentales:

En arduino los pines analógicos se definen y tienen las propiedades siguientes: http://arduino.cc/en/Tutorial/AnalogInputPins

En arduino para tratar las entradas y salidas analógicas usamos las siguientes funciones:

Entradas analógicas:

Las Salidas PWM (Pulse Width Modulation) permiten generar salidas analógicas desde pines digitales. Arduino Uno no posee salidas analógicas puras, sin embargo el Arduino Due sí tiene salidas analógicas puras mediante dos DAC. El arduino due, posee dos salidas analógicas puras mediante dos conversores digital a analógico. Estos pines pueden usarse para crear salidas de audio usando la librería correspondiente.

Diferentes valores de una señal PWM:

Para el Wemos D1 Mini, solo hay un pin de entrada analógica y de pines con capacidad PWM como indica esta imagen:

Ejemplos Entradas y Salidas Digitales

Input Pullup

Usar la resistencia interna de pull up de Arduino para detectar la pulsación de un botón (leer estado de una entrada digital) y encender el led 13 (integrado en placa) cuando tenga pulsado el botón y apagarlo cuando lo libere. Adicionalmente sacar por el monitor serie el estado de pulsación del botón con un 1 o un 0, de esta forma abriendo el Serial Plotter es posible ver la señal que recibe Arduino.

Imprimir por pantalla el tiempo de loop, que nos servirá para analizar la importancia de este tiempo en el comportamiento del programa.

NOTA: Al contrario que en anteriores prácticas, usamos un botón conectado al pin digital 2 configurado como INPUT_PULLUP. En este caso al leer el pin 2 con digitalRead() me devuelve 1 cuando no está pulsado el botón (abierto) y me devuelve 0 cuando está pulsado el botón (cerrado)

Esquema de conexión:

Diagrama eléctrico:

Resultado:

Tutorial: http://arduino.cc/en/Tutorial/InputPullupSerial

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_2017/tree/master/Ejercicio16-pullup

NOTA: Ver efecto de la diferencia del tiempo de loop cuando pulso o no pulso el botón debido a el Serial.println que se ejecuta al pulsar el botón.

NOTA: Si el tiempo de loop es muy largo podemos perder pulsaciones rápidas. Probar a poner un delay.

Interruptor

Con la base del ejemplo anterior pero en lugar de mantener pulsado el botón para encender el led, con una pulsación enciende y con otra apaga el led. Ahora el led ponerlo en el pin 10 en lugar del 13. Para hacer esto debemos detectar flancos para encender y apagar.

Más información sobre como detectar flancos: http://rufianenlared.com/flancos/

Esquema de conexión:

Resultado:

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_2017/tree/master/Ejercicio17-pullup_interruptor

Función Detecta Flanco

Hacer tres funciones para detectar que hagan las siguientes funciones y que se le pase como parámetro el pin donde detectar el flanco:

  • Detectar flanco ascendente
  • Detectar flanco descendente
  • Detectar flanco. Devuelve 1 para un flanco ascendente y un -1 para un flanco descendente.

Para usar estas funciones, deben llamarse en cada loop y para no perder un flanco, deben llamarse como máximo cada 300 ms.

Usarla en el ejemplo anterior del interruptor.

Solución:

Comprobar que si quiere detectar flanco en dos pines con una misma función no es posible. Para solucionarlo es necesario usar clases.

Solución:

Contador de pulsos

Usar Arduino para contar pulsaciones de un pulsador. Modificar el ejemplo anterior para contar el número de veces que se pulsa un botón detectando flancos ascendentes o descendentes y sacarlo por el monitor serie. Adicionalmente encender o apagar el led cada vez que haya 4 pulsaciones del botón.

NOTA: comprobar los rebotes y pensar cómo eliminarlos.

Solución: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_2017/tree/master/Ejercicio18-cuentapulsos

Entradas y Salidas Digitales Arduino

Una señal digital es un tipo de señal generada por algún tipo de fenómeno electromagnético en que cada signo que codifica el contenido de la misma puede ser analizado en término de algunas magnitudes que representan valores discretos, en lugar de valores dentro de un cierto rango. Por ejemplo, el interruptor de la luz sólo puede tomar dos valores o estados: abierto o cerrado, o la misma lámpara: encendida o apagada.

Más información:

Los sistemas digitales, como por ejemplo un microcontrolador, usan la lógica de dos estados representados por dos niveles de tensión eléctrica, uno alto, H y otro bajo, L (de High y Low, respectivamente, en inglés). Por abstracción, dichos estados se sustituyen por ceros y unos, lo que facilita la aplicación de la lógica y la aritmética binaria. Si el nivel alto se representa por 1 y el bajo por 0, se habla de lógica positiva y en caso contrario de lógica negativa.

Cabe mencionar que, además de los niveles, en una señal digital están las transiciones de alto a bajo y de bajo a alto, denominadas flanco de bajada y de subida, respectivamente. En una señal digital, se denomina flanco a la transición del nivel bajo al alto (flanco de subida) o del nivel alto al bajo (flanco de bajada).

Características de las Señales Digitales

Flanco vs Pulso

Periodo, ancho de pulso, flancos

Tipos de señales digitales

  • Discreta: puede tomar un conjunto de valores
  • Binaria: Encendido (1) – Apagado (0)

Tecnologías de construcción

Arduino trabaja con tecnología TTL (transistor-transistor logic): http://es.wikipedia.org/wiki/Tecnolog%C3%ADa_TTL

Características

  • Su tensión de alimentación característica se halla comprendida entre los 4,75V y los 5,25V. Normalmente TTL trabaja con 5V.
  • Los niveles lógicos vienen definidos por el rango de tensión comprendida entre 0,0V y 0,8V para el estado L (bajo) y los 2,4V y Vcc para el estado H (alto).
  • La velocidad de transmisión entre los estados lógicos es su mejor base, si bien esta característica le hace aumentar su consumo siendo su mayor enemigo. Motivo por el cual han aparecido diferentes versiones de TTL como FAST, LS, S, etc y últimamente los CMOS: HC, HCT y HCTLS. En algunos casos puede alcanzar poco más de los 250 MHz.
  • Las señales de salida TTL se degradan rápidamente si no se transmiten a través de circuitos adicionales de transmisión (no pueden viajar más de 2 m por cable sin graves pérdidas).

Tecnología CMOS: https://es.wikipedia.org/wiki/Semiconductor_complementario_de_%C3%B3xido_met%C3%A1lico

Comparación TTL y CMOS: http://digital.ni.com/public.nsf/allkb/2D038D3AE1C35011862565A8005C5C63

Los nuevos arduinos basados en procesadores ARM de 32 bits, usan tecnología TTL a 3.3V

Entradas y Salidas Digitales en Arduino

En arduino los pines digitales se describen y tienen la propiedades siguientes:  http://arduino.cc/en/Tutorial/DigitalPins

En arduino para tratar las entradas y salidas digitales usamos las siguientes funciones:

En Visualino disponemos de las funciones para leer de entradas digitales y escribir en salidas digitales en la agrupación de bloques “Pin Functions”. Visualino pone automáticamente el pinMode dependiendo si he elegido leer o escribir sobre el pin.

En la imagen siguiente se muestra el estado por defecto de una I/O digital en un microcontrolador de Arduino. Se ha simplificado con interruptores la compleja electrónica que hay dentro. Por defecto los digital I/O pins están configurados como inputs en un estado de alta impedancia (equivalente a una resistencia de 100 Mohms en frente del pin), es decir, SW3 a ON y no hace falta llamar a la función pinMode() aunque es recomendable para aclarar el código.

  • PinMode(x, INPUT) –> SW3 = ON (resto a OFF). Los valores leídos serán aleatorios si el pin de Arduino está al aire. El pin está en un estado de alta impedancia (resistencia de 100 Mohms).
  • PinMode(x,INPUT_PULLUP) –> SW3 = ON & SW4 = ON (resto a OFF). Los valores leídos sin nada conectado al pin es HIGH. La Resistencia R1 tiene un valor dependiendo del microcontrolador, pero tiene un valor entre 20kOhm y 150kOhm.
  • PinMode(x, OUTPUT) & digitalWrite(x,HIGH) –> SW2 = ON & SW1 = +5V (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una resistencia adecuada a la salida el pin para no superar los 40mA (source) máximos admitidos
  • PinMode(x, OUTPUT) & digitalWrite(x,LOW) –> SW2 = ON & SW1 = GND (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una adecuada para no superar los 40mA (sink) máximos admitidos

Más información sobre pullup resistors: https://learn.sparkfun.com/tutorials/pull-up-resistors

En el caso que el pin esté configurado como OUTPUT, hay diferencia entre sink (recogida de corriente) y source (fuente de corriente) de un pin digital configurado como salida.

En el primer caso para encender el LED debe ponerse digitalWrite() a HIGH y en el segundo a LOW

 

En el caso que el pin de entrada esté configurado como INPUT. Cuando el botón no está pulsado, en el primer caso leo digitalRead() un valor HIGH y en el segundo LOW y cuando pulso el botón, en el primer caso leo digitalRead() un valor LOW y en el segundo HIGH. Como véis, dependiendo de la configuración de lo conectado cambia lo leído. Estas configuraciones se denominan resistencias de pull up y resistencia de pull down respectivamente.

En el caso que el pin de entrada configurado como INPUT_PULLUP. Si el botón no está pulsado leo HIGH (no se producen lecturas aleatorias con el pin al aire) y cuando pulso el botón leo LOW. Esta es la mejor forma de leer entradas digitales sin necesidad de poner elementos externos a Arduino.

Más información sobre como usar las internal pullups: https://www.baldengineer.com/arduino-pull-ups.html

NOTA: muy buen tutorial para entender las resistencias de pullup y pulldown http://www.instructables.com/id/Understanding-the-Pull-up-Resistor-With-Arduino/

Puertos Digitales Arduino Avanzado

La mayoría de los pines de los microcontroladores son multipropósito, es decir, en función de su configuración se comportan de una forma u otra, tal y como se muestra en la figura.

El ATmega328p como cualquier otro microcontrolador tiene registros, algunos de estos registros están conectados con los puertos de entrada/salida, cada puerto tiene un nombre específico y sus registros asociados, de hecho, el 328p tiene los puertos B, C y D, y cada puerto 8 pines de la MCU conectados. En función del encapsulado puede haber una restricción en el número de pines. Por ejemplo en el paquete de 28 pines PDIP.

Cada pin puede tener múltiples funciones, como la generación 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 el pin 6 del PORTC corresponde al botón de reinicio.  En esta imagen se puede ver todas las funciones alternativas que cada pin puede tener.

NOTA: Esta nomenclatura del los GPIO luego el IDE de Arduino se ha encargado de nombrarlos de una forma más sencilla y consecutiva, más fácil de entender.

Por ejemplo en el Wemos D1 basado en el ESP8266, internamente los GPIO tiene una nomenclatura que puede llevar a error, pero luego el soporte para estas placas en el IDE de Arduino hace que coincida con la nomenclatura de Arduino y con lo etiquetado en la placa.

Entradas y Salidas Digitales Arduino a Fondo

Una señal digital es un tipo de señal generada por algún tipo de fenómeno electromagnético en que cada signo que codifica el contenido de la misma puede ser analizado en término de algunas magnitudes que representan valores discretos, en lugar de valores dentro de un cierto rango. Por ejemplo, el interruptor de la luz sólo puede tomar dos valores o estados: abierto o cerrado, o la misma lámpara: encendida o apagada.

Más información:

Los sistemas digitales, como por ejemplo un microcontrolador, usan la lógica de dos estados representados por dos niveles de tensión eléctrica, uno alto, H y otro bajo, L (de High y Low, respectivamente, en inglés). Por abstracción, dichos estados se sustituyen por ceros y unos, lo que facilita la aplicación de la lógica y la aritmética binaria. Si el nivel alto se representa por 1 y el bajo por 0, se habla de lógica positiva y en caso contrario de lógica negativa.

Cabe mencionar que, además de los niveles, en una señal digital están las transiciones de alto a bajo y de bajo a alto, denominadas flanco de bajada y de subida, respectivamente. En una señal digital, se denomina flanco a la transición del nivel bajo al alto (flanco de subida) o del nivel alto al bajo (flanco de bajada).

Para interactuar con los pines digitales de Arduino ya conocemos las funciones que nos ofrece Arduino en https://www.arduino.cc/en/Tutorial/DigitalPins como digitalRead() y digitalWrite().

Pero a bajo nivel estas funciones están manejando registros de los puestos B, C y D. Los estados y comportamiento de cada puerto y sus pines se maneja mediante tres registros. 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 lógica, cada bit en los registros DDRX puede ser 1 ó 0, poner un bit específico de DDRX a 1 configura el pin como salida y ponerla a 0 configura el pin como una entrada.

Los pines usados en la placa Arduino poseen tres puertos en el caso de ATmega328p (Arduino Uno):

  • B (pines digitales del 8 al 13)
  • C (entradas analógicas)
  • D (pines digitales del 0 al 7)

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 (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)

Dentro del Microcontrolador ATmega328P esta es la relación de los puestos con los pines:

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)

Dentro de la avr-libc tenemos un fichero de definición de los registros para cada microcontrolador. Estos fichero se pueden encontrar en la ruta: C:\Program Files (x86)\Arduino\hardware\tools\avr\avr\include\avr

Para ATmega328p el fichero iom328p.h es donde se encuentran las definiciones para el microcontrolador. Ver que las direcciones de los registros van desplazados 0x20 posiciones de memoria por los fuses que son 4 bytes de memoria permanente llamados fuses que determinan cómo se va a comportar el microcontrolador, si tiene bootloader, a que velocidad y voltaje va a funcionar, etc… .

El fichero <avr/sfr_defs.h> está incluido en todos los ficheros <avr/ioxxxx.h> que usan unas macros definidas en sfr_defs.h que hacen que los special function registers parezcan variables de C a las que podemos llamar para obtener su valor:

 
#define PORTA   _SFR_IO8(0x02)
#define EEAR    _SFR_IO16(0x21)
#define UDR0    _SFR_MEM8(0xC6)
#define TCNT3   _SFR_MEM16(0x94)
#define CANIDT  _SFR_MEM32(0xF0)

Por ejemplo, las limitaciones eléctricas en el microcontrolador son por puerto y por pin:

Práctica: Registros PORT Arduino

Un ejemplo que configura pines 0,1,2,3 como entradas digitales y los pines 4,5,6,7 como salidas digitales, se hace mediante el comando: DDRD = 0b11110000;

Si queremos poner todos los pines 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.

La relación entre los registros B, C y D y los pines es:

Leer los registros al poner varios pines como INPUT, INPUT_PULLUP y OUTPUT.

Probar con este código:

 
void setup() {
  //PORTD maps to Arduino digital pins 0 to 7
  pinMode(2, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  digitalWrite(3, HIGH);
  pinMode(4, INPUT);
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  Serial.begin(9600);
}

void loop() {
  Serial.println("DDRD - The Port D Data Direction Register. DDRX, 1=OUTPUT, 0=INPUT");
  Serial.println(DDRD, DEC);
  Serial.println(DDRD, HEX);
  Serial.println(DDRD, BIN);  
  Serial.println("PIND - The Port D Input Pins Register. Lectura INPUT");
  Serial.println(PIND, DEC);
  Serial.println(PIND, HEX);
  Serial.println(PIND, BIN);
  Serial.println("PORTD - The Port D Data Register. Escritura OUTPUT");
  Serial.println(PORTD, DEC);
  Serial.println(PORTD, HEX);
  Serial.println(PORTD, BIN);
  delay(10000);
}

Poner los pines de input (2, 4 y 6) a 5V y GND y ver como cambia el contenido del registro PIN.

Solución Ejercicio07: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio07-Registos_PORT_simple

Manejo Registros de Pines Arduino

Par poder manejar registros disponemos de unas funciones especiales para registros.

Operadores Bit a Bit:

  • & (bitwise and)
  • | (bitwise or)
  • ^ (bitwise xor)
  • ~ (bitwise not)
  • << (bitshift left) (IMPORTANTE)
  • >> (bitshift right)

Tutorial de matemáticas con bits: http://playground.arduino.cc/Code/BitMath

Referencia de <avr/sfr_defs.h> Special Function Registers:

Manipulación de puertos: https://www.arduino.cc/en/Reference/PortManipulation

Tutorial de Bit Math: http://playground.arduino.cc/Code/BitMath

Escribir en un pin

Ya podemos decir al Atmega cómo serán utilizados sus pines, pero queremos saber cómo leer y escribir datos en dichos pines, de modo que para escribir datos en un determinado puerto, se utiliza el registro PORTx, éste es fácil de recordar, donde x es el nombre del puerto, y después de la configuración de un pin como salida es sólo una cuestión de poner 0 o 1 en el registro PORTx para controlar que el pin de este en estado alta o baja.

Un ejemplo sería:

 
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.

Se debe tener cuidado cuando se utiliza PORTD y el puerto serie porque los pines 0 y 1 del PORTD son los utilizados por la USART y si se pone estos dos como entradas o salidas, la USART será incapaz de leer o escribir datos en los pines. Este es un ejemplo del cuidado que se debe tener al usar esta programación en lugar de la capa de programación que nos ofrece Arduino.

Leer un Pin

Para leer en el pin y poder leer los datos de los sensores o cuando se pulsa un botón 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 al microcontrolador que queremos algunos pines como entradas digitales, y luego usando PINX leemos sus valores

Ejemplo:

 
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

Es casi tan fácil como puede ser usar el digitalWrite o digitalRead de Arduino, pero con acceso directo al puerto se puede ahorrar espacio en el la memoria flash y también puede ganar mucha velocidad, porque las funciones Arduino puede tomar más de 40 ciclos de reloj para leer o escribir un solo bit en un puerto, además para leer un solo bit el código es bastante complejo con un montón de líneas que ocupan por lo menos unos 40 bytes, que podría ser un pequeño ahorro en flash y es un gran paso para acelerar cualquier programa.

No es normal que se necesite leer o escribir en un puerto completo en cada momento, por ejemplo, si queremos encender un LED, o leer un botón sólo 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 la librería C de AVR tiene algunas pocas palabras definidas como Px(0..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); // Configura el pin 2 de PORTD como salida.
PORTD = (1<<PD2); // El pin 2 de PORTD tiene ahora un valor lógico 1.

La operación << es un operador binario que desplaza hacia la izquierda el número de posiciones especificadas.

Para leer el estado de un botón:

 
DDRD = 0b11111101; //Configura pin 1 de PORTD como entrada y el resto salida
char my_var = 0; //Variable para guardar la información leída en PORTD
my_var = (PIND & (1<<PD1)); /* Lee el pin 1 de PORTD y lo coloca en la variable.*/

Explicación de la operación binaria bit a bit: https://www.arduino.cc/en/Reference/Bitshift

También se puede utilizar la macro Px0..7 varias veces en una misma instrucción, por ejemplo, en este código, se ejecutará algo de código sólo si al menos uno de los botones está pulsado:

 
DDRD = 0b11111100; // Los pines 0 y 1 de PORTD son entradas, y el resto salidas.
if (PIND & ((1<<PD0) | (1<<PD1))) {
/* Algún código dentro del if() que se ejecutará solo si al menos uno de los dos botones se encuentra activado. */
}

Mediante los registros también podemos controlar las resistencias internas de pullup. Cuando hay un botón que puede tener dos estados, uno es desconectado, y cuando se presiona hará una conexión entre los pines del microcontrolador y permite por ejemplo, conectarse a masa, pero cuando se desconecta, no hay nada que fuerce un valor estable en el pin de entrada, y el pin puede leer 1 ó 0 ya que el pin es muy sensible al ruido electromagnético, como una pequeña antena. Se puede resolver este problema de dos maneras similares, una es para conectar una resistencia de 10 Kohms o más entre el Vcc (+5 v) y el pin de entrada, o usar los pull-ups del microcontrolador que tienen integrados, también hace más simples los circuitos.

Para habilitar las resistencias pullup tenemos que hacer algo que puede resultar un poco extraño, 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. Ejemplo:

 
DDRD = 0b00000000; // Todos los pines de PORTD son entradas.
PORTD = 0b00001111; // Habilito las Pull-ups de los pines 0,1,2 y 3
char my_var = 0; // Creo una variable para guardar la información leída en PORTD.
my_var = PIND; // Leo PORTD y colocó la información en la variable.

Si se ejecuta este código, sin tener nada conectado a PORTD, los cuatro bits más altos de la variable my_var puede ser 0 ó 1, cualquier combinación posible de ellos porque son flotantes (actúan como pequeñas antenas), pero los cuatro bits más bajos leerá todos un 1 debido a que el pull-ups imponen una señal de 5V débil que se lee como un valor lógico 1.

En un sentido básico esto es todo lo que se necesita saber para dominar la manipulación directa de los puertos. La manipulación de bits enseña cosas más ingeniosas como las máscaras de bits, las operaciones AND, OR, NOT y XOR y cómo configurar y limpiar los bits en un registro y algunos buenos trucos con las operaciones de desplazamiento derecho e izquierdo, esta técnica puede acelerar un sketch y son muy útiles cuando se utilizan los puertos digitales.

Ventajas y Desventajas que nos ofrece al utilizar los registros:

  • Desventajas:
    • El código es mucho más difícil de depurar y mantener, y es mucho más difícil de entender. Solo lleva algunos microsegundos al procesador ejecutar código, pero podría llevar 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. Con DDRD = B11111110, 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 el puerto serial deje de funcionar por cambiar el pin 0 a una salida.
  • Ventajas:
    • Se puede cambiar los pines de estado muy rápido, en fracciones de microsegundos. Las funciones digitalRead() y digitalWrite() se componen cada una de ellas de cerca de una docena de líneas de código, lo cual se convierte en unas cuantas instrucciones máquina.
    • Cada instrucción máquina 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.
    • 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, se pueden 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.

En las librerías podemos ver que usan la manipulación de registros en lugar de las instrucciones que nos ofrece el core de Arduino lo que las hace más rápidas.

En el siguiente enlace de github puede verse el fichero wiring_digital.c que es parte del core de Arduino donde se puede ver el código de las instrucciones pinMode(), digitalWrite() y digitalRead(): https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_digital.c

Y en el fichero pins_arduino.h está la definición de pines: https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/variants/standard/pins_arduino.h

Más información en:

Práctica: Operaciones con Entradas y Salidas Digitales

Leer Pin

  • Configurar mediante el registro DDRB el pin 9 como entrada y el resto de pines del PORT B como salida.
  • Configurar el pin 9 como INPUT_PULLUP mediante el registro PORTB
  • Usando el registro PORTB poner a LOW todos los pines de OUTPUT salvo el pin 10
  • Sacar por el monitor serie el estado de los pines del PORTB mediante el registro PINB

Solución Ejercicio08: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio08-Leer_pin

Velocidad Read

Para demostrar que la operación digitalRead() es lenta hacer un ejercicio que lea 1000, 10000 y 100000 veces el puerto 9 configurado como entrada, mediante digitalRead() y mediante PIND y saque el tiempo que ha costado cada operación.

Solución Ejercicio 09: https://github.com/jecrespo/aprendiendoarduino-Curso_Arduino_Avanzado_2017/tree/master/Ejercicio09-Velocidad_Read

Manejo Arduino Básico

La estructura básica de un sketch de Arduino es bastante simple y se compone de al menos dos partes. Estas dos partes son obligatorios y encierran bloques que contienen declaraciones, estamentos o instrucciones.

setup() es la parte encargada de recoger la configuración y loop() es la que contiene el programa que se ejecuta cíclicamente (de ahí el término loop –bucle-). Ambas funciones son necesarias para que el programa trabaje.

La función de configuración (setup) debe contener la inicialización de los elementos y esta función sólo se ejecuta una vez justo después de hacer el reset y no se vuelve a ejecutar hasta que no haya otro reset. Es la primera función a ejecutar en el programa y se utiliza para configurar, inicializar variables, comenzar a usar librerías, etc…

La función bucle (loop) contiene el código que se ejecutará continuamente (lectura de entradas, activación de salidas, etc). Esta función es el núcleo de todos los programas de Arduino y se usa para el control activo de la placa. La función loop se ejecuta justo después de setup.

La estructura del sketch está definida en el siguiente enlace: http://arduino.cc/en/Tutorial/Sketch

Los componentes principales de un sketch de Arduino son:

  • Variables, son un espacio en memoria donde se almacenan datos y estos datos pueden variar.
  • Funciones, son un trozo de código que puede ser usado/llamado desde cualquier parte del sketch. A la función se le puede llamar directamente o pasarle unos parámetros, en función de cómo esté definida.
  • setup() y loop(), son dos funciones especiales que es obligatorio declarar en cualquier sketch.
  • Comentarios, fundamentales para documentar el proyecto

Se puede resumir un sketch de Arduino en los siguientes diagramas de flujo:

Para más información de como programar Arduino ver: https://aprendiendoarduino.wordpress.com/2016/10/16/programacion-arduino-2/

Cargar un Programa en Arduino

Una vez tenemos el programa hay que compilarlo y mandarlo a Arduino para que se ejecute de forma infinita.

Pasos a seguir:

  • Abrir la aplicación Arduino
  • Abrir el sketch que queremos cargar

  • Leer el programar y entender lo que está haciendo
  • Seleccionar la placa y el puerto adecuado

  • Cargar el programa pulsando el botón “subir”. El programa se compila y luego se verá parpadeando los leds Tx y Rx de Arduino, indicando que se está cargando el fichero binario (.hex) en la flash del Arduino. Cuando aparezca el mensaje “subido” habremos acabado.
  • Un momento después el programa se está ejecutando el Arduino

Cuando cargamos un programa en Arduino, estamos usando el bootloader de Arduino, que es un pequeño programa cargado en el microcontrolador que permite subir el código sin usar hardware adicional. El bootloader está activo unos segundos cuando se resetea la placa, después comienza el programa que tenga cargado el Arduino en su memoria Flash. El led integrado en la placa (pin 13) parpadea cuando el bootloader se ejecuta.

Entradas y Salidas Digitales en Arduino

En arduino los pines digitales se describen y tienen la propiedades siguientes:  http://arduino.cc/en/Tutorial/DigitalPins

En arduino para tratar las entradas y salidas digitales usamos las siguientes funciones:

En la imagen siguiente se muestra el estado por defecto de una I/O digital en un microcontrolador de Arduino. Se ha simplificado con interruptores la compleja electrónica que hay dentro. Por defecto los digital I/O pins están configurados como inputs en un estado de alta impedancia (equivalente a una resistencia de 100 Mohms en frente del pin), es decir, SW3 a ON y no hace falta llamar a la función pinMode() aunque es recomendable para aclarar el código.

  • PinMode(x, INPUT) –> SW3 = ON (resto a OFF). Los valores leídos serán aleatorios si el pin de Arduino está al aire. El pin está en un estado de alta impedancia (resistencia de 100 Mohms).
  • PinMode(x,INPUT_PULLUP) –> SW3 = ON & SW4 = ON (resto a OFF). Los valores leídos sin nada conectado al pin es HIGH. La Resistencia R1 tiene un valor dependiendo del microcontrolador, pero tiene un valor entre 20kOhm y 150kOhm.
  • PinMode(x, OUTPUT) & digitalWrite(x,HIGH) –> SW2 = ON & SW1 = +5V (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una resistencia adecuada a la salida el pin para no superar los 40mA (source) máximos admitidos
  • PinMode(x, OUTPUT) & digitalWrite(x,LOW) –> SW2 = ON & SW1 = GND (resto a OFF). Estado de baja impedancia, no hay resistencia interna y es necesario poner una adecuada para no superar los 40mA (sink) máximos admitidos

Entradas y Salidas Analógicas en Arduino

Los microcontroladores de Arduino contienen en la placa un conversor analógico a digital de 6 canales. El conversor tiene una resolución de 10 bits, devolviendo enteros entre 0 y 1023. Los pines analógicos de Arduino también tienen todas las funcionalidades de los pines digitales. Por lo tanto, si necesitamos más pines digitales podemos usar los pines analógicos. La nomenclatura para los pines analógicos es A0, A1, etc…

En arduino los pines analógicos se definen y tienen las propiedades siguientes: http://arduino.cc/en/Tutorial/AnalogInputPins

En arduino para tratar las entradas y salidas digitales usamos las siguientes funciones:

Otras funciones interesantes con entradas/salidas analóicas:

PWM

Arduino Uno tiene entradas analógicas que gracias a los conversores analógico digital puede entender ese valor el microcontrolador, pero no tiene salidas analógicas puras y para solucionar esto, usa la técnica de PWM.

Las Salidas PWM (Pulse Width Modulation) permiten generar salidas analógicas desde pines digitales. Arduino Uno no posee salidas analógicas puras.

La modulación por ancho de pulsos (también conocida como PWM, siglas en inglés de pulse-width modulation) de una señal o fuente de energía es una técnica en la que se modifica el ciclo de trabajo de una señal periódica (una senoidal o una cuadrada, por ejemplo), ya sea para transmitir información a través de un canal de comunicaciones o para controlar la cantidad de energía que se envía a una carga.

El ciclo de trabajo de una señal periódica es el ancho relativo de su parte positiva en relación con el período. duty cycle = (tiempo que la salida está a uno o HIGH)/ (periodo de la función)

En Arduino la frecuencia de PWM es de 500Hz. Pero es un valor que puede modificarse en caso que lo necesitemos. Definición de PWM en la web de Arduino: http://arduino.cc/en/Tutorial/PWM