应急灯开关位置传感器

在每个设计自主应急照明系统的人之前或早或晚,都会出现打开和关闭应急灯的问题。如何以最方便,最透明的方式做到这一点,以免破坏带有附加开关的房间的设计?


削减的解决方案之一。


背景


在现代世界中,许多事情都与不间断电源联系在一起。如果没有24/7全天候运行的计算机和通讯,大多数类型的智力活动已经是不可想象的。这既不是好事也不是坏事,您需要忍受它。特别是如果您的工作场所不是在现代办公室中,而在现代办公室中装满UPS和备用柴油发电机,而是在普通住宅高层建筑的公寓中。碰巧的是,在前苏联的大多数城市中,住房能源供应的可靠性亟待提高。结果,连接家庭电源插座和最近的发电站的螺纹具有定期断开的不良习惯。每六个月一次,一天一次和三次。

这就是为什么在开始对新公寓进行维修时,我最初在该项目中铺设了并行布线,以实现不间断的电源和照明。

我非常想拥有一台带有ICE的强大发电机,能够为整个公寓提供正常的电力,但是我不得不放弃了这个想法。在私人住宅中,根本不会提出这个问题,但公寓则是另一套。

第一个问题是排出的废气,这些废气绝对没有地方可以放在带有中央供暖系统的房屋的公寓中。好吧,您不会将软管扔到窗户外面,因为烟雾会立即被吸入附近最近的邻居窗户。

第二个问题是噪音。是的,当您在街上听到现代的逆变器四冲程发电机时,它似乎非常安静。而且,如果您挂了一个附加的消音器,则完全静音。但是请相信我,断电意味着完全安静的公寓楼,即使是这样安静的隆隆声,所有邻居都能听到。

简而言之,与生成器有关的想法死了,很明显,没有诞生。在其余的实际选择中,仅剩余电池。

此外,我可以简单地叙述自己的情感磨难和第一人称的决定,而无需主张普遍真理。我立即警告说,我对某人的论点似乎令人信服,所作的决定是有争议的。但是,尽管如此,此处描述的所有内容当前都已在硬件中实现并成功执行了任务。

如果有人对问题的实际问题感兴趣,而无论我怎么过这样的生活,他都可以跳过许多字母,直接进入现成解决方案的描述。

简要介绍选择电池类型


尽管此问题与本文的主题无关,但我也想在此插入5美分。而且,在这样的陈述下不可避免地产生的讨论通常包含许多毫无用处的有用信息。

如今,在替代能源迅速发展的世纪中,与家用太阳能或风能微型发电站相结合的,价格相对可承受的工业生产的家庭不间断电源系统已经开始出现。他们中最先进的使用锂离子电池,并具有整个系统有效性的电子“增强器”。

以我为例,由于客观原因,关于任何小型发电厂的讨论都没有,只有“启蒙”时期从普通插座可再生的备用电源才是有趣的。因此,决定自己集体管理公寓UPS的所有电子填充物。由于我的双手完全没有束缚,因此需要确定的第一件事是要使用哪种类型的电池。

起初,我这样想:“为什么不锂?时尚新潮青年。密封,节能,耐用。”但是当我看价格时,我的锂热明显减弱了。结合学校算术进行的快速搜索显示,即使是(非常)最诚实的5000 mAH的“笨拙”中国26650银行,其成本也将是相同能量强度的酸性电池的五倍。而且,如果您没有从价格的底部按价格排序,则差异很容易达到8到10倍。

以及如何弥补如此巨大的价值差异?

是的,锂每千克和立方米能更有效地存储能量,而像香烟盒一样大的电池很容易制成10公斤铅酸电池。但是这个事实对固定使用是否如此重要?

当然,良好的“锂”,但采用正确的方法将持续更长的时间。但是十倍?

同时,另一方面,未知来源的电池的潜在爆炸性,更复杂的充电算法,处理问题(每个无家可归的人都知道在哪里使用铅电池来牟利,而锂则要复杂得多)。

简而言之,考虑到这些因素的总和,我决定将锂的概念推迟到下一次迭代。也许几年后会发生变化,但是就目前而言,铅是我们的一切。

, , . , , , .

至于铅蓄电池,也有几种选择。 “正确”的选择是专用的备用电池,这些备用电池用于UPS,蜂窝通信基站以及其他类似地方,要求它们能够承受和传递大电流,偶尔会产生强烈的放电,并且在工作时不会分散人们的注意力服务。

