إنه ضار للضوء أو كيفية الحفاظ على شحن بطارية السيارة

ما زلت سلسلة من المقالات حول بناء الدراجات في مجال إدارة دائرة الطاقة المنخفضة الجهد. هذه المرة سأتحدث عن جهاز يمنع التفريغ العميق لبطارية السيارة من قبل مختلف المستهلكين الثانويين.


واحدة من العواقب المحتملة لتفريغ غير المنضبط.

يعد شراء أول سيارة أو دراجة نارية علامة فارقة في حياة كل شخص ، وخاصة المهندس. بعد كل شيء ، من الذي إلى جانب المزايا الواضحة لخيله الحديدي الجديد ، ينتبه على الفور إلى عيوبه غير الواضحة؟ من الذي يبدأ على الفور التفكير في أي تحسينات وإضافات على المعيار؟ بالطبع ، إذا كانت هذه سيارة من الفئة العليا ، وحتى علامة تجارية "عصرية" ، فقد يبدو في البداية أنها تمتلك كل شيء على الإطلاق. ولكن ، كما تبين الممارسة ، في هذه الحالة ، يدحض الوقت الانطباعات الأولى. إذا اشتريت سيارة من الدرجة الاقتصادية ، فبدأ يديك في الحكة حرفيًا في اليوم الأول!

الرغبة في "تحزيم" سيارتك بعدة أجهزة إلكترونية مساعدة أمر طبيعي تمامًا. ومع ذلك ، بعد فترة وجيزة من تنفيذ جميع هذه الخطط ، تواجه الحياة صاحب السيارة مع واقع قاسي. اتضح أنه حتى أحدث الأجهزة المبنية على أحدث قاعدة عنصرية لا تزال حريصة للغاية على الكهرباء. وبطارية السيارة التي تبدو ضخمة جدًا ليست مفاعلًا نوويًا على الإطلاق ، ويمكنها بسهولة "الجلوس" تحت وطأة كل هؤلاء المستهلكين الذين يبدو أنهم غير ضارين في غضون أيام.

لكي لا أتدفق أكثر إلى المواقف المجردة والافتراضية ، سأذهب مباشرة إلى قصتي. بعد شراء سيارة ، كانت الأولى هي الرغبة في وضع المسجل فيها. وقد تم ذلك في أقصر وقت ممكن ، تمليه بالكامل تقريبا سرعة تسليم الطرود من AliExpress. من الواضح أن مصدر الطاقة المنتظم من ولاعة السجائر كان غير مريح للغاية ، وسرعان ما حصل المسجل على اتصال ثابت بأقرب خط من الشبكة على متن الطائرة عبر محول نبض 12 / 5v. ومنذ ذلك الحين بعبارة ملطفة ، وليس بالأمس ، لم يكن هذا المحول حديثًا ، حتى لاحتياجاته الخاصة ، كما اتضح لاحقًا ، كان يتناول ما يصل إلى 21 مللي أمبير حاليًا. الآن دعنا نقدر مقدار قدرة هذا المحول على تغذية بطارية جديدة مشحونة بالكامل بسعة 60 Ah. الحساب بسيط للغاية ومخيب للآمال.


لذلك ، في أقل من أربعة أشهر ، فإن المحول الذي لم يتم تحميله بأي شيء سيهبط البطارية حرفيًا "إلى الصفر". إذا أخذنا في الاعتبار أن بطارية ليست جديدة تمامًا يمكن أن تتحول بسهولة إلى أرملة أقل ، وأن الشحن بعد مدينة pokatushki بعيد عن 100٪ ، يبدأ يوم ممطر بسهولة خلال شهر مع خطاف.

وهذا كل شيء ، وأكرر ، فقط محول الجهد. نعم ، اليوم يمكنك شراء محول لا يحتاج إلا إلى نصف مللي أمبير لاحتياجاته الخاصة ، لكني أعطيت هذا المثال فقط لإظهار كيف أن الماء ببطء وثقة يشحذ حجرًا ، بل حتى يتلاعب ، لكن يتصرف باستمرار ، يستهلك المستهلك الطاقة مما يبدو ضخمًا جدًا البطارية.

