Es ist schĂ€dlich fĂŒr Licht oder wie man die Ladung einer Autobatterie hĂ€lt

Ich setze die Artikelserie zum Fahrradbau im Bereich des Niederspannungs-Stromkreismanagements fort. Dieses Mal werde ich ĂŒber ein GerĂ€t sprechen, das die tiefe Entladung einer Autobatterie durch verschiedene sekundĂ€re Verbraucher verhindert.


Eine der möglichen Folgen einer unkontrollierten Entladung.

Der Kauf des ersten Autos oder Motorrads ist ein wichtiger Meilenstein im Leben eines jeden Menschen, insbesondere eines Ingenieurs. Denn wer außer den offensichtlichen Vorteilen seines neuen Eisenpferdes achtet sofort auf seine nicht offensichtlichen Nachteile? Wer denkt sofort ĂŒber Verbesserungen und ErgĂ€nzungen des Standards nach? Wenn es sich um ein Auto aus dem oberen Segment und sogar um eine "modische" Marke handelt, scheint es natĂŒrlich zunĂ€chst so, als hĂ€tte es absolut alles. Aber wie die Praxis zeigt, widerlegt die Zeit in diesem Fall die ersten EindrĂŒcke. Wenn Sie ein Auto der Economy-Klasse kaufen, jucken Ihre HĂ€nde schon am ersten Tag buchstĂ€blich!

Der Wunsch, Ihr Auto mit verschiedenen elektronischen HilfsgerĂ€ten zu "stopfen", ist ganz natĂŒrlich. Bald nach der Umsetzung all dieser PlĂ€ne konfrontiert das Leben den Autobesitzer jedoch mit einer harten RealitĂ€t. Es stellt sich heraus, dass selbst die modernsten GerĂ€te, die auf der neuesten elementaren Basis gebaut wurden, immer noch sehr auf ElektrizitĂ€t bedacht sind. Und eine Autobatterie, die so riesig erscheint, ist ĂŒberhaupt kein Kernreaktor und kann sich innerhalb weniger Tage leicht unter dem Gewicht all dieser scheinbar harmlosen Verbraucher „hinsetzen“.

Um nicht weiter in abstrakte und hypothetische Situationen zu fließen, gehe ich direkt zu meiner Geschichte. Nach dem Kauf eines Autos war das erste der Wunsch, einen Registrar darin zu setzen. Dies geschah innerhalb kĂŒrzester Zeit, fast vollstĂ€ndig abhĂ€ngig von der Geschwindigkeit der Zustellung des Pakets von AliExpress. Es ist klar, dass die regelmĂ€ĂŸige Stromversorgung ĂŒber den ZigarettenanzĂŒnder Ă€ußerst unpraktisch war und der Rekorder ĂŒber einen 12/5-V-Impulswandler schnell eine stationĂ€re Verbindung zur nĂ€chsten Leitung des Bordnetzes herstellte. Und seitdem Um es milde auszudrĂŒcken, nicht gestern, dieser Konverter war nicht einmal modern, fĂŒr seine eigenen BedĂŒrfnisse, wie sich spĂ€ter herausstellte, aß er bis zu 21 mA Strom. Lassen Sie uns nun abschĂ€tzen, wie viel dieser Konverter nur eine neue und voll aufgeladene Batterie mit einer KapazitĂ€t von 60 Ah versorgen kann. Arithmetik ist extrem einfach und enttĂ€uschend.


In weniger als vier Monaten landet ein Konverter, der nicht mit irgendetwas beladen ist, die Batterie buchstĂ€blich auf Null. Wenn wir berĂŒcksichtigen, dass sich eine Batterie, die nicht ganz frisch ist, leicht als weniger verwitwet herausstellen kann und die Ladung nach der Stadt Pokatushki bei weitem nicht 100% betrĂ€gt, beginnt ein regnerischer Tag leicht innerhalb eines Monats mit einem Haken.

Und das ist alles, wiederhole ich, nur ein Spannungswandler. Ja, heute können Sie einen Konverter kaufen, der nur einen halben Milliampere fĂŒr seinen eigenen Bedarf benötigt, aber ich habe dieses Beispiel gegeben, um zu zeigen, wie langsam und sicher das Wasser einen Stein schĂ€rft, selbst wenn es geringfĂŒgig ist, aber stĂ€ndig wirkt, zieht der Verbraucher Energie aus dem, was so riesig zu sein scheint Batterie.

Wir gehen noch weiter, der Rekorder im FHD @ 30fps-Aufnahmemodus verbraucht fast 300 mA von der + 5V-Quelle, die nach der Konvertierung unter BerĂŒcksichtigung des Wirkungsgrads etwa 150 mA Strom aus dem integrierten Netzwerk liefert. Angenommen, der Wandler wird durch einen modernen ersetzt, und wir berechnen die Entladezeit nur mit diesem Strom.


Etwas mehr als zwei Wochen, aber in der Praxis - zehn Tage. Jetzt droht nach dem nÀchsten Urlaub oder der nÀchsten GeschÀftsreise die Aussicht auf Beleuchtung (und möglicherweise auf Batteriewechsel).

Und so passierte es mir: Als ich einen kurzen Zwangsurlaub machte, dachte ich nicht, dass in einer Woche oder so sogar ein Zentralschloss die TĂŒr fĂŒr mich nicht öffnen könnte.

