في الجزأين السابقين ، تحدثت عن كيفية إنشاء واجهة المستخدم الرسومية ، وبدأت في التحكم في محرك متدرج وتنظيم العمل مع الملفات الموجودة على محرك أقراص فلاش USB.
سأكتب اليوم عن عملية الطباعة ، وإخراج الطبقات المطبوعة على شاشة التمييز ، والأشياء المتبقية ، غير الضرورية جدًا:
4. إخراج صور الطبقات إلى شاشة التمييز.
5. كل شيء صغير ، مثل التحكم في الإضاءة والمراوح ، وتحميل وحفظ الإعدادات ، إلخ.
6. ميزات إضافية للراحة والملاءمة.
- الجزء 1: 1. واجهة المستخدم.
- الجزء 2: 2. العمل مع نظام الملفات على محرك أقراص فلاش USB. 3. التحكم في محرك متدرج لحركة المنصة.
- 3: 4. . 5. , .. 6. .
4.
4.1 -
كيف يمكن للمتحكم الدقيق ، الذي لا يحتوي على أجهزة طرفية متخصصة ، أن يكون قادرًا على جعل الصورة على مصفوفة عالية الدقة بسرعة 74 مليون بكسل في الثانية (دقة 2560 × 1440 ، 20 إطارًا في الثانية) يتم تحديثها عبر واجهة MIPI؟ الإجابة: استخدام FPGA مع ذاكرة SDRAM بسعة 16 ميجابايت متصلة بها وشريحتي واجهة MIPI - SSD2828. تستحق دائرتان صغيرتان لأن العرض مقسم منطقيًا إلى نصفين ، يتم خدمة كل منهما بواسطة قناته المنفصلة الخاصة به ، لذلك يتم الحصول على شاشتين في واحد.
يتم تخزين صورة العرض في أحد بنوك SDRAM الأربعة ، وتكون شريحة FPGA مسؤولة عن خدمة SDRAM وإخراج الصورة منها إلى SSD2828. يولد FPGA إشارات التزامن الرأسية والأفقية لمحركات الأقراص SSD2828 ومحركات الأقراص
تدفق مستمر لقيم ألوان البكسل عبر 24 سطرًا (8R 8G 8B) في كل من SSD2828. تبين أن معدل الإطارات يبلغ حوالي 20 هرتز.
يتم توصيل FPGA بالميكروكونترولر بواجهة تسلسلية (SPI) يمكن من خلالها للميكروكونترولر نقل الصورة. يتم إرساله في حزم ، يحتوي كل منها على سطر واحد من الصورة (يتم حساب الخطوط على طول الجانب القصير من الشاشة - 1440 بكسل). بالإضافة إلى هذه البيانات ، تحتوي الحزمة أيضًا على رقم بنك SDRAM ورقم السطر والمجموع الاختباري - CRC16. تتلقى FPGA هذه الحزمة ، وتتحقق من المجموع الاختباري ، وإذا كان كل شيء على ما يرام ، يحفظ البيانات في منطقة SDRAM المناسبة. إذا لم تتطابق CRC ، تقوم FPGA بتعيين إشارة على أحد أطرافها ، وهي متصلة أيضًا بالمتحكم الدقيق ، والتي بموجبها يفهم المتحكم الدقيق أن البيانات لم تصل بشكل طبيعي ويمكنه تكرار الإرسال. للحصول على صورة كاملة ، يجب على المتحكم الدقيق إرسال 2560 من هذه الحزم إلى FPGA.
يتم تمثيل بيانات الصورة داخل الحزمة بتنسيق بت: 1 - بكسل مضاء ، 0 - بكسل مظلمة. للأسف ، هذا يستبعد تمامًا إمكانية تنظيم ضبابية التدرج الرمادي لحواف الطبقات المطبوعة - الصقل. لتنظيم طريقة التعتيم هذه ، من الضروري إعادة كتابة التكوين (البرنامج الثابت) لـ FPGA ، والذي لست مستعدًا له بعد. لفترة طويلة جدًا وليس طويلة جدًا ، كنت أعمل مع FPGA ، وسأضطر عمليًا إلى إعادة إتقان كل شيء.
بالإضافة إلى حزم البيانات ، يمكن للمتحكم الدقيق أيضًا إرسال أمر تحكم للإشارة إلى بنك SDRAM لقراءة البيانات للإخراج على الشاشة وتشغيل / إيقاف إخراج الصورة.
ترتبط رقائق SSD2828 أيضًا بالمتحكم الدقيق عبر SPI. يعد ذلك ضروريًا لتكوين سجلاتهم عند تشغيلها ، أو نقلها إلى وضع السكون أو الوضع النشط.
هناك عدة خطوط أخرى بين المتحكم الدقيق و FPGA / SSD2828 - إشارة إعادة الضبط وإشارات تحديد الشريحة لكل من الدوائر الدقيقة.
بشكل عام ، مخطط العمل هذا بعيد كل البعد عن الأمثل ، في رأيي. على سبيل المثال ، سيكون من المنطقي توصيل FPGA بالمتحكم الدقيق عبر واجهة ذاكرة خارجية متوازية ، وسيتم نقل البيانات بشكل أسرع بكثير من عبر SPI بحد تردد يبلغ 20 ميجاهرتز (عندما يرتفع التردد ، يتوقف FPGA عن تلقي البيانات بشكل طبيعي). بالإضافة إلى ذلك ، لا يتم توصيل إشارة إعادة التعيين بإدخال FPGA المادي لإعادة الضبط ، ولكن كإشارة منطقية عادية ، أي أن FPGA لا تقوم بإعادة ضبط الأجهزة عليها. وقد لعب هذا أيضًا نكتة قاسية ، سيتم مناقشتها أدناه.
لقد اكتشفت كل هذا من خلال فهم الكود المصدري للشركة المصنعة. لقد قمت بنقل وظائف العمل مع FPGA من شفرة المصدر الخاصة بهم كما هي ، وما زلت لا أفهم تمامًا كيف يعمل كل شيء. لحسن الحظ ، علق الصينيون على الكود الخاص بهم بما يكفي (باللغة الصينية) ليكونوا قادرين على اكتشافه دون صعوبة كبيرة.
4.2 قراءة الطبقات من ملف طباعة
حسنًا ، لقد قمنا بفرز مخرجات الصورة النهائية بشكل أو بآخر ، والآن سأخبرك قليلاً عن كيفية استخراج هذه الصور من الملفات المعدة للطباعة. ملفات .Pws و .photons و .hoton و .cbddlp هي في الأساس مجموعة من صور الطبقات. جاء هذا التنسيق ، على حد علمي ، من شركة Chitu الصينية ، التي أتت بفكرة صنع لوحات بمثل هذه الدائرة (متحكم - FPGA - SDRAM - SSD2828). لنفترض أنك تريد طباعة نموذج بارتفاع 30 مم وبسمك كل طبقة 0.05 مم. يقوم برنامج التقطيع بتقطيع هذا النموذج إلى طبقات بالسماكة المحددة ولكل منها يشكل صورته.
وبالتالي ، يتم الحصول على 30 / 0.05 = 600 صورة بدقة 1440 × 2560. يتم حزم هذه الصور في ملف الإخراج ، ويتم إدخال الرأس مع جميع المعلمات هناك ، ويتم إرسال هذا الملف بالفعل إلى الطابعة. صور الطبقة عميقة 1 بت ويتم ضغطها بواسطة خوارزمية RLE بايت واحد في كل مرة ، مع البت الأكثر أهمية يشير إلى قيمة اللون وتمثل البتات السبع الأقل أهمية عدد التكرارات. تسمح لك هذه الطريقة بضغط صورة الطبقة من 460 كيلوبايت إلى حوالي 30-50. تقرأ الطابعة الطبقة المضغوطة وتفك ضغطها وترسلها سطراً بسطر إلى FPGA.
تقوم الشركة المصنعة بذلك على النحو التالي:
- — 1, 1, 0. , (1440), .
- , 1440 (180 ).
- FPGA .
هذه هي الطريقة المكونة من ثلاث خطوات التي يستخدمها الصينيون. كما اتضح ، تم القيام بذلك بحيث يمكن عرض صورة الطبقة في شكل مختزل على شاشة الواجهة ، مما يُظهر للمستخدم ما يتم طباعته. تم تشكيل هذه الصورة للتو من مجموعة البايت. على الرغم من أن ما منع من تكوينه على الفور من البتات التي تم فك تشفيرها غير واضح. وما منع تكوين صورة نقطية لنقلها إلى FPGA في نفس الدورة غير واضح أيضًا.
الآن أستخدم نفس الطريقة ، وإن كانت محسّنة. لتوضيح ماهية التحسين ، أحتاج إلى توضيح نقطة أخرى. بيانات خط العرض ليست مصفوفة صلبة من الحمولة. يوجد في المنتصف عدد قليل من وحدات البكسل "غير العاملة" نظرًا لحقيقة أن جهازي تحكم في العرض متصلان على الجانب القصير ، ولكل منهما 24 بكسل "غير عاملة" عند الحواف. وبالتالي ، فإن البيانات المرسلة الفعلية لسطر واحد من الصورة تتكون من 3 أجزاء: بيانات النصف الأول (وحدة التحكم الأولى) ، وسيطة "غير عاملة" 48 بكسل ، وبيانات النصف الثاني (وحدة التحكم الثانية).
لذلك ، عند تكوين مصفوفة البايت داخل الحلقة ، قام الصينيون بفحص ما إذا كان قد تم الوصول إلى نهاية النصف الأول ، وإذا لم يكن الأمر كذلك ، فإن القيمة تمت كتابتها بواسطة المؤشر *، وإلا عن طريق المؤشر * (ص + 48) . من الواضح أن هذا التحقق من كل من قيم 1440 ، وحتى تعديل المؤشر لنصفها ، لم يساهم في سرعة الحلقة. لقد قسمت هذه الحلقة الواحدة إلى حلقتين منفصلتين - في الأول ، يتم ملء النصف الأول من المصفوفة ، وبعد هذه الحلقة ، يزداد المؤشر بمقدار 48 وتبدأ الحلقة الثانية للنصف الثاني من المصفوفة. في الإصدار الأصلي ، تمت قراءة الطبقة وعرضها في 1.9 ثانية ، وقد أدى هذا التعديل وحده إلى تقليل وقت القراءة والإخراج إلى 1.2 ثانية.
يتعلق تغيير آخر بنقل البيانات إلى FPGA. في المصادر الأصلية ، يحدث ذلك من خلال DMA ، ولكن بعد بدء النقل عبر DMA ، تنتظر الوظيفة اكتمالها وبعد ذلك فقط تبدأ في فك تشفير وتشكيل سطر جديد من الصورة. لقد أزلت هذا التوقع بحيث يتم إنشاء السطر التالي أثناء نقل البيانات من السطر السابق. أدى هذا إلى تقليل الوقت بمقدار 0.3 ثانية أخرى ، إلى 0.9 لكل طبقة. وهذا عند التجميع بدون تحسين ، إذا قمت بالتجميع باستخدام التحسين الكامل ، فسيقل الوقت إلى حوالي 0.53 ثانية ، وهو أمر مقبول بالفعل. من بين هذه 0.53 ثانية ، يستغرق الأمر حوالي 0.22 ثانية لحساب CRC16 وحوالي 0.19 ثانية لتكوين صورة نقطية من صفيف بايت قبل الإرسال. لكن نقل جميع الخطوط إلى FPGA نفسها يستغرق حوالي 0.4 ثانية ومع هذا ، على الأرجح ،لا يمكن فعل أي شيء - كل شيء هنا يعتمد على الحد الأقصى لتردد SPI المسموح به لـ FPGA.
إذا كان بإمكاني كتابة تكوين FPGA بنفسي ، فيمكنني إعطائه فك ضغط RLE ، ويمكن أن يؤدي ذلك إلى تسريع إخراج الطبقة بترتيب من حيث الحجم ، ولكن كيف يتم ذلك؟
ونعم ، كنت سأكتب عن الدعامة المرتبطة بحقيقة أن FPGA لا تتم إعادة ضبطه بواسطة الأجهزة على إشارة إعادة التعيين من وحدة التحكم الدقيقة. لذلك ، عندما تعلمت بالفعل كيفية عرض صور الطبقات ، أكملت عملية الطباعة نفسها ، واجهت خطأ غير مفهوم - بمجرد الخروج من 5-10 ، بدأت الطباعة بشاشة مضاءة بالكامل. أرى في مصحح الأخطاء أن الطبقات تُقرأ بشكل صحيح ، ويتم إرسال البيانات إلى FPGA حسب الحاجة ، ويؤكد FPGA صحة CRC. أي أن كل شيء يعمل ، وبدلاً من رسم طبقة - شاشة بيضاء تمامًا. من الواضح أن اللوم يقع على FPGA أو SSD2828. مرة أخرى ، قمت بمراجعة تهيئة SSD2828 مرة أخرى - كل شيء على ما يرام ، تتم تهيئة جميع السجلات الموجودة فيها بالقيم المطلوبة ، ويمكن ملاحظة ذلك أثناء قراءة التحكم للقيم منها. ثم وصلت بالفعل إلى اللوحة باستخدام الذبذبات. واكتشفت أنه عند حدوث مثل هذا الفشل ، لا تكتب FPGA أي بيانات إلى SDRAM. إشارة WE ،السماح بالكتابة ، يقف متجذرًا في البقعة في المستوى غير النشط. وربما كنت سأقاتل مع هذا العيب لفترة طويلة ، لولا صديق نصحني بمحاولة إعطاء FPGA أمرًا صريحًا لإيقاف إخراج الصورة قبل إعادة التعيين ، بحيث في وقت إعادة التعيين ، لا توجد مكالمات مضمونة من FPGA إلى SDRAM. حاولت ذلك وأنه يعمل! هذا الخطأ لم يظهر نفسه مرة أخرى. في النهاية ، توصلنا إلى استنتاج مفاده أن IP-core لوحدة تحكم SDRAM داخل FPGA لم يتم تنفيذه بشكل صحيح تمامًا ، ولا تحدث إعادة تعيين وتهيئة وحدة تحكم SDRAM بشكل طبيعي في جميع الحالات. هناك شيء يتداخل مع إعادة الضبط الصحيحة إذا تم الوصول إلى البيانات الموجودة في SDRAM في هذه اللحظة. مثله…الذي نصح بالمحاولة قبل إعادة الضبط لإعطاء FPGA أمرًا صريحًا لإيقاف تشغيل إخراج الصورة ، بحيث في وقت إعادة التعيين ، يتم ضمان عدم وجود مكالمات من FPGA إلى SDRAM. حاولت ذلك وأنه يعمل! هذا الخطأ لم يظهر نفسه مرة أخرى. في النهاية ، توصلنا إلى استنتاج مفاده أن IP-core لوحدة التحكم SDRAM داخل FPGA لم يتم تنفيذه بشكل صحيح تمامًا ، ولا تحدث إعادة تعيين وتهيئة وحدة تحكم SDRAM بشكل طبيعي في جميع الحالات. هناك شيء يتداخل مع إعادة الضبط الصحيحة إذا تم الوصول إلى البيانات الموجودة في SDRAM في هذه اللحظة. مثله…الذي نصح بالمحاولة قبل إعادة الضبط لإعطاء FPGA أمرًا صريحًا لإيقاف إخراج الصورة ، بحيث في وقت إعادة التعيين ، لا توجد مكالمات من FPGA إلى SDRAM. حاولت ذلك وأنه يعمل! هذا الخطأ لم يظهر نفسه مرة أخرى. في النهاية ، توصلنا إلى استنتاج مفاده أن IP-core لوحدة تحكم SDRAM داخل FPGA لم يتم تنفيذه بشكل صحيح تمامًا ، ولا تحدث إعادة تعيين وتهيئة وحدة تحكم SDRAM بشكل طبيعي في جميع الحالات. هناك شيء يتداخل مع إعادة الضبط الصحيحة إذا تم الوصول إلى البيانات الموجودة في SDRAM في هذه اللحظة. مثله…أن IP-core لوحدة تحكم SDRAM داخل FPGA لم يتم تنفيذه بشكل صحيح تمامًا ، وإعادة تعيين وحدة تحكم SDRAM وتهيئتها لا تعمل بشكل طبيعي في جميع الحالات. هناك شيء ما يمنع إعادة الضبط الصحيحة إذا تم الوصول إلى البيانات الموجودة في SDRAM في هذه اللحظة. مثله…أن IP-core لوحدة تحكم SDRAM داخل FPGA لم يتم تنفيذه بشكل صحيح تمامًا ، وإعادة تعيين وحدة تحكم SDRAM وتهيئتها لا تعمل بشكل طبيعي في جميع الحالات. هناك شيء يتداخل مع إعادة الضبط الصحيحة إذا تم الوصول إلى البيانات الموجودة في SDRAM في هذه اللحظة. مثله…
4.3 واجهة المستخدم أثناء طباعة الملف
بعد تحديد المستخدم للملف والبدء في طباعته ، تظهر الشاشة التالية:
هذه شاشة قياسية إلى حد ما لطابعات فوتوبوليمير.
تشغل صورة الطبقة المكشوفة حاليًا أكبر مساحة من الشاشة.
تتم مزامنة عرض هذه الصورة مع الإضاءة الخلفية - عند تشغيل الإضاءة الخلفية ، يتم عرض الصورة ، وعند إيقاف تشغيل الإضاءة الخلفية ، يتم مسح الصورة. تم تشكيل الصورة كما لو كانت شاشة الأشعة فوق البنفسجية - على طول الجانب القصير من الصورة. لم أتسرع في استخدام المؤشرات على طول إزاحات السطر لهذه الصورة ، ولكن قبل عرضها مباشرة ، أعطيت وحدة التحكم في العرض أمرًا لتغيير اتجاه الإخراج للبيانات التي يتم سكبها ، أي تبين أن مساحة هذه الصورة "مقلوبة" على جانبها.
يوجد أدناه معلومات حول تقدم الطباعة - الوقت المنقضي والمقدر للطباعة ، والطبقة الحالية والعدد الإجمالي للطبقات ، وشريط تقدم بنسب مئوية على يمينها. أريد أيضًا إضافة الارتفاع الحالي بالمليمترات بعد عدد الطبقات ، فقط لأكون.
على اليمين توجد أزرار الإيقاف المؤقت والإعدادات والمقاطعة. عندما تضغط على الإيقاف المؤقت في البرنامج الثابت ، يتم تعيين علامة الإيقاف المؤقت ويعتمد السلوك الإضافي على حالة الطابعة في الوقت الحالي. إذا تعطل النظام الأساسي للطبقة التالية أو بدأ تعرض الطبقة بالفعل ، فستكمل البرامج الثابتة التعريض وبعد ذلك فقط سترفع المنصة إلى ارتفاع الإيقاف المؤقت (الذي تم تعيينه في الإعدادات) ، حيث ستنتظر حتى ينقر المستخدم على زر "متابعة":
يحدث رفع النظام الأساسي للإيقاف المؤقت أولاً بالسرعة المحددة في معلمات الملف ، وبعد الارتفاع المحدد في نفس المعلمات ، تزداد السرعة.
عند مقاطعة الطباعة ، ستظهر نافذة تؤكد هذا الإجراء ، وفقط بعد التأكيد ، ستتوقف الطباعة وسترتفع المنصة إلى أقصى ارتفاع للمحور. سرعة الرفع ، وكذلك أثناء الإيقاف المؤقت ، متغيرة - أولاً ببطء لفصل الطبقة عن الفيلم ، ثم زيادتها إلى الحد الأقصى.
لا يعمل زر الإعدادات بعد ، ولكن عند النقر فوقه ، سيتم نقل المستخدم إلى شاشة بها معلمات طباعة يمكن تغييرها - وقت تعرض الطبقة ، وارتفاع وسرعة الرفع ، إلخ. الآن أنا أنهيه. هناك أيضًا فكرة لإعطاء الفرصة لحفظ المعلمات التي تم تغييرها مرة أخرى إلى الملف المطبوع.
5. كل شيء صغير ، مثل التحكم في الإضاءة والمراوح ، وتحميل الإعدادات وحفظها ، إلخ.
تحتوي اللوحة على 3 مخرجات MOSFET عالية الطاقة - واحدة لمصابيح LED للأشعة فوق البنفسجية واثنتان للمراوح (على سبيل المثال ، تبريد صمامات الإضاءة الخلفية وتبريد الشاشة). لا يوجد شيء مثير للاهتمام هنا - مخرجات المتحكم الدقيق متصلة ببوابات هذه الترانزستورات والتحكم فيها سهل مثل وميض LED. للحصول على دقة عالية لوقت التعرض ، يتم تشغيله في الدورة الرئيسية من خلال وظيفة ضبط وقت التشغيل:
UVLED_TimerOn(l_info.light_time * 1000);
void UVLED_TimerOn(uint32_t time)
{
uvled_timer = time;
UVLED_On();
}
ويتم إيقاف تشغيله من مقاطعة الميلي ثانية للمؤقت عندما يصل عداد الإضاءة الخلفية إلى الصفر:
...
if (uvled_timer && uvled_timer != TIMER_DISABLE)
{
uvled_timer--;
if (uvled_timer == 0)
UVLED_Off();
}
...
5.1 الإعدادات ، التحميل من الملف والحفظ في EEPROM
يتم تخزين الإعدادات في EEPROM الموجود على متن الساعة 24c16. هنا ، على عكس تخزين الموارد في ذاكرة فلاش كبيرة ، كل شيء بسيط - لكل نوع من البيانات المخزنة ، يكون عنوان الإزاحة داخل EEPROM مشفرًا بشكل ثابت. في المجموع ، يخزن ثلاث مجموعات: إعدادات المحور Z ، وإعدادات النظام العامة (اللغة ، والصوت ، وما إلى ذلك) وعدادات الوقت لمكونات الطابعة الرئيسية - الإضاءة ، والعرض والمروحة.
تحتوي هياكل الكتل المخزنة على إصدار البرنامج الثابت الحالي ومجموع اختباري بدائي - فقط مجموع 16 بت لقيم جميع البايت في الكتلة. عند قراءة الإعدادات من EPROM ، يتم فحص CRC وإذا لم تتوافق مع القيمة الحقيقية ، ثم يتم تعيين قيم افتراضية لمعلمات هذه الكتلة ، ويتم حساب CRC جديد ويتم حفظ الكتلة في EPROM بدلاً من القديم. إذا كانت كتلة القراءة لا تتطابق مع الإصدار الحالي ، فيجب تحديثها إلى الإصدار الحالي وسيتم حفظها في شكل جديد بدلاً من الإصدار القديم. لم يتم تنفيذ ذلك بعد ، ولكن سيتم القيام به في المستقبل لتحديث البرنامج الثابت بشكل صحيح.
يمكن تغيير بعض الإعدادات من خلال الواجهة ، ولكن لا يمكن تغيير معظمها إلا عن طريق تحميل ملف التكوين. هنا لم أغير عاداتي وكتبت المحلل اللغوي الخاص بي لمثل هذه الملفات.
هيكل هذا الملف قياسي: اسم المعلمة + علامة المساواة + قيمة المعلمة. سطر واحد - معلمة واحدة. يتم تجاهل المسافات وعلامات الجدولة في بداية السطر وبين علامة التساوي والاسم والقيمة. يتم أيضًا تجاهل الأسطر والأسطر الفارغة التي تبدأ بحرف التجزئة - "#" ، هذا الحرف يحدد الأسطر مع التعليقات. لا يهم حالة الأحرف في أسماء المعلمات والأقسام.
بالإضافة إلى المعلمات ، يحتوي الملف أيضًا على أقسام أسماؤها محاطة بأقواس مربعة. بعد اسم القسم الذي تمت مواجهته ، يتوقع المحلل اللغوي أن المعلمات التي تنتمي إلى هذا القسم فقط هي التي ستذهب إلى أبعد من ذلك حتى يتم العثور على اسم قسم آخر. بصراحة ، لا أعرف لماذا قدمت هذه الأقسام. عندما فعلت هذا ، كان لدي نوع من التفكير المرتبط بهم ، لكن الآن لا يمكنني تذكر ذلك.
لاختصار المقارنات بين اسم المعلمة المقروء بأسماء محددة مسبقًا ، يتم تحليل الحرف الأول من اسم القراءة أولاً ، ثم تتم مقارنة الأسماء التي تبدأ بهذا الحرف فقط.
تكوين محتوى ملف
# Stepper motor Z axis settings
[ZMotor]
# .
# : 0 1. : 1.
# .
invert_dir = 1
# .
# : -1 1. : -1.
# -1,
# , . 1
# .
home_direction = -1
# Z . ,
# 0, - .
home_pos = 0.0
# .
# : -32000.0 32000.0.
# : -3.0
# .
# , .
min_pos = -3.0
# .
# : -32000.0 32000.0.
# : 180.0
# .
# , .
max_pos = 180.0
# .
# : 0 1. : 1.
# ,
# 1, - 0.
min_endstop_inverting = 1
# .
# : 0 1. : 1.
# ,
# 1, - 0.
max_endstop_inverting = 1
# 1 .
steps_per_mm = 1600
# ,
# , /. : 6.0.
homing_feedrate_fast = 6.0
# ,
# , /. : 1.0.
homing_feedrate_slow = 1.0
# , /2.
acceleration = 0.7
# , /.
feedrate = 5.0
# ( ,
# ..), /2.
travel_acceleration = 25.0
# ( ,
# ..), /. 30
# ,
# 5 /.
travel_feedrate = 25.0
# , .
current_vref = 800.0
# , .
current_hold_vref = 300.0
# ,
# . . 0
# .
hold_time = 30.0
# ,
# . .
# hold_time. 0 .
# , .
off_time = 10.0
# General settings
[General]
# (0.001 )
# .
# : 0 15000. : 700 (0.7 ).
buzzer_msg_duration = 700
# (0.001 )
# , .
# : 0 15000. : 70 (0.07 ).
buzzer_touch_duration = 70
# 180 .
# .
# : 0 1. : 0.
rotate_display = 0
# , .
# LCD-. -
# .
# : 0 15000. : 10. 0 .
screensaver_time = 10
عند تحديد مثل هذا الملف (بامتداد .acfg) في قائمة الملفات ، سيسأل البرنامج الثابت عما إذا كان المستخدم يريد تنزيل الإعدادات من هذا الملف وتطبيقها ، وبعد التأكيد ، سيبدأ تحليل هذا الملف.
إذا تم العثور على خطأ ، فسيتم عرض رسالة تشير إلى نوع الخطأ ورقم السطر. يتم التعامل مع الأخطاء التالية:
- اسم قسم غير معروف
- اسم معلمة غير معروف
- قيمة معلمة غير صالحة - عندما ، على سبيل المثال ، تتم محاولة تعيين قيمة نصية لمعلمة رقمية
إذا كان أي شخص مهتمًا - إليك ورقة كاملة بالوظائف الرئيسية الثلاث للمحلل اللغوي
void _cfg_GetParamName(char *src, char *dest, uint16_t maxlen)
{
if (src == NULL || dest == NULL)
return;
char *string = src;
// skip spaces
while (*string != 0 && maxlen > 0 && (*string == ' ' || *string == '\t' || *string == '\r'))
{
string++;
maxlen--;
}
// until first space symbol
while (maxlen > 0 && *string != 0 && *string != ' ' && *string != '\t' && *string != '\r' && *string != '\n' && *string != '=')
{
*dest = *string;
dest++;
string++;
maxlen--;
}
if (maxlen == 0)
dest--;
*dest = 0;
return;
}
//==============================================================================
void _cfg_GetParamValue(char *src, PARAM_VALUE *val)
{
val->type = PARAMVAL_NONE;
val->float_val = 0;
val->int_val = 0;
val->uint_val = 0;
val->char_val = (char*)"";
if (src == NULL)
return;
if (val == NULL)
return;
char *string = src;
// search '='
while (*string > 0 && *string != '=')
string++;
if (*string == 0)
return;
// skip '='
string++;
// skip spaces
while (*string != 0 && (*string == ' ' || *string == '\t' || *string == '\r'))
string++;
if (*string == 0)
return;
// check param if it numeric
if ((*string > 47 && *string < 58) || *string == '.' || (*string == '-' && (*(string+1) > 47 && *(string+1) < 58) || *(string+1) == '.'))
{
val->type = PARAMVAL_NUMERIC;
val->float_val = (float)atof(string);
val->int_val = atoi(string);
val->uint_val = strtoul(string, NULL, 10);
}
else
{
val->type = PARAMVAL_STRING;
val->char_val = string;
}
return;
}
//==============================================================================
void CFG_LoadFromFile(void *par1, void *par2)
{
sprintf(msg, LANG_GetString(LSTR_MSG_CFGFILE_LOADING), cfgCFileName);
TGUI_MessageBoxWait(LANG_GetString(LSTR_WAIT), msg);
UTF8ToUnicode_Str(cfgTFileName, cfgCFileName, sizeof(cfgTFileName)/2);
if (f_open(&ufile, cfgTFileName, FA_OPEN_EXISTING | FA_READ) != FR_OK)
{
if (tguiActiveScreen == (TG_SCREEN*)&tguiMsgBox)
tguiActiveScreen = (TG_SCREEN*)((TG_MSGBOX*)tguiActiveScreen)->prevscreen;
TGUI_MessageBoxOk(LANG_GetString(LSTR_ERROR), LANG_GetString(LSTR_MSG_FILE_OPEN_ERROR));
BUZZ_TimerOn(cfgConfig.buzzer_msg);
return;
}
uint16_t cnt = 0;
uint32_t readed = 0, totalreaded = 0;
char *string = msg;
char lexem[128];
PARAM_VALUE pval;
CFGREAD_STATE rdstate = CFGR_GENERAL;
int16_t numstr = 0;
while (1)
{
// read one string
cnt = 0;
readed = 0;
string = msg;
while (cnt < sizeof(msg))
{
if (f_read(&ufile, string, 1, &readed) != FR_OK || readed == 0 || *string == '\n')
{
*string = 0;
break;
}
cnt++;
string++;
totalreaded += readed;
}
if (cnt == sizeof(msg))
{
string--;
*string = 0;
}
numstr++;
string = msg;
// trim spaces/tabs at begin and end
strtrim(string);
// if string is empty
if (*string == 0)
{
// if end of file
if (readed == 0)
break;
else
continue;
}
// skip comments
if (*string == '#')
continue;
// upper all letters
strupper_utf(string);
// get parameter name
_cfg_GetParamName(string, lexem, sizeof(lexem));
// check if here section name
if (*lexem == '[')
{
if (strcmp(lexem, (char*)"[ZMOTOR]") == 0)
{
rdstate = CFGR_ZMOTOR;
continue;
}
else if (strcmp(lexem, (char*)"[GENERAL]") == 0)
{
rdstate = CFGR_GENERAL;
continue;
}
else
{
rdstate = CFGR_ERROR;
string = LANG_GetString(LSTR_MSG_UNKNOWN_SECTNAME_IN_CFG);
sprintf(msg, string, numstr);
break;
}
}
// get parameter value
_cfg_GetParamValue(string, &pval);
if (pval.type == PARAMVAL_NONE)
{
rdstate = CFGR_ERROR;
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
// check and setup parameter
switch (rdstate)
{
case CFGR_ZMOTOR:
rdstate = CFGR_ERROR;
if (*lexem == 'A')
{
if (strcmp(lexem, (char*)"ACCELERATION") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
cfgzMotor.acceleration = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'C')
{
if (strcmp(lexem, (char*)"CURRENT_HOLD_VREF") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val < 100)
pval.uint_val = 100;
if (pval.uint_val > 1000)
pval.uint_val = 1000;
cfgzMotor.current_hold_vref = pval.uint_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"CURRENT_VREF") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val < 100)
pval.uint_val = 100;
if (pval.uint_val > 1000)
pval.uint_val = 1000;
cfgzMotor.current_vref = pval.uint_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'F')
{
if (strcmp(lexem, (char*)"FEEDRATE") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
if (pval.float_val > 40)
pval.float_val = 40;
cfgzMotor.feedrate = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'H')
{
if (strcmp(lexem, (char*)"HOLD_TIME") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val == 0)
pval.uint_val = TIMER_DISABLE;
else if (pval.uint_val > 100000)
pval.uint_val = 100000;
cfgzMotor.hold_time = pval.uint_val * 1000;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"HOME_DIRECTION") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.int_val != -1.0 && pval.int_val != 1.0)
pval.int_val = -1;
cfgzMotor.home_dir = pval.int_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"HOME_POS") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
cfgzMotor.home_pos = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"HOMING_FEEDRATE_FAST") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
if (pval.float_val > 40)
pval.float_val = 40;
cfgzMotor.homing_feedrate_fast = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"HOMING_FEEDRATE_SLOW") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
if (pval.float_val > 40)
pval.float_val = 40;
cfgzMotor.homing_feedrate_slow = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'I')
{
if (strcmp(lexem, (char*)"INVERT_DIR") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.int_val < 0 || pval.int_val > 1)
pval.int_val = 1;
cfgzMotor.invert_dir = pval.int_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'M')
{
if (strcmp(lexem, (char*)"MAX_ENDSTOP_INVERTING") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.int_val < 0 || pval.int_val > 1)
pval.int_val = 1;
cfgzMotor.max_endstop_inverting = pval.int_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"MAX_POS") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
cfgzMotor.max_pos = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"MIN_ENDSTOP_INVERTING") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.int_val < 0 || pval.int_val > 1)
pval.int_val = 1;
cfgzMotor.min_endstop_inverting = pval.int_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"MIN_POS") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
cfgzMotor.min_pos = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'O')
{
if (strcmp(lexem, (char*)"OFF_TIME") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val > 100000)
pval.uint_val = 100000;
else if (pval.uint_val < cfgzMotor.hold_time)
pval.uint_val = cfgzMotor.hold_time + 1000;
else if (pval.uint_val == 0)
pval.uint_val = TIMER_DISABLE;
cfgzMotor.off_time = pval.int_val * 60000;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'S')
{
if (strcmp(lexem, (char*)"STEPS_PER_MM") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val < 1)
pval.uint_val = 1;
if (pval.uint_val > 200000)
pval.uint_val = 200000;
cfgzMotor.steps_per_mm = pval.uint_val;
rdstate = CFGR_ZMOTOR;
break;
}
} else
if (*lexem == 'T')
{
if (strcmp(lexem, (char*)"TRAVEL_ACCELERATION") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
cfgzMotor.travel_acceleration = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
if (strcmp(lexem, (char*)"TRAVEL_FEEDRATE") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.float_val < 0.1)
pval.float_val = 0.1;
cfgzMotor.travel_feedrate = pval.float_val;
rdstate = CFGR_ZMOTOR;
break;
}
}
string = LANG_GetString(LSTR_MSG_UNKNOWN_PARAMNAME_IN_CFG);
sprintf(msg, string, numstr);
break;
case CFGR_GENERAL:
rdstate = CFGR_ERROR;
if (*lexem == 'B')
{
if (strcmp(lexem, (char*)"BUZZER_MSG_DURATION") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val > 15000)
pval.uint_val = 15000;
cfgConfig.buzzer_msg = pval.uint_val;
rdstate = CFGR_GENERAL;
break;
}
if (strcmp(lexem, (char*)"BUZZER_TOUCH_DURATION") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val > 15000)
pval.uint_val = 15000;
cfgConfig.buzzer_touch = pval.uint_val;
rdstate = CFGR_GENERAL;
break;
}
} else
if (*lexem == 'R')
{
if (strcmp(lexem, (char*)"ROTATE_DISPLAY") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val > 0)
{
cfgConfig.display_rotate = 1;
LCD_WriteCmd(0x0036);
LCD_WriteRAM(0x0078);
}
else
{
cfgConfig.display_rotate = 0;
LCD_WriteCmd(0x0036);
LCD_WriteRAM(0x00B8);
}
rdstate = CFGR_GENERAL;
break;
}
} else
if (*lexem == 'S')
{
if (strcmp(lexem, (char*)"SCREENSAVER_TIME") == 0)
{
if (pval.type != PARAMVAL_NUMERIC)
{
string = LANG_GetString(LSTR_MSG_INVALID_PARAMVAL_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (pval.uint_val > 15000)
cfgConfig.screensaver_time = 15000 * 60000;
else if (pval.uint_val == 0)
pval.uint_val = TIMER_DISABLE;
else
cfgConfig.screensaver_time = pval.uint_val * 60000;
rdstate = CFGR_GENERAL;
break;
}
}
string = LANG_GetString(LSTR_MSG_UNKNOWN_PARAMNAME_IN_CFG);
sprintf(msg, string, numstr);
break;
}
if (rdstate == CFGR_ERROR)
break;
}
f_close(&ufile);
if (tguiActiveScreen == (TG_SCREEN*)&tguiMsgBox)
{
tguiActiveScreen = (TG_SCREEN*)((TG_MSGBOX*)tguiActiveScreen)->prevscreen;
}
if (rdstate == CFGR_ERROR)
{
TGUI_MessageBoxOk(LANG_GetString(LSTR_ERROR), msg);
BUZZ_TimerOn(cfgConfig.buzzer_msg);
}
else
{
CFG_SaveMotor();
CFG_SaveConfig();
TGUI_MessageBoxOk(LANG_GetString(LSTR_COMPLETED), LANG_GetString(LSTR_MSG_CFGFILE_LOADED));
}
}
//==============================================================================
بعد التحليل الناجح للملف ، يتم تطبيق الإعدادات الجديدة على الفور وحفظها في EPROM.
يتم تحديث عدادات ساعات العمل لمكونات الطابعة فقط في EPROM عند طباعة الملف أو مقاطعته.
6. ميزات إضافية للراحة والملاءمة
6.1 ساعة مع التقويم
حسنًا ، فقط للقيام بذلك. لماذا نهدر الخير - ساعة الوقت الحقيقي المستقلة المدمجة في المتحكم الدقيق ، والتي يمكن أن تعمل على بطارية ليثيوم عندما تكون الطاقة العامة متوقفة وتستهلك القليل جدًا لدرجة أن CR2032 ، وفقًا للحسابات ، يجب أن تكون كافية لعدة سنوات. علاوة على ذلك ، قدمت الشركة المصنعة على السبورة كوارتز 32 كيلو هرتز المطلوب لهذه الساعة. يبقى فقط لصق حامل البطارية باللوحة ولحام الأسلاك منه إلى الطرح المشترك وإلى الطرف الخاص بالمتحكم الدقيق ، وهو ما فعلته في المنزل.
يتم عرض الوقت واليوم والشهر في الجزء العلوي الأيسر من الشاشة الرئيسية:
يتم استخدام نفس ساعة الوقت الفعلي لحساب وقت الطباعة وساعات تشغيل المكونات. ويتم استخدامها أيضًا في شاشة التوقف الموضحة أدناه.
6.2 تأمين الشاشة من النقرات غير المقصودة أثناء الطباعة
تم ذلك بناء على طلب أحد معارفه. حسنًا ، لم لا ، يمكن أن يكون مفيدًا في بعض الحالات. يتم تشغيل القفل وإيقاف تشغيله بالضغط لفترة طويلة (حوالي 2.5 ثانية) على رأس شاشة الطباعة. عندما يكون القفل نشطًا ، يتم عرض قفل أحمر في الزاوية اليمنى العليا. في نهاية الطباعة ، يتم تحرير القفل تلقائيًا.
6.3 تقليل تيار المحرك في وضع الانتظار ، وإيقاف تشغيل المحرك
صُنعت لتقليل تراكم الحرارة الكلية داخل هيكل الطابعة. يمكن وضع المحرك في وضع الانتظار مع تيار مخفض بعد وقت عدم الحركة الذي تم تكوينه. هذه الميزة ، بالمناسبة ، منتشرة في سائقي المحركات السائر "الكبار" من نوع TB6560. بالإضافة إلى ذلك ، في الإعدادات ، يمكنك ضبط الوقت الذي سيتم بعده ، في حالة عدم وجود حركة ، إلغاء تنشيط المحرك تمامًا. لكن هذا سيؤدي أيضًا إلى حقيقة أن صفير المحور ، إذا تم تنفيذه ، سيصبح غير صالح. يمكن تعطيل هاتين الميزتين تمامًا في نفس الإعدادات.
6.4 شاشة التوقف
مثل الساعة - فقط لأنني أستطيع. في حالة عدم الضغط على الشاشة بعد الوقت المحدد في الإعدادات ، تتحول الشاشة إلى وضع محاكاة ساعة سطح المكتب الرقمية:
بالإضافة إلى الوقت ، يتم أيضًا عرض التاريخ الكامل مع يوم الأسبوع. يخرج البرنامج الثابت من هذا الوضع بالضغط على أي جزء من الشاشة. بالنظر إلى أن الأرقام كبيرة جدًا ، واستهلاك الكهرباء عند إيقاف تشغيل المحرك أقل من 2 وات ، قد تعمل الطابعة المزودة بمثل هذه الشاشة كساعة للغرفة :) أثناء الطباعة ، تظهر شاشة التوقف أيضًا بعد وقت محدد ، ولكن مع إضافة واحدة - تقدم الطباعة في الجزء السفلي من الشاشة:
في الإعدادات ، يمكنك ضبط وقت استجابة شاشة التوقف أو تعطيلها.
6.5 فحص الإضاءة الخلفية والعرض
يمكن الوصول إلى هذه الشاشة من قائمة "الخدمة" وستكون مفيدة عند فحص صمامات الإضاءة الخلفية أو شاشة الأشعة فوق البنفسجية. في الجزء العلوي ، يتم تحديد إحدى الصور الثلاث ، والتي سيتم عرضها على شاشة UV - الإطار ، الإضاءة الكاملة للشاشة بأكملها ، المستطيلات. يوجد في الجزء السفلي زران يعملان على تشغيل وإيقاف الإضاءة الخلفية والعرض. سيتم إيقاف تشغيل الضوء المضمن تلقائيًا بعد دقيقتين ، وعادة ما تكون هذه المرة كافية لأي اختبار. عند الخروج من هذه الشاشة ، سيتم إيقاف تشغيل كل من الإضاءة الخلفية والشاشة تلقائيًا.
6.6 الإعدادات
يمكن الوصول إلى هذه الشاشة أيضًا من قائمة الأدوات. هناك عدد قليل جدًا من الإعدادات هنا ، ولكي أكون صادقًا ، لم أتوصل مطلقًا إلى الإعدادات التي ستكون مطلوبة كثيرًا بحيث يكون من المنطقي وضعها في الواجهة ، وليس فقط في ملف التكوين. سيضيف هذا أيضًا القدرة على إعادة تعيين عدادات وقت التشغيل لمكونات الطابعة ، حسنًا ، لا أعرف بعد الآن :)
بالطبع ، هنا يمكنك ضبط الوقت والتاريخ (نظرًا لوجود ساعة) في الشاشة التي تفتح بشكل منفصل:
يمكنك ضبط ارتفاع رفع النظام الأساسي على الإيقاف المؤقت وتشغيله وإيقافه صوت نقرات العرض والرسائل. عند تغيير الإعدادات ، ستكون القيم الجديدة صالحة فقط حتى يتم إيقاف تشغيل الطاقة ولن يتم حفظها في EPROM. لحفظها ، بعد تغيير المعلمات ، اضغط على زر حفظ في القائمة (بأيقونة مرنة).
يتم إدخال القيم العددية في شاشة خاصة:
هنا قمت بتطبيق جميع الميزات التي كنت أفتقدها في الطابعات الأخرى.
- أزرار "±" و "." تعمل فقط إذا كانت المعلمة المحررة يمكن أن تكون سالبة أو كسرية ، على التوالي.
- إذا تم الضغط على أي زر رقمي أولاً بعد الدخول إلى هذه الشاشة ، فسيتم استبدال القيمة القديمة بالرقم المقابل. إذا كان الزر "." ، فسيتم استبداله بـ "0". أي ، ليست هناك حاجة لمحو القيمة القديمة ، يمكنك البدء فورًا في إدخال قيمة جديدة.
- زر "" ، يصفر القيمة الحالية.
الضغط على الزر "رجوع" لن يتم تطبيق القيمة الجديدة. لتطبيقه ، تحتاج إلى النقر فوق "موافق".
6.7 أخيرًا - شاشة معلومات الطابعة
يمكن الوصول إلى هذه الشاشة مباشرة من القائمة الرئيسية. أهم شيء هنا هو إصدار البرنامج الثابت / FPGA وعدادات وقت التشغيل. في الجزء السفلي ، لا تزال هناك معلومات حول مؤلف الواجهة وعنوان المستودع على GitHub. مؤلف الواجهة هو أساس المستقبل. إذا استمررت في إتاحة إمكانية تكوين الواجهة من خلال ملف نصي بسيط ، فستكون هناك فرصة لتحديد اسم المؤلف.
النهاية
هذا هو الجزء الأخير من مشروع الحيوانات الأليفة الخاص بي. يعيش المشروع ويتطور ، على الرغم من أنه ليس بالسرعة التي أريدها ، ولكنه فعال بالفعل.
ربما كان يجب أن أضع المزيد من التعليمات البرمجية ... لكنني لا أعتقد أن هناك أجزاء في الكود الخاص بي للتفاخر بها. في رأيي ، من المهم وصف كيفية عملها وما تم إنجازه ، والكود موجود ، كل شيء على GitHub ، من سيكون مهتمًا ، يمكنني مشاهدته هناك بالكامل. أعتقد.
إنني أتطلع إلى تلقي أسئلتك وتعليقاتك ، وشكرًا على اهتمامك بهذه المقالات.
- الجزء الأول: 1. واجهة المستخدم.
- الجزء 2: 2. العمل مع نظام الملفات على محرك أقراص فلاش USB. 3. التحكم في محرك متدرج لحركة المنصة.
- الجزء الثالث:4. إخراج صور الطبقات إلى شاشة الإضاءة الخلفية. 5. كل شيء صغير ، مثل التحكم في الإضاءة والمراوح ، وتحميل وحفظ الإعدادات ، إلخ. 6. ميزات إضافية للراحة والملاءمة.
الروابط
مجموعة MKS DLP على
مصادر البرامج الثابتة الأصلية من Aliexpress من الشركة المصنعة في
مخططات GitHub من الشركة المصنعة لنسختين من اللوحة على GitHub
مصادري على GitHub