另一个“正确”的选择是电动自动装载机或柴油潜艇的牵引电池。这些电池具有很高的耐久性,并且对深度放电具有良好的耐受性,同时不会失去所有者“加水”的机会。

好吧,“错误”的选择是离最近汽车厂的起动机电池。这样的电池可以短暂地向山上释放一千瓦的电能,但是任何放电都会给山峰带来压力,就像懒惰的人一样。一次性电池的荣耀通常根植于现代钙电池中:“放电-变化”。

好吧,那么,我希望没有人怀疑我最终选择实施哪个选项?对,第三。这里的原因不仅在于著名的疣状两栖动物,而且在于简单的实用计算。入门电池的价格比所有其他选件便宜几倍,有时其价格接近锂。而且,当您更换食物会导致两星期的食物消耗量减少时,在健康无动于衷的情况下,您不会感到费解,不用晃动昂贵的电池。此外,如果您不让这些电池在失去脉冲之前就用完,那么可以算出它们在备用电池中的使用寿命为多年。您总是可以放一个更大容量的电池,将放电极限设置得更小,并且仍然可以获得比“正确”电池更好的价格/容量比,遭受深重的痛苦。

此事的主要目的是至少在电池安装位置的“墙壁上的孔”处提供通风。好吧,组织常规的短路保护,因为电流源的低电压会降低警惕性,并且一切都可能会非常糟糕地结束。

单身公寓目前的战争


经过对电池类型最终决定的道德保证后,一个新的理由立即出现了仔细思考的问题。

让我提醒您,起初我想获得一个230 V的交流发电机。但是,在与客观现实相适应并在思维上转向电池后,思维的惯性已经将我带到了中国知名的在线市场,以便选择合适的DC-AC转换器。在研究特性的过程中,被遗忘的“修正正弦波”一词首先浮出水面,然后常识开始引发令人不安的问题。

问题的实质如下。用小电池来满足公寓的所有能源需求仍然无法正常工作。锅炉,微波炉,洗衣机,功能强大的台式计算机仍然无法承受电能。由于这种正弦波的修改,冰箱,抽油烟机甚至是平扇也将无法正常工作。当然,有些逆变器具有正弦波,但它们不仅更昂贵,而且效率更低。而且强大的消费者问题仍然没有解决。

哪些消费者仍在预算范围内?它们的数量并不多:诸如笔记本电脑和电话/平板电脑之类的消费电子产品,路由器,ARM服务器,心情就像一台电视,当然还有照明。此外,本文的初衷(以及我的个人动机)恰恰是要确保笔记本电脑形式的备用工作站的功能以及最小的家庭舒适度,例如厕所的光线。

几乎所有这些设备都需要5到21 V的恒定电压才能工作,因此没有客观的需要首先将电池电压提高到230 V AC,然后再降低并再次将其拉直至正负初始水平。在这些转换中,很容易损失多达50%的能量,而我一点都不笑。

简而言之,这就是使用低压直流网络作为替代方案的想法的顺利实施。在对导线中的损耗进行近似计算之后,最初的12(13.8)V变成了更实用的24(27.6)V。

起初,我甚至想摄取36(41.4)B的热量,但是在研究了计划用于这种经济环境的某些电子元件的特性之后,我不得不节制食欲。

因此,在横截面为3.5 mm 2的纯铜的替代布线中,最终从串联的两个汽车电池施加电压。

, . ( ) . , .

为了使消费者能够进入房间,每个“大”网点增加了一个网点。因此,没有人会把普通插座与备用插座混淆,因此安装了具有不同宽度扁平触点的“美国”型产品。首先,这将不允许真空吸尘器连接到直流网络,其次,与欧洲的插座不同,这种插座在使用“正确”的插头时始终使用相同的极性。


对于这些插座,制造了笔记本适配器,将电压降低到所需的18-20 V,并配备了相应的连接器。很明显,脚跟都是由通常的5伏USB电荷制成的,可用于所有小物件。好吧,以防万一,购买了一对小型的24/230 V转换器,功率分别为50和200瓦。

从中国订购了使用美国插头,相同的电源线以及现成的脉冲降压转换器板进行充电的外壳。仅需使用烙铁来连接导线。

