Entradas y Salidas Arduino

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 relacionados con los puertos de entrada/salida, cada puerto tiene un nombre específico y sus registros asociados, de hecho, el 328p tiene el puerto B, C y D, y cada puerto un diferente número de pines (Esta es una restricción del paquete de 28 pines PDIP y no desde el microcontrolador, ya que un PDIP 40 pines, por ejemplo, tiene 4 puertos con los 8 bits cada uno), el único puerto que tiene el total de sus 8 pines de entradas/salidas es PORTD.

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 pin 6 del PORTC le corresponde al botón de reinicio.  En esta imagen se puede ver todas las funciones alternativas que cada pin puede tener.

Entradas y Salidas Digitales a Fondo

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. Para empezar 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)

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 ver el fichero iom328p.h donde se encuentran las definiciones para el microcontrolador. Ver que las direcciones de los registros van desplazados 0x20 posiciones de memoria por los registros de propósito general.

Probar con este código (Ejercicio 59):

void setup() {
  //PORTD maps to Arduino digital pins 0 to 7
  pinMode(2, INPUT_PULLUP);
  pinMode(3, OUTPUT);
  digitalWrite(3, HIGH);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, OUTPUT);
  digitalWrite(5, HIGH);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, OUTPUT);
  digitalWrite(7, HIGH);
  Serial.begin(9600);
}
void loop() {
  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("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("PORTD - The Port D Data Register. Escritura OUTPUT");
  Serial.println(PORTD, DEC);
  Serial.println(PORTD, HEX);
  Serial.println(PORTD, BIN);
  delay(10000);
}

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)

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

Las limitaciones eléctricas en el microcontrolador son por puerto y por pin:

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.

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 de cuidado que se debe tener al usar esta programación en lugar de la capa de programación que nos ofrece Arduino.

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)); /* Le 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 se pulsa dos botones al mismo tiempo:

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 los dos botones se
  encuentran activados. */
}

Ver ejemplo Leer_pin en https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio59-Puertos_Digitales/

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 los operaciones de desplazamiento derecho e izquierdo, todas cosas bueno a saber, ya que puede acelerar un sketch y son muy útiles cuando se utilizan los puertos digitales.

Debemos ser conscientes de que un botón no da una buena y transición limpia entre 0 a 1 o de 1 a 0, pero en su lugar la señal puede tener problemas de rebote, esto es debido a las propiedades mecánicas del botón y no un defecto de diseño. Hay dos maneras, mediante un condensador pequeño cerca de la botón para el rebote del valor, o que esta eliminación de rebotes esté en el mismo código, que es más fácil de hacer cuando tenemos un montón de botones y es más barato que la adición de una gran cantidad de componentes a nuestro circuito. La forma más simple de de hacer esto es sólo insertar un pequeño retraso entre las consecutivas lecturas de un botón, esto es un método de bloqueo, porque el microcontrolador se detendrá por algunos milisegundos, hay otras formas más inteligentes que usar temporizadores, pero para proyectos de 2 ó 3 botones que no requieren una sincronización muy precisa se trata de una método de uso común. 

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.

Más información en:

Ejercicio Entradas y Salidas Digitales

Ver ejercicio simple con los registros PORT, PIN y DDR y sacarlos por pantalla.

Ejercicio Registros_PORT_simple y leer_pin de https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio59-Puertos_Digitales/

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 Velocidad_Read de https://github.com/jecrespo/Aprendiendo-Arduino/tree/master/Ejercicio59-Puertos_Digitales/

Entradas y Salidas Analógicas a Fondo

Ya hemos visto cómo manejar con registros las entradas y salidas de Arduino, pero nos centrado en las digitales. Para el caso de las entradas y salidas analógicas hay algunos detalles adicionales que no se han visto. La forma de manejar con registros las entradas analógicas correspondientes al puerto C con POR y PIN es para usar esos pines como I/O digitales, puesto que los pines de los microcontroladores son multipropósito como se ha dicho anteriormente.

En las entradas analógicas entran en juego los conversores Analógico Digital (ADC) y en las salidas analógicas entra el PWM que usa uno de los timers de microcontrolador para hacer la forma de onda PWM.

Toda la información de ADC para entradas analógicas se encuentra en la página 305 y la información de PWM para salidas analógicas está en la página 125, 149 y 189 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

PWM

