Cómo protesté el indicador UPS

durante la depuración

A finales de los 90 obtuve UPS. Hermoso, con un indicador LED y un montón de botones, tenía dos baterías adentro y podía soportar la vida útil de mi computadora en ese momento (¡junto con el monitor!) Por hasta 15 minutos. Los tiempos en Kamchatka eran difíciles, las luces se apagaban regularmente, por lo que este dispositivo era muy útil. Pasé por toda la crisis energética con él, y más de una vez salvó mis documentos de término de la repentina pérdida de electricidad. Y también, podría conectar una grabadora y, a la luz de una vela, escuchar la radio o sus cintas favoritas, preparándose una cena en una estufa de gas portátil ...

Naturalmente, el UPS se estaba rompiendo. La primera vez que su transformador se quemó. No el que es grande y está en el inversor, sino uno pequeño, probablemente para medir el voltaje en la red. Al no encontrar la misma fábrica, configuré una propia y el dispositivo funcionó durante más tiempo. Entonces se detuvo. Durante mucho, mucho tiempo, no pude encontrar la razón. Tuve que soldar diferentes partes, verificar su rendimiento y soldarlas nuevamente. El problema no fue encontrado. El dispositivo destripado cayó debajo de la cama durante un par de años, hasta que, un buen día, se me ocurrió la idea de aplicar 5 voltios directamente al controlador. Y he aquí: hubo un pitido del altavoz incorporado y los números aparecieron en el indicador LED. ¡Estaba vivo! Además, es una cuestión de tecnología: caminé por el circuito de alimentación con un voltímetro y descubrí que se había soldado un fusible en el tablero, ¡que insolentemente parecía una resistencia!El fusible (quemado naturalmente) fue reemplazado y el UPS volvió a la vida.

Desafortunadamente, mi reparación y dos años debajo de la cama no desaparecieron para el dispositivo por nada. De alguna manera incomprensible, el puerto se quemó en el controlador, que fue responsable del brillo del LED verde "en línea" y del segmento más bajo de todos los indicadores de segmento digital. No hay nada que hacer al respecto, tuve que aceptarlo. Después de un tiempo, dejé Kamchatka y nuestros caminos se separaron.

Pasaron los años y, al llegar a visitar a mis padres, en la esquina más alejada encontré mi batería ininterrumpida favorita: abandonada, sucia, sin baterías y patas de goma. En ese momento, ya había adquirido mi propia vivienda en otra ciudad, por lo que se tomó la decisión de refugiarme en mí mismo, restaurar su eficiencia y usarlo para el propósito previsto.

Desafío

En primer lugar, el UPS fue lavado y secado. Luego, en cierta tienda de repuestos de radio, se compraron pies de goma adecuados y baterías nuevas. Para mi gran sorpresa, se encontró un transformador adecuado en la misma tienda, a cambio de mi producto casero. Un par de días de trabajo, y la batería ininterrumpida lavada y actualizada chirrió felizmente y comenzó a cargar sus nuevas baterías. Todo estaba bien, pero el indicador seguía sin funcionar.

La idea de arreglarlo me vino antes. Después de dibujar todos los números (y algunas letras) del indicador de siete segmentos en la hoja del cuaderno, me di cuenta de que es posible determinar el estado del segmento más bajo por el estado del resto. El LED verde se puede encender cuando otros LED no están encendidos. Hubo muchas reflexiones sobre cómo se podría hacer esto: desde un simple chip ROM a un simple FPGA. Pero, como era estudiante y vivía en Kamchatka, no tuve la oportunidad de adquirir algo más complicado que la lógica mezquina. La fijación del indicador fue pospuesta.

dibujar segmentos

Esta vez decidí abordar el problema en serio. Habiendo hurgado en contenedores, nuevamente no encontré en mí ni ROM, ni FPGA y ningún CPLD. Pero, Arduino Pro Mini, o más bien, su clon chino barato con Ali Express, cayó en manos de. Compré Arduin para hacer una mini computadora basada en una tarjeta WiFi SD de Transcend. Desafortunadamente, la tarjeta murió durante los experimentos, y la placa con el microcontrolador permaneció inactiva. ¡Nada, le encontramos un nuevo desafío!

Trabajo

