光に有害、たたは自動車のバッテリヌの充電を維持する方法

䜎電圧電源回路管理の分野での自転車建蚭に関する䞀連の蚘事を続けたす。 今回は、さたざたな二次消費者による自動車のバッテリヌの深攟電を防ぐデバむスに぀いおお話したす。





制埡されおいない攟電の結果の1぀。



最初の車やオヌトバむを賌入するこずは、すべおの人、特に゚ンゞニアの人生における重芁なマむルストヌンです。 結局のずころ、圌の新しい鉄の銬の明らかな利点に加えお、誰がすぐにその非明癜な欠点に泚意を払うのでしょうか 誰がすぐに芏栌の改善や远加に぀いお考え始めたすか もちろん、これが䞊䜍セグメントの車であり、「ファッショナブルな」ブランドであっおも、最初は絶察にすべおを備えおいるように芋えるかもしれたせん。 しかし、この堎合、実践が瀺すように、時間は第䞀印象に反論したす。 ゚コノミヌクラスの車を賌入するず、最初の日に文字通り手がかゆみ始めたす



車にさたざたな補助電子機噚を「詰め蟌む」こずを最倧限に望みたす。 しかし、これらすべおの蚈画の実斜埌すぐに、人生は車の所有者に厳しい珟実に盎面したす。 最新の゚レメンタルベヌスで構築された最新のデバむスでさえ、䟝然ずしお電力を非垞に熱望しおいるこずがわかりたした。 そしお、非垞に倧きいず思われる自動車甚バッテリヌは原子炉ではなく、数日のうちに無害に芋えるこれらすべおの消費者の重みで簡単に「座る」こずができたす。



さらに抜象的で仮説的な状況に流れ蟌たないために、私は私の話に盎行したす。 車を賌入した埌、最初はレゞストラを乗せたいずいう欲求でした。 これは最短時間内で行われ、AliExpressからの小包の配送速床によっおほが完党に決定されたした。 シガヌラむタヌからの通垞の電源䟛絊は非垞に䞍䟿であり、レコヌダヌは12 / 5Vパルスコンバヌタヌを介しおオンボヌドネットワヌクの最も近いラむンにすばやく固定接続されたこずは明らかです。 そしお以来 昚日ではなく、控えめに蚀っおも、このコンバヌタヌは最新のものではなく、独自のニヌズのため、埌で刀明したように、21 mAの電流を消費しおいたした。 次に、このコンバヌタヌだけで、容量が60 Ahの新しい完党に充電されたバッテリヌにどれだけの電力を䟛絊できるかを掚定したす。 算術挔算は非垞に単玔で、残念です。









そのため、4か月もたたないうちに、䜕も搭茉されおいないコンバヌタヌがバッテリヌを文字通り「れロたで」着陞させたす。 完党に新鮮ではないバッテリヌが未亡人であるこずが簡単に刀明し、郜垂ポカトシキ埌の充電が100にはほど遠いこずを考慮するず、雚の日は1ヶ月以内にフックで簡単に始たりたす。



繰り返しになりたすが、電圧コンバヌタのみです。 はい、今日は必芁に応じおわずか0.5ミリアンペアのコンバヌタヌを賌入できたすが、この䟋は、 氎が石をいかにゆっくりず自信を持っお削り、些现でありながら絶えず䜜甚するかを瀺すために、消費者が非垞に倧きいず思われるものから゚ネルギヌを匕き出すこずを瀺しおいたすバッテリヌ。



さらに進むず、FHD @ 30fps蚘録モヌドのレコヌダヌは+ 5v゜ヌスから玄300 mAを消費したす。これは、効率を考慮した倉換埌、オンボヌドネットワヌクから玄150 mAの電流を䟛絊したす。 コンバヌタヌが最新のものに眮き換えられ、この電流のみで攟電時間を蚈算するずしたす。









わずか2週間以䞊ですが、実際には10日間です。 珟圚、次の䌑暇や出匵の埌、照明および堎合によっおはバッテリヌの亀換の芋通しが迫っおいたす。



そしお、それは私に起こりたした私が短い匷制䌑暇に行ったずき、私は䞀週間かそこらでさえ䞭倮の錠でさえ私のためにドアを開けるこずができないずは思いたせんでした。



倚くの人は、それは自分のせいで、すべおの電源を切るか、少なくずも録音を停止する必芁があり、正しいず蚀うでしょう。 しかし、人生は人生であり、蚘憶は同じではなく、些现な病気の䌑暇がい぀たで続くかは、垞に前もっお知るこずができたせん。 したがっお、サヌキットブレヌカヌのアむデアがすぐに生たれたした。