我不会在本文中谈论发电厂,特别是因为其中没有任何有趣的内容,因此我将直接探讨“底层”问题之一,即利用电池累积的能量用于照明的问题。

灯光


因此,在将公寓与主配线平行布线时,会画出电压为24(27.6)V的备用DC网络的电线,其中,将由一对这样的电线组成的回路缠绕到每个开关盒中,然后再与230 V网络电线一起缠绕引至天花板灯(如果房间中有几盏,则替代电线仅引出一根)。

在灯管领域如何处理DC网络的输出是一个单独的方法。作为光源,选择了常规的24伏LED灯带。根据固定装置的设计,其不同长度的段(与房间的面积成比例)可以直接安装在它们的外壳中,也可以粘在可以很好发光的表面上,也可以从不那么引人注目的地方粘上。

无论如何,这比技术问题更具美感,而现在又是另一回事了。

因此,在每个开关的框中,我都有一个230 V网络的相线回路,用于接通“普通”灯,以及一个直流网络的两线回路,用于应急照明。从此而舞。

最后,摆在我面前的挑战是创建一个可以将三种状态彼此分离的设备:

  • 交流电源正常→关闭应急灯。
  • 交流网络断电,开关触点闭合→打开应急灯。
  • 交流网络断电,开关触点打开→关闭应急灯。

我们认为这些动作是单方面的,例如,关闭已经关闭的灯光不会改变任何东西。如果解决了确定交流网络操作和开关触点位置的任务,则可以将这些状态彼此区分开。同时,我具有以下初始条件:

  1. 主照明灯和应急照明灯的管理对一个管理机构应该是完全透明的,没有其他按钮的“集体农场”。
  2. , , , .
  3. , .
  4. – , .
  5. - .
  6. ( ), .
  7. .

当然,零箱中没有交流电源是一个非常复杂的设计计算错误。没有它,就不可能确定开关输入端是否存在电压。从技术上讲,可以将直流网络的负极线设为零,但这与我的职权范围中完全不可破坏的第5段相矛盾。

传感器


尽管如此,仍然存在测量电源电压的替代方法。对于我来说,不必确定电压的存在,而在开关的触点闭合的情况下,确定相线中的电流就足够了。毕竟,如果有电流,那么就有电压。此外,精确读取电流可以使紧急照明不仅在断电的情况下启动,而且在灯泡老化的情况下也可以启动。此方法不允许在交换机关闭时确定AC网络的状态,但是这种状态不会打扰我,因为一旦开关断开,应急照明灯也将不工作。

确定导线中电流的存在非常简单,尤其是在该电流是交流电的情况下。例如,您可以在此处应用霍尔传感器,该传感器检测电线周围的磁场。但是,您可以使用一个普通的电流互感器,它由带绕组的环形磁路组成。交流电线穿过该环,从而在磁路中产生磁场。该场继而在绕组中感应出一个次级电流,该次级电流与导线中的初级电流成比例。因此,这种简单的设备使您能够测量任何电线中的交流电强度,而不会断开它,并且通常与主电路之间没有任何电流连接。

电流钳对任何电工来说都是非常有用的工具,其工作原理是相同的。

如果开关附近有这样的变压器,则足以测量其次级绕组上的电压,以找出灯电路中是否有电流在流动。正如我所说,电流的近似表示有两个事实:网络230 V中存在电压,并且开关闭合。这些事实中的第一个对于应急照明激活装置的操作是必不可少的。

我将来的设备的第二个“输入参数”应该是开关触点的位置。

业余同事之间发起的“头脑风暴”带来了确定开关位置的各种选择,主要归结为修改设计以便为其添加另一对触点。这里的想象力范围很大。

可以采用双开关而不是单个开关,并机械地“并联”其两半,以使它们成为一个整体。此选项在外观上没有什么不同,并且在那些照明为双回路且开关最初为双回路的房间中并没有解决问题。

其他选择包括在开关机构中引入带有磁体的微型开关或簧片开关。但是在研究了应用断路器的设计之后,这些选择也消失了。即使对于紧凑的微型开关,优质开关的整体式陶瓷基座也完全没有任何机会,并且由于键的行程太小以及簧片开关的操作/释放曲线中的磁滞,磁铁和簧片开关也无法工作。

«» , . , , , . , , , .

简而言之,有必要提出一种无需修改开关的方法。并找到了这种方法。

