Saltar al contenido principal
LibreTexts Español

27.1: AnalogWrite ()

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

    Mientras que algunos microcontroladores más avanzados contienen un convertidor digital a analógico (DAC) para producir una señal de salida analógica continuamente variable, la mayoría no. El ATMega 328P en el Uno es uno de los que no lo hacen ¿Cómo entonces podemos conseguir que el controlador produzca señales analógicas? Un método es usar un banco completo de pines de puerto, digamos los ocho bits del puerto B, y alimentarlos directamente a un DAC externo de entrada paralela. Luego escribimos cada byte en el puerto a la velocidad deseada. El DAC (junto con un filtro de reconstrucción de paso bajo) reconstruye estos datos en una señal suave. El segundo método se basa en la modulación de ancho de pulso. Este esquema se emplea en las placas Uno y muchas otras placas Arduino. Tenga en cuenta que no todas las cargas funcionarán correctamente con una señal PWM simple. Algunas cargas pueden requerir un procesamiento adicional de la señal PWM (como el filtrado).

    La clave para entender la modulación por ancho de pulso es considerar el “área bajo la curva”. Supongamos que tenemos una señal de pulso de un voltio que dura un periodo de cinco segundos, baja durante cinco segundos y luego se repite. La misma zona se lograría con un pulso de cinco voltios que se mantuvo alto por apenas un segundo de cada diez. Es decir, el valor promedio de cualquiera de los pulsos en el transcurso de diez segundos es de medio voltio. De igual manera, si ese pulso de cinco voltios se mantuvo alto durante dos segundos, entonces en el transcurso del periodo de diez segundos el valor promedio sería de un voltio. En otras palabras; cuanto mayor sea el ciclo de trabajo, mayor será el nivel de voltaje promedio. Si aceleramos esto y los pulsos fueran milisegundos o microsegundos de ancho y se repitieran una y otra vez, podríamos filtrar el tren de pulsos de paso bajo y lograr resultados que varían suavemente. Para algunas cargas, ni siquiera tendríamos que filtrar el resultado. Los ejemplos incluirían accionar un elemento calefactor resistivo para controlar la temperatura o accionar un LED para controlar el brillo.

    El Uno logra PWM mediante el uso de sus tres contadores internos. Cada uno de estos tiene un componente A y B, por lo que es posible generar seis señales PWM. El sistema Arduino los configura previamente para ti. Puedes saber qué pines son compatibles con PWM con solo mirar la placa. Los pines PWM tendrán una tilde (~) junto a su número de pin Arduino. La función AnalogWrite () te quita una gran cantidad de trabajo. Aquí está la documentación en línea repetida para su conveniencia:

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

    AnalogWrite () 1

    Descripción

    Escribe un valor analógico (onda PWM) en un pin. Se puede utilizar para encender un LED a diferentes brillos o accionar un motor a varias velocidades. Después de una llamada a AnalogWrite (), el pin generará una onda cuadrada constante del ciclo de trabajo especificado hasta la siguiente llamada a AnalogWrite () (o una llamada a DigitalRead () o DigitalWrite () en el mismo pin). La frecuencia de la señal PWM es aproximadamente 490 Hz.

    En la mayoría de las placas Arduino (las que tienen el ATmega168 o ATmega328), esta función funciona en los pines 3, 5, 6, 9, 10 y 11. En el Arduino Mega, funciona en los pines 2 a 13. Las placas Arduino más antiguas con un ATmega8 solo admiten AnalogWrite () en los pines 9, 10 y 11.

    El Arduino Due admite AnalogWrite () en los pines 2 a 13, más los pines DAC0 y DAC1. A diferencia de los pines PWM, DAC0 y DAC1 son convertidores de Digital a Analógico, y actúan como verdaderas salidas analógicas.

    No es necesario llamar a pinMode () para establecer el pin como salida antes de llamar a AnalogWrite ().

    La función AnalogWrite no tiene nada que ver con los pines analógicos ni con la función AnalogRead.

    Sintaxis

    AnalogWrite (pin, valor)

    Parámetros

    pin: el pin en el que escribir.

    valor: el ciclo de trabajo: entre 0 (siempre apagado) y 255 (siempre encendido).

    Devoluciones

    nada

    Notas y problemas conocidos

    Las salidas PWM generadas en los pines 5 y 6 tendrán ciclos de trabajo superiores a los esperados. Esto se debe a las interacciones con las funciones millis () y delay (), que comparten el mismo temporizador interno utilizado para generar esas salidas PWM. Esto se notará principalmente en configuraciones de ciclo de trabajo bajo (por ejemplo, 0 - 10) y puede resultar en un valor de 0 que no apague completamente la salida en los pines 5 y 6.

    La conclusión es que llamas a AnalogWrite () con solo el pin de interés y un valor de ciclo de trabajo que va desde 0 (off) hasta 255 (completamente activado). Eso es todo lo que hay para ello. Pero, ¿por qué es el rango de 0 a 255 en lugar del 0 al 100 por ciento más obvio? Echemos un vistazo al código, como de costumbre, ligeramente limpiado para tu comodidad (el código original se puede encontrar en el archivo wiring_analog.c):

    void analogWrite(uint8_t pin, int val)
    {
        pinMode(pin, OUTPUT);
    
        if (val == 0)
        {
            digitalWrite(pin, LOW);
        }
        else
        {
            if (val == 255)
            {
                digitalWrite(pin, HIGH);
            }
            else
            {
                switch( digitalPinToTimer(pin) )
                {
                    case TIMER0A:
                        // connect pwm to pin on timer 0, channel A
                        sbi(TCCR0A, COM0A1);
                        OCR0A = val; // set pwm duty
                        break;
    
                    case TIMER0B:
                        // connect pwm to pin on timer 0, channel B
                        sbi(TCCR0A, COM0B1);
                        OCR0B = val; // set pwm duty
                        break;
    
                    // and so on for TIMER1A through TIMER2B
                    
                    case NOT_ON_TIMER:
                    default:
                        if (val < 128)
                            digitalWrite(pin, LOW);
                        else
                            digitalWrite(pin, HIGH);
                }
            }
        }
    }
    

    Lo primero que vemos es una llamada a pinMode () para garantizar que el pin esté configurado para la salida. Si bien es posible simplemente requerir que los programadores hagan que esta función se llame a sí mismos antes de cada uso, es más seguro colocarla dentro de la función. Tenga en cuenta que si solo estuviera usando este pin en este modo, podría hacer esta llamada usted mismo solo una vez como parte de su rutina de configuración y hacer su propia versión de AnalogWrite () habiendo eliminado esta parte (y tal vez algunas otras secciones) para afeitarse el tiempo de ejecución.

    pinMode(pin, OUTPUT);
    

    El código luego hace una comprobación rápida para ver si queremos completamente encendido o completamente apagado, en cuyo caso prescinde del temporizador y simplemente llama a DigitalWrite ().

    if (val == 0)
    {
        digitalWrite(pin, LOW);
    }
    else
    {
        if (val == 255)
        {
            digitalWrite(pin, HIGH);
        }
    

    En este punto, se debe haber ingresado un valor del 1 al 254. El pin se traduce a un temporizador y, a través de una declaración de interruptor/caso, se activa el registro de control del temporizador apropiado y el valor del ciclo de trabajo se carga en el registro de recuento asociado. Estos temporizadores son unidades de ocho bits. Por lo tanto, pueden contener valores entre 0 y 255. Es por ello que el valor del “ciclo de trabajo” va de 0 a 255 en lugar de 0 a 100 por ciento. (Técnicamente, el temporizador uno es una unidad de 16 bits pero se usa aquí con ocho.)

    switch( digitalPinToTimer(pin) )
    {
        case TIMER0A:
            // connect pwm to pin on timer 0, channel A
            sbi(TCCR0A, COM0A1);
            OCR0A = val; // set pwm duty
            break;
    
        case TIMER0B:
            // connect pwm to pin on timer 0, channel B
            sbi(TCCR0A, COM0B1);
            OCR0B = val; // set pwm duty
            break;
    
        // and so on for TIMER1A through TIMER2B
    

    Por último, si se usa AnalogWrite () en un pin que no está configurado con un temporizador, la función hace lo mejor que puede, es decir, llamar a DigitalWrite (). Cualquier valor por debajo de la mitad (128) producirá un valor bajo y cualquier valor a la mitad o por encima producirá un alto:

    case NOT_ON_TIMER:
    default:
        if (val < 128)
            digitalWrite(pin, LOW);
        else
            digitalWrite(pin, HIGH);
    

    Vale la pena repetir que en las placas Arduino que contienen un DAC interno como el Due, esta función producirá verdaderas señales de salida analógicas (el código para eso no se muestra).


    1. http://arduino.cc/en/Reference/AnalogWrite

    This page titled 27.1: AnalogWrite () 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.