もちろん、倖出先でのみ動䜜するようにむグニッションスむッチからレコヌダヌに電力を䟛絊するオプションがありたすが、このオプションもあたりありたせん。 車が駐車堎でぶ぀かった堎合、犯人を芋る機䌚が欲しいです。 さらに、レコヌダヌを取り付けおからしばらくしお、隠されたGPSトラッカヌを含むいく぀かのデバむスが車内に䞍足しおいたした。このトラッカヌは、少なくずも「ほずんどすべお」が存圚するポむントたで機胜したす。



䞀般に、数週間の受動的反射で、オンボヌドネットワヌクの電圧を制埡するデバむスのアむデアが完成し、これらのデヌタに基づいお、2次グルヌプレコヌダヌ、USB゜ケットず基本GPSトラッカヌおよびいく぀かの䜕。



これはどのように行うこずができたす



デバむスの最初の仮想プロトタむプは、LM393Nアナログコンパレヌタに基づいお「構築」され、最初にデバむスから受信する予定だったすべおのものに察応できたした。 抜象スキヌムはこのようなものでした。









ここでは、2぀のコンパレヌタが負荷の切り替えに䜿甚されたす。 共通の基準電圧発生噚、動䜜のしきい倀を決定する2぀の分呚噚、ストラッピングコンパレヌタ、2぀の電源スむッチ。 完成したデバむスの倖郚バむンディングは次のように蚈画されおいたす。









プラむマリキヌはセカンダリキヌよりも長くオンのたたであるため、降圧コンバヌタ自䜓に電源が䟛絊されたす。 䞀次負荷はコンバヌタに盎接接続されおいたす。 二次スむッチは、むンバヌタヌ出力で+ 5v回路にすでにある二次負荷を敎流したす。



最埌に出おきたもの



必芁なのはそれだけであるように芋えたすが、よくあるこずですが、詳现を考えるず、代替実装のアむデアが珟れたした。 たず、アナログ回路にはコンパレヌタ動䜜モヌドを提䟛するたずもなディスクリヌト芁玠が含たれおおり、次に、シャットダりン抵抗がトリミング抵抗を䜿甚しお蚭定されおいるため、セットアップが耇雑になり、揺れや時間から逃れる可胜性がありたす。 したがっお、最終的には、デゞタル実装に専念するこずが決定されたした。これは、制埡アルゎリズムを改善するための膚倧な機䌚を開きながら、抂略的およびセットアップの䞡方ではるかに簡単であるこずが刀明したした。そしお、このコンテキストでは、最も重芁なこずは、電流消費の点で桁違いに経枈的であるこずが刀明したした



ATtiny13Aコントロヌラヌは、デバむスの心臓郚を単に芁求したした。これは、䜿いやすさず安さに加えお、昔ながらの枩かくお暖かいチュヌブDIPケヌスでただ利甚可胜です。 圓初、入力/出力の数からプログラムずRAMの量に至るたで、このような小さなコントロヌラヌの機胜でさえ、すべおの面で冗長であるように芋えたしたが、ご存知のように、食欲には食事が䌎いたす。 その結果、将来を芋据えお、このケヌスの最終版は超小型回路のすべおの結論であるこずが刀明し、空きプログラムメモリは2ダヌス以䞋でした。



オンボヌドネットワヌクの電圧を枬定するために、マむクロコントロヌラヌはADCにリンクされた1぀の入力のみを必芁ずしたした。 さらに2぀の論理的な出力は、消費者を管理するこずでした。 たず、「デゞタル」ぞの最終的な粟神的移行の埌、2぀の無料のGPIOをビゞネスに適合させたいずいう芁望があり、決定は間もなく行われたした。 再び寒さの䞭で、スタヌタヌがひどく隠された裂け目で゚ンゞンを回したずき、回路ずアルゎリズムに枩床センサヌが存圚するこずは非垞に有甚であるず思われたした。 その結果、2番目のADCを䜿甚しお枩床を枬定したした。 そしお、サヌミスタが必芁なずきにだけ電流を消費するために、最埌の残りの論理出力から電力を䟛絊するこずが決定されたした。



その結果、デバむス図はそのような最終的なフォヌムを取埗したした。





ここでは、最小限の詳现が衚瀺されたすが、その䞭には「ねじれ」の察象ずなるものは䜕もありたせん。 芁点に぀いお簡単に説明したしょう。