上面,我介绍了一个电流互感器,它使您能够确定电线中是否存在交流电而不会破裂和电接触。但是任何变压器都是双向设备(与同一个霍尔传感器相反),其初级绕组和次级绕组是可互换的。

如果我们向这样的变压器的次级绕组施加交流电,则它会在旋入环的导线两端感应出电压。而且,最重要的是,绕组从电源消耗的能量将取决于导线中的次级电流是否找到其圆周运动的方式。

在这里,它变得更加有趣。电线穿过环,该环在几厘米内立即连接到开关的触点之一。仅仅为该电流提供从开关的另一侧的返回路径,以便获得用于“感测”触点位置的装置。并且由于该电流是交变的原因,常规电容器可以成为其桥梁。


原则上,在某些条件下关闭该电路,不需要电容器。如果将足够高频率的电流“泵入”变压器,则导线之间的杂散电容将足以通过。


因此,组织这种检测器需要什么:

  1. 电流互感器。
  2. 变压器输出电压表。
  3. 高频交流电的来源。
  4. 电流表流过反向变压器的绕组。

在选择最佳参数方面,最困难的是变压器,在电流互感器模式下应提供频率为50 Hz的可接受电压,而在断路器状态的有效“传感”模式下,其在数百KHz频率下具有可接受的传输系数。该元素无法在用于对电子电路建模的程序中进行仿真,即使进行数学计算,也很难做到。我不得不拿起烙铁,花了数小时来驱动各种选择,以寻求最好的选择。

匝数和最佳负载电阻是根据经验选择的,而不是我没有错过最佳比率的事实。作为实验的结果,出现了以下结构:

  • 铁氧体磁芯,导磁率为10,000,尺寸为10x6x4毫米。
  • 用0.25毫米漆包线缠绕30圈。
  • 绕组的有效负载为1 kOhm。

磁导率非常大,也许可以使用5000个或什至2000个单位的圆环,但是我拥有足够数量的这些圆环。通常,在这种情况下的渗透率是一个折衷值。太低会使变压器不适合在50 Hz的频率下工作,而太高则会使数百kHz以上的频率损坏一切。

多次实验证实了该想法的真实性,并获得了以下结果:

  • 在电流互感器模式下,传输系数约为每瓦流动功率(电压220-230 V)一毫伏。
  • 在探针模式下,根据泄漏的频率和电容,开关的闭合触点和断开触点的电流差达到两到三倍。

就这样。这两个值对于可靠地固定流动电流和确定开关触点的位置都绰绰有余。它仅取决于特定的实现。


中铁


与大多数其他设计不同,此处已经处于审议的早期阶段,因此决定立即使用微控制器。根据需求和经验,选择权取决于ATtiny13A。该芯片具有ADC,并且能够使用1.1 V的内部基准源代替电源电压。有一个非常适合生成探测信号的PWM。而且,后来证明很重要的是,有一个EEPROM可让您存储校准数据。

, « , , - «» , ».

. . . , , , .

在这里,您需要至少结合使用发电机,电压表和某种触发器来存储两次测量之间的当前状态。通常,任何推测性草图仅在“核心”上至少需要三种情况,并且该控制器被证明更加实用。

负载为10-20 W的电流互感器的电压为10-20 mV,该电压太小而无法将其提供给DAC输入,甚至限制为1.1V。因此,除了控制器之外,您还需要一个传输系数约为100的放大器,以至少增加信号电压高达数百毫伏。

通常,电流互感器输出信号的电压不仅取决于负载功率,还取决于其性质。例如,纯净的负载(例如Ilyich的灯泡)会产生毫伏级的正弦波。具有简单脉冲功率的相同功率的LED灯泡可使电压达到或更高的短脉冲串。我们可以玩这个游戏,但是,首先,我想制造一个通用设备,其次,在公寓里,有一盏灯带有一个配备PFC电路的外部电源单元(也就是说,其消耗特性接近于活动状态)。

我不会用中间选项来折磨读者,而是立即给出设备的最终示意图。


此处,通过经济型线性稳压器LM2931-5.0的直流电压为控制器供电。就外壳,功能和引出线而言,该稳定器与流行的78L05相似,但与之不同之处在于更低的固有功耗(在10 mA的负载下约为500μA)和对输入电压短脉冲的更大容忍度。如果计划使用不超过20 V的电压,则可以使用更经济的LP2950-5.0类似物。

