Saltar al contenido principal
LibreTexts Español

14.4: Parte Terza — Cambio de Frecuencia

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

    Cambiar la forma de la onda es ciertamente útil. ¿Qué hay de darle al usuario el control sobre la frecuencia? Al principio, parece obvio usar un potenciómetro para un control de frecuencia: Podríamos leer un voltaje fuera de él usando la función analogRead () y usarlo para el valor de retardo. El problema con esto es lo mismo que el cálculo directo de un valor sinusoidal, es decir, la falta de velocidad. El tiempo de adquisición del ADC limitará severamente la frecuencia de salida que podemos crear. Por otro lado, verificar un interruptor para alto/bajo es un proceso muy rápido. Sería una cuestión sencilla cablear otro interruptor, similar al interruptor de forma de onda, que incrementaría una variable global en cada pulsación del botón. Esta variable sería utilizada entonces por la función delayMicroseconds () de la siguiente manera:

    unsigned int g_period=100; // init at 100 microseconds
    
    // set data and wait one sample period
    PORTD = p[u++];
    delayMicroseconds( g_period );
    

    Cuando se activa el interruptor de frecuencia, el tiempo de retardo se incrementaría así:

    BumpPeriod( 1 );
    

    La función BumpPeriod () “oculta” la variable global y también permite restringir su rango con wrap-around en los extremos:

    void BumpPeriod( char c )
    {
          g_period += c;
    
          // wrap within range, c could be negative
          if( g_period < 1 )      g_period = 200;
          if( g_period > 200 )    g_period = 1;
    }
    

    Un tiempo de retardo de 1 microsegundo produce un periodo de 256 microsegundos (ignorando la sobrecarga de procesamiento) para una frecuencia máxima de aproximadamente 3.9 kHz. De manera similar, un retraso de 200 microsegundos produce una frecuencia mínima de aproximadamente 20 Hz. Recuerde, g_period es una variable global por lo que se define fuera de y antes de todas las funciones. De esta manera todas las funciones pueden acceder a él.

    Si bien podríamos copiar y pegar el código de forma de onda para el interruptor de frecuencia (con ediciones, por supuesto), hay un problema práctico con él. Cada pulsación del botón cambiará el período de la muestra en solo un microsegundo. Tenemos un rango de 200:1 por lo que todo el lapso requeriría 200 pulsaciones discretas de botón. Eso probablemente no sea algo que a la mayoría de la gente le gustaría. Una solución común a este problema es tener el botón “incremento automático” si se mantiene presionado durante un periodo de tiempo lo suficientemente largo. Este tiempo de retención podría ser del orden de 500 milisegundos. No queremos que el incremento automático se ejecute demasiado rápido por lo que podríamos incrementar el periodo una unidad por cada 100 milisegundos siguientes. Eso equivale a 10 pulsaciones de botón por segundo, o el equivalente a demasiado espresso. A ese ritmo podemos cubrir el lapso en 20 segundos con sólo mantener un dedo en el botón. Este proceso se puede endulzar agregando un botón de decremento para ir junto con el botón de incremento. El código de cambio de decremento es prácticamente el mismo que lo que usaremos para el incremento, excepto que la llamada a BumpPeriod () pasa un valor negativo. Si el span fuera mucho mayor, también podríamos considerar agregar botones “gruesos” para incremento y decremento que simplemente pasarían un valor mayor a BumpPeriod () (por ejemplo, 10). Otra opción sería pasar un valor mayor a la función después de que haya transcurrido un período de tiempo más largo (por ejemplo, 1 cada 100 milisegundos si el botón se mantiene entre 0.5 y 2 segundos, y 10 si se mantiene más tiempo).

    Antes de mirar la función de incremento automático, debemos recordar agregar código para la inicialización del conmutador de frecuencia (junto con el interruptor mismo, por supuesto). Si colocamos este switch en el pin 1 de Arduino (puerto B.0), nuestro código de inicialización ahora se ve así:

    // all port B
    #define FREQBIT         0x01
    #define WAVEBIT         0x02
    #define SINEWAVE        0x04
    #define SQUAREWAVE      0x08
    #define RAMPWAVE        0x10
    
    void BumpPeriod( char c );
    unsigned int g_period=100; //init at 100 microsec
    
    void setup()
    {
          // set all of port D for output
          DDRD = 0xff;
    
          // set B.2:4 for output
          DDRB |= (SINEWAVE | SQUAREWAVE | RAMPWAVE);
    
          // set B.0:1 for input, enable pull-up
          DDRB &= (~(FREQBIT | WAVEBIT));
          PORTB |= (FREQBIT | WAVEBIT);
    }
    

    El procesamiento del interruptor en sí es similar al interruptor de forma de onda excepto que necesitaremos alguna información de sincronización. Después de todo, la función de incremento automático necesita determinar si el tiempo de retención inicial de 500 milisegundos ha pasado, y una vez que lo ha hecho, solo debe incrementarse una vez cada uno después de 100 milisegundos. Por lo tanto, junto con las variables para mantener los estados actual y anterior del conmutador, necesitaremos variables para mantener el tiempo en que se activó el interruptor (tiempo de transición del interruptor), la hora actual y el tiempo de incremento automático o “bump”:

    unsigned char freqswitch=FREQBIT, currentfreqswitch=FREQBIT;
    unsigned long transitiontime, currenttime, bumptime=500;
    

    Las variables xxxtime deben ser todas unsigned long s ya que se usarán con la función millis () time.

    Así es como funciona el algoritmo: Primero obtenemos el estado actual del switch. No hacemos nada a menos que el interruptor esté “abajo”. Si está abajo, registramos la hora actual y vemos si el estado del interruptor anterior estaba “arriba”. Si es así, esta es la transición inicial. Registramos el tiempo de esta transición e incrementamos el periodo. También establecemos una variable (bumptime) al tiempo de retención en milisegundos (500).

    // check if freq switch active
    currentfreqswitch = PINB & FREQBIT;
    
    if( !currentfreqswitch ) // selected (down)
    {
          currenttime = millis();
          if( currentfreqswitch != freqswitch ) // transition
          {
                transitiontime = currenttime;
                BumpPeriod( 1 );
                bumptime = 500;   // msec before auto inc
          }
    

    Si los estados del interruptor son los mismos, entonces sabemos que el interruptor está siendo retenido. Antes de que podamos iniciar el incremento automático, necesitamos saber si se ha mantenido el tiempo suficiente (tiempo de baches, o 500 milisegundos inicialmente). Si simplemente restamos la hora actual del tiempo de transición inicial sabremos cuánto tiempo ha estado retenido el conmutador. Si esta diferencia es al menos tan grande como nuestro objetivo entonces podemos incrementar el periodo. Como no queremos incrementarlo de nuevo hasta que pasen otros 100 milisegundos, necesitamos incrementar el tiempo de baches en 100.

          else  // being held, is it long enough?
          {
                if( currenttime-transitiontime > bumptime )
                {
                      BumpPeriod( 1 );
                      bumptime += 100;
                }
          }
    }
    

    Por último e independientemente del estado del switch, actualizamos la variable del conmutador de frecuencia anterior al estado actual:

    freqswitch = currentfreqswitch;
    

    Observe que una vez que se suelta el botón, se ignora hasta que se vuelve a presionar. En ese instante se registrará un nuevo tiempo de transición y el tiempo de bache se restablecerá a 500 milisegundos y el proceso se repetirá como se describió anteriormente. Si el botón se suelta antes del tiempo de retención, no se produce ningún incremento automático, y la siguiente pulsación del botón reinicializará los tiempos de transición y golpe. Al igual que en el caso del interruptor de forma de onda, es importante tener en cuenta que si estás usando un circuito de debounce de hardware que invierte, también se invertirá la lógica de “switch selected” (elimina el “no”).

    El código de procesamiento del interruptor de frecuencia debe colocarse en línea con el código del interruptor de forma de onda, ya sea inmediatamente antes o después de él. El resultado dentro de la función loop () debería verse algo como esto:

          // select default sine and light corresponding wave shape LED
    
          while( 1 )
          {
                // only check switches at start of each cycle
                if( !u )
                {
                      // process wave switch
                      // process freq switch
                }
    
                // set data and wait one sample period
                PORTD = p[u++];
                delayMicroseconds( g_period );
          }
    }
    

    Editar, compilar, transferir y probar el código. Si te sientes enérgico, agrega también un botón de decremento.


    This page titled 14.4: Parte Terza — Cambio de Frecuencia 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.