نذهب إلى أبعد من ذلك ، يستهلك المسجل في وضع التسجيل FHD @ 30fps حوالي 300 مللي أمبير من مصدر + 5 فولت ، والذي بعد التحويل مع مراعاة الكفاءة يعطي حوالي 150 مللي أمبير من التيار من الشبكة المدمجة. افترض أن المحول قد تم استبداله بواحد حديث ، ونحن نحسب وقت التفريغ فقط مع هذا التيار.


ما يزيد قليلا عن أسبوعين ، ولكن في الممارسة - عشرة أيام. أصبح احتمال الإضاءة (وربما تغيير البطارية) يلوح في الأفق بعد عطلة العمل أو الرحلة التالية.

وهذا ما حدث لي: عندما ذهبت في إجازة قسرية قصيرة ، لم أكن أعتقد أنه خلال أسبوع أو نحو ذلك ، لن يتمكن القفل المركزي من فتح الباب أمامي.

سيقول الكثيرون إن هذا خطأهم ، وأنه يجب إلغاء تنشيط كل شيء ، أو على الأقل إيقاف التسجيل ، وسيكونون على حق. ولكن الحياة هي الحياة ، والذاكرة ليست هي نفسها ، والوقت الذي تستغرقه الإجازة المرضية التي يصعب الوصول إليها لا يمكن دائمًا معرفته مقدمًا. لذلك ، نشأت فكرة قاطع الدائرة على الفور.

بالطبع ، هناك خيار لتشغيل المسجل من مفتاح الإشعال حتى يعمل فقط أثناء التنقل ، ولكن هذا الخيار أيضًا ليس شديدًا ، لأنه إذا كانت السيارة تصطدم بموقف السيارات ، أود أن أحصل على فرصة لرؤية الجاني. بالإضافة إلى ذلك ، بعد وقت قصير من تثبيت المسجل ، كانت السيارة تعاني من نقص في عدد من الأجهزة الأخرى ، بما في ذلك تعقب GPS مخفي ، والذي يجب أن يعمل ، إن لم يكن حتى النهاية ، ثم على الأقل حتى عندما يكون "كل شيء تقريبًا" موجودًا بالفعل.

بشكل عام ، لعدة أسابيع من الانعكاس السلبي ، فكرة الجهاز الذي يجب أن يتحكم في جهد الشبكة على متن الطائرة وعلى أساس هذه البيانات للسيطرة على امدادات الطاقة لمجموعتين من المستهلكين: الثانوية (مسجل ، USB مقبس) والأساسية (GPS- تعقب وعدد قليل ذلك).

كيف يمكن القيام بذلك


"النماذج" الافتراضية الأولى للجهاز "بنيت" على أساس المقارنة التناظرية LM393N وكانت قادرة على كل ما كان مخططا في الأصل ليتم تلقيها من الجهاز. وكان مخطط مجردة شيء من هذا القبيل.


هنا يتم استخدام اثنين من المقارنة لتبديل الأحمال. مولد جهد مرجعي شائع ، فواصل يحددان عتبات التشغيل ، مقارنات الربط ، مفتاحان للطاقة. تم التخطيط للربط الخارجي للجهاز النهائي على النحو التالي.


يظل المفتاح الأساسي قيد التشغيل لفترة أطول من المفتاح الثانوي ، لذلك يتم تشغيل المحول التدريجي نفسه من خلاله. ترتبط الأحمال الأولية مباشرة بالمحول. يعمل المفتاح الثانوي على تحويل الأحمال الثانوية الموجودة بالفعل في دائرة + 5 فولت عند خرج العاكس.

ما خرج في النهاية


يبدو أن كل ما هو مطلوب ، ولكن ، كما يحدث في كثير من الأحيان ، مع التفكير في التفاصيل ، ظهرت أفكار للتطبيقات البديلة. أولاً ، احتوت الدائرة التناظرية على جبل لائق من العناصر المنفصلة التي توفر أوضاع تشغيل المقارنة ، وثانياً ، من المفترض أن يتم تعيين عتبات الرحلة باستخدام مقاومات القطع ، مما يعقد الإعداد ويخلق احتمال "الابتعاد" عن الهز والوقت. لذلك ، في النهاية ، تقرر التعمق في تطبيق رقمي ، والذي اتضح أنه أبسط بكثير من الناحية التخطيطية والإعدادية ، مع إتاحة فرص هائلة لتحسين خوارزمية التحكم ، والأهم من ذلك ، في هذا السياق ، اتضح أن الأمر كان أكثر ضخامة من حيث الاستهلاك الحالي.