Las Salidas PWM (Pulse Width Modulation) permiten generar salidas analógicas desde pines digitales. Arduino Uno no posee salidas analógicas puras. 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.

Definición de PWM en la web de Arduino: http://arduino.cc/en/Tutorial/PWM

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.

PWM tiene varios usos en los microcontroladores:

  • Dimming un LED
  • Obtener una salida analógica
  • Ofrecer un voltaje analógico entre el 0% y el 100% de Vcc
  • Generar señales de audio
  • Controlar velocidad de motores
  • Generar una señal modulada, por ejemplo para manejar un LED infrarrojo de un mando a distancia.

Para generar la señal PWM se utiliza los timers configurándose varias formas de trabajo. La forma de onda PWM, en el modo de trabajo más sencillo (Fast PWM), se genera de la  forma mostrada en la siguiente gráfica:

  1. El registro del contador se pone en marcha desde cero y cuenta de modo ascendente. En el momento de empezar la cuenta se activa el pin de salida del PWM.
  2. Cuando el valor de este registro se iguala al de otro registro de comparación se conmuta el pin de salida. El registro del contador sigue contando en forma normal.
  3. Cuando el valor del registro del contador llega al final (TOP) vuelve a comenzar (BOTTOM). El pin de salida vuelve a cambiar.
  4. El tiempo que tarda el contador en llegar al final fija el periodo de la señal.

Los microcontroladores usan varios modos de PWM, uno de ellos el el Fast PWM que puede ser generado con 8, 9 y 10 bits, una resolución mayor de 8 bits solo es posible usando un timer de 16 bits. Otro modo de PWM es Phase Correct PWM que es el que debería usarse para el control de motores. Otro modo es Frequency and Phase Correct PWM.

Esta imagen explica cómo funciona el phase correct PWM, en este caso el timer cuenta hacia arriba y luego hacia abajo:

En los microcontroladores AVR, el PWM está disponible con todos los timers. Timer 0 y timer 2 dan una resolución de 8 bit mientras que el timer 1 ofrece una resolución de 16 bits. Con 8 bits hay 256 pasos individuales y en 16 bit hay una resolución de 65536 pasos.

La forma de generar la onda PWM es diferente en cada uno de los modos y la señal obtenida es diferente.

Puesto que las ondas generadas son diferentes, el centro de la parte en HIGH no es constante en el fast PWM y sí en el phase correct PWM, esa es la principal diferencia entre ambos modos y la razón de porque para control de motores es mejor usar el phase correct PWM.

El modo PWM en el AVR se controla por hardware. Esto significa que todo, se lleva a cabo por la CPU AVR. Todo lo que necesita hacer es inicializar e iniciar el temporizador, y establecer el ciclo de trabajo. El ATmega328p tiene 3 timers PWM para controlar 6 salidas PWM. Estos temporizadores generan interrupciones cuando alcanzan el overflow o cuando alcanzan el registro de comparación. Los registros de control del timer/counter n (n va de 0  a 2) son TCCRnA y TCCRnB y tienen los principales controles de los temporizadores.

Estos registros tienen varios grupos de bits:

  • Waveform Generation Mode bits (WGM): these control the overall mode of the timer. (These bits are split between TCCRnA and TCCRnB.)
  • Clock Select bits (CS): these control the clock prescaler
  • Compare Match Output A Mode bits (COMnA): these enable/disable/invert output A
  • Compare Match Output B Mode bits (COMnB): these enable/disable/invert output B

Los registros de comparación de salida OCRnA y OCRnB establece los niveles en los que las salidas A y B se verán afectados. Cuando el valor del temporizador coincide con el valor del registro, la salida correspondiente será modificado como se especifica en el modo.

TCCR0B – Timer/counter0 Control Register

Los registros para el control de PWM  con el timer 0 son TCCR0A y TCCR0B y dentro de ellos los bits WGM02, WGM01 y WGM00:

Para utilizar fast PWM rápido dos modos para elegir, los modos 3 y 7, la principal diferencia entre estos dos modos es que en el modo 3 TOP se fija en 0xFF y en el modo de 7 TOP es definido por el registro TOP, esto significa que si existe la necesidad, podemos cambiar el número máximo que el temporizador hará hasta que haga overflow, así que esto significa que podemos controlar la frecuencia y el ciclo de trabajo.

Más información sobre PWM:

