استخدام وحدات تحكم Cypress في UDB PSoC لتقليل الانقطاعات في طابعة ثلاثية الأبعاد



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

مقدمة طويلة


هذا المقال هو الجزء الثاني من ثلاثية تصور. يوجد الجزء الأول هنا (التحكم RGB LED عبر وحدة التحكم الصغيرة من Cypress UDB PSoC).

بالإضافة إلى وحدات التحكم في UDB PSoC من Cypress ، حيث يتم تنفيذ واجهات معينة عليها ، سيكون من المثير للاهتمام التحقق من كيفية جعل هذه الكتل أسهل للمبرمجين من خلال تفريغ المعالج المركزي من بعض المهام كثيفة الاستخدام للموارد. ولكن من أجل توضيح ما سأفعله ، يجب أن أكتب مقدمة شاملة.

في خريف عام 2015 ، اشتريت طابعة MZ3D 3D جديدة تمامًا ، وبحلول ربيع عام 2016 سئمت من كيف هزت محركاتها المتصاعدة. لقد كانت الأوقات برية ، لقد نجحنا بأفضل ما نستطيع ، لذلك كان الحل الوحيد هو التبديل من microstep 1/16 إلى 1/32. أظهرت المراسلات مع المصنع أن هذا غير ممكن في اردوينو. كما اتضح ، كان هناك قيود في "البرامج الثابتة" لتلك السنوات ، مع تردد خطوة أعلى من 10 كيلو هرتز ، لم يتم اتخاذ خطوات افتراضية ، ولكن خطوتين ظاهرتين ، وإلا فإن النظام ببساطة لم يكن لديه ما يكفي من الوقت لمعالجة جميع المقاطعات "الخطوة". لم يكن هناك سوى مخرج واحد - لسحب كل شيء على منصة ARM. لقد كان السحب والإفلات وليس التنزيل ، حيث لم تكن هناك حلول ARM جاهزة في ذلك الوقت أيضًا. في غضون أسبوعين قمت بنقل كل هذا إلى STM32F4 ، أصبح صوت المحركات أكثر متعة ، تم حل المشكلة.

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

بشكل دوري ، عدت إلى هذا السؤال بالمعنى النظري البحت. على سبيل المثال ، تسللت الفكرة في يوم من الأيام إلى ذهني أنه بدلاً من استخدام وحدة تحكم باهظة الثمن ، يمكنك أن تأخذ ثلاثة STM32F103C8T6 ، حيث يكلف اللوح الجاهز 110 روبل ، مع الأخذ في الاعتبار التسليم ، والرقاقة نفسها أرخص. في واحد منهم لإخراج فقط وظيفة التحكم في المحرك. دعه يقضي كل ما لديه من قوة الحوسبة على هذه الوظيفة. يعمل الآخرون (ربما حتى واحدًا) على حل المهام الأخرى (أوامر المعالجة ، والعمل مع PWM ، والحفاظ على درجة الحرارة ، وما إلى ذلك) في بيئة هادئة. يحتوي هذا الحل أيضًا على جانب جانبي ضخم - إجمالي عدد المسامير للعديد من وحدات التحكم ضخم للغاية. في أحد STM32 ، اضطررت إلى وضع مآس لفترة طويلة ، والتي يجب تعيينها إلى. على الرغم من أن أرجل الموقت المخرج وأرجل ADC من ARMs يتم تعيينها بشكل أكثر مرونة من وحدات التحكم القديمة (يمكن إخراج واحد من وحدة الأجهزة إلى واحدة من عدة أرجل مادية) ، ولكن عند طي سوليتير ، قد تفهم أن المرونة قد لا تكون كافية. إذا كان هناك العديد من وحدات التحكم ، فإن الخيار يزداد. على من يخدم محركات السائر ، بشكل عام ، نحن ببساطة نخصص كل الأرجل كمخرجات رقمية. الآخرين لديهم أيضا حيث يستدير.