Viele werden sagen, dass es ihre eigene Schuld ist, dass alles stromlos sein muss oder zumindest die Aufnahme beenden muss, und sie werden Recht haben. Aber das Leben ist das Leben, und die Erinnerung ist nicht dieselbe, und wie lange der kleine Krankenstand dauert, ist nicht immer im Voraus bekannt. Daher entstand sofort die Idee eines Leistungsschalters.

Es gibt natĂŒrlich eine Option, den Rekorder ĂŒber den ZĂŒndschalter mit Strom zu versorgen, so dass er nur unterwegs funktioniert, aber diese Option ist auch nicht sehr, weil Wenn das Auto auf dem Parkplatz stĂ¶ĂŸt, möchte ich die Chance haben, den TĂ€ter zu sehen. Kurz nach der Installation des Rekorders war das Auto mit mehreren weiteren GerĂ€ten unterbesetzt, darunter ein versteckter GPS-Tracker, der funktionieren sollte, wenn nicht bis zum Ende, dann zumindest bis „fast alles“ bereits vorhanden ist.

Im Allgemeinen ist fĂŒr mehrere Wochen der passiven Reflexion die Idee eines GerĂ€ts, das die Spannung des Bordnetzes steuern und auf diesen Daten basieren soll, um die Stromversorgung fĂŒr zwei Verbrauchergruppen zu steuern: sekundĂ€r (Rekorder, USB-Buchse) und einfach (GPS-Tracker und einige wenige) was).

Wie könnte das gemacht werden?


Die ersten virtuellen Prototypen des GerĂ€ts wurden auf Basis der analogen Komparatoren LM393N „gebaut“ und konnten alles, was ursprĂŒnglich geplant war, vom GerĂ€t empfangen. Das abstrakte Schema war ungefĂ€hr so.


Hier werden zwei Komparatoren zum Schalten von Lasten verwendet. Ein gemeinsamer Referenzspannungsgenerator, zwei Teiler, die die Schwellenwerte fĂŒr den Betrieb bestimmen, Umreifungskomparatoren, zwei Leistungsschalter. Die externe Bindung des fertigen GerĂ€ts ist wie folgt geplant.


Der PrimĂ€rschlĂŒssel bleibt lĂ€nger eingeschaltet als der SekundĂ€rschlĂŒssel, sodass der AbwĂ€rtswandler selbst ĂŒber ihn mit Strom versorgt wird. PrimĂ€rlasten werden direkt an den Umrichter angeschlossen. Der SekundĂ€rschalter pendelt die SekundĂ€rlasten, die sich bereits im + 5V-Stromkreis am Wechselrichterausgang befinden.

Was am Ende herauskam


Es scheint alles zu sein, was benötigt wird, aber wie so oft tauchten beim Gedanken an die Details Ideen fĂŒr alternative Implementierungen auf. Erstens enthielt die analoge Schaltung einen anstĂ€ndigen Berg diskreter Elemente, die Komparatorbetriebsmodi bereitstellen, und zweitens sollten die Auslöseschwellen mithilfe von TrimmwiderstĂ€nden eingestellt werden, was die Einrichtung erschwert und die Wahrscheinlichkeit schafft, von Ruckeln und Zeit wegzukommen. Am Ende wurde daher beschlossen, sich mit einer digitalen Implementierung zu befassen, die sich sowohl schematisch als auch im Setup als viel einfacher herausstellte und gleichzeitig enorme Möglichkeiten zur Verbesserung des Steuerungsalgorithmus eröffnete. In diesem Zusammenhang erwies sie sich vor allem als um eine GrĂ¶ĂŸenordnung wirtschaftlicher im Hinblick auf den Stromverbrauch.

Der ATtiny13A-Controller hat lediglich nach dem Herzen des GerĂ€ts gefragt, das neben Benutzerfreundlichkeit und Billigkeit immer noch in einem warmen, warmen Röhren-DIP-GehĂ€use fĂŒr Oldfag erhĂ€ltlich ist. Anfangs schienen die FĂ€higkeiten selbst eines so kleinen Controllers an allen Fronten ĂŒberflĂŒssig zu sein, von der Anzahl der Ein- / AusgĂ€nge bis zur Menge an Programm und RAM. Wie Sie wissen, kommt der Appetit jedoch mit einer Mahlzeit. Mit Blick auf die Zukunft möchte ich daher sagen, dass die endgĂŒltige Version des Falls alle Schlussfolgerungen des Mikroschaltkreises enthielt und nicht mehr als zwei Dutzend Bytes freien Programmspeicher vorhanden waren.

Um die Spannung des Bordnetzes zu messen, benötigte der Mikrocontroller nur einen Eingang, der mit dem ADC verbunden ist. Zwei weitere logische Ergebnisse waren die Verwaltung der Verbraucher. ZunĂ€chst bestand nach dem endgĂŒltigen mentalen Übergang zum „Digitalen“ der Wunsch, zwei kostenlose GPIOs an das Unternehmen anzupassen, und die Entscheidung ließ nicht lange auf sich warten. Als der Anlasser erneut in der KĂ€lte den Motor mit einem schlecht verdeckten Riss drehte, schien das Vorhandensein eines Temperatursensors in der Schaltung und im Algorithmus sehr nĂŒtzlich zu sein. Als Ergebnis wurde der zweite ADC zur Messung der Temperatur verwendet. Damit der Thermistor nur dann Strom verbraucht, wenn er benötigt wird, wurde beschlossen, ihn vom letzten verbleibenden logischen Ausgang mit Strom zu versorgen.