La visualización dinámica se implementa en el módulo de visualización: las señales de segmento son comunes a los cuatro indicadores, de modo que solo uno de ellos se enciende a la vez. Además: como con el quinto indicador, también están conectados tres LED. Cinco señales de selección le permiten especificar qué indicador (o línea de LED) se está utilizando ahora. Estas señales de selección se escanean secuencialmente a una velocidad bastante alta y, debido a la inercia de la visión, parece que todos los indicadores están encendidos al mismo tiempo.

Al principio, quería sortear la solución más simple: un ciclo ordinario que verifica las señales de seis segmentos de trabajo y enciende o apaga el séptimo que no funciona. De hecho, esto es solo una emulación de la ROM, que pensé al principio.

Para hacer esto, tuve que conectar seis segmentos de trabajo a la entrada del microcontrolador y un segmento que no funciona a la salida.

Después de esbozar una pequeña matriz que compara los diversos estados de las entradas con la salida y el bucle que omite esta matriz, cargué todo en el controlador e inmediatamente tuve un problema: el segmento inferior siempre brillaba. Primer pensamiento: el cant en el programa. Sin embargo, no importa cuánto haya mirado el código, no se encontraron errores. Al final, se entendió que mi ciclo no estaba sincronizado de ninguna manera con el cambio de indicadores. Si leemos el estado de los segmentos al final del ciclo de selección de un indicador, entonces es probable que iluminemos o bajemos el siguiente segmento en el siguiente. El desastre

Sin pensarlo dos veces, solde cinco señales de selección de indicador a las entradas Arduino libres restantes, las configuré para generar una interrupción y comencé a usar un controlador de interrupciones en lugar de un bucle. Se mejoró, pero no resolvió el problema. En los lugares correctos, el segmento ardió como debería, pero en aquellos lugares donde se suponía que debía extinguirse, no había un resplandor residual brillante.

Después de pensar un poco más, decidí que este efecto puede aparecer si el ciclo de búsqueda en la matriz del estado deseado para los segmentos lleva más tiempo que el tiempo de grabación del indicador. En este caso, también salimos de nuestra fase y gestionamos el segmento del siguiente indicador. Es necesario que pase el menor tiempo posible entre el momento de recibir la interrupción desde la señal de selección hasta el comando de control del segmento. Esto solo se puede hacer de una manera: eliminar el código que toma la decisión sobre el estado del segmento del controlador de interrupciones, ejecutarlo en el bucle principal con una prioridad mínima y guardar el resultado en una especie de búfer global. El manejador de interrupciones solo tendrá que leer el valor de este búfer global y extinguir o iluminar el segmento, dependiendo de su contenido. En el peor de los casossolo podemos llegar tarde con el cambio en el estado del segmento en un indicador determinado, pero no subiremos al siguiente.

Esa fue la decisión correcta. Pero finalmente funcionó solo después de sincronizar el ciclo de toma de decisiones con una interrupción usando spin-lock y prohibí el procesamiento de interrupciones durante este ciclo. ¡Y no solo ganó, sino que ganó como debería!

Hubo otro problema con los indicadores: la mayoría de las veces solo mostraban números. Sin embargo, después de encender el UPS, comenzó el proceso de prueba, durante el cual, además de los números, aparecieron dos palabras más: TEST y PASS. Y si, las letras T, E y P simplemente se pudieran agregar a la matriz de caracteres válidos, y S fuera igual a 5, entonces la letra A no era nada, desde el punto de vista de mi programa, del ocho. El ciclo de decisión simplemente encontró el patrón apropiado en la matriz y dibujó el segmento inferior. Era necesario pensar en algo para evitar esto.

Y se me ocurrió. Durante la llegada de una señal sobre un cambio de indicador, es necesario determinar a qué indicador pertenece y guardar el estado de sus segmentos en una variable especialmente asignada para él. Ahora, en cualquier momento, puedo determinar con bastante precisión el contenido actual de los cuatro indicadores a la vez. Y si los símbolos P, 5 y 5 se muestran en el primero, tercero y cuarto, respectivamente, entonces el segundo símbolo es definitivamente A, y no necesita iluminar el segmento inferior. Por si acaso, también agregué el procesamiento de la palabra FAIL, que nunca había visto, pero que esperaba que apareciera.