LP2950-5.0 30 . 24 . - , , , , . 50%, 100%.

变压器未在图中显示,但其绕组连接到引脚TR1和TR2。

作为切换负载的关键元件,使用了低电流P沟道MOS晶体管2SJ196(对于任何LED灯来说,最高1 A的电流都应足够),但是可以使用任何其他适合引脚排列,最大电流和最大漏极电压的电流。

除了控制器和有源元件的按键外,还使用了两个晶体管。需要一种在紧急电源的电压下控制钥匙的快门的装置。第二个用作来自电流互感器输出的放大器信号。

在这一点上,您可以使用运算放大器,但就细节而言,增益极小,您将不得不忘记工作在几百千赫兹以上的频率。

馈送给ADC的不是来自变压器本身的放大信号,而是其包络,可以通过单个样本进行测量,并且在一段时间内不能通过“流式”数字化来测量。为了隔离包络,使用了两个肖特基二极管,它们根据倍压电路连接。这样的包含物形成了经典的幅度检测器,其中二极管自身两端的电压降得到了很大的补偿。

传感器的工作原理很简单。首先,考虑测量导线电流所需的动作算法。

在电流测量模式下,PB0引脚进入输出模式,并通过逻辑零接地。这样可以防止将来自控制器的任何信号发送到点TR1。并行地,在引脚PB3上执行相同的操作,其结果是电容器C2的顶部输出接地。该电容器与电阻器R1一起构成一个截止频率约为1500 Hz的低通滤波器。由于使用了该滤波器,大大降低了各种高频噪声在形成被测信号中的作用。

然后,将高电平施加到PB4,为信号放大器供电。瞬变完成后,来自变压器输出的50 Hz电流被放大并到达整流器,在此它为电容器C8充电。

使用ADC1测量电容器C8的电荷,并从获得的电压值得出关于流经变压器的“初级”电流的结论。

主动感应的执行方式有所不同。首先,将PB0引脚转换为PWM求解器,并以数百千赫兹至兆赫兹的频率向其馈送信号。该信号被电阻分压器稍微衰减,并在点TR1馈入电流互感器绕组。电容器C1与分压器R4的一个上臂共同构成一个截止频率约为1.5 MHz的低通滤波器,从而降低了矩形脉冲产生的高频谐波的水平。

通过变压器绕组后,来自点TR2的探测信号到达相同的放大器和检测器,同样,最后将电容器C8充电至与变压器“外部”电路中的负载成比例的电压。以相同的方式,使用微控制器的ADC测量电容器的电荷。

现在解释一些“松散”。

电阻R5旨在限制电源开关栅极的电压,对于低压MOSFET而言,该电压通常不应超过20 V.在我的情况下,DC网络的电压最高为30 V,这表明需要使用1:3分压器,该分压器与R3一起获得。当从低于20 V的电源供电时,不需要电阻R5(由跳线代替)。

电容器C4和C5并联连接,以实现2 F的电容。这对电容器值得注意,因为它必须同时很好地传输低频和高频信号。在这里,可以使用几个微法拉的电解电容器和一个一百或两个纳法拉的陶瓷的并联连接,但是与微法拉的“陶瓷”相比,如此小容量的“电解质”在尺寸上不会增加​​。没错,不可能以2微法拉的价格购买陶瓷电容器,因此我将其中两个放在了相同的位置。

, 50 2 , . , . . 100 , , . .

电阻R4和R1形成一个分压,该分压将PWM输出处的五伏交流电压与电流互感器的输出电压大致相等。

如前所述,电容器C8累积待测电压。最好是漏电流最小的高质量电容器。

特别值得一提的是,连接到微控制器复位引脚的两针“梳” TP1 / TP2。这些触点不仅用于重启,还用于进入校准模式,如下所述。简单地,在实现所有愿望清单之后,控制器不再具有空闲引脚,并且在固件调试期间出现了添加一个简单控件的需求。因此,我必须为此使用控制器复位脚。

«» AVR RESET GPIO. , . , . , , , , RESET.

通常,电路非常简单,所有“魔术”都在微控制器的固件中实现。然而,在原型制造之后,事实证明用于“探针”的可靠操作的布线电容通常是不够的。事实证明,流经变压器的电流差与干扰水平相当,电路的运行变得不可靠。