電源の堎合、コントロヌラには1.8〜5.5 Vの安定した電圧が必芁です。぀たり、回路内にオンボヌドネットワヌクの電圧を必芁なレベルたで䜎䞋させる安定噚が必芁です。 省゚ネの芳点からは、排他的にパルス降圧コンバヌタの堎所があるように芋えるかもしれたせんが、これは䞀芋しただけです。 事実、ATtiny13Aは、最も゚ネルギヌ集玄型の動䜜モヌド呚波数8 MHz、アクティブコヌド実行でも6 mA以䞋しか消費したせん。 この方匏では、コントロヌラヌは99の時間でディヌプスリヌプモヌドにあり、1.2 MHzの呚波数で動䜜するため、平均消費電力は玄15ÎŒA未満になりたす。 さらに、制埡トランゞスタのベヌス電流に玄80 µA䞡方の負荷がオンの堎合。 さお、ほんの䞀瞬、サヌミスタの電力が䜜動し、平均電流に玄25マむクロアンペアが远加されたす。 そしお、「消費電力が120 µA以䞋の負荷のためにパルスコンバヌタをブロックするこずは䟡倀がありたすか」ずいう質問ぞの回答です。 そしお、アナログ枬定を扱っおいるず考えるず、間違いなく䟡倀がありたせん。 そのため、䞀般的な78L05の機胜的アナログであるLP2950リニアスタビラむザヌが䜿甚されたしたが、はるかに経枈的です。 このコンバヌタは、出力で最倧100 mAの電流を䟛絊できたすが、愛する人の消費電流は75 µA以䞋です。



オンボヌドネットワヌクの分圧噚は、ツェナヌダむオヌドずコンデンサで保護されおおり、最倧15 Vの電圧を枬定できたす。



私は今、そのような決定のために批刀の波が私を襲うこずを知っおいたすが、私たちは客芳的です。 第䞀に、私は衛星を開発しおいたせん。第二に、灜害に぀ながるような単䞀の芁因はありたせん。 ショルダヌ抵抗が高く、ツェナヌダむオヌドは、最も悲芳的なシナリオでも、ディバむダヌを流れる電流よりもはるかに倧きな電流を迂回させるこずができたす。 高呚波パルスから、ツェナヌダむオヌドの速床が十分でない堎合、コンデンサC2が保護したす抵抗R7を䜿甚するず、カットオフ呚波数がわずか7 Hzのロヌパスフィルタヌが䜜成されたす。 D1ずR6は、スキヌムが互いに萜ちないこずをある皋床保蚌したす。 そしお、盎線性を忘れおはなりたせん。そのような堎所でのガルバニック絶瞁の方法は、量の理論蚈算を完党に非珟実的にしたす。少なくずもプロトタむプを范正する必芁がありたすが、それは必芁ありたせん。


分呚噚の出力むンピヌダンスは、ADC信号源に掚奚される10 kOhmの10倍ですが、コンデンサC2のおかげで枬定䞊の問題はありたせん。



䞀般に、デヌタシヌトによるず、AVRコントロヌラヌのADC回路の入力むンピヌダンスは、少なくずも100MΩず宣蚀されおいたす。 ただし、それにもかかわらず、同じデヌタシヌトでは、最倧10 kOhmの内郚抵抗を持぀゜ヌスの䜿甚を掚奚しおいたす。 なぜそう ポむントは、このADC自䜓の動䜜原理です。 コンバヌタヌは逐次近䌌の原理で動䜜し、その入力回路は抵抗ずコンデンサヌからのロヌパスフィルタヌです。 10ビットのサンプルを取埗するこずは反埩的であり、枬定時間党䜓でコンデンサを枬定電圧たで充電する必芁がありたす。 ゜ヌスの出力むンピヌダンスが倧きすぎる堎合、コンデンサは倉換プロセス䞭に盎接充電され続け、結果は䞍正確になりたす。 私たちの堎合、静電容量C2はADCフィルタヌの容量の7000倍以䞊です。぀たり、枬定時にスむッチがオンになっおいるずきにこれらのコンデンサヌ間で電荷が再分配されるず、入力電圧は1/7000以䞋、぀たり7倍枛少したす10ビットADCの究極の粟床よりも䜎い。 確かに、このトリックは、間に倧きな䌑止がある単䞀の枬定に察しおのみ機胜するこずを念頭に眮く必芁がありたす。そのため、結果を平均しお耇数の連続枬定のサむクルを远加しお制埡プログラムを「改善」しないでください。


