Saltar al contenido principal
LibreTexts Español

21.1: PinMode ()

  • Page ID
    82460
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    O “Programación de Direcciones de Puertos y Tu Bicicleta”

    En este recorrido, vamos a empezar a mirar IO digital. La IO digital nos permitirá leer el estado de un pin de entrada así como producir un alto o bajo lógico en un pin de salida. Los ejemplos incluyen leer el estado de un interruptor externo y encender y apagar un LED o motor.

    Si cada conexión externa potencial entre un microcontrolador y el mundo exterior tuviera un cable dedicado, el número de pines para los paquetes de controladores sería enorme. El ATMega 328P en la placa Uno tiene cuatro puertos de 8 bits más conexiones para alimentación, tierra y similares, sin embargo, solo tiene 28 pines físicos. ¿Cómo es esto posible? Simple, vamos a multiplex los pines, es decir, hacer múltiples usos para cada uno. Si, por ejemplo, tuviéramos que mirar el bit 0 º del puerto IO B, esto lleva a un solo pin externo. Este pin se puede programar para que se comporte tanto en modo de entrada (lectura) como en modo de salida (escritura). En general, cada bit de un puerto se puede programar de forma independiente; algunos para entrada, algunos para salida, o todos ellos iguales. Obviamente, antes de usar un puerto necesitamos decirle al controlador de qué manera debe comportarse. En el sistema Arduino esto se suele hacer a través de una llamada a la función de biblioteca PinMode (). Aquí está la descripción de la función a partir de la referencia en línea: 1

    Figura\(\PageIndex{1}\): pinMode docs

    PinMode ()

    Descripción

    Configura el pin especificado para que se comporte como entrada o salida. Consulte la descripción de los pines digitales para obtener detalles sobre la funcionalidad de los pines.

    A partir de Arduino 1.0.1, es posible habilitar las resistencias internas pullup con el modo INPUT_PULLUP. Además, el modo INPUT deshabilita explícitamente los pullups internos.

    Sintaxis

    PinMode (pin, modo)

    Parámetros

    pin: el número del pin cuyo modo desea establecer

    modo: INPUT, OUTPUT o INPUT_PULLUP. (consulte la página de pines digitales para una descripción más completa de la funcionalidad).

    Devoluciones

    Ninguno

    Entonces primero tendríamos que pensar en términos de un número pin de Arduino en lugar de un número de bit de puerto. A continuación se muestra una tabla de designaciones de pines Arduino Uno frente a los nombres de puertos y pines ATMega 328P.

    Tabla\(\PageIndex{1}\): Definiciones de Pin Uno de Arduino.
    Designador Arduino Designador de E/S de propósito general Comentar
    A0 PORTC bit 0 Entrada ADC 0
    A1 PORTC bit 1 Entrada ADC 1
    A2 PORTC bit 2 Entrada ADC 2
    A3 PORTC bit 3 Entrada ADC 3
    A4 PORTC bit 4 Entrada ADC 4
    A5 PORTC bit 5 Entrada ADC 5
    0 PORTD bit 0 RX
    1 PORTD bit 1 TX
    2 PORTD bit 2
    3 PORTD bit 3 PWM
    4 PORTD bit 4
    5 PORTD bit 5 PWM
    6 PORTD bit 6 PWM
    7 PORTD bit 7
    8 PORTB bit 0
    9 PORTB bit 1 PWM
    10 PORTB bit 2 PWM
    11 PORTB bit 3 PWM
    12 PORTB bit 4
    13 PORTB bit 5 LED incorporado

    La convención de nomenclatura es razonablemente lógica pero menos que perfecta. Tenga en cuenta que los designadores A0 a A5 son para las entradas analógicas y los restantes son para E/S digital. A0 a A5 son, de hecho, constantes globales predefinidas que se mapean de nuevo a valores numéricos (por ejemplo, A0 es 14 para el Uno, ver el archivo pins_arduino.h para más detalles). También hay que señalar que los canales analógicos son de solo entrada. El controlador no puede producir voltajes analógicos continuamente variables por sí solo. Esto no quiere decir que sea imposible obtener control analógico; solo que va a tomar un poco más de trabajo (como veremos pronto).

    Prácticamente hablando, la convención de nomenclatura de pines no es tan mala ya que los pines están etiquetados justo en el tablero, ver Figura\(\PageIndex{2}\).

    Arduino Uno.
    Figura\(\PageIndex{2}\): Arduino Uno.

    Por ejemplo, directamente encima del logotipo de Arduino Uno puedes detectar un “8” junto a un pin ubicado en el borde de un encabezado de 10 pines. De acuerdo con la tabla anterior, este es el bit 0 del puerto B. Para establecer este conector en modo de salida para accionar un circuito externo, podría escribir:

    pinMode( 8, OUTPUT );
    

    Bien, entonces, ¿qué pasa si estamos usando este controlador en un sistema que no es Arduino, y hay una manera más rápida de lograr esto (en cuanto a ejecución de código)? Echemos un vistazo al código de esta función. Se encuentra en un archivo llamado wiring_digital.c Aquí están los bits y piezas relevantes (ligeramente alterados para que algunas porciones sean un poco más claras):

    void pinMode(uint8_t pin, uint8_t mode)
    {
        uint8_t bit, port, oldSREG;
        volatile uint8_t *reg, *out;
    
        bit = digitalPinToBitMask( pin );
        port = digitalPinToPort( pin );
    
        if (port == NOT_A_PIN) return; // bad pin # requested
    
        reg = portModeRegister( port );
        out = portOutputRegister( port );
    
        if (mode == INPUT) {
            oldSREG = SREG;
            cli();
            *reg &= ~bit;
            *out &= ~bit;
            SREG = oldSREG;
        }
        else
        {
            if (mode == INPUT_PULLUP)
            {
                oldSREG = SREG;
                cli();
                *reg &= ~bit;
                *out |= bit;
                SREG = oldSREG;
            }   
            else // must be OUTPUT mode
            {
                oldSREG = SREG;
                cli();
                *reg |= bit;
                SREG = oldSREG;
            }
        }
    }
    

    Después de la declaración de algunas variables locales, el número de pin de Arduino especificado es decodificado tanto en un puerto como en su máscara de bits asociada por las funciones DigitalPinto... (). Si se especifica un pin que no existe en este procesador en particular, el puerto se establecerá en un valor de error por lo que verificamos esto y rescatamos si es así. ¿Qué es una máscara de babor? Eso es solo un valor de 8 bits con todos los 0s excepto un 1 en la posición de bit deseada (a veces es el complemento de esto, es decir, todos los 1s con un 0- depende de si pretendes establecer, borrar, AND u OR). El puerto... Las funciones de registro () realizan operaciones de decodificación similares. Por ejemplo, el llamado “registro de modo puerto” es mejor conocido como el registro de dirección de datos, o DDR. El ATMega 328P tiene tres de estos, DDRB a DDRD. Establecer un bit establece el modo en salida (escritura) mientras que la limpieza lo pone en modo de entrada (lectura).

    Estas cuatro “funciones” son realmente tablas de búsqueda disfrazadas de funciones. Es simplemente un acceso a una matriz llena de valores apropiados. Acceder a una matriz es mucho más rápido que llamar a una función. Considere la función portmodereRegister (), que dado un designador de puerto, devolverá el puerto de dirección de datos real que podemos manipular (por ejemplo, DDRC). Esto se define como:

    #define portModeRegister(P)
        ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) )
    

    Port_to_mode_PGM resulta ser una matriz 2 llena con el símbolo de error predefinido y las direcciones de los registros de dirección de datos apropiados:

    const uint16_t PROGMEM port_to_mode_PGM[] = {
        NOT_A_PORT,
        NOT_A_PORT,
        (uint16_t) &DDRB,
        (uint16_t) &DDRC,
        (uint16_t) &DDRD,
    };
    

    pgm_read_word es una función especial utilizada para leer valores del espacio del programa versus el espacio de datos normal (recuerde, este controlador usa una arquitectura Harvard con memoria dividida).

    El resultado final es que recuperaremos la dirección del registro de dirección de datos requerido y eso es a lo que necesitaremos acceder para establecer el modo de entrada o salida.

    El siguiente trozo de código es solo tres posibilidades if/else, una para cada uno de los modos que podríamos solicitar. Primero echemos un vistazo al modo OUTPUT.

    else {
        oldSREG = SREG;
        cli();
        *reg |= bit;
        SREG = oldSREG;
    }
    

    Probablemente el registro más importante en un microcontrolador es el registro de estado, aquí llamado SREG. Contiene un montón de bits de bandera que significan una serie de estados. Por ejemplo, hay un bit para indicar que una operación aritmética se desbordó (es decir, había muy pocos bits para contener el resultado). Hay un poco para indicar que el resultado es negativo, y así sucesivamente. Uno de los bits controla si el sistema responderá o no a interrupciones. Una interrupción es un evento de alta prioridad que puede detener (interrumpir) la ejecución de su código mientras el código de interrupción (ISR o rutina de servicio de interrupción) se ejecuta en su lugar. Una interrupción puede ocurrir en cualquier momento, incluso cuando tu código está en medio de algo importante. Para evitar que esto suceda, primero guardamos el contenido actual del registro de estado y luego borramos el bit de estado que permite que ocurran interrupciones. Esa es la llamada cli () (que, como resulta, es una expansión en línea a una sola instrucción de ensamblaje llamada, lo adivinaste, CLI). Una vez que sepamos que no seremos interrumpidos, podremos tocar con el DDR. reg es el DDR para este puerto en particular. Nosotros O con la máscara de bits de interés, es decir, establecemos ese bit. Esto selecciona la dirección como salida. Finalmente, restauramos el contenido original del registro de estado, habilitando interrupciones (asumiendo que se había configurado para empezar).

    Entonces, cuando originalmente escribimos

    pinMode( 8, OUTPUT );
    

    La función decodificó el pin 8 de Arduino como bit 0 del puerto B (es decir, PORTB). También determinó que el DDR correspondiente es DDRB. La máscara de bit 0 es 0x01 y fue OR con el contenido de DDRB, seleccionando así la dirección para ese bit como modo de salida.

    La cláusula para el modo INPUT es similar:

    if (mode == INPUT)
    {
        oldSREG = SREG;
        cli();
        *reg &= ~bit;
        *out &= ~bit;
        SREG = oldSREG;
    }
    

    Tenga en cuenta que aquí el DDR es ANDed con el complemento de la máscara de bits para borrar el bit, colocándolo así en modo de entrada. Este código también borra el mismo bit en el registro de salida que desactiva la resistencia pull-up en el pin externo. Por el contrario, tenga en cuenta que el modo INPUT_PULLUP establece este bit.

    Cosas geniales seguro, pero ¿y si no estás usando un Arduino o necesitas hacerlo más rápido? Si no tiene ningún código de interrupción en ejecución, puede tocar de manera segura directamente con el puerto y el DDR. Recuerda, queríamos que el bit 0 del puerto B estuviera listo para la salida. Eso significa que necesitamos establecer el bit 0 de DDRB.

    DDRB |= 0x01;
    

    o usar la macro

    bitSet( DDRB, 0 );
    

    O bien haría el truco, este último probablemente un poco más claro y menos propenso a errores. Y aquí hay algo muy útil para recordar: ¿Y si necesitas establecer un montón de pines o incluso un puerto completo en modo de entrada o salida? Usando PinMode () tendrías que hacer llamadas individuales para cada pin. Por el contrario, si necesitara establecer los seis bits inferiores del puerto B en el modo de salida 3, simplemente podría hacer esto:

    DDRB |= 0x3f; // set Uno pins 8 through 13 to OUTPUT mode
    

    ¿Significa esto que nunca debemos usar PinMode ()? ¡No! Hemos visto que esta función se vincula perfectamente con la placa Arduino Uno y es a la vez más robusta y más flexible. Es solo que a veces una bicicleta nos va a servir mejor que un vehículo motorizado y es bueno que tengamos la opción.


    1. http://www.arduino.cc/en/Reference/PinMode
    2. Estos se encuentran en avr/pgmspace.h y pins_arduino.h
    3. Puede hacer esto para escribir valores fuera del puerto de manera paralela, varios bits a la vez, por ejemplo, para alimentar un DAC de entrada paralela.

    This page titled 21.1: PinMode () is shared under a CC BY-NC-SA 4.0 license and was authored, remixed, and/or curated by James M. Fiore via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.