Infolgedessen hat das GerĂ€tediagramm eine solche endgĂŒltige Form erhalten.


Hier sehen wir das Minimum an Details, und unter ihnen unterliegt nichts irgendeiner Art von "Verdrehung". Lassen Sie uns kurz auf die wichtigsten Punkte eingehen.

FĂŒr die Stromversorgung benötigt der Controller eine stabile Spannung von 1,8 bis 5,5 V, was bedeutet, dass sich im Stromkreis ein Stabilisator befinden muss, der die Spannung des Bordnetzes auf das erforderliche Niveau senkt. Unter dem Gesichtspunkt der Energieeinsparung scheint es einen Platz fĂŒr einen ausschließlich gepulsten AbwĂ€rtswandler zu geben, dies ist jedoch nur auf den ersten Blick möglich. Tatsache ist, dass ATtiny13A selbst im energieintensivsten Betriebsmodus (Frequenz 8 MHz, aktive CodeausfĂŒhrung) nicht mehr als 6 mA verbraucht. In diesem Schema befindet sich der Controller 99% der Zeit im Tiefschlafmodus und arbeitet auch mit einer Frequenz von 1,2 MHz, was zu einem durchschnittlichen Verbrauch von ungefĂ€hr weniger als 15 uA fĂŒhrt. Plus ca. 80 ”A zu den Basisströmen der Steuertransistoren (wenn beide Lasten eingeschaltet sind). Nun, fĂŒr einen kleinen Bruchteil einer Sekunde wird die Leistung des Thermistors aktiviert, wodurch der durchschnittliche Strom um etwa 25 Mikroampere erhöht wird. Und hier ist die Antwort auf die Frage: "Lohnt es sich, einen Impulswandler fĂŒr eine Last mit einem Verbrauch von nicht mehr als 120 ”A zu laden?" Es scheint nicht so einfach. Und wenn wir bedenken, dass es sich um analoge Messungen handelt, dann lohnt es sich definitiv nicht. Daher wurde der Linearstabilisator LP2950 verwendet, ein Funktionsanalogon des beliebten 78L05, der jedoch wesentlich wirtschaftlicher ist. Dieser Wandler kann bis zu 100 mA Strom am Ausgang abgeben und verbraucht fĂŒr den geliebten Menschen nicht mehr als 75 ”A.

Mit dem durch eine Zenerdiode und einen Kondensator geschĂŒtzten Spannungsteiler des Bordnetzes können Sie Spannungen bis 15 V messen.

Ich weiß, dass mich jetzt eine Welle der Kritik fĂŒr eine solche Entscheidung treffen wird, aber wir werden objektiv sein. Erstens entwickle ich keinen Satelliten, und zweitens gibt es keinen solchen Faktor, der zu einer Katastrophe fĂŒhren wĂŒrde. Der Schulterwiderstand ist hoch, die Zenerdiode kann viel mehr Strom umleiten als derjenige, der durch den Teiler fließen kann, selbst im pessimistischsten Szenario. Wenn die Zenerdiode nicht genĂŒgend Drehzahl hat, schĂŒtzt der Kondensator C2 vor Hochfrequenzimpulsen (mit einem Widerstand R7 wird ein Tiefpassfilter mit einer Grenzfrequenz von nur 7 Hz erzeugt). D1 und R6 versichern in gewissem Maße, dass das Schema voneinander abfĂ€llt. Und man sollte die LinearitĂ€t nicht vergessen, jede Methode der galvanischen Isolierung an einem solchen Ort wird die theoretische Berechnung von GrĂ¶ĂŸen völlig unrealistisch machen, wir mĂŒssen zumindest den Prototyp kalibrieren, aber wir brauchen ihn nicht.

Der Ausgangswiderstand des Teilers ist zehnmal höher als die empfohlenen 10 kOhm fĂŒr die ADC-Signalquelle, aber dank des Kondensators C2 treten keine Messprobleme auf.

Im Allgemeinen wird die Eingangsimpedanz der ADC-Schaltungen der AVR-Controller gemĂ€ĂŸ Datenblatt mit mindestens 100 Megaohm angegeben. In demselben Datenblatt wird jedoch empfohlen, Quellen mit einem Innenwiderstand von bis zu 10 kOhm zu verwenden. Warum so? Der Punkt ist das Funktionsprinzip dieses ADC selbst. Der Wandler arbeitet nach dem Prinzip der sequentiellen Approximation und seine Eingangsschaltung ist ein Tiefpassfilter aus einem Widerstand und einem Kondensator. Das Erhalten eines 10-Bit-Abtastwerts ist iterativ, und es ist erforderlich, dass der Kondensator wĂ€hrend der gesamten Messzeit auf die volle gemessene Spannung aufgeladen wird. Wenn die Ausgangsimpedanz der Quelle zu groß ist, wird der Kondensator wĂ€hrend des Umwandlungsprozesses weiterhin direkt aufgeladen und das Ergebnis ist ungenau. In unserem Fall betrĂ€gt die KapazitĂ€t C2 mehr als das Siebentausendfache der KapazitĂ€t des ADC-Filters. Wenn die Ladung beim Einschalten zum Zeitpunkt der Messung zwischen diesen Kondensatoren umverteilt wird, verringert sich die Eingangsspannung um nicht mehr als das 1/7000, was dem Siebenfachen entspricht weniger als die ultimative Genauigkeit eines 10-Bit-ADC. Sie mĂŒssen jedoch berĂŒcksichtigen, dass dieser Trick nur fĂŒr einzelne Messungen mit signifikanten Pausen zwischen ihnen funktioniert. Sie sollten das Steuerungsprogramm also nicht "verbessern", indem Sie einen Zyklus fĂŒr mehrere aufeinanderfolgende Messungen hinzufĂŒgen und das Ergebnis mitteln.