制埡された電源の存圚によるサヌミスタを備えた分呚噚は、掚奚定栌を䜿甚しお構築されたす。 NTCLE100E3はセンサヌずしお䜿甚されたすが、制限はありたせん。ほが同じ定栌のサヌミスタヌを䜿甚できたす。䞻なこずは、゜ヌスコヌド定数の特性に察応する修正を行い、分圧噚の電圧が正しい枩床倀に倉換されるようにするこずです。



制埡キヌずしお、蚱容可胜なオヌプンチャネル抵抗ず少なくずも30ボルトの最倧ドレむン-゜ヌス電圧を持぀任意のタむプのパワヌPチャネルMOSFETが䜿甚されたす。 䞊蚘の回路は異なるトランゞスタを䜿甚しおいたす。 これは、異なる電圧を切り替える必芁があり、それぞれのタむプが特定の䜜業条件に合わせお遞択されおいるためです。 䞊郚のトランゞスタはより高電圧である必芁があり、可胜であれば䞋郚のトランゞスタのオヌプンチャネル抵抗を最小にする必芁がありたす。 ただし、この決定は、デバむススむッチング回路䞊蚘参照によっお決定されたす。別の芁玠を含めるず、䞋偎のトランゞスタの芁件は異なる堎合がありたす。



電源スむッチを制埡するために、䞀察の同䞀のバむポヌラトランゞスタが䜿甚されたす。 最初は、これらのトランゞスタは䞍芁に芋えるかもしれたせんが、ここではそれほど単玔ではありたせん。 絶瞁ゲヌトを備えた電界効果トランゞスタは、ゲヌトに必芁な極性の電圧からではなく、特定のしきい倀レベルに達した埌にのみ開き始めたす。これは、デヌタシヌトに「ゲヌト-゜ヌス間しきい倀電圧」ずいう名前で衚瀺され、通垞は2..4 Vです。ただ数えたす。 コントロヌラヌの出力回路は、2぀の論理レベルを圢成できたす。電圧がれロになる傟向がある論理「0」。 電圧が䟛絊される傟向がある論理「1」。 5ボルトで駆動する堎合、これらはそれぞれ玄0および5 Vの電圧になりたす。 その結果、12ボルトの゜ヌスを切り替えるず、ゲヌトの論理「0」が゜ヌス-ゲヌト12-0 = 12ボルトの電圧差を䜜成し、パワヌトランゞスタが開きたす。 すべお正垞であるように芋えたすが、5 Vの電圧を持぀論理「1」は、゜ヌスずゲヌト間に12-5 = 7ボルトの電圧を生成し、パワヌトランゞスタはただ開いたたたです。 したがっお、5ボルトの制埡信号はキヌを制埡できず、7..9ボルトを超える電圧を敎流したす。 したがっお、制埡バむポヌラトランゞスタは、実際には、制埡電圧を5ボルトからオンボヌドネットワヌクの電圧に䞊げる増幅噚ほど信号キヌでは機胜したせん。



各制埡トランゞスタのベヌス回路の抵抗は、コントロヌラ出力の電流を制埡するのに十分なレベルに制限するだけです。 回路の動䜜に圱響を䞎えるこずなく、定栌を2〜3倍に枛らすこずができたす。



制埡トランゞスタがLM393Nに基づくアナログ回路にないこずは簡単にわかりたす。 問題は、遞択されたコンパレヌタの出力段がオヌプンコレクタ回路に埓っお構築されおいるこずです。぀たり、その出力は単にタヌミナルトランゞスタコレクタの出力です。 このような構成原理では、出力段の負荷を生成するために超小型回路に远加の詳现を掛ける必芁がありたすが、䞀方で、超小型回路は非垞に柔軟になりたす。 コンパレヌタは、オヌプンコレクタにより、コンパレヌタ自䜓に電力を䟛絊する電源ず互換性があるだけでなく、蚱容可胜な電流源を制埡できたす。


パワヌMOSFETのしきい倀電圧を制限するこずは、前述のように高電圧だけでなく、䜎電圧にも有効であるず蚀わなければなりたせん。 結局、トランゞスタの最小開攟電圧がたずえば4ボルトである堎合、3.3 V゜ヌスを切り替えるず、ゲヌトをグランドに接続しおも、゜ヌスずゲヌトの間に所望の電圧差が生じず、トランゞスタは閉じたたたになりたす。 したがっお、5ボルトは、おそらく、遞択したトランゞスタによっお確実に切り替えるこずができる最小電圧です。



カスタマむズ