طلبت وحدة التحكم ATtiny13A ببساطة قلب الجهاز ، والذي ، بالإضافة إلى سهولة الاستخدام والرخيصة ، لا يزال متاحًا في علبة DIP الأنبوبة الدافئة لدافئة oldfag. في البداية ، كانت قدرات حتى وحدة التحكم الصغيرة هذه زائدة عن الحاجة على جميع الجبهات ، من عدد المدخلات / المخرجات إلى مقدار البرنامج وذاكرة الوصول العشوائي ، ومع ذلك ، كما تعلمون ، تأتي الشهية مع وجبة. نتيجة لذلك ، وبالنظر إلى المستقبل ، أقول إن الإصدار النهائي للحالة هو كل استنتاجات الدائرة الصغيرة ، ولم يكن هناك أكثر من عشرين بايت من ذاكرة البرنامج المجانية.

لقياس جهد الشبكة على متن الطائرة ، تطلب متحكم إدخال واحد فقط ، وهو مرتبط إلى ADC. نواتج أكثر منطقية هما إدارة المستهلكين. بادئ ذي بدء ، بعد الانتقال الذهني النهائي إلى "الرقمية" ، كانت هناك رغبة في تكييف اثنين من GPIOs الحرة لرجال الأعمال ، والقرار لم يمض وقت طويل في المقبلة. عندما قام المبدئ ، مرة أخرى ، في البرد ، بإدارة المحرك بدموع مخفي بشكل سيء ، بدا وجود مستشعر درجة الحرارة في الدائرة والخوارزمية مفيدًا للغاية. نتيجة لذلك ، تم استخدام ADC الثاني لقياس درجة الحرارة. ولكي يستهلك الثرمستور التيار فقط عند الحاجة ، تقرر تشغيله من آخر خرج منطقي.

نتيجة لذلك ، اكتسب مخطط الجهاز مثل هذا النموذج النهائي.


هنا نرى الحد الأدنى من التفاصيل ، ومن بينها لا شيء يخضع لأي نوع من "التواء". دعنا نذهب لفترة وجيزة على النقاط الرئيسية.

بالنسبة لامدادات الطاقة ، تحتاج وحدة التحكم إلى جهد ثابت من 1.8 إلى 5.5 فولت ، مما يعني أنه يجب أن يكون هناك مثبت في الدائرة يخفض جهد الشبكة الموجودة على اللوحة إلى المستوى المطلوب. من وجهة نظر توفير الطاقة ، قد يبدو أن هناك مكانًا لمحول التدريجي النبضي الحصري ، ولكن هذا للوهلة الأولى فقط. الحقيقة هي أن ATtiny13A حتى في وضع التشغيل الأكثر استخدامًا للطاقة (تردد 8 ميجاهرتز ، تنفيذ الرمز النشط) لا يستهلك أكثر من 6 مللي أمبير. في هذا المخطط ، تكون وحدة التحكم 99٪ من الوقت في وضع السكون العميق وتعمل أيضًا بتردد قدره 1.2 ميجاهرتز ، مما ينتج عنه معدل استهلاك أقل من 15 15A تقريبًا. زائد ، ما يقرب من 80 µA إلى التيارات الأساسية من الترانزستورات التحكم (إذا كان كلا الأحمال قيد التشغيل). حسنًا ، بالنسبة لجزء صغير من الثانية ، يتم تنشيط قوة الثرمستور ، مما يضيف حوالي 25 ميكرومتر إلى متوسط ​​التيار. وإليكم الإجابة على السؤال "هل يستحق الأمر تحميل محول نبض من أجل حمولة لا يتجاوز استهلاكها 120 أمبير؟" لا يبدو ذلك واضحًا جدًا. وإذا اعتبرنا أننا نتعامل مع القياسات التناظرية ، فمن المؤكد أنها لا تستحق ذلك. لذلك ، تم استخدام جهاز التثبيت الخطي LP2950 ، وهو تمثيلي وظيفي لجهاز 78L05 الشهير ، ولكنه أكثر اقتصادا. يمكن لهذا المحول أن يعطي ما يصل إلى 100 مللي أمبير من التيار عند الخرج ، بينما يستهلك ما لا يزيد عن 75 ميكروليتر للعزيز.

