Programacion avanzada

De ArduWiki
Revisión del 17:18 26 jun 2018 de Kike GL (Discusión | contribuciones) (Página creada con «La mayoría de los puertos de los uC son multipropósito, es decir, en función de su configuración se comportan de una forma u otra. El ATmega 328p como cualquier otro uC...»)

(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Saltar a: navegación, buscar

La mayoría de los puertos de los uC son multipropósito, es decir, en función de su configuración se comportan de una forma u otra. El ATmega 328p como cualquier otro uC tiene registros para cada puerto donde define si sera usado como entrada o salida. ATmega 328p tiene 3 bancos o grupos de puertos: PB (D8~D13), PC (A0~A5) y PD (D0~D7), es decir a PB y PC le faltan puertos debido a que no se dispone de pines suficientes al ser ATmega 328p un DIP-28 (en comparación del tradicional DIP-40 de la mayoria de uC).

Banco 27 26 25 24 23 22 21 20
PB - - D13 D12 D11 D10 D9 D8
PC - - A5 A4 A3 A2 A1 A0
PD D7 D6 D5 D4 D3 D2 D1 D0

ATmega 328p tiene 3 registro de 8 bits con los que administra estos 3 bancos:

  • DDRx, determina si es entrada (0) o salida (1)
  • PORTx, controla si el pin esta en nivel HIGH o LOW. También define Pull-up si es entrada.
  • PINx, permite leer estado (solo lectura).

Registros

Antes de meterme de lleno a explicarte los registros, quiero que primero veamos para que podría servir este esfuerzo.

Ventajas de usar registros:

  • Cada instrucción máquina necesita un ciclo de reloj a 16 MHz. Las funciones digitalRead() y digitalWrite() se componen cada una de ellas de varias instrucciones máquina, lo que puede influir negativamente en aplicaciones muy dependientes del tiempo. El Registro PORTx puede hacer el mismo trabajo en muchos menos ciclos de reloj.
  • Si necesitas cambiar de estado muchos pines en lugar de hacer un loop for que tomara mucho tiempo puedes echar mano directamente al registro y establecer varios pines de una sola vez.
  • Si estas al limite de memoria Flash, puedes usar este truco para hacer que tu código use menos bytes de programación.

Desventajas de usar registros:

  • El código no es fácil de entender para novatos.
  • Es mucho mas fácil que cometas errores de difícil depuración. Por ejemplo con DDRD = B11111111, pone todos los pines (D0~D7) como salida, incluso el pin D0 (Rx) lo que causar que el puerto serial deje de funcionar.

Nota: En las librerías es muy recomendable usar la manipulación directa de registros, así se hacen mucho mas rápidas.


Digitales

Bien ahora que ya sabes para que nos meteremos en como manejar estos registros. En principio tenemos dos métodos: masivo es decir todos los puertos de un banco a la vez o puntual es decir un puerto de un banco en particular.

Masivo

Ya sabemos que ATmega 326P maneja 3 bancos (B, C y D) y que cada uno agrupa a un numero de puertos. Por otro lado tenemos 3 registros (DDRx, PORTx y PINx) que podemos manejar.

DDRD = 0b11111111;  //D0~D7 como salidas.
DDRD = 0b00000000;  //D0~D7 como entradas.
DDRB = 0b00000111;  //D8~D10 como salida D11~D13 como entradas.
PORTD = 0b11111111; //D0~D7 en HIGH.
PORTD = 0b00000000; //D0~D7 en LOW.
PORTB = 0b00000101; //D8+D10 en HIGH, D8, D11~D13 en LOW.
byte variable = PIND; //Guarda en una variable los estados de D0~D7 como un byte.

Se debe tener cuidado cuando se usa el PORTD y el puerto serie D0 (Rx) y D1 (Tx) son los usados por la UART y si se pone estos puertos como entradas o salidas, la UART será incapaz de leer o escribir datos en estos puertos. 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.

Mediante los registros también podemos controlar las resistencias internas de pull-up que se usan basicamente para no dejar los pines al aire ya que esto genera ruido eléctrico. Se puede resolver este problema de dos maneras: poner una resistencia externa de 10K a Vcc (+5V) o usar los Pull-up internos del uC que producen el mismo efecto y hacen mas simple el circuitos.

Para habilitar las resistencias pull-up tenemos primero que configurar como entrada (0) el puerto mediante registro DDRx y luego escribir un 1 en el registro PORTx.

DDRD = 0b00000000;    //Configura todos los puertos del banco PORTD (D0~D7) como entradas.
PORTD = 0b00001111;   //Habilitar las Pull-ups de los puertos D0~D3.

Es casi tan fácil como usar las funciones digitalRead() y digitalWrite() de Arduino, pero con acceso directo al puerto se puede ahorrar espacio en la memoria flash porque cada operación de leer el estado de un solo puerto ocupa 40 bytes de instrucciones 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.

Puntual

No es normal que se necesite definir, leer o escribir en un banco completo siempre, por ejemplo, si queremos encender un LED tendremos que actual sobre un solo puerto y no sobre todo el banco. Para eso podemos usar la macro Px donde x estará entre 0~7 para definir el puerto en particular. Ver tabla arriba.

DDRD = (1<<PD2);  //Configura el D2 como salida.
PORTD = (1<<PD4); //Pone D4 en HIGH.
byte variable = (PIND & (1<<PD1)); //Lee D1 y almacena su valor en la variable.

También podemos usar la macro Px varias veces en una misma instrucción, por ejemplo, en este código, se ejecutará algo de código sólo si los puertos D10 y D11 están en HIGH al mismo tiempo.

DDRB = 0b00001100; // Los pines D10 y D11 son entradas, y el resto (D8+D9+D12+D13) salidas.
if (PINB & ((1<<PD2) | (1<<PD3))) {
   //Algún código que se ejecutará solo si los dos botones (D10+D11) se encuentran en HIGH.
}

La manipulación directa de los puertos puede ser acompañada de las máscaras de bits, los operadores lógicos (NOT, AND, OR, y XOR) y operaciones de desplazamiento a la derecha e izquierda.

El siguiente ejemplo lee alternativamente 100 veces el pin D9 con digitalRead() y mediante el registro PINB y calcula el tiempo en microsegundos de ambas operaciones. Te sorprenden los resultados ?


Analogicos

ADC

PWM

Vea tambien

Referencias