我如何假体化UPS指示器

在调试期间

在90年代后期,我得到了UPS。它很漂亮,带有一个LED指示灯和一堆按钮,里面装有两块电池,可以当时(连同显示器一起)支持计算机的使用寿命,长达15分钟。当时在堪察加半岛的日子很艰难,灯光经常关闭,所以这个装置非常方便。我和他一起经历了整个能源危机,而且不止一次,他从突然断电的情况下保存了我的学期论文。而且,您可以将磁带录音机连接到它,然后用蜡烛点燃收音机或您喜欢的磁带,在便携式煤气炉上准备晚餐...

自然,UPS崩溃了。他的变压器第一次烧毁。不是一个很大的逆变器,而是一个很小的,可能用于测量网络中的电压。没有找到同一家工厂,我建立了一家自制工厂,该设备工作了一段时间。然后停下来。很长很长时间,我找不到原因。我必须焊接不同的零件,检查它们的性能,然后再焊接回去。找不到问题。损坏的设备掉到床下了几年,直到有一天,我想到将5伏电压直接施加到控制器上的想法。瞧瞧:内置扬声器发出哔哔声,LED指示灯上出现数字。他还活着!此外,这是技术问题:我用电压表在电源电路中走动,发现电路板上已经焊接了保险丝,看上去就像是电阻!保险丝(自然熔断)被更换,UPS投入使用。

不幸的是,我的维修和在床底下工作了两年没有花光设备。端口以某种无法理解的方式烧毁了控制器,导致绿色的“在线” LED发光以及所有数字段指示器的最低段。没什么可做的-我不得不接受。过了一段时间,我离开了堪察加半岛,我们的路开了。

多年过去了,到达探望父母的那一刻,在最远的角落,我发现了我最喜欢的不间断电池:被遗弃,肮脏,没有电池和橡胶脚。到那时,我已经在另一个城市购置了自己的住房,因此决定离开自己的庇护所,恢复其效率并将其用于预定目的。

挑战赛

首先,将UPS洗涤并干燥。然后,在某个无线电零件商店中,购买了合适的橡胶脚和新电池。令我惊讶的是,在同一家商店中发现了一个合适的变压器,以换取我的自制产品。几天的工作之后,经过冲洗,更新的不间断电池愉快地发出吱吱声,并开始为其新电池充电。一切都很好,但是指示器仍然无法正常工作。

修复它的想法是我以前想到的。在将七段指标的所有数字(和一些字母)绘制在笔记本纸上之后,我意识到可以通过其余状态确定最低段的状态。当其他LED不点亮时,绿色LED可以点亮。关于如何做到这一点有很多想法:从简单的ROM芯片到简单的FPGA。但是,由于我是学生,住在堪察加半岛,所以我没有机会获得比琐碎逻辑更复杂的东西。修复指标已推迟。

画段

这次我决定认真解决这个问题。翻遍垃圾箱后,我再也没有发现ROM,FPGA和CPLD。但是,Arduino Pro Mini或更确切地说是他与Ali Express的廉价中文克隆产品落入了手中。我买了Arduin是为了从创见公司制造一款基于WiFi SD卡的微型计算机。不幸的是,该卡在实验过程中死亡,带有微控制器的电路板保持空闲状态。没什么,我们发现了她一个新的挑战!

工作时间

动态显示在显示模块中实现:段信号对于所有四个指示器都是通用的,因此一次只能点亮其中一个。此外:就像第五个指示灯一样,也连接了三个LED。五个选择信号使您可以指定现在正在使用哪个指示器(或LED线)。这些选择信号以相当高的速度顺序扫描,并且由于视觉惯性,似乎所有指示灯都同时点亮。

首先,我想解决最简单的解决方案:一个普通的周期,检查六个工作段的信号,然后打开或关闭第七个工作段的信号。实际上,这只是ROM仿真,我一开始就考虑过。

为此,我必须将六个工作段连接到微控制器的输入,将一个不工作段连接到输出。