يسمح لك مقسم الجهد للشبكة الداخلية ، المحمي بواسطة صمام ثنائي زينر ومكثف ، بقياس الفولتية حتى 15 فولت.

أعلم أن موجة النقد الآن ستضربني لمثل هذا القرار ، لكننا سنكون موضوعيين. أولاً ، لا أقوم بتطوير قمر صناعي ، وثانياً ، لا يوجد عامل وحيد من شأنه أن يؤدي إلى كارثة. مقاومة الكتفين مرتفعة ، حيث يمكن لصمام ثنائي زينر أن يحول تيارًا أكثر بكثير من الذي يمكن أن يتدفق عبر الفاصل ، حتى في السيناريو الأكثر تشاؤماً. من النبضات عالية التردد ، عندما لا يكون لثنائي زينر سرعة كافية ، فإن المكثف C2 سوف يحمي (مع المقاوم R7 فإنه ينشئ مرشح تمرير منخفض مع تردد قطع 7 هرتز فقط). D1 و R6 إلى حد ما تأمين المخطط من السقوط بعضها البعض. ولا ينبغي لأحد أن ينسى الخطية ، فإن أي طريقة للعزل الجلفاني في مثل هذا المكان ستجعل الحساب النظري للقيم غير واقعي تمامًا ، وسيتعين علينا معايرة النموذج الأولي على الأقل ، لكننا لسنا بحاجة إليه.

تكون مقاومة خرج المقسم أعلى بعشرة أضعاف من 10 كيلو أوم الموصى بها لمصدر إشارة ADC ، ولكن بفضل المكثف C2 لا توجد مشاكل في القياس.

بشكل عام ، يتم الإعلان عن ممانعة إدخال دارات ADC لوحدات التحكم AVR وفقًا لصفحة البيانات على الأقل 100 ميجا أوم. ومع ذلك ، توصي ورقة البيانات نفسها باستخدام مصادر ذات مقاومة داخلية تصل إلى 10 كيلو أوم. لماذا هذا النقطة هي مبدأ تشغيل ADC نفسه. يعمل المحول على مبدأ التقريب المتسلسل ، ودائرة الإدخال هي مرشح تمرير منخفض من المقاوم والمكثف. الحصول على عينة من 10 بتات أمر تكراري ، ومن الضروري أن يتم شحن المكثف إلى الجهد المقاس الكامل خلال وقت القياس بأكمله. إذا كانت مقاومة الخرج للمصدر كبيرة جدًا ، فسيستمر تحميل المكثف مباشرة خلال عملية التحويل وستكون النتيجة غير دقيقة. في حالتنا ، تبلغ السعة C2 أكثر من سبعة آلاف مرة من قدرة مرشح ADC ، مما يعني أنه عندما يتم إعادة توزيع الشحنة بين هذه المكثفات عند تشغيلها في وقت القياس ، فإن جهد الدخل سينخفض ​​بما لا يزيد عن 1/7000 ، أي سبع مرات أقل من الدقة النهائية ل ADC 10 بت. صحيح ، عليك أن تضع في اعتبارك أن هذه الخدعة تعمل فقط على قياسات فردية مع فترات توقف كبيرة بينها ، لذلك يجب ألا "تحسن" برنامج التحكم عن طريق إضافة دورة إليها لعدة قياسات متتالية مع متوسط ​​النتيجة.

تم تصميم الفاصل ذو الثرمستور بسبب وجود مصدر طاقة متحكم به باستخدام التصنيفات الموصى بها. يستخدم NTCLE100E3 كجهاز استشعار ، ولكن لا توجد قيود ، يمكنك استخدام أي الثرمستور من نفس التصنيف تقريبًا ، والشيء الرئيسي هو إجراء تصحيحات تتوافق مع خصائصه في ثوابت شفرة المصدر بحيث يتم تحويل جهد الفاصلة إلى قيمة درجة الحرارة الصحيحة.