مشكلة واحدة مع هذا النهج هو كيفية مزامنة وحدات التحكم هذه؟ من الناحية النظرية ، يحتوي MAX Max RTOS على كل ما تحتاجه. ينشئ معالج الأوامر قائمة من المهام لرؤوس متحركة. يقوم بشكل دوري بتعديلها (من خلال تنسيق التسارع مع المهام التي وصلت حديثًا). لذلك يجب مشاركة ذاكرة المشكل والأداء. يحتوي RTOS MAX على وظيفة تنظيم مثل هذه الذاكرة المشتركة. لقد وصفتها هنا (نظرة عامة على RTOS الروسية ، الجزء 7. وسائل تبادل البيانات بين المهام). ولكن من الناحية العملية ، يفسد كل شيء بسيطًا: خدمة محركات السائر هي نوع من المهام المهمة للوقت. أقل تأخير ، وحصلنا على تدفقات بلاستيكية لطابعة ثلاثية الأبعاد ، لآلات CNC الأخرى - حسنًا ، على سبيل المثال ، الخيوط غير الصحيحة. أي اتصال عبر واجهات تسلسلية ليس الأسرع. زائد - الوقت للتحكيم والاحتياجات الرسمية الأخرى. واتضح أن جميع المكاسب من إزالة الوظيفة من المعالج الرئيسي تذهب إلى النفقات العامة. بالطبع ، استفدت من موقفي الرسمي: ذهبت وناقشت هذه المشكلة مع مطوري هذا النظام الفرعي. للأسف قالوا إن هناك تزامنًا بدون حمل كبير في نظام التشغيل ، ولكن بالنسبة للمعدات التي تدعم الحافلات المقابلة. الآن ، إذا أخذت بنية TigerShark كأساس ، فسوف يقوم نظام التشغيل بتنظيم كل شيء لي دون أي تكاليف إضافية. تعد وحدات التحكم التي تم تصنيعها وفقًا لهذه البنية أكثر تكلفة من الطابعة ثلاثية الأبعاد بالكامل التي أردت وضعها عدة مرات. بشكل عام ، مرة أخرى غير مقبول.

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

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

حصان كروي في فراغ


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

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

دع الطابعة تصل سرعتها القصوى إلى 200 مم / ثانية. اترك 200 خطوة مطلوبة لكل ملليمتر واحد من الحركة (هذا الرقم يتوافق مع طابعة حقيقية MZ3D-256C مع microstep 1/32). ثم يجب تزويد البقول بتردد يصل إلى 200 * 200 = 40،000 هرتز = 40 كيلو هرتز. في مثل هذا التردد ، يمكن استدعاء مهمة إرسال نبضات الخطوة. يجب أن تشكل النبضات برمجيًا ، وكذلك تحسب المدة التي يجب أن يطلق عليها المقاطعة التالية لتنشيطها.

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

ما هي المعلومات المطلوبة للحصان الكروي لاجتياز قسم خطي في فراغ؟

  • عدد الخطوات.
  • الفترة الزمنية بين الخطوات.

معلمتين. يحتوي UDB فقط على بطاريتين وسجلين للمعلمات D0 و D1. يبدو أن كل شيء يمكن تحقيقه. نحن نقدر فقط عمق البت الذي يجب أن يكون لهذه السجلات.

أولا ، عدد الخطوات. إذا كان هناك 8 أرقام ، فعند دورة واحدة من تشغيل UDB ، ستتمكن الطابعة من تحريك رأس الطابعة الديكارتية بأكثر قليلاً من 1 مم (200 خطوة ميكروية). ليس كافي إذا كانت السعة 16 بت ، فسيكون عدد الخطوات 65536. هذا هو 65536/200 = 327 ملليمتر. مقبول لمعظم الطرز بالنسبة إلى Core و Delta وغيرها يجب تقدير ذلك ، ولكن ككل - يمكن تقسيم المقطع إلى عدة أجزاء. لن يكون هناك الكثير (اثنان ، حسنا ، بحد أقصى ثلاثة).

الآن هذه الفترة. دع تردد الساعة يكون 48 ميجا هرتز. 48000000/65536 = 732. أي أن الحد الأدنى المسموح للتردد الذي يمكن الحصول عليه باستخدام مقسم 16 بت هو 732 هرتز. كثير جدا في Marlin Firmware ، الحد الأدنى هو 120 هرتز (والذي يناظر تقريبًا 8 ميغاهرتز مقسومًا على نفس الثابت 65536). سيكون لدينا لجعل السجلات 24 بت. عندها سيكون الحد الأدنى للتردد مساوياً لـ 48000000 / (2 ^ 24) = 48000000/16777216 = 2.861 هرتز.