Der Teiler mit einem Thermistor aufgrund des Vorhandenseins einer gesteuerten Stromquelle wird unter Verwendung der empfohlenen Nennwerte gebaut. NTCLE100E3 wird als Sensor verwendet, es gibt jedoch keine EinschrÀnkungen. Sie können jeden Thermistor mit ungefÀhr derselben Nennleistung verwenden. Die Hauptsache besteht darin, Korrekturen vorzunehmen, die seiner Charakteristik in den Quellcodekonstanten entsprechen, damit die Spannung des Teilers in den richtigen Temperaturwert umgewandelt wird.

Als Steuertasten werden Leistungs-P-Kanal-MOSFETs jeglicher Art mit einem akzeptablen Open-Channel-Widerstand und einer maximalen Drain-Source-Spannung von mindestens 30 Volt verwendet. Die obige Schaltung verwendet verschiedene Transistoren. Dies geschieht, weil sie unterschiedliche Spannungen schalten mĂŒssen und der Typ von jeder fĂŒr bestimmte Arbeitsbedingungen ausgewĂ€hlt wurde. Der obere Transistor sollte eine höhere Spannung aufweisen und der untere sollte nach Möglichkeit einen minimalen offenen Kanalwiderstand aufweisen. Ich wiederhole jedoch, dass diese Entscheidung von der GerĂ€teschaltschaltung (siehe oben) vorgegeben wird. Mit einer weiteren Einbeziehung können die Anforderungen an den unteren Transistor unterschiedlich sein.

Zur Steuerung von Leistungsschaltern wird ein Paar identischer Bipolartransistoren verwendet. Auf den ersten Blick scheinen diese Transistoren ĂŒberflĂŒssig zu sein, aber hier ist es nicht so einfach. Feldeffekttransistoren mit einem isolierten Gate beginnen sich nicht ab einer Spannung der erforderlichen PolaritĂ€t am Gate zu öffnen, sondern erst nach Erreichen eines bestimmten Schwellenwerts, der in den DatenblĂ€ttern unter dem Namen „Gate-Source-Schwellenspannung“ aufgefĂŒhrt ist und normalerweise 2..4 V betrĂ€gt zĂ€hle einfach. Die Ausgangsschaltung der Steuerung kann zwei logische Ebenen bilden: logische "0" mit einer Spannung gegen Null; und logische "1" mit Spannung, die zur Versorgung neigt. Bei einer Spannung von 5 Volt sind dies Spannungen von etwa 0 bzw. 5 V. Infolgedessen erzeugt beim Schalten einer 12-Volt-Quelle eine logische „0“ am Gate eine Spannungsdifferenz von Source-Gate 12 - 0 = 12 Volt. Der Leistungstransistor ist offen. Alles scheint normal zu sein, aber eine logische „1“ mit einer Spannung von 5 V erzeugt eine Spannung zwischen 12 - 5 = 7 Volt zwischen der Quelle und dem Gate, und der Leistungstransistor bleibt weiterhin offen. Somit kann das FĂŒnf-Volt-Steuersignal den SchlĂŒssel nicht steuern, der die Spannung ĂŒber 7,9 Volt umwandelt. Daher arbeiten die Steuerbipolartransistoren weniger mit SignalschlĂŒsseln als vielmehr mit VerstĂ€rkern, die die Steuerspannung von 5 Volt auf die Spannung des Bordnetzes erhöhen.

Der Widerstand in der Basisschaltung jedes der Steuertransistoren begrenzt einfach den Strom der ReglerausgÀnge auf einen Pegel, der ausreicht, um ihn zu steuern. Ihre Nennwerte können zwei- bis dreimal reduziert werden, ohne dass dies Auswirkungen auf den Betrieb der Schaltung hat.

Es ist leicht zu erkennen, dass sich die Steuertransistoren nicht in der auf dem LM393N basierenden analogen Schaltung befanden. Die Sache ist, dass die Ausgangsstufe des ausgewĂ€hlten Komparators gemĂ€ĂŸ der offenen Kollektorschaltung aufgebaut ist, dh ihr Ausgang ist einfach der Ausgang des Klemmen-Transistorkollektors. Dieses Konstruktionsprinzip erfordert, dass zusĂ€tzliche Teile an den Chip gehĂ€ngt werden, um die Last der Ausgangsstufe zu erzeugen, macht den Chip jedoch andererseits sehr flexibel. Ein offener Kollektor ermöglicht es dem Komparator, jede akzeptable Stromquelle zu steuern, und ist nicht nur mit der kompatibel, die den Komparator selbst mit Strom versorgt.

