Saltar al contenido principal
LibreTexts Español

24.2: Un ejemplo práctico: conmutador Round-Robin

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

    Echemos un vistazo a cómo podríamos implementar un switch round-robin. Un switch round-robin funciona de manera circular; paso a través de una secuencia de ajustes y volviendo al inicio una vez que se completa la secuencia. Para nuestro ejemplo, consideraremos un solo pulsador de contacto momentáneo que recorre una serie de velocidades del ventilador: apagado, bajo, medio y alto. El sistema arrancará en el estado apagado y cada pulsación sucesiva del botón avanzará el estado de bajo a alto. Una vez que se alcanza el nivel alto, una prensa adicional hará que el estado vuelva a apagarse y el proceso continúa de la misma manera. Cada ajuste de velocidad se indicará a través de su propio LED y solo un LED estará encendido en un momento dado. Para mantener limpio el código, solo examinaremos el pulsador y la parte LED del código y no consideraremos cómo podríamos controlar la velocidad de un ventilador. Además, asumiremos que se incorpora un circuito de desrebote de hardware apropiado con el pulsador (por ejemplo, un Schmitt 74HC14 con red RC) y que al presionar el botón se producirá un alto lógico en nuestro pin de entrada Arduino. Además, se supone que los controladores LED están activos altos (es decir, un alto en un pin de salida del Arduino encenderá un LED).

    Lo primero que hay que recordar es que un microcontrolador típico es capaz de verificar el estado de un pin de entrada en una fracción de un microsegundo. En consecuencia, no podemos simplemente verificar el pin para ver si es un alto lógico, y si es así, pasar a la siguiente velocidad del ventilador. En la fracción de segundo tardaría en que un humano presione y suelte el botón, el microcontrolador podría recorrer el código de verificación de pines miles de veces, ciclando la velocidad del ventilador para cada comprobación de bucle. La velocidad final del ventilador sería una conjetura de cualquiera. En cambio, tenemos que buscar la transición de baja a alta ya que esto ocurre solo una vez con cada pulsación de botón. En efecto, esto también señala la necesidad de desrebotar el switch. Sin ella, una sola prensa podría resultar en más de una docena de bordes bajos a altos debido a la vibración del contacto del interruptor que resulta en una configuración aleatoria del ventilador. Una forma de realizar la detección de borde positivo es usar dos variables, una para el estado actual del conmutador y una segunda para el estado anterior del conmutador (es decir, el estado que se midió inmediatamente antes del estado actual). Si el estado actual es alto (activado o presionado) y el estado anterior es bajo (apagado o no presionado) entonces sabemos que hemos detectado un borde positivo y podemos realizar nuestro procesamiento. Tenga en cuenta que en la siguiente iteración del bucle, el estado actual será alto pero el estado anterior ahora también será alto (es decir, el estado actual anterior de la iteración del bucle anterior) y por lo tanto no se realiza ningún procesamiento. En este punto no importaría cuánto tiempo el usuario mantenga el dedo en el botón ya que todos los estados actuales y anteriores serán altos. Cuando el usuario finalmente suelte el botón el estado actual será bajo con un estado previo de alto. Esta es la ventaja negativa y, de nuevo, no tenemos necesidad de procesarla. La siguiente iteración de bucle producirá estados actuales y anteriores de bajo sin necesidad de procesar. Eventualmente, cuando el usuario vuelva a presionar el botón veremos un estado actual de alto con un estado previo de bajo y procesaremos este borde.

    Necesitaremos tres variables para los estados, a saber, los estados de pulsador actual y anterior y otra para los LED (que indican la velocidad del ventilador). Los pulsadores pueden considerarse como variables booleanas (0/1) mientras que la variable LED oscilará entre 0 y 3, siendo 0 “apagada” y 3 “alta”. Por simplicidad, usaremos globals para estos, los caracteres sin firmar funcionarán muy bien. Los cuatro LEDs se posicionarán fuera de los pines 8 a 11 de Arduino Uno (PORTB. 0:3) y el pulsador se conectará al pin 12 (PORTB.4). La función setup () tendrá que establecer las direcciones adecuadas para estos bits de puerto. Esto requeriría cinco llamadas separadas a PinMode () pero solo dos operaciones simples si operamos directamente. Aquí está la porción inicial:

    // declare the state variables and initialize at 0 for “off”
    unsigned char currentstate=0;
    unsigned char priorstate=0;
    unsigned char ledstate=0;
    
    // define some values for the LED/fan state. These are conveniently
    // chosen to be the FAN/LED bit positions in PORTB
    #define FAN_OFF     0
    #define FAN_LO      1
    #define FAN_MED     2
    #define FAN_HIGH    3
    
    // declare bit masks for the four LED bits and pushbutton
    #define LED_OFF  0x01
    #define LED_LOW  0x02
    #define LED_MED  0x04
    #define LED_HIGH 0x08
    #define PBUTTON  0x10
    
    // the LED_XXX masks could also be defined like so:
    // #define LED_OFF (1<<FAN_OFF)
    
    // a convenience
    #define LED_ALL  (LED_OFF|LED_LOW|LED_MED|LED_HIGH)
    
    setup()
    {
        // set for output
        DDRB |= LED_ALL;
        // set for input
        DDRB &= ~PBUTTON;
    
        // light up the “off” LED
        PORTB |= LED_OFF;
        // by default, outputs are 0 but if for some reason the other
        // LEDs could be on, be sure to set them to off before continuing
        // PORTB &= ~(LED_LOW|LED_MED|LED_HIGH);
    }
    

    El código de bucle necesita verificar el par correcto de estado/estado anterior actual. Si no se encuentra, no hay nada que hacer. Si se encuentra, necesitamos apagar el LED existente y luego verificar el estado actual del LED/ventilador, incrementar al siguiente estado y encender ese LED. Hay un par de formas de hacer esto. La primera es quizás una solución obvia usando una construcción de interruptor/caso. El segundo método es un poco más compacto (juego de palabras intencionado).

    loop()
    {
        // get current button state
        currentstate = PINB & PBUTTON;
    
        // do we have a positive going edge?
        if( currentstate && !priorstate )
        {
            switch( ledstate )
            {
                case FAN_OFF:
                    PORTB &= ~LED_OFF;    // turn off old LED
                    PORTB |= LED_LOW;     // turn on new
                    ledstate = FAN_LOW;   // increment speed
                    break;
                    
                case FAN_LOW:
                    PORTB &= ~LED_LOW;
                    PORTB |= LED_MED;
                    ledstate = FAN_MED;
                    break;
                    
                case FAN_MED:
                    PORTB &= ~LED_MED;
                    PORTB |= LED_HIGH;
                    ledstate = FAN_HIGH;
                    break;
                    
                case FAN_HIGH:
                    PORTB &= ~LED_HIGH;
                    PORTB |= LED_OFF;
                    ledstate = FAN_OFF;
                    break;
            }
        }
    
        // update state for next loop iteration
        priorstate = currentstate;
    }
    

    La versión alternativa se puede encontrar en la página siguiente. Tenga en cuenta el ahorro de código. En lugar de verificar el estado de cada ventilador, apagamos todos los LED e incrementamos el estado del ventilador. Si el estado se incrementara más allá del máximo entonces nos aseguramos de establecerlo en el estado apagado. Este nuevo estado nos dice qué LED encender. Esta versión funciona con poco código debido a la forma en que elegimos nuestras posiciones de bits. Esto no siempre es posible (o incluso deseable) pero puede ser una técnica práctica de vez en cuando.

    loop()
    {
        // get current button state
        currentstate = PINB & PBUTTON;
    
        // do we have a positive going edge?
        if( currentstate && !priorstate )
        {
            // increment to new state
            if( ledstate < FAN_HIGH )
                ledstate++;
            else
                ledstate = FAN_OFF;
    
            // lazy: turn off all LEDs
            PORTB &= ~LED_ALL;
            // turn on new LED
            PORTB |= (1<<ledstate);
        }
    
        // update state for next loop iteration
        priorstate = currentstate;
    }
    

    This page titled 24.2: Un ejemplo práctico: conmutador Round-Robin 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.