جيد وقف نظرية مملة! دعنا ننتقل إلى ممارسة! قم بتشغيل PSoC Creator وحدد File-> New-> Project:



بعد ذلك ، قمت بتحديد اللوح الذي لدي ، والذي ستأخذ منه البيئة المعلومات الأساسية حول وحدة التحكم المستخدمة وإعداداتها:



أشعر بالفعل أنني على استعداد لإنشاء مشروع من البداية ، لذلك حدد Empty Schematic :



إعطاء بيئة العمل اسم PSoC3DTest :



وهنا هو ، مشروع الانتهاء!



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



انقر بزر الماوس الأيمن على المشروع وحدد إضافة عنصر مكون :



نقول إننا بحاجة إلى إضافة مستند UDB وتغيير الاسم إلى StepperController والنقر فوق " إنشاء جديد" :



ظهر المكون في الشجرة ، بالإضافة إلى - تم فتح محرر هذا المكون:



ضع كتلة Datapath في النموذج:



بعد تحديد هذه الكتلة ، نذهب إلى خصائصها ونغير عمق البت من 8 إلى 24. ويمكن ترك المعلمات المتبقية دون تغيير.



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



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



اسم العداد لا تتم إعادة ضبطه أبدًا ، لذلك يتم دائمًا توصيل مدخلات إعادة الضبط بصفر ، ويؤخذ في الاعتبار عندما تكون الآلة (الموضحة أدناه) في حالة واحدة ، ويتم تحميلها في جميع الحالات الأخرى (في البداية حددت حالات محددة من الماكينة ، لكن اتضح أنه بهذه الطريقة الصعبة ، موارد PLD أقل بكثير ، ولكن النتيجة هي نفسها). قيمة التحميل عشرية. صحيح ، سيقوم محرر جيد على الفور باستبدال هذه القيمة بالسداسي عشري:



عندما يتم حساب العداد إلى صفر ، فإنه سيتم الإبلاغ عن هذا إلى السلسلة باسم One_Finished . مع العداد - هذا كل شيء.

ما نوع أعلام الحالة التي ستستخدمها أجهزتنا؟ حصلت على مثل هذا (أذكرك بالنقر المزدوج على قائمة المخرجات في Datapath لتعيينها):





سوف أستخدم البطارية A0 كعداد لمدة النبضة ، لذلك عندما تصل قيمتها إلى الصفر ، سيتم وضع علامة على العلم ، والتي أعطيتها اسم Pulse_Finished . سوف البطارية A1 عد البقول بالنسبة لي. لذلك ، فإن التصفير الخاص به سوف يصيح العلامة Process_Finished .

نحن نبني الرسم البياني انتقال الأوتوماتون:



المتغير الذي يحدد حالته يسمى الحالة . تعيين هذا المتغير على الفور إلى سجل عنوان تعليمة ALU. في البداية نسيت أن أفعل ذلك ، لذا لم أكن أفهم لوقت طويل سبب عدم عمل الجهاز. انقر نقرًا مزدوجًا فوق كتلة الإدخالات في Datapath:



وتطابق:



نبدأ في التعامل مع الرسم البياني الانتقالية وتعليمات ALU المرتبطة به.

لنبدأ مع حالة الخمول . انها مشبعة جدا في أعمالها.

أولاً ، يتم وضع قيمة سجلات البيانات D0 و D1 باستمرار في البطاريات A0 و A1 ، على التوالي:



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



القيمة الرئيسية هنا هي ملء البطارية A1 ، عداد النبض. عندما يدخل البرنامج القيمة D1 ، ينتقل على الفور إلى A1. من المؤكد أن البرنامج لن يكون لديه وقت لبدء العملية حتى الإجراء التالي. يتم التحقق من هذه القيمة لتكوين شرط لإنهاء هذه الحالة ، أي أنه لا يوجد مكان آخر لملئها.

الآن دعونا نرى ما يتم على مستوى الرسم البياني الانتقالية:



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