Todo, con el indicador digital terminado. Solo queda arreglar el LED verde en línea. Pero aquí me esperaba una sorpresa ... La idea era esta: el LED verde (en línea) siempre se ilumina solo. Si los LED amarillo (batería) o rojo (batería baja) están encendidos, el verde no debería encenderse. Por lo tanto, soldamos cables de estos LED al microcontrolador, ponemos un simple if () con un "OR" lógico y todo debería funcionar. Pero resultó que cuando el LED amarillo está encendido, de hecho, no se ilumina constantemente, sino que parpadea rápidamente. Rápido, pero no lo suficiente para que if () salte y no encienda el LED verde. Resultó que cuando se trabaja desde la red, el LED verde se ilumina a pleno brillo, pero al cambiar a la batería, se quemó a la mitad del brillo, pero aún se quemó. Bueno, no es un problema, pensé, pondré un filtro de paso bajo simple:Cortaré todos los flashes rápidos, pero dejaré solo los lentos que corresponden a la transición a la batería y viceversa. El análisis del tiempo de parpadeo del LED amarillo trajo la siguiente sorpresa: el período de los pulsos que se le suministra es muy inestable y puede alcanzar valores bastante grandes. Resultó que el filtro debería pasar señales no superiores a 0.5-1 Hz. Esto no es muy bueno, ya que tenemos un retraso bastante grande al cambiar la señal en línea, pero es bastante tolerable.dado que tenemos un retraso bastante grande al cambiar la señal en línea, pero es bastante soportable.dado que tenemos un retraso bastante grande al cambiar la señal en línea, pero es bastante soportable.

Decidí hacer el filtro muy simple. Supervisamos 50 veces, a intervalos regulares, el estado de los LED amarillo y rojo. Si uno de ellos se quema, entonces aumentamos el contador especial en uno. Luego, verificamos el valor del contador, y si los LED de control se iluminan durante el 50% del tiempo verificado, entonces creemos que está encendido, y si es menos, entonces está apagado. En el proceso de depuración, tuve que reducir esta cifra al 10%. ¿Por qué? No lo descubrí.

asamblea final

¡Y todo funcionó! Solo quedaba montar bellamente la placa Arduino en la caja del UPS con cinta de doble cara y una pistola adhesiva.



Para los curiosos:
el código resultante
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>



#define AVG_TIME    50
#define THS_TIME    45


#define SD_SEG_A    _BV(0)
#define SD_SEG_B    _BV(1)
#define SD_SEG_C    _BV(2)
#define SD_SEG_D    _BV(3)
#define SD_SEG_E    _BV(4)
#define SD_SEG_F    _BV(5)
#define SD_SEG_G    _BV(6)
#define SD_LED_RED  SD_SEG_A
#define SD_LED_YLW  SD_SEG_C

#define LD_SEL_LED  _BV(0)
#define LD_SEL_1    _BV(1)
#define LD_SEL_2    _BV(2)
#define LD_SEL_3    _BV(3)
#define LD_SEL_4    _BV(4)

#define GET_SEL     (PINC & 0x1f)


#define SD_SYM_NONE (0)
#define SD_SYM_0    (SD_SEG_A | SD_SEG_B | SD_SEG_C | SD_SEG_D | SD_SEG_E | SD_SEG_F)
#define SD_SYM_1    (SD_SEG_B | SD_SEG_C)
#define SD_SYM_2    (SD_SEG_A | SD_SEG_B | SD_SEG_D | SD_SEG_E | SD_SEG_G)
#define SD_SYM_3    (SD_SEG_A | SD_SEG_B | SD_SEG_C | SD_SEG_D | SD_SEG_G)
#define SD_SYM_4    (SD_SEG_B | SD_SEG_C | SD_SEG_F | SD_SEG_G)
#define SD_SYM_5    (SD_SEG_A | SD_SEG_C | SD_SEG_D | SD_SEG_F | SD_SEG_G)
#define SD_SYM_6    (SD_SEG_A | SD_SEG_C | SD_SEG_D | SD_SEG_E | SD_SEG_F | SD_SEG_G)
#define SD_SYM_7    (SD_SEG_A | SD_SEG_B | SD_SEG_C)
#define SD_SYM_8    (SD_SEG_A | SD_SEG_B | SD_SEG_C | SD_SEG_D | SD_SEG_E | SD_SEG_F | SD_SEG_G)
#define SD_SYM_9    (SD_SEG_A | SD_SEG_B | SD_SEG_C | SD_SEG_D | SD_SEG_F | SD_SEG_G)
#define SD_SYM_E    (SD_SEG_A | SD_SEG_D | SD_SEG_E | SD_SEG_F | SD_SEG_G)
#define SD_SYM_P    (SD_SEG_A | SD_SEG_B | SD_SEG_E | SD_SEG_F | SD_SEG_G)
#define SD_SYM_T    (SD_SEG_D | SD_SEG_E | SD_SEG_F | SD_SEG_G)