كمفاتيح تحكم ، يتم استخدام منافذ MOSFET ذات القنوات الكهربائية من أي نوع بمقاومة قناة مفتوحة مقبولة وأقصى جهد مصدر تصريف يبلغ 30 فولت على الأقل. الدائرة أعلاه يستخدم الترانزستورات المختلفة. يتم ذلك لأن عليهم تبديل الفولتية المختلفة ونوع كل منهم تم اختياره لظروف عمل محددة. يجب أن يكون الترانزستور العلوي عالي الجهد ، ويجب أن يكون للجزء السفلي ، إن أمكن ، مقاومة دنيا للقناة المفتوحة. ولكني أكرر أن هذا القرار تمليه دائرة تبديل الجهاز (انظر أعلاه) ، مع إدراج آخر قد تكون متطلبات الترانزستور السفلي مختلفة.

للسيطرة على مفاتيح الطاقة ، يتم استخدام زوج من الترانزستورات متطابقة القطبين. في البداية قد يبدو أن هذه الترانزستورات لا لزوم لها ، ولكن هنا ليس بهذه البساطة. تبدأ الترانزستورات ذات التأثير الميداني مع بوابة معزولة في فتح ليس من أي جهد من القطبية المطلوبة على البوابة ، ولكن فقط بعد الوصول إلى مستوى عتبة معين ، والذي يظهر في أوراق البيانات تحت اسم "الجهد عتبة بوابة إلى مصدر" وعادة ما يساوي 2..4 V. والآن دعونا مجرد عد. يمكن أن تشكل دائرة خرج وحدة التحكم مستويين منطقيين: المنطقي "0" مع الجهد تميل إلى الصفر. والمنطقية "1" مع الجهد تميل إلى العرض. عند تشغيله بواسطة 5 فولت ، ستكون هذه الفولتية حوالي 0 و 5 فولت ، على التوالي. نتيجة لذلك ، عند تبديل مصدر بجهد 12 فولت ، فإن "0" منطقية على البوابة ستحدث فرق جهد لبوابة المصدر 12 - 0 = 12 فولت ، يكون ترانزستور الطاقة مفتوحًا. يبدو أن كل شيء طبيعي ، لكن المنطق "1" بجهد 5 فولت سيخلق جهدًا بين 12 - 5 = 7 فولت بين المصدر والبوابة ، وسيظل ترانزستور الطاقة مفتوحًا. وبالتالي ، لا يمكن لإشارة التحكم بخمسة فولت أن تتحكم في المفتاح ، الذي يخفف الجهد فوق 7..9 فولت. لذلك ، لا تعمل الترانزستورات ثنائية القطب للتحكم في الواقع مع مفاتيح الإشارات بقدر ما تعمل مكبرات الصوت التي ترفع فولطية التحكم من 5 فولت إلى فولطية الشبكة الموجودة على متن الطائرة.

يحد المقاوم في الدائرة الأساسية لكل من ترانزستورات التحكم ببساطة تيار مخرجات التحكم إلى مستوى يكفي للتحكم فيه. يمكن تخفيض تصنيفاتها مرتين إلى ثلاث مرات دون عواقب لتشغيل الدائرة.

من السهل أن نرى أن ترانزستورات التحكم لم تكن في الدائرة التناظرية على أساس LM393N. الشيء هو أن مرحلة الإخراج من المقارنة المحددة بنيت وفقا لدائرة جامع مفتوحة ، وهذا هو ، ناتجها ببساطة إخراج جامع الترانزستور الطرفي. يتطلب مبدأ البناء هذا تعليق الأجزاء الإضافية على الشريحة لإنشاء حمل مرحلة الإخراج ، ولكن ، من ناحية أخرى ، يجعل الشريحة مرنة للغاية. يسمح المجمّع المفتوح للمقارن بالتحكم في أي مصدر حالي مقبول ، وليس متوافقًا فقط مع المصدر الذي يوفر الطاقة للمقارِّن نفسه.

لا بد لي من القول إن الحد من الجهد الكهربائي لعتبة MOSFET لا يعمل فقط نحو الفولتية العالية ، كما هو مذكور أعلاه ، ولكن أيضًا على الجهد المنخفض. بعد كل شيء ، إذا كان الحد الأدنى لجهد الفتح للترانزستور ، على سبيل المثال ، 4 فولت ، فعند التبديل بين مصدر 3.3 فولت ، حتى توصيل البوابة بالأرض لن يخلق فرق الجهد المطلوب بين المصدر والبوابة وسيظل الترانزستور مغلقًا. لذا ، فإن 5 فولت ، هي الحد الأدنى من الجهد الذي يمكن تبديله بشكل صحيح بواسطة الترانزستورات المحددة.