Ich muss sagen, dass die Begrenzung der Schwellenspannung eines Leistungs-MOSFET nicht nur auf hohe Spannungen wirkt, wie oben erwĂ€hnt, sondern auch auf niedrige. Wenn die minimale Öffnungsspannung des Transistors beispielsweise 4 Volt betrĂ€gt, wird beim Schalten der 3,3-V-Quelle selbst beim Verbinden des Gates mit Masse nicht die gewĂŒnschte Spannungsdifferenz zwischen der Source und dem Gate erzeugt, und der Transistor bleibt geschlossen. 5 Volt sind also möglicherweise die minimale Spannung, die von den ausgewĂ€hlten Transistoren zuverlĂ€ssig geschaltet werden kann.

Anpassung


Das Einrichten eines GerÀts ist eine separate Konversation. Einerseits gibt es kein einziges Abstimmelement in der Schaltung, andererseits handelt es sich um Messspannungen mit einer Genauigkeit von nicht schlechter als 0,1 V. Wie kann man all dies verbinden? Es gibt zwei Möglichkeiten. Der erste besteht darin, die WiderstÀnde R6, R7 und R8 mit einer Toleranz von mindestens 1% (oder besser 0,1%) zu verwenden. Die zweite beinhaltet die Verwendung herkömmlicher WiderstÀnde mit Messung ihrer realen WiderstÀnde und Korrektur von Koeffizienten im Quellcode des Programms.

Die erste Methode ist gut fĂŒr die Massenproduktion, aber es ist viel attraktiver fĂŒr uns, uns nicht mit der Suche nach den erforderlichen hochprĂ€zisen Werten zu beschĂ€ftigen. Gehen wir also den zweiten Weg. Der Widerstand kann mit einem gewöhnlichen Multimeter gemessen werden, seine Genauigkeit ist hier völlig ausreichend. Ein weiteres Messobjekt ist die Spannung des Stabilisators, der die Schaltung versorgt.Der ADC der Steuerung kann in verschiedenen Modi arbeiten, aber aus mehreren GrĂŒnden ist es fĂŒr uns bequemer, einen zu verwenden, bei dem das Ergebnis der digitalen Umwandlung relativ zur Versorgungsspannung gezĂ€hlt wird. Deshalb ist es wichtig, es so genau wie möglich zu kennen.

Die Berechnung ist Ă€ußerst einfach und besteht aus der Berechnung des Teilungskoeffizienten des Widerstandsteilers und des Anteils der Übersetzung des Ergebnisses in LSB wĂ€hrend der Analog-Digital-Wandlung.


Ux ist die Eingangsspannung des Teilers;
Ru ist der Widerstand des Oberarms des Teilers (dem Ux zugefĂŒhrt wird);
Rd ist der Widerstand des Unterarms des Teilers (der mit der Erde verbunden ist);
Uref - Referenzspannung des ADC (d. H. Controller-Versorgungsspannung);
1024 - die Anzahl der diskreten Werte am Ausgang eines 10-Bit-ADC;
LSB ist der numerische Wert, den das Programm vom ADC erhÀlt.

Beginnen wir mit dem Spannungsteiler R6-R7. Zum Beispiel nehmen wir reale WiderstĂ€nde, die den im Diagramm angegebenen vollstĂ€ndig entsprechen. Wir nehmen auch genau 5,0 V Leistung. Ein Beispiel fĂŒr die Berechnung der Ergebnisse der Umwandlung einer Spannung von 13,5 Volt:


Mit der richtigen Genauigkeit bei der Messung der Anfangswerte ist das Ergebnis genau genug, um es nicht in den Feldtests anzupassen, sondern so zu verwenden, wie es ist.

Die Formel zur Berechnung des Temperaturteilers ist im Prinzip nicht anders, nur Ru ist eine Variable, und Ux kann gleich Uref genommen werden. Das Ergebnis sieht folgendermaßen aus:


Nehmen wir zum Beispiel den Wert von R8 aus der Schaltung und R9 aus dem Datenblatt auf NTCLE100E3 bei einer Temperatur von 0 ° C:


, R8 R9 , , , . . , R9 , 0.5 m, . , , 0.01 .

Der Widerstand des Thermistors bei verschiedenen Temperaturen ist im Datenblatt angegeben, aber es ist natĂŒrlich besser, ihn selbst zu messen. Letzteres kann ein Problem darstellen, daher schlage ich als Kompromiss vor, den tatsĂ€chlichen Widerstand bei einer Temperatur zu messen und so den Korrekturkoeffizienten fĂŒr die WiderstĂ€nde bei allen anderen Temperaturen aus dem Datenblatt zu ermitteln. Die meisten Thermistoren haben eine mehr oder weniger lineare AbhĂ€ngigkeit des Widerstands von der Temperatur, so dass diese Methode als ziemlich zuverlĂ€ssig angesehen werden kann.

TatsÀchlich werden alle Konstanten im Programm unter der Annahme berechnet, dass die realen Werte vollstÀndig dem Schema entsprechen, falls Teile mit kleinen Toleranzen verwendet werden.

Firmware


Das vollstĂ€ndige Projektarchiv fĂŒr AtmelStudio (gcc-avr 5.4.0 Compiler) kann hier heruntergeladen werden , auch das bereits zusammengestellte Hex . Und unter der Katzenliste der Quelldatei, um nicht weit zu gehen.

Quellcode
//#define F_CPU 1200000UL //    

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

//#define DBG