#define GET_SYM     (~PIND & 0x7f)


#define BROKEN_SEG  (SD_SEG_D)



static uint8_t sd_symbols[] = {                 // list of known symbols
    SD_SYM_NONE,
    SD_SYM_0, SD_SYM_1, SD_SYM_2, SD_SYM_3, SD_SYM_4,
    SD_SYM_5, SD_SYM_6, SD_SYM_7, SD_SYM_8, SD_SYM_9,
    SD_SYM_E, SD_SYM_P, SD_SYM_T
};
volatile static uint8_t sel, symbol;            // current input signals
volatile static short fb0, fb1, fb2, fb3, fb4;  // display frame buffer


// display routine
ISR(PCINT1_vect) {
    sel = GET_SEL;
    symbol = GET_SYM;

    if (((sel & LD_SEL_LED) && fb0) ||
            ((sel & LD_SEL_1) && fb1) ||
            ((sel & LD_SEL_2) && fb2) ||
            ((sel & LD_SEL_3) && fb3) ||
            ((sel & LD_SEL_4) && fb4)){
        PORTD &= ~BROKEN_SEG;
    }
    else {
        PORTD |= BROKEN_SEG;
    }
}


//
// entry point
//

int main(void)
{
    int cur_time;
    int led_on_time;
    uint8_t last_symbol_1, last_symbol_2, last_symbol_3, last_symbol_4;
    int i;

    // setup GPIO ports
    DDRC = 0;
    DDRD = BROKEN_SEG;

    // setup pin change interrupt
    PCICR |= _BV(PCIE1);
    PCMSK1 |= _BV(PCINT8) | _BV(PCINT9) | _BV(PCINT10) | _BV(PCINT11) | _BV(PCINT12);

    cur_time = 0;
    led_on_time = 0;
    last_symbol_1 = last_symbol_2 = last_symbol_3 = last_symbol_4 = 0;
    fb0 = fb1 = fb2 = fb3 = fb4 = 0;

    while(1) {
        // sync with display strobe
        sei();
        while (sel == 0) {}
        cli();

        // if select one of segment indicator
        if (sel & (LD_SEL_1 | LD_SEL_2 | LD_SEL_3 | LD_SEL_4)) {
            // looking for displayed symbol
            for (i = 0; i < 14; i++) {
                uint8_t sd_symbol = sd_symbols[i];
                if ((symbol & ~BROKEN_SEG) == (sd_symbol & ~BROKEN_SEG)) {
                    short val;
                    if (sd_symbol & BROKEN_SEG) {
                        val = 1;
                    } else {
                        val = 0;
                    }

                    if (sel & LD_SEL_1) {
                        last_symbol_1 = sd_symbol;
                        fb1 = val;
                    } else if (sel & LD_SEL_2) {
                        last_symbol_2 = sd_symbol;
                        fb2 = val;
                    } else if (sel & LD_SEL_3) {
                        last_symbol_3 = sd_symbol;
                        fb3 = val;
                    } else if (sel & LD_SEL_4) {
                        last_symbol_4 = sd_symbol;
                        fb4 = val;
                    }

                    // PASS workaround
                    if ((last_symbol_1 == SD_SYM_P) &&(last_symbol_2 == SD_SYM_8) &&
                            (last_symbol_3 == SD_SYM_5) && (last_symbol_4 == SD_SYM_5)) {
                        fb2 = 0;
                    }
                    // FAIL workaround
                    else if ((last_symbol_1 == SD_SYM_E) &&(last_symbol_2 == SD_SYM_8) &&
                            (last_symbol_3 == SD_SYM_1) && (last_symbol_4 == SD_SYM_1)) {
                        fb1 = 0;
                        fb2 = 0;
                        fb4 = 1;
                    }

                    break;
                }
            }
        }
        // if select LED line
        else if (sel & LD_SEL_LED) {
            if (cur_time++ > AVG_TIME) {
                if (led_on_time < THS_TIME) {
                    fb0 = 0;
                } else {
                    fb0 = 1;
                }
                cur_time = 0;
                led_on_time = 0;
            } else {
                if ((symbol & (SD_LED_RED | SD_LED_YLW)) == 0) {
                    led_on_time++;
                }
            }
        }

        // reset sync flag
        sel = 0;
    }

    return 0;
}

Source: https://habr.com/ru/post/es397697/


All Articles