تعديل


إعداد الجهاز هو محادثة منفصلة. من ناحية ، لا يوجد عنصر ضبط واحد في الدائرة ، ولكن من ناحية أخرى ، نحن نتعامل مع قياس الفولتية بدقة لا تقل عن 0.1 فولت. كيفية ربط كل هذا؟ هناك طريقتان. الأول هو استخدام المقاومات R6 و R7 و R8 بتسامح لا يقل عن 1 ٪ (أو أفضل 0.1 ٪). والثاني ينطوي على استخدام المقاومات التقليدية مع قياس المقاومة الحقيقية وتصحيح المعاملات في شفرة المصدر للبرنامج.

الطريقة الأولى جيدة للإنتاج بالجملة ، لكنها أكثر جاذبية بالنسبة لنا لعدم الاهتمام بالبحث عن القيم العالية الدقة اللازمة ، لذلك دعونا نذهب في الاتجاه الثاني. يمكن قياس المقاومة بمقياس متعدد عادي ، دقتها هنا كافية. كائن آخر للقياس سيكون الجهد من استقرار تغذية الدائرة.يمكن أن تعمل وحدة التحكم ADC بوحدة التحكم في أوضاع مختلفة ، ولكن لعدة أسباب ، من الأنسب لنا استخدام طريقة يتم فيها احتساب نتيجة التحويل الرقمي بالنسبة لجهد الإمداد. هذا هو السبب في أنه من المهم أن نعرف ذلك بأكبر قدر ممكن من الدقة.

الحساب بسيط للغاية ويتألف من حساب معامل القسمة للمقسم المقاوم ونسبة ترجمة النتيجة في LSB أثناء التحويل التمثيلي إلى الرقمي.


Ux هو جهد الدخل من الفاصل ؛
Ru هي مقاومة الذراع العليا للمقسّم (التي يتم تزويد Ux بها) ؛
Rd هي مقاومة ذراع المقسم السفلي (التي ترتبط بالأرض) ؛
Uref - الجهد المرجعي لـ ADC (أي جهد تزويد جهاز التحكم) ؛
1024 - عدد القيم المنفصلة عند إخراج ADC 10 بت ؛
LSB هي القيمة العددية التي حصل عليها البرنامج من ADC.

لنبدأ مع مقسم الجهد R6-R7. على سبيل المثال ، نأخذ مقاومات حقيقية تتوافق تمامًا مع تلك المشار إليها في المخطط. نأخذ أيضًا طاقة 5.0 فولت تمامًا ، مثال على حساب نتائج تحويل الجهد بجهد 13.5 فولت:


مع الدقة المناسبة في قياس القيم الأولية ، تكون النتيجة دقيقة بما يكفي لعدم ضبطها في الاختبارات الميدانية ، ولكن لاستخدامها كما هي.

لا تختلف صيغة حساب مقسم درجة الحرارة ، من حيث المبدأ ، فقط رو سيكون متغيرًا ، ويمكن اعتبار Ux مساوية لـ Uref. النتيجة ستبدو هكذا:


على سبيل المثال ، لنأخذ قيمة R8 من الدائرة ، و R9 من ورقة البيانات على NTCLE100E3 عند درجة حرارة 0 درجة مئوية:


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

يمكن العثور على مقاومة الثرمستور في درجات حرارة مختلفة في ورقة البيانات ، ولكن من الأفضل ، بالطبع ، قياسها بنفسك. قد يمثل الأخير بعض المشكلات ، لذلك ، كحل وسط ، أقترح قياس المقاومة الحقيقية عند درجة حرارة واحدة وبالتالي العثور على معامل التصحيح للمقاومة في جميع درجات الحرارة الأخرى من ورقة البيانات. تعتمد معظم الثرمستورات اعتمادًا خطيًا أو أقل للمقاومة على درجة الحرارة ، لذلك يمكن اعتبار هذه الطريقة موثوقة تمامًا.

في الواقع ، يتم حساب جميع الثوابت في البرنامج على أساس افتراض أن القيم الحقيقية تتوافق تمامًا مع المخطط ، في حالة استخدام أجزاء ذات تفاوتات صغيرة.