Para cambiar la frecuencia de PWM que por defecto en Arduino UNO está a 500 HZ, se puede usar la función definida en http://playground.arduino.cc/Code/PwmFrequency, donde indicando el pin y el divisor de frecuencia. Esta función cambia los registros TCCRnB en el timer correspondiente en función del pin, con el divisor que queramos.

O simplemente en el setup() del sketch inicializar los bits CS00, CS01 y CS02 como se indica en este enlace: https://arduino-info.wikispaces.com/Arduino-PWM-Frequency, pero teniendo en cuenta que estos cambios en los timers 0, 1 y 2 puede tener efectos en otras funciones que usen los timers como delay(), millis() o la librería servo:

  • Changes on pins 3, 5, 6, or 11 may cause the delay() and millis() functions to stop working. Other timing-related functions may also be affected.
  • Changes on pins 9 or 10 will cause the Servo library to function incorrectly.

La frecuencia resultante de PWM es la resultante de la división de la frecuencia base entre el divisor.

  • The base frequency for pins 3, 9, 10, and 11 is 31250 Hz.
  • The base frequency for pins 5 and 6 is 62500 Hz.
  • The divisors available on pins 5, 6, 9 and 10 are: 1, 8, 64, 256, and 1024.
  • The divisors available on pins 3 and 11 are: 1, 8, 32, 64, 128, 256, and 1024.

También se puede hacer PWM en todos los pins, programando nosotros en lugar de dejarlo a la CPU del microcontrolador: http://playground.arduino.cc/Main/PWMallPins

ADC

El microcontrolador de Arduino UNO contiene 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.

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

El datasheet de ATmega advierte de hacer lecturas rápidas entre pines analógicos (analogRead). Esto puede causar ruido eléctrico e introducir jitter en el sistema analógico. Se aconseja que después de manipular pines analógicos (en modo digital), añadir un pequeño retraso antes de usar analogRead () para leer otros pines analógicos.

Un microcontrolador solo entiende señales digitales (1’s y 0’s), por lo tanto para poder leer señales analógicas necesitamos los convertidores Analógico a Digital (ADC). Esta conversión consiste en la transcripción de señales analógicas en señal digital, con el propósito de facilitar su procesamiento (codificación, compresión, etcétera) y hacer la señal resultante (digital) más inmune al ruido y otras interferencias a las que son más sensibles las señales analógicas.

Para el ATMega328p toda la información del conversor analógico a digital se encuentra en la página 305 de http://www.atmel.com/Images/Atmel-42735-8-bit-AVR-Microcontroller-ATmega328-328P_datasheet.pdf

El ATMega328p, al igual que toda la gama ATMega de Atmel y otros microcontroladores, tienen un ADC integrado y no necesita ningún hardware adicional, esto nos permite simplemente conectar un sensor analógico. El ADC interno del microcontrolador tiene una resolución de 10 bits, esto significa que la tensión analógica de entrada se convierte en un valor numérico entre 0 y 1023.

Aunque el ATmega328P tiene 6 pines que son capaces de ser utilizados como pines de entrada analógicos (Port C), sólo hay un ADC en el microcontrolador, pero entre el ADC y los pines hay un multiplexor analógico, esto permite que podamos elegir qué pin está conectado al ADC, esto significa que aunque podemos utilizar todos los pines, sólo se puede leer el valor de uno de ellos a la vez, para casi todas las aplicaciones esto es más que suficiente, pero en algunos casos limitados que necesitan lecturas ADC de alta velocidad se podría necesitar el uso de ADC externos. En el caso de la ATmega328P los pines que se pueden utilizar una entrada analógica son todos los del puerto C.

También se puede cambiar la tensión máxima (siempre por debajo de Vcc) que utiliza el ADC, es la  llamada tensión de referencia y es la tensión contra la que todas las entradas analógicas hacen las conversiones. Esta tensión de referencia se toma del pin Aref. Reducir el voltaje máximo del ADC tiene sentido para mejorar la resolución del ADC. Con 5V la resolución es de 5/1023 = 4,88 mV para cada valor, pero para un sensor que no pasa de 3.3V la resolución es de 3.3/1023 = 3.22mV.