绘制了一个小数组,将输入的各种状态与输出和绕过该数组的循环进行比较后,我将所有内容加载到控制器中,并立即遇到问题:下部始终发光。首先想到的是:程序中的倾斜。但是,无论我看了多少代码,都没有发现错误。最后,人们发现我的周期与指标的切换完全没有同步。如果在选择一个指标的周期结束时读取分段的状态,则很有可能在下一个分段时点亮或降低下一个分段。一团糟。

我三思而后行,将五个指示器选择信号焊接到其余的免费Arduino输入上,将它们设置为生成中断,然后开始使用中断处理程序而不是循环。情况变好了,但是并没有解决问题。在正确的位置,该片段应燃烧,但是在应该熄灭的那些地方,没有明亮的残留辉光。

考虑了更多时间后,我决定,如果在所需状态的数组中对段进行的搜索周期花费的时间比指示器的燃烧时间长,则可能会出现这种效果。在这种情况下,我们还将退出阶段并管理下一个指标的细分。从选择信号接收中断到段控制命令的时间必须尽可能短。这只能以一种方式完成:从中断处理程序中删除决定段状态的代码,以最小的优先级在主循环中运行该代码,并将结果保存在一种全局缓冲区中。中断处理程序仅需读取此全局缓冲区的值并根据段的内容熄灭或点亮该段。最坏的情况我们只能在某个指标中改变细分市场的状态,但是我们不会进入下一个指标。

那是正确的决定。但是最终只有在我将决策周期与使用自旋锁的中断同步并在此周期内禁止中断处理之后,它才起作用。它不仅赚了,而且还应赚!

指标还有另一个问题:大多数时候它们只显示数字。但是,打开UPS后,测试过程开始,在测试过程中,除了数字外,还出现了两个单词:TEST和PASS。而且,如果可以将字母T,E和P简单地添加到有效字符数组中,而S与5s相同,那么从我的程序的角度来看,字母A从8个字符开始算是什么。决策周期只是在数组中找到合适的模式,然后画出底部。为了避免这种情况,有必要提出一些建议。

然后我想到了。在有关指标变化的信号到达期间,有必要确定其属于哪个指标,并将其分段的状态保存在为其专门分配的变量中。现在,我可以随时准确地一次确定所有四个指标的当前内容。并且,如果符号P,5和5分别显示在第一,第三和第四位,则第二个符号肯定是A,并且不需要点亮下部。为了以防万一,我还添加了对FAIL的处理,这是我从未见过的,但我希望它会出现。

一切,数字指示器结束。仅用于固定绿色的在线LED。但是,这让我感到惊讶……这个想法是这样的:绿色LED(在线)始终单独点亮。如果黄色(电池)或红色(电池低)LED亮起,则绿色不应亮起。因此,我们将这些LED的焊锡丝焊接到微控制器上,将一个简单的if()与逻辑“或”相加,一切都应该工作。但是事实证明,当黄色LED点亮时,实际上并不会持续点亮,而是快速闪烁。快速,但不足以让if()跳过并且不点亮绿色LED。事实证明,在网络上工作时,绿色LED会以全亮度点亮,但是切换到电池时,它会以一半的亮度燃烧,但仍会燃烧。好吧,我想我没问题,我将放一个简单的低通滤波器:我将切断所有快速闪烁,但仅留下与电池过渡相对应的慢速闪烁,反之亦然。对黄色LED闪烁时间的分析带来以下惊喜:提供给它的脉冲周期非常不稳定,可以达到很大的值。事实证明,滤波器应通过不高于0.5-1 Hz的信号。这不是很好,因为在更改在线信号时会有相当大的延迟,但这是可以容忍的。因为我们在更改在线信号时会有相当大的延迟,但这是可以忍受的。因为我们在更改在线信号时会有相当大的延迟,但这是可以忍受的。

我决定使过滤器非常简单。我们会定期监控50次黄色和红色LED的状态。如果其中之一燃烧,则我们将特殊计数器增加一。然后,我们检查计数器的值,如果控制LED在检查的时间的50%内点亮,则我们认为它是打开的,如果少则表明它是关闭的。在调试过程中,我不得不将此数字降低到10%。为什么-没弄清楚。

最终组装

一切都成功了!只需用双面胶带和胶枪将Arduino板精美地安装在UPS机箱中即可。



对于好奇:
结果代码
#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/zh-CN397697/


All Articles