إدراج


يمكن تنزيل أرشيف المشروع الكامل لـ AtmelStudio (برنامج التحويل البرمجي gcc-avr 5.4.0) هنا ، كما تم نشر الملف سداسي عشرية مجمّع بالفعل . وتحت قائمة القط للملف المصدر ، حتى لا تذهب بعيدا.

شفرة المصدر
//#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 فولت. هذا المبدأ نفسه يمنع بشكل كبير تبديل الحالة تحت تأثير ضوضاء وتداخل ADC. يمكن أيضًا تغيير الثوابت التي تحدد عرض التباطؤ رأسياً وأفقياً.



بالإضافة إلى التباطؤ ، تتم أيضًا إضافة تأخيرات تبديل الحالة إلى البرنامج بشكل أساسي لحالات التغيرات في الجهد قصير الأجل ، على سبيل المثال ، عند بدء تشغيل المحرك. من غير المنطقي لمدة ثلاث ثوان أن تفصل حمولة نصف أمبير عند استخدام المبدئ ، وتستهلك عدة مئات من الأمبيرات. حتى لو انخفض جهد النظام الكهربائي في هذه اللحظة إلى "طوارئ" 8-9 فولت بالكامل.

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

لا يحتوي الصف العلوي من الجدول على التباطؤ أو تأخير الانتقال ، وعندما تتجاوز درجة الحرارة + 50 درجة مئوية من الأسفل إلى الأعلى ، يتم إيقاف تشغيل الأحمال على الفور ودون قيد أو شرط. أيضًا ، تم تقديم مهلة منفصلة للخروج لهذا الوضع ، أكبر بكثير من تلك المستخدمة عند عبور حدود الخلية المتبقية. لا يمكن العودة من هذا الوضع إلا إذا كانت مؤشرات درجة الحرارة أقل من العتبة أثناء المهلة بأكملها.

يتم استخدام الوكالة الدولية للطاقة باعتبارها "القلب" الذي يحدد دورة القياس. تم تكوينه لإنشاء مقاطعة بدلاً من إعادة التشغيل القسري وببساطة يستيقظ وحدة التحكم بشكل دوري من النوم العميق لدورة العمل التالية.

قد يبدو أن مثل هذا الحل يحرمنا من الوظيفة الرئيسية لجهاز توقيت الوكالة - لمنع التجميد. ولكن في الواقع هذا ليس كذلك. تم تصميم Watchdog بحيث يتم إعادة تعيين العلامة التي تتضمن إنشاء مقاطعة بدلاً من إعادة التشغيل بواسطة الأجهزة في كل مرة يتم تشغيلها. وبالتالي ، إذا تجمد جهاز التحكم ، فلن يتم تنفيذ حلقة البرنامج الرئيسية فقط ، ولكن أيضًا معالج المقاطعة التابع للوكالة الدولية للطاقة. هذا يعني أن العملية الأولى ستقوم بمسح علامة المقاطعة ، وستبدأ العملية التالية بالفعل إعادة تشغيل كاملة.

يتم تعليق بقية رمز البرنامج بالكامل ولا يحتاج إلى تحليل مفصل. تبين أن حجم البرنامج الثابت النهائي هو 1006 بايت ، مما يترك احتياطيًا صغيرًا لتصحيح واحد أو اثنين من التصحيحات الطفيفة.

بالمناسبة ، يعتمد حجم الثنائي النهائي على خيارات التحسين ، وهو أمر منطقي. ولكن كما اتضح ، في حالتي ، أعطى خيار O2 الحد الأدنى للطول ، بينما أنتج خيار Os ، المصمم لتقليل الحجم ، رمزًا بالكاد يصل إلى 1024 بايت المخصصة. لذلك إذا كان البرنامج في حدود إمكانات جهاز التحكم ولم يكن مناسبًا تمامًا ، فمن المنطقي دائمًا اللعب مع هذه المعلمة.

في الواقع ، لا يلمع الكود مع الأمثلية من حيث الحجم ، وإذا رغبت في ذلك ، يمكن تقليصه بمئات البايتات الأخرى دون فقدان الوظيفة.

مجلس التخطيطي وملفات الأسلاك

Eagle .

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


All Articles