نفس النص
always @ (posedge clock) begin : Idle_state_logic case(State) Idle : begin Start_Prev <= (Start); IsIdle <= (1); if (( Start&(!Start_Prev)&(!Process_Finished) ) == 1'b1) begin State <= One ; end end 


وفقًا لذلك ، يكون الشرط Start & (! Start_Prev) صحيحًا فقط عند حدوث اختلاف إيجابي في خط البداية بين المقاييس .

بالإضافة إلى ذلك ، عندما يكون الجهاز في هذه الحالة ، يتم إدخال إخراج IsIdle في حالة واحدة ، لإبلاغ البيئة الخارجية بأن الكتلة غير فعالة. مع هذا النهج ، يتم إنفاق موارد PLD أقل مما إذا كانت الدولة == إنشاء الخمول قد قُدمت إلى المخرجات.

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



بعد ذلك ، يدخل الجهاز الحالة الأولى :



في هذه الحالة ، يتم تعيين إخراج الخطوة إلى 1. يتم تطبيق نبض الخطوة على برنامج التشغيل. بالإضافة إلى ذلك ، يتم إعادة تعيين قيمة المشغل IsIdle . يتم إبلاغ البيئة الخارجية أن الوحدة في المرحلة النشطة.

يتم إنهاء هذه الحالة بواسطة إشارة One_Finished ، والتي سيتم رفعها إلى واحدة عندما يكون عداد السبعة بتات صفراً. اسمحوا لي أن أذكركم بأن إشارة One_Finished يتم إنشاؤها بواسطة هذا العداد المحدد:



أثناء وجود الجهاز في هذه الحالة ، يتم تحميل ALU في البطارية A0 (ضبط مدة النبضة) القيمة من السجل D0. دعني أريك فقط ملاحظة قصيرة تقول هذا:



سيتم استخدام القيمة المحملة في الحالة التالية. بوجودها ، يولد الجهاز تأخيرًا يحدد مدة النبضة:



تتم إعادة تعيين إخراج الخطوة إلى صفر. تنخفض البطارية A0 ، كما يتضح من الإدخال المختصر التالي:



وإذا نقرت عليه نقرًا مزدوجًا - إدخال كامل:



عندما تصل قيمة A0 إلى الصفر ، سيتم رفع علامة Pules_Finished ، وستنتقل الآلة إلى حالة Decrement :



في هذه الحالة ، في ALU ، تتناقص قيمة مركم A1 ​​، مما يحدد عدد النبضات:



النسخة الكاملة من السجل:



اعتمادًا على النتيجة ، يحدث انتقال إما إلى النبض التالي أو إلى حالة الخمول . انقر نقرًا مزدوجًا على الحالة لرؤية التحولات مع مراعاة الأولويات:



في الواقع ، مع UDB كل شيء. الآن نجعل الرمز المقابل. للقيام بذلك ، انقر بزر الماوس الأيمن على المحرر وحدد إنشاء رمز :



نذهب إلى مخطط المشروع:



ونحن نقدم الدائرة التي يوجد فيها عدد معين من وحدات التحكم هذه. اخترت خمسة (ثلاثة محاور زائد اثنين من البثق). الطابعات التي تحتوي على عدد كبير من البثق لن تعتبر رخيصة. يمكنك وضع FPGA عليها. على طول الطريق ، لمعرفة التعقيد الحقيقي ، رميت كتلة USB-UART (لتلقي البيانات من جهاز كمبيوتر أو نفس Raspberry Pi) و UART حقيقية (ستوفر التواصل مع وحدة Wi-Fi رخيصة ESP8266 أو ، على سبيل المثال ، شاشة ذكية يمكنها إرسال GCODE عبر UART). لم أقم بإضافة PWMs وما إلى ذلك ، لأن تعقيدها واضح تقريبًا ، ولا يزال النظام الحقيقي بعيدًا. اتضح بطريقة ما مثل هذا:



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



في الجدول الذي يظهر ، انقر نقرًا مزدوجًا على عنصر PLL_OUT :



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



الآن انقر نقرًا مزدوجًا على الخط Clock_1 :



اضبط تردد الساعة لكتل ​​UDB على MHz 48:



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





سأعرض سطحي وظيفتي التهيئة وبدء المقطع الذي أضفتهما. يمكن رؤية الباقي في المثال على المقال.

 void `$INSTANCE_NAME`_Start() { `$INSTANCE_NAME`_SingleVibrator_Start(); //"One" Generator start } void `$INSTANCE_NAME`_PrepareStep(int nSteps,int duration) { CY_SET_XTND_REG24(`$INSTANCE_NAME`_Datapath_1_D0_PTR, duration>92?duration-92:0); CY_SET_XTND_REG24(`$INSTANCE_NAME`_Datapath_1_D1_PTR, nSteps>1?nSteps-1:0); } 

لقد استبدلت اسم main.c بـ main.cpp للتحقق من أن بيئة التطوير ستستجيب بشكل طبيعي لـ C ++ ، لأن البرامج الثابتة Marlin موجهة للكائنات. تمطر بشكل متوقع الأخطاء التي تم القضاء عليها عن طريق إضافة شيء منتظم:



نفس النص
 extern "C" { #include "project.h" } 


بالنسبة للإطلاق العالمي للمحركات ، قمت بعمل هذه الوظيفة (إنها عملية صعبة للغاية ، ولكن بالنسبة للتجارب مع حصان كروي في الفراغ ، ستقوم بذلك ، في التجارب ، يكون وقت التطوير أكثر أهمية من الجمال):
 void StartSteppers() { Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (1); Stepper_Control_Reg_Write (0); } 

تبدأ بإشارة " البداية" ، في الحال ، لمدة ثلاثة تدابير ، ثم تسقطها مرة أخرى.

حسنًا ، لنبدأ التجارب. أولاً ، قم فقط بتخطي محركات X و Y (في المثال ، تقوم المجموعة الأولى من المكالمات بتهيئة جميع وحدات التحكم ، بينما تقوم المجموعة الثانية بتعيين وحدات التحكم X و Y على العدد المطلوب من الخطوات وتبدأ العملية):

 int main(void) { CyGlobalIntEnable; /* Enable global interrupts. */ StepperController_X_Start(); StepperController_Y_Start(); StepperController_Z_Start(); StepperController_E0_Start(); StepperController_E1_Start(); StepperController_X_PrepareStep (10,1000); //    StepperController_Y_PrepareStep (50,500); StartSteppers(); //   for(;;) { } } 

نحن ننظر إلى النتيجة:



تحقق من مدة النبض الإيجابي:



هذا صحيح. أخيرًا ، نتحقق من مدى عمل المقاطعة. إضافة متغير عداد عالمي:

 static int nStep=0; 

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

 extern "C" { CY_ISR(StepperFinished) { if (nStep == 1) { StepperController_X_PrepareStep (5,500); StartSteppers(); nStep += 1; } } } 

وفي الوظيفة الرئيسية ، أضفت حرفيًا سطرين: تضمين المقاطعات وتعيين هذا المتغير جدًا. وأنا أسند بالفعل عندما بدأت الآلات. خلاف ذلك ، جاء طلب مقاطعة كاذبة. لا يوجد سبب محدد لمحاربته الآن. المشروع تجريبي.



نفس النص
 int main(void) { CyGlobalIntEnable; /* Enable global interrupts. */ isr_1_StartEx(StepperFinished); StepperController_X_Start(); StepperController_Y_Start(); StepperController_Z_Start(); StepperController_E0_Start(); StepperController_E1_Start(); /* Place your initialization/startup code here (eg MyInst_Start()) */ StepperController_X_PrepareStep (10,1000); StepperController_Y_PrepareStep (20,500); StartSteppers(); nStep = 1; for(;;) { } } 


نحن نتحقق من النتيجة (في الخطوة الثانية يجب أن يعمل المحرك X فقط ، ويجب أن تصبح الخطوات نصف مقدارها):



هذا صحيح.

الخاتمة


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

دقة ضبط وتيرة الكتلة المقدمة غير مقبولة. على وجه الخصوص ، سوف يوفر تردد نبضي قدره 40000 هرتز مع فاصل 1200 و 39966 هرتز مع مقسم 1201. الترددات الوسيطة بين هاتين القيمتين على هذه الكتلة غير قابلة للتحقيق.

ربما هناك بعض أوجه القصور في ذلك. لكننا سنتعامل معهم في المقالة التالية للتحقق مما إذا كانت هناك موارد كافية من UDB.

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

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


All Articles