El ADC interno también se puede utilizar en un modo de 8 bits, donde sólo se utilizan los 8 bits más significativos de la resolución de 10 bits completa, esto podría ser útil cuando se trabaja en ambientes ruidosos y sólo necesita 8 bits de resolución, el uso de este modo es un plus debido a que no es necesario dedicar más tiempo de CPU calculando los 10 bits completos. El ADC también puede configurarse para que lleve a cabo una conversión y detenerse o puede ser configurado para funcionar en un modo de funcionamiento libre, la primera opción es la mejor opción cuando queremos leer diferentes pines, y el segundo es mejor cuando sólo tenemos que leer un pin y esto puede ahorrar algo de tiempo entre las conversiones.

También tenemos que tener cuidado de la frecuencia máxima de trabajo del ADC, este valor se especifica en la ficha técnica y es de 200 kHz, este es el valor del reloj interno de la circuitería del ADC y se genera dividiendo el reloj principal ATmega, que en el caso del UNO es 16 MHz, este divisor del reloj se realiza mediante pre-escaladores y sólo hay un rango limitado de valores, por lo que la frecuencia máxima que podemos utilizar y estar dentro de la frecuencia máxima de trabajo es 125 kHz. El siguiente pre-escalador supone usar el ADC a 250 kHz, en este caso no se puede garantizar la resolución de 10 bits, pero si una resolución de 8 bits. De todas formas en caso de necesitar un ADC más rápido se podría usar uno externo.

El ADC puede trabajar en dos modos: single conversion mode y free running mode. En modo single conversion el ADC hace una sola conversión y para, pero en modo free running el ADC está continuamente convirtiendo, es decir, hace una conversión y luego comienza con la siguiente.

El ADC en microcontroladores AVR utiliza una técnica conocida como aproximación sucesiva mediante la comparación de la tensión de entrada con la mitad de la tensión de referencia generada internamente. La comparación continúa dividiendo de nuevo la tensión y actualizando cada bit del registro ADC a 1 si el voltaje es HIGH en la comparación o 0 en el otro caso. Este proceso tiene dura 10 veces (por cada bit de resolución del ADC) y genera como resultado la salida binaria.

Los registros utilizados en el manejo de las entradas analógicas son:

  • ADMUX: ADC Multiplexer Selection Register. Selector del canal del multiplexor del ADC y el voltaje de referencia.
  • ADCSRA: ADC Control and Status Register A. Control del ADC y su estado.
  • ADCSRB: ADC Control and Status Register B.
  • ADCL: ADC Data Register Low. Cuando la conversión ADC ha finalizado, el resultado se deja en estos dos registros.
  • ADCH: Data Register High
  • DIDR0: Digital Input Disable Register 0. Para deshabilitar la entrada digital de los pines analógicos.

Más información:

6 comentarios en “Entradas y Salidas Arduino

  1. Eduardo

    Hola estoy haciendo unas pruebas sobre este tutorial, que me viene muy bien para un proyecto que estoy realizando.
    Tengo un problema que no entiendo lo que pasa, estoy trabajado con tres salidas que necesito activar muy rápidas que serian el pin 10, 11 y 19.
    lo que me pasa es que me funcionan bien la 10 y la 19 pero la 11 no se activa poniendo PORTB=(1<<PB3);
    pero si se activa con digitalWrite(11,HIGH);
    Tengo que decir que estoy usando en mi programa las interrupciones, el timer 2 y la libreria

    He realizado un ejemplo en el que me pasa lo mismo:

    void setup() {
    DDRB = 0b00001100; // Los pines de 10 y 11 del puerto B son salidas.
    }

    void loop() {
    PORTB=(1<<PB2); // Activamos el pin 10
    PORTB=(1<<PB3); // Activamos el pin 11
    delay(500);
    PORTB=(0<<PB2); //Apagamos el pin 10
    PORTB=(0<<PB3); //Apagamos el pin 11
    delay(500);
    }

    En este caso se activa el pin 11 pero no el 10, y no entiendo el por que.
    Agradecería si me pudierais echar una mano del porque me falla.

    Gracias.

    Le gusta a 1 persona

    Responder
  2. egchiappara

    lo que sucede es que al registro PORTx no escribes un bit sino un byte. por lo que (0<<PB3) equivale a PORTB=B00001000; por lo tanto los valores anteriores son pisados.compruebalo colocando delays entre las instrucciones de asignacion.
    Saludos, Esteban

    Le gusta a 1 persona

    Responder

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.