Saltar al contenido principal
LibreTexts Español

22.1: DigitalWrite ()

  • Page ID
    81965
  • \( \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}}\)

    \( \newcommand{\vectorA}[1]{\vec{#1}}      % arrow\)

    \( \newcommand{\vectorAt}[1]{\vec{\text{#1}}}      % arrow\)

    \( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vectorC}[1]{\textbf{#1}} \)

    \( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)

    \( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)

    \( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)

    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)

    \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)

    “Escribir a un puerto” implica que deseamos controlar algún dispositivo externo. Esto podría implicar configurar o borrar un solo bit para encender un LED o un motor. También se podría requerir un conjunto de bits, por ejemplo, para enviar código ASCII byte a byte o escribir palabras de datos en un convertidor digital a analógico (DAC) externo. Es importante recordar que el microcontrolador tiene una cantidad limitada de corriente de sumidor/fuente disponible por pin (40 mA por cada pin del ATMega 328P pero no más de 200 mA en total para el chip). Por lo tanto, es posible accionar un puñado de LEDs a 10 mA cada uno con una conexión directa que consiste en una resistencia limitadora de corriente y el LED, pero no es posible encender incluso un motor de CC relativamente pequeño. Las cargas de corriente (o voltaje) más altas requerirán algún tipo de controlador o circuito de interfaz. Renunciaremos a esa discusión y solo nos centraremos en la porción del código aquí.

    Como los pines del puerto de salida solo pueden estar en un estado alto o bajo, se les conoce como salidas digitales. Si bien es posible generar señales analógicas, ya sea a través de modulación de ancho de pulso o circuitería externa adicional, la naturaleza digital alta/baja de los pines de puerto es todo lo que hay. En términos generales, la mayoría de los microcontroladores no producen voltajes analógicos continuamente variables en sus pines de puerto. Sin embargo, hay excepciones a esto. Por ejemplo, la placa Arduino Due 1 utiliza la CPU Atmel SAM3X8E ARM Cortex-M3 que contiene dos DAC internos de 12 bits.

    Antes de escribir, se tiene que configurar el puerto para la dirección adecuada. Esto significa usar PinMode () o el registro de dirección de datos asociado, DDRx, para establecer el modo a salida antes de que podamos considerar escribir datos en un dispositivo externo. Si un solo bit de puerto es todo lo que se requiere, PinMode () es muy sencillo y robusto. Si es necesario controlar varios bits juntos, puede ser más fácil ir directamente a DDRx.

    Así como hay dos métodos para establecer el modo de salida, lo mismo es cierto para escribir los datos en sí; un método efectivo para bits simples y otro para conjuntos de bits.

    Para escribir un solo bit, la función digitalWrite () es una buena opción. Aquí está la documentación en línea para ello, que se encuentra en http://arduino.cc/en/Reference/DigitalWrite:

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

    DigitalWrite ()

    Descripción

    Escriba un valor ALTO o BAJO en un pin digital.

    Si el pin ha sido configurado como SALIDA con PinMode (), su voltaje se ajustará al valor correspondiente: 5V (o 3.3V en placas de 3.3V) para ALTO, 0V (tierra) para BAJO.

    Si el pin está configurado como INPUT, escribir un valor ALTO con DigitalWrite () habilitará una resistencia interna de 20K pullup (vea el tutorial sobre pines digitales). Escribir BAJO desactivará el pullup. La resistencia pullup es suficiente para encender un LED tenuemente, por lo que si los LED parecen funcionar, pero muy tenuemente, esta es una causa probable. El remedio es establecer el pin a una salida con la función pinMode ().

    NOTA: El pin digital 13 es más difícil de usar como entrada digital que los otros pines digitales porque tiene un LED y una resistencia conectada a él que está soldada a la placa en la mayoría de las placas. Si habilita su resistencia interna de 20k pull-up, colgará alrededor de 1.7 V en lugar de los 5V esperados porque el LED incorporado y la resistencia en serie bajan el nivel de voltaje, lo que significa que siempre regresa BAJO. Si debe usar el pin 13 como entrada digital, use una resistencia externa desplegable.

    Sintaxis

    DigitalWrite (pin, valor)

    Parámetros

    pin: el número de pin

    valor: ALTO o BAJO

    Devoluciones

    ninguno

    Ejemplo

    int ledPin = 13;                       // LED connected to digital pin 13
    
    void setup()
    {
        pinMode(ledPin, OUTPUT);           // sets the digital pin as output
    }
    
    void loop()
    {
        digitalWrite(ledPin, HIGH);        // sets the LED on
        delay(1000);                       // waits for a second
        digitalWrite(ledPin, LOW);         // sets the LED off
        delay(1000);                       // waits for a second
    }
    

    Del código de ejemplo, esto es bastante fácil de usar. Simplemente establezca primero la dirección y luego escriba en el designador de pines Arduino apropiado. Recuerda, los designadores de pines son los números escritos junto a los encabezados en la placa Uno, ¡no son los números de bit de puerto en el ATMega 328P! Vale la pena volver a leer la nota de referencia respecto al pin 13 de Arduino. El pin 13 está cableado a un LED de señalización de montaje en superficie integrado ubicado justo al lado de dicho pin. Esto también significa que la corriente de fuente total disponible para el pin 13 se reduce algo ya que la corriente LED también se aplicará siempre. Arduino pin 13 es PORTB bit 5 (a veces escrito taquigrafía como PORTB.5).

    Entonces, ¿qué hace la función digitalWrite ()? Aquí está el código para DigitalWrite (), ligeramente limpiado para su placer visual 2:

    void digitalWrite( uint8_t pin, uint8_t val )
    {
        uint8_t timer, bit, port, oldSREG;
        volatile uint8_t *out;
        
        timer = digitalPinToTimer( pin );
        bit = digitalPinToBitMask( pin );
        port = digitalPinToPort( pin );
    
        if (port == NOT_A_PIN) return;
    
        if (timer != NOT_ON_TIMER) turnOffPWM( timer );
    
        out = portOutputRegister( port );
    
        oldSREG = SREG;
        cli();
    
        if (val == LOW)    *out &= ~bit;
        else               *out |= bit;
    
        SREG = oldSREG;
    }
    

    Vamos a desarmar esto, pieza por pieza, poco a poco. Después de las declaraciones iniciales de datos vemos un grupo de llamadas a funciones que sirven para traducir el designador de pines Arduino en puertos, bits y temporizadores ATMega 328P apropiados. (El negocio del temporizador lo abordaremos en breve.) Esta sección termina con una comprobación de errores. Si el pin especificado no existe, la función se amortigua y no hace nada.

    timer = digitalPinToTimer( pin );
    bit = digitalPinToBitMask( pin );
    port = digitalPinToPort( pin );
    
    if (port == NOT_A_PIN) return;
    

    La serie AVR de controladores, como la mayoría de los controladores, contiene temporizadores/contadores internos. Estos permiten al controlador cronometrar eventos con precisión o producir señales de pulso (específicamente, modulación de ancho de pulso). El sistema Arduino preconfigura seis de las salidas disponibles con temporizadores para su uso con la función AnalogWrite (). Como no todos los pines tienen esta capacidad, necesitamos traducir el pin Arduino a un temporizador asociado con la función DigitalPintoTimer (). Analizaremos más de cerca los temporizadores más adelante, pero por ahora solo es importante entender que cualquier temporizador asociado necesita apagarse antes de que podamos usar nuestra función básica de escritura digital.

    if (timer != NOT_ON_TIMER) turnOffPWM( timer );
    

    El código para desactivar la funcionalidad PWM es poco más que una sentencia switch/case. La llamada cbi () se utiliza para borrar un bit específico en un puerto, en este caso el registro de control temporizador-contador asociado (TCCRx). Puede encontrar más información en http://playground.arduino.cc/Main/AVR.

    static void turnOffPWM( uint8_t timer )
    {
        switch (timer)
        {
            case TIMER0A:    cbi(TCCR0A, COM0A1);    break;
            case TIMER0B:    cbi(TCCR0A, COM0B1);    break;
            case TIMER1A:    cbi(TCCR1A, COM1A1);    break;
            case TIMER1B:    cbi(TCCR1A, COM1B1);    break;
            case TIMER2A:    cbi(TCCR2A, COM2A1);    break;
            case TIMER2B:    cbi(TCCR2A, COM2B1);    break;
        }
    }
    

    Después de esto, el puerto Arduino especificado se traduce a un registro de salida, es decir, PortX.

    out = portOutputRegister( port );
    

    Como se vio con PinMode (), se guarda el registro de estado, se borra el bit de interrupción global para deshabilitar todas las interrupciones a través de la llamada cli (), el puerto deseado (PortX) se anD con el complemento de la máscara de bits para borrarlo (es decir, configurarlo bajo) o OR con la máscara de bits a fijarlo (es decir, establecerlo alto). El registro de estado se restablece entonces a su estado original:

    oldSREG = SREG;
    cli();
    
    if (val == LOW)    *out &= ~bit;
    else               *out |= bit;
    
    SREG = oldSREG;
    

    Eso es más o menos. Ahora, si quieres escribir un montón de bits a un grupo de conexiones de puerto de salida, simplemente puedes buscar el puerto correspondiente para esos pines Arduino (es decir, PortX) y configurarlos o borrarlos directamente. Por ejemplo, si desea borrar los bits 0, 1 y 4 del puerto B (es decir, 00010011 o 0x13), podría hacer lo siguiente (suponiendo que ningún temporizador esté activo):

    PORTB &= ~0x13; // clear bits
    

    o para establecerlos:

    PORTB |= 0x13; // set bits
    

    Esto dejaría los otros bits completamente intactos. Por el contrario, supongamos que desea establecer los cuatro bits inferiores (es decir, del 0 al 3) en el patrón binario 0101. Eso equivale a 0x05. Podrías hacer lo siguiente:

    PORTB = (PORTB & 0xf0)| 0x05;
    

    El primer fragmento borra los cuatro bits inferiores y la parte final establece el patrón binario. Esto sería preferible a borrar los bits 1 y 3 y luego establecer los bits 0 y 2, ya que eso provocaría dos conjuntos distintos de patrones de voltaje de salida en los pines externos. Concedido, el primer conjunto existirá por muy poco tiempo pero esto puede crear problemas en algunas aplicaciones.


    1. http://arduino.cc/en/Main/ArduinoBoardDue
    2. El original se puede encontrar en el archivo wiring_digital.c

    This page titled 22.1: DigitalWrite () 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.