#define TEMPERATURE_OVERHEAT 753 // LSB-  +50⁰C
#define TEMPERATURE_GIST     8   //    ( LSB)     
#define VOLTAGE_GIST         3   //    ( LSB)     

#define INTERVAL             WDTO_1S //     (1 )
#ifndef DBG
#define CELL_CHANGE_TIMEOUT  90  //      (  INTERVAL,   254)
#define OVERHEAT_TIMEOUT     300 //      "" (  INTERVAL)
#else
#define CELL_CHANGE_TIMEOUT  2
#define OVERHEAT_TIMEOUT     3
#endif

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

typedef enum {st_none = 0b00, st_primary = 0b01, st_secondary = 0b10, st_both = 0b11} t_states; //    
                                                                                                //       ,      
typedef enum {adc_temperature, adc_voltage} t_measure;                                          //   
typedef enum {move_null, move_up, move_down} t_movement;                                        //      

//    
struct t_coordidates {
  signed char row, col;
};

//       
struct t_correction {
  t_movement voltage, temperature;
};

#define CELLS_ROWS 3 //      ( )
#define CELLS_COLS 5 //      ( )

//  
const t_states CELLS[CELLS_ROWS][CELLS_COLS] = {
  {st_both, st_both,    st_both,    st_primary, st_none},
  {st_both, st_both,    st_primary, st_none,    st_none},
  {st_both, st_primary, st_none,    st_none,    st_none}
};

// LSB- ,      
const unsigned int ROWS_EDGES[CELLS_ROWS - 1] = {
  241, // 0⁰C
  157  // -10⁰C
};

// LSB- ,      
const unsigned int COLS_EDGES[CELLS_COLS - 1] = {
  864, // 13.5V
  800, // 12.5V
  787, // 12.3V
  768  // 12.0V
};

unsigned int overheat_rest_time = 0; //       ""
unsigned char cell_change_time  = 0; //      
unsigned char no_cur_cell_time  = 0; //  ,            

#define NULL_CELL (struct t_coordidates){.col = -1, .row = -1} // ,   
#define NULL_CORRECTION (struct t_correction){.voltage = move_null, .temperature = move_null} // ,   

struct t_correction moved_from = NULL_CORRECTION; //       
struct t_coordidates cur_cell  = NULL_CELL,       //      
                     next_cell = NULL_CELL;       //  -   

//  
static void init_pins() {
  DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB3);     //   2 (PB3), 5 (PB0)  6 (PB1)  
  PORTB &= ~(1 << PB0) & ~(1 << PB1) & ~(1 << PB3); //      2 (PB3), 5 (PB0)  6 (PB1)
}

// /    
static void toggle_thermal_sensor(bool state) {
  if(state) {
    PORTB |= (1 << PB1);  //  state ,      6 (PB1)

    _delay_ms(5); //    
  } else {
    PORTB &= ~(1 << PB1); //  state  ,      6 (PB1)
  }
}

//   
static unsigned int measure_adc(t_measure measure) {
  if(measure == adc_temperature) {
    toggle_thermal_sensor(true); //    ,    

    ADMUX = 0b10; //      -   3 (PB4)
  } else {
    ADMUX = 0b01; //      -   7 (PB2)
  }

  ADCSRA = (1 << ADPS2) | //       = 16 (75 )
           (1 << ADIE) |  //    
           (1 << ADEN);   //  

  set_sleep_mode(SLEEP_MODE_ADC); //   "" 
  do {
    sleep_cpu(); //      ,      ,   
  } while(ADCSRA & (1 << ADSC)); //        ,  

  ADCSRA = 0; //  

  toggle_thermal_sensor(false); //     

  return ADC; //  10-  
}

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

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

  sei(); //  
}

//          
static void toggle_loads(t_states states) {
  unsigned char port = PORTB & ~((1 << PB3) | (1 << PB0)),     //           ,   
                bits = (((states & st_primary) >> 0) << PB3) | //        
                       (((states & st_secondary) >> 1) << PB0);

  PORTB = port | bits; //    
}

//     t_coordidates
static bool cells_equal(struct t_coordidates cell1, struct t_coordidates cell2) {
  return cell1.row == cell2.row && cell1.col == cell2.col;
}

//          LSB- 
static signed char get_cell_row(unsigned int temperature) {
  signed char row = 0;

  while(row < CELLS_ROWS - 1) {          //          
    if(temperature >= ROWS_EDGES[row]) { //  temperature     ,    
      return row;
    } else {
      ++row;
    }
  }

  return CELLS_ROWS - 1; //  temperature         ,       
}

//          LSB- 
static signed char get_cell_col(unsigned int voltage) {
  signed char col = 0;

  while(col < CELLS_COLS - 1) {      //          
    if(voltage >= COLS_EDGES[col]) { //  voltage     ,    
      return col;
    } else {
      ++col;
    }
  }

  return CELLS_COLS - 1; //  voltage         ,       
}

//    ,       
static void get_row_edges(signed char row, unsigned int *upper, unsigned int *lower) {
  *upper = row > 0 ? ROWS_EDGES[row - 1] : 0xffff - TEMPERATURE_GIST; //       ,    
  *lower = row < CELLS_ROWS - 1 ? ROWS_EDGES[row] : TEMPERATURE_GIST; //       ,    
}

//    ,       
static void get_col_edges(signed char col, unsigned int *upper, unsigned int *lower) {
  *upper = col > 0 ? COLS_EDGES[col - 1] : 0xffff - VOLTAGE_GIST; //      (  )  ,    
  *lower = col < CELLS_COLS - 1 ? COLS_EDGES[col] : VOLTAGE_GIST; //      (  )  ,    
}

//    -              
static void gisteresis_correction(struct t_coordidates* new_cell, unsigned int temperature, unsigned int voltage) {
  unsigned int upper_edge, lower_edge;

  get_row_edges(cur_cell.row, &upper_edge, &lower_edge); //    
  if(new_cell->row > cur_cell.row && moved_from.temperature == move_up && temperature >= lower_edge - TEMPERATURE_GIST) {
    --new_cell->row; //   -   ,    ,        ,    
  }

  if(new_cell->row < cur_cell.row && moved_from.temperature == move_down && temperature <= upper_edge + TEMPERATURE_GIST) {
    ++new_cell->row; //   -   ,    ,        ,    
  }

  get_col_edges(cur_cell.col, &upper_edge, &lower_edge); //    
  if(new_cell->col > cur_cell.col && moved_from.voltage == move_up && voltage >= lower_edge - VOLTAGE_GIST) {
    --new_cell->col; //   -   ,     (  ),        ,    
  }

  if(new_cell->col < cur_cell.col && moved_from.voltage == move_down && voltage <= upper_edge + VOLTAGE_GIST) {
    ++new_cell->col; //   -   ,     (  ),        ,    
  }
}

//       stdlib::abs()
 static unsigned char absolute(signed char value) {
  return value >= 0 ? value : -value;
}

//      -
static void calc_movement(struct t_coordidates new_cell) {
  moved_from = NULL_CORRECTION;                                                   // -   
  if(!cells_equal(new_cell, NULL_CELL) && !cells_equal(cur_cell, NULL_CELL)) {    //         ,  -
    if(absolute(new_cell.row - cur_cell.row) == 1) {                              //      
      moved_from.temperature = new_cell.row < cur_cell.row ? move_up : move_down; //   
    }

    if(absolute(new_cell.col - cur_cell.col) == 1) {                              //      
      moved_from.voltage = new_cell.col < cur_cell.col ? move_up : move_down;     //   
    }
  }
}

//   -
static void set_next_cell(struct t_coordidates cell) {
  next_cell = cell;
  cell_change_time = 0; //    
}

//    
static void set_cur_cell(struct t_coordidates cell) {
  cur_cell = cell;
  no_cur_cell_time = 0; //        
  set_next_cell(NULL_CELL); //  -
}

// ,      
static void change_cell(struct t_coordidates new_cell) {
  if(cells_equal(new_cell, NULL_CELL)) { //         
    toggle_loads(st_none);
  } else {
    toggle_loads(CELLS[new_cell.row][new_cell.col]); //         
  }

  calc_movement(new_cell); //     
  set_cur_cell(new_cell);  //   
}

//  
static void main_proc(void) {
  unsigned int temperature, voltage; // 10- LSB-    
  struct t_coordidates cell;         //      -

  if(overheat_rest_time) { //      ""  ,          
    --overheat_rest_time;
  } else {
    temperature = measure_adc(adc_temperature); //  
    if(temperature >= TEMPERATURE_OVERHEAT) {   //      +50C,  :
      change_cell(NULL_CELL);                   //      (   )
      overheat_rest_time = OVERHEAT_TIMEOUT;    //        
    } else {
      voltage = measure_adc(adc_voltage);   //  

      cell.col = get_cell_col(voltage);     //    -  
      cell.row = get_cell_row(temperature); //    -  

      if(cells_equal(cur_cell, NULL_CELL)) { //        ,         
        change_cell(cell);
      } else {
        gisteresis_correction(&cell, temperature, voltage); //              

        if(cells_equal(cell, cur_cell)) { //   -   ,      
          set_next_cell(NULL_CELL);
          no_cur_cell_time = 0; //    ,  
        } else {
          if(no_cur_cell_time++ > CELL_CHANGE_TIMEOUT) { //    CELL_CHANGE_TIMEOUT+1        cur_cell,      
            change_cell(cell); //    ,     
          } else {
            if(cells_equal(next_cell, NULL_CELL) || !cells_equal(next_cell, cell)) { //  -       ,   
              set_next_cell(cell);
            } else {
              if(++cell_change_time >= CELL_CHANGE_TIMEOUT) { //   ,       , ,    
                change_cell(cell);
              }
            }
          }
        }
      }
    }
  }
}

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

//    ,        ADSC  measure_adc()
EMPTY_INTERRUPT(ADC_vect);

//  
int main(void) {
  init_pins();       //  
  init_interrupts(); //    watchdog
	
  while(true) {                          //  ,       
    set_sleep_mode(SLEEP_MODE_PWR_DOWN); //        
    sleep_cpu();                         //        watchdog 

    main_proc();                         //          
  }
}


: L:0x6A, H:0xFF.

Das Prinzip des Programms ist wie folgt. Es gibt eine Tabelle mit AusgangszustÀnden, in der die horizontale Achse die Spannung des elektrischen Systems und die vertikale Achse die Temperatur ist. Das Programm misst die Spannung und Temperatur und findet in der Tabelle eine Zelle, die den Zustand der Last bestimmt. Die Tabelle selbst hat die folgende Form:


Die oberste Zeile der Tabelle definiert den Überhitzungsmodus und wird von einem separaten einfachen Zweig verarbeitet, sodass er nicht im Quellcode enthalten ist.

FĂŒr den normalen Betrieb reicht es jedoch nicht aus, nur die Werte zu messen und die Zellen zu zĂ€hlen. Die Spannung des elektrischen Systems kann sowohl von selbst als auch unter dem Einfluss von Schaltlasten durch unser GerĂ€t schwanken. Die zweite ist besonders gefĂ€hrlich, da sie tatsĂ€chlich eine Kette positiver RĂŒckkopplungen erzeugt, die zum Auftreten periodischer Schwankungen im Zustand fĂŒhrt.

, - , . , , . , - , , , , . .. - , , . . . , , .

Um den Schwingungsprozess zu unterdrĂŒcken, wurde ein Hysteresemechanismus in das Programm eingefĂŒhrt, der nach dem Überschreiten der Zustandsgrenze die gerade gekreuzte Linie ein wenig gegen die Bewegung des Arbeitspunkts nach hinten verschiebt. Wenn Sie beispielsweise die Grenze von 12,5 V von unten nach oben ĂŒberschreiten, um in die entgegengesetzte Richtung zu ĂŒberqueren, mĂŒssen Sie auf 12,4 V absteigen. Dieses Prinzip verhindert das Umschalten des Zustands auch unter dem Einfluss von ADC-Rauschen und Interferenzen erheblich. Konstanten, die die Breite der Hysterese vertikal und horizontal bestimmen, können ebenfalls leicht geĂ€ndert werden.



ZusĂ€tzlich zur Hysterese werden dem Programm auch Zustandsschaltverzögerungen hinzugefĂŒgt, hauptsĂ€chlich fĂŒr FĂ€lle von kurzfristigen SpannungsĂ€nderungen, beispielsweise beim Starten des Motors. Es macht drei Sekunden lang keinen Sinn, die Last von einem halben Ampere zu trennen, wenn der Anlasser verwendet wird und mehrere hundert Ampere verbraucht. Selbst wenn die Spannung des elektrischen Systems in diesem Moment auf vollstĂ€ndig "Notfall" 8-9 Volt abfĂ€llt.

, . «» , . , , «» , - (, , , , , - ).

Die obere Reihe der Tabelle weist weder eine Hysterese noch eine Übergangsverzögerung auf. Wenn die Temperatur von unten nach oben + 50 ° C ĂŒberschreitet, werden die Lasten sofort und bedingungslos abgeschaltet. Außerdem wurde fĂŒr diesen Modus ein separates Exit-Timeout eingefĂŒhrt, das erheblich grĂ¶ĂŸer ist als dasjenige, das beim Überschreiten der verbleibenden Zellgrenzen verwendet wird. Eine RĂŒckkehr aus diesem Modus ist nur möglich, wenn die Temperaturanzeigen wĂ€hrend des gesamten Timeouts stabil unter dem Schwellenwert liegen.

Ein Watchdog wird als „Herz“ verwendet, das den Messzyklus festlegt. Es ist so konfiguriert, dass anstelle eines erzwungenen Neustarts ein Interrupt generiert wird, und weckt den Controller einfach regelmĂ€ĂŸig aus dem Tiefschlaf fĂŒr den nĂ€chsten Arbeitszyklus.

Es scheint, dass eine solche Lösung uns die Hauptfunktion des Watchdog-Timers vorenthĂ€lt - das Einfrieren zu verhindern. In Wirklichkeit ist dies jedoch nicht der Fall. Watchdog ist so konzipiert, dass ein Flag, das die Erzeugung eines Interrupts anstelle eines Neustarts enthĂ€lt, bei jedem Auslösen von der Hardware zurĂŒckgesetzt wird. Wenn der Controller einfriert, wird nicht nur die Hauptprogrammschleife, sondern auch der Watchdog-Interrupt-Handler nicht ausgefĂŒhrt. Dies bedeutet, dass der erste Vorgang das Interrupt-Flag löscht und der nĂ€chste bereits einen vollstĂ€ndigen Neustart initiiert.

Der Rest des Programmcodes ist vollstĂ€ndig kommentiert und bedarf keiner detaillierten Analyse. Die GrĂ¶ĂŸe der endgĂŒltigen Firmware betrug 1006 Byte, so dass eine kleine Reserve fĂŒr ein oder zwei kleinere Korrekturen ĂŒbrig bleibt.

Die GrĂ¶ĂŸe der endgĂŒltigen BinĂ€rdatei hĂ€ngt ĂŒbrigens von den Optimierungsoptionen ab, was logisch ist. Wie sich jedoch herausstellte, gab die O2-Option in meinem Fall die MindestlĂ€nge an, wĂ€hrend die Os-Option zur Minimierung der GrĂ¶ĂŸe einen Code erzeugte, der kaum in die zugewiesenen 1024 Bytes passte. Wenn das Programm also an der Grenze der Funktionen des Controllers liegt und nicht gut passt, ist es immer sinnvoll, mit diesem Parameter zu spielen.

Der Code leuchtet tatsĂ€chlich nicht optimal in Bezug auf die GrĂ¶ĂŸe und kann, falls gewĂŒnscht, um weitere hundert Bytes verkleinert werden, ohne die FunktionalitĂ€t zu verlieren.

Platinenschema und Verdrahtungsdateien

Eagle .

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


All Articles