因此,我不得不放弃自由悬挂在电线上的铁氧体环,并在设备的新版本中直接向板上添加高压电路,以方便进行探测任务。

这里的重点是增加一个专用电容器,使其导通,以最短的方式闭合通过开关触点到RF电流的路径。


电容器C10必须设计为至少千伏的电压,并且应根据折衷原则选择其电容,以使操作可靠性足以满足实际使用,并且使流经灯的杂散电容电流不会太大。在实践中,如有必要,您可以尝试使用该名称“玩转”。

在任何情况下,配备这种传感器的开关都不再被认为是理想的。而是,它类似于带有指示器的开关,因此,首先,它可能导致杂散光或劣质LED灯的闪烁,其次,它可能引起电击,尽管强度不强。因此,您无需使用照明布线,仅依靠墙壁上的开关,始终关闭入口处的“开关”即可。

而且,由于我仍然必须在板上添加交流网络的一部分,因此我在此处添加了两个截止电抗器,这不会让高频探测电流进入布线。探测电压频率的实际值可以达到几兆赫兹,作为无线电爱好者,我非常讨厌用我自己的双手来增加网络中的干扰量。

扼流圈L1和L2必须通电,并用明显粗细的线缠绕在哑铃或环形铁芯上。不能使用“电阻”轴向设计中的信号扼流圈。

现在,电流互感器的主要匝是一条穿过环的线,并焊接到板上的点TR3和TR4。最好屏蔽该导线,同时将屏幕连接到环两侧的TR5和TR6。

TR6 , . - , . , .


固件代码和组合的HEX文件以及电路和印刷电路板的布局都附在文章的末尾。

调谐检测器的算法很简单。每三秒钟一次,控制器将从深度睡眠中唤醒,进行测量,并在必要时在一个方向或另一个方向上更改控制键的状态。因此,对开关位置变化的反应可能会有长达三秒钟的延迟。不是很方便,但是这样做是为了节省备用电源的能量,其次,为了显着减小轮询间隔而不允许在不同的测量阶段进行瞬变的持续时间。最小间隔可以认为等于一秒,但随后电路几乎始终处于有源功耗模式。

好了,总而言之,关于配置。由于不同的传感器必须根据灯消耗的电流,长度和其他接线功能,干扰程度等在完全不同的条件下工作,因此无法在固件中放入通用的自适应参数集。因此,安装后的每个传感器都需要在现场进行一次校准。

每次打开时,没有校准数据时或在闭合触点TP1和TP2之后,传感器都会进入校准模式。五次闪烁的应急灯指示进入校准的第一阶段。

闪烁五次后,如果之前已将其打开,则将给出7.5秒的时间将开关设置到“关闭”位置。在这段时间之后,将测量AC网络中始终存在的干扰水平。所获得的值用作占空比测量的起点。同样在此刻,以不同的频率感测断路器,以便随后选择最“对比”的频率。

接下来,第二个校准阶段开始,应急灯闪烁两次。开关将开关拨至“ on”位置需要7.5秒的时间,在超时后,程序将测量灯泡消耗的电流。如果灯泡具有多个亮度级别,则在打开后,必须立即将其调到最小,以便将来传感器在任何可用级别下都能正常工作。

第三和最后校准阶段的开始以应急灯的三次闪烁为标志,并且要求开关保持在“ on”模式,并且照明网络必须以更高的水平(即,面板上的主断路器或次级断路器)断电。 7.5秒在这种情况下,执行已经以不同频率打开的断路器的第二次发声,并考虑到在第一阶段获得的值,选择通过通断开关的电流差最大的频率。

应急灯的一次闪烁表示成功完成了校准,如果在第三阶段之后仍然关闭了照明网络的电源,则在下一个调查操作周期中打开应急照明灯。

如果在不同条件下的电流和电阻的测量值太近且无法用于可靠检测,则校准将失败。在这种情况下,当开关位置不成功时,应急照明灯会闪烁两次,或者当标准照明灯消耗量太低时会闪烁三下。

如果传感器持续不愿意在最后两次闪烁时进行校准,则应尝试增加C10的容量。


该设备非常简单,紧凑,可以安装在开关盒中,但并不是说它很容易配置。当然,它没有采用现代“智能家居”的组成部分,因为它没有5G,云控制功能,甚至没有提供带有GPS的普通WiFi。然而,这些设备中有八个执行其唯一的功能,并且在停电条件下不需要它们。

固件源代码(Atmel Studio 7)
#define F_CPU 9600000 //   (  : avrdude.exe -U lfuse:w:0x7a:m -U hfuse:w:0xff:m)

#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>

//#define PROTEUS

typedef unsigned char bool; //   
#define true  (0 == 0)
#define false (0 != 0)

#define MAX_U10BIT 0b0000001111111111 //      

#define INTERVAL         3   //  , 
#define CUR_MINIMAL_DIFF 50  //      , LSB
#define RES_MINIMAL_DIFF 50  //      , LSB
#define FREQ_DIV_OFFSET  2   //     
#define FREQ_MAXIMAL_DIV 6   //     

EEMEM unsigned int  EEPROM_cur_edge;
EEMEM unsigned int  EEPROM_res_edge; 
EEMEM unsigned char EEPROM_frequency_dividor;

unsigned int cur_edge, res_edge; //   ,   EEPROM    
unsigned char frequency_dividor; //   ,   EEPROM    
unsigned char clk = 0; //   watchdog
bool tp_reset = false; //   TP1  TP2

//   
static void init_vars(void) {
  if(MCUSR & (1 << EXTRF)) { // ,       TP1  TP2
    tp_reset = true;
    MCUSR &= ~(1 << EXTRF); //  EXTRF       ,   
  }
}

//  
static void init_pins(void) {
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB4); //       
}

//    watchdog
static void init_interrupts(void) {
  sleep_enable(); //   

  WDTCR = (1 << WDCE) | (1 << WDE); //  watchdog
  WDTCR = (1 << WDTIE) | WDTO_1S; // watchdog      ,  1 

  sei(); //  
}

//   
void init_settings(void) {
  cur_edge = eeprom_read_word(&EEPROM_cur_edge); //   
  res_edge = eeprom_read_word(&EEPROM_res_edge); //   
  frequency_dividor = eeprom_read_byte(&EEPROM_frequency_dividor); //   
}

//   
static void toggle_load(bool state) {
  if(state) {
    PORTB |= (1 << PB1);
  } else {
    PORTB &= ~(1 << PB1);
  }
}

//  
static void blink_load(unsigned char count) {
  for(unsigned char i = 0; i < count; ++i) {
    _delay_ms(200);
    toggle_load(true);
    _delay_ms(200);
    toggle_load(false);
  }
}

//   (   )
static void stop(void) {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  while(true) sleep_cpu();
}

//   
static void toggle_amp(bool state) {
  if(state) {
    PORTB |= (1 << PB4); //     PB4
    _delay_ms(250);      //       200 .
  } else {
    PORTB &= ~(1 << PB4);
  }
}

//  
static void toggle_lpf(bool state) {
  if(state) {
    DDRB |= (1 << PB3); //  PB3    (  "0")     C2
  } else {
    DDRB &= ~(1 << PB3); //  PB3    ( )   C2  
  }
}

//    
static void toggle_gen(bool state) {
  if(state) {
    TCCR0A |= (1 << COM0A0) | (1 << WGM01); //    ( )    OC0A      OCR0A
#ifndef PROTEUS
    TCCR0B |= (1 << CS00); //    1
#else
    TCCR0B |= (1 << CS00) | (1 << CS02); //    1024
#endif
    OCR0A = FREQ_DIV_OFFSET + frequency_dividor; //   ,         OC0A
  } else {
    TCCR0A = 0; //  
  }
}

//  
static void toggle_adc(bool state) {
  if(state) {
    DDRB &= ~(1 << PB2); //  PB2    ( )
    ADMUX = 0b01 | (1 << REFS0); // PB2, 1.1v reference
    ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | //       = 128 (75 )
             (1 << ADIE) |  //    
             (1 << ADEN);   //  
  } else {
    ADCSRA = 0; //  
    DDRB |= (1 << PB2); //  PB2    (  "0")   C8
    _delay_ms(50); //      C8
  }
}

//  
static unsigned int do_adc(void) {
  set_sleep_mode(SLEEP_MODE_ADC); //   "" 
  do {
    sleep_cpu(); //      ,      ,   
  } while(ADCSRA & (1 << ADSC)); //        ,  

  return ADC;
}

/*
//  
static void blink_bin(unsigned int value, unsigned char count) {
  for(unsigned char i = 0; i < count; ++i) {
    _delay_ms(1000);
    toggle_load(true);
    if(value & (1 << (count - i - 1))) {
      _delay_ms(500);
    } else {
      _delay_ms(50);
    }
    toggle_load(false);
  }
}
*/

//   
static unsigned int get_current(void) {
  unsigned int cur;

  toggle_lpf(true); //  
  _delay_ms(150);
  toggle_adc(true); //  
  _delay_ms(50); //    C8
  cur = do_adc(); //  
  toggle_adc(false);
  toggle_lpf(false);

  return cur;
}

//    
static unsigned int get_resistance(void) {
  unsigned int res;

  toggle_gen(true); //  
  _delay_ms(150);
  toggle_adc(true); //  
  _delay_ms(50); //    C8
  toggle_gen(false); //   ,   C8     
  res = do_adc(); //     
  toggle_adc(false);

  return MAX_U10BIT - res; //      ,      
}

//   
static bool is_current(void) {
  return (get_current() >= cur_edge);
}

//   
static bool is_toggled_on(void) {
  return (get_resistance() <= res_edge);
}

//  
static void do_main(void) {
  toggle_amp(true); //  

  if(is_current()) {
    toggle_load(false); //  ,  
  } else {
    if(is_toggled_on()) {
      toggle_load(true); //  ,  
    } else {
      toggle_load(false); //  ,  
    }
  }

  toggle_amp(false); //  
}

//    
static bool first_on(void) {
  return (frequency_dividor == 0xff); //   EEPROM   0xFF,        FREQ_MAXIMAL_DIV
}

//  
static void calibrate(void) {
  unsigned int cur_off, cur_on, res_off, res_on, res_on_tmp, res_off_array[FREQ_MAXIMAL_DIV + 1], diff, max_diff, frequency_dividor_tmp;

  blink_load(5); //    ,    
  _delay_ms(7500); //      

  toggle_amp(true); //  

  cur_off = get_current(); //      ( )

  //      
  for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
    res_off_array[frequency_dividor] = get_resistance();
  }

  blink_load(2); //     
  _delay_ms(7500); //      

  cur_on = get_current(); //     

  blink_load(3); //     
  _delay_ms(7500); //      

  
  res_off = MAX_U10BIT;
  res_on = MAX_U10BIT;
  frequency_dividor_tmp = 0;
  max_diff = 0;
  //      
  for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
    res_on_tmp = get_resistance();

    //   ,      
    if(res_off_array[frequency_dividor] > res_on_tmp) {
      diff = res_off_array[frequency_dividor] - res_on_tmp;
      if(diff > max_diff) {
        res_off = res_off_array[frequency_dividor];
        res_on = res_on_tmp;
        frequency_dividor_tmp = frequency_dividor;
        max_diff = diff;
      }    
    }
  }
  frequency_dividor = frequency_dividor_tmp;

  toggle_amp(false); //  
  
  if(cur_on > cur_off + CUR_MINIMAL_DIFF) { 
    cur_edge = cur_off + (cur_on - cur_off) / 2; //    ,     
 
    if(res_on + RES_MINIMAL_DIFF < res_off) {
      res_edge = res_off - (res_off - res_on) / 2; //    ,      

      //   
      eeprom_write_word(&EEPROM_cur_edge, cur_edge);
      eeprom_write_word(&EEPROM_res_edge, res_edge);
      eeprom_write_byte(&EEPROM_frequency_dividor, frequency_dividor);
      
      blink_load(1); //  
    } else {
      blink_load(2); //    
      if(first_on()) stop();
    }
  } else {
    blink_load(3); //    
    if(first_on()) stop();
  }
}

ISR(WDT_vect) {
  WDTCR |= (1 << WDTIE); //    watchdog   ""    
}

EMPTY_INTERRUPT(ADC_vect); //     ,     

int main(void)
{
  init_vars();
  init_pins();       
  init_interrupts(); 
  init_settings();

  if(tp_reset || first_on()) {
    calibrate(); //          
  }

  //  
  while(true) {
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_cpu(); //   watchdog

    if(++clk >= INTERVAL) {
      do_main(); //  
      clk = 0;
    }
  }
}


包含图表,接线和完整的Atmel Studio 7项目的存档

All Articles