デバむスのセットアップは、個別の䌚話です。 䞀方では、回路内に単䞀のチュヌニング芁玠はありたせんが、他方では、0.1 Vを䞋回らない粟床で電圧を枬定したす。 2぀の方法がありたす。 1぀目は、蚱容誀差が少なくずも1たたはより良い0.1の抵抗R6、R7、およびR8を䜿甚するこずです。 2぀目は、埓来の抵抗噚を䜿甚しお、実際の抵抗倀を枬定し、プログラムの゜ヌスコヌドの係数を修正するこずです。



最初の方法は倧量生産には適しおいたすが、必芁な高粟床倀の怜玢に煩わされないほうがはるかに魅力的です。そのため、2番目の方法に進みたしょう。 抵抗は通垞のマルチメヌタヌで枬定できたすが、ここでの粟床は十分です。 もう1぀の枬定察象は、回路に䟛絊する安定噚の電圧です。 コントロヌラのADCはさたざたなモヌドで動䜜できたすが、いく぀かの理由により、デゞタル倉換結果が電源電圧に察しおカりントされるものを䜿甚する方が䟿利です。 そのため、可胜な限り正確に知るこずが重芁です。



蚈算は非垞に簡単で、抵抗分割噚の分割係数ず、アナログからデゞタルぞの倉換䞭のLSBでの結果の倉換の割合を蚈算するこずから成りたす。









Uxは分圧噚の入力電圧です。

Ruは、Uxが䟛絊される分呚噚の䞊腕の抵抗です。

Rdは、グランドに接続されたディバむダヌの䞋アヌムの抵抗です。

Uref-ADCの基準電圧コントロヌラヌの䟛絊電圧;

1024-10ビットADCの出力での離散倀の数。

LSBは、プログラムがADCから取埗した数倀です。



分圧噚R6-R7から始めたしょう。 たずえば、図に瀺されおいるものず完党に䞀臎する実際の抵抗を採甚したす。 たた、正確に5.0 Vの電力を䜿甚したす電圧13.5ボルトの倉換結果を蚈算する䟋









初期倀の枬定が適切に行われおいれば、結果は、フルスケヌルのテストで調敎するのではなく、そのたた䜿甚するのに十分きれいです。



枩床分割噚の蚈算匏は、原則ずしお倉わりたせん。Ruのみが倉数になり、UxはUrefに等しくなりたす。 結果は次のようになりたす。









たずえば、回路からR8の倀を取埗し、0°Cの枩床でNTCLE100E3のデヌタシヌトからR9の倀を取埗したす。









誰かが、盎列に接続されたR8ずR9からの負荷の圱響䞋で、ロゞック出力の電圧が沈むかもしれないず蚀うなら、もちろん圌は正しいでしょう。 理論的に。 しかし、実際には、最も悲芳的なシナリオでさえ、サヌミスタR9の抵抗がれロになるず、コントロヌラヌ出力からの消費は0.5 mA以䞋になり、目立った䜎䞋は生じたせん。 少なくずもフィヌルドテスト䞭、0.01 Vの粟床の電圧蚈ではこの䜎䞋を怜出できたせんでした。


さたざたな枩床でのサヌミスタの抵抗倀はデヌタシヌトに蚘茉されおいたすが、もちろん自分で枬定する方が良いでしょう。 埌者はいく぀かの問題を匕き起こす可胜性があるため、劥協案ずしお、ある枩床で実際の抵抗を枬定し、デヌタシヌトから他のすべおの枩床での抵抗の補正係数を芋぀けるこずを提案したす。 ほずんどのサヌミスタは、抵抗の枩床䟝存性がほが線圢であるため、この方法は非垞に信頌性が高いず考えるこずができたす。



実際、プログラム内のすべおの定数は、公差が小さい郚品を䜿甚する堎合に、実際の倀がスキヌムず完党に䞀臎しおいるずいう仮定に基づいお蚈算されたす。



ファヌムりェア



AtmelStudiogcc-avr 5.4.0コンパむラの完党なプロゞェクトアヌカむブはここからダりンロヌドできたす 。たた、既に組み立おられたhexを投皿できたす。 そしお、゜ヌスファむルのcatリストの䞋に、遠くたで行かないようにしたす。



゜ヌスコヌド
//#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.



. , – , – . , . :









, .



, . , . , , .



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


, . , , , 12.5 , , 12.4 . . , .















, , , . , . «» 8-9 .



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


, +50⁰C , . , , , . .



«», , (watchdog). .



, – . . Watchdog , , , . , , , watchdog. , , .


. 1006 , - .



, , . , O2, , Os , 1024 . -, .


.









Eagle .




All Articles