
مقدمة
لطالما رغبت في تعلم تقنية برمجة كتل UDB في وحدات تحكم Cypress PSoC ، ولكن بطريقة ما لم تصل كل يدي. وهكذا ، نشأت مشكلة يمكن القيام بها. عند فهم المواد من الشبكة ، أدركت أن التوصيات العملية للعمل مع UDB تقتصر على أشكال مختلفة من العدادات و PWMs. لسبب ما ، يقوم جميع المؤلفين بعمل اختلافات في هذين المثالين القانونيين ، لذلك قد يكون وصف شيء آخر مثيرًا للاهتمام للقراء.
لذا حدثت مشكلة في إدارة مجموعة طويلة من مصابيح LED WS2812B RGB ديناميكيًا. النهج الكلاسيكية في هذه المسألة معروفة. يمكنك أن تأخذ Arduino التافه ، ولكن هناك يذهب المخرج برمجيًا ، لذلك أثناء إخراج البيانات ، كل شيء آخر خامل ، وإلا ستفشل الرسوم البيانية للتوقيت. يمكنك أخذ STM32 وبيانات الإخراج إما من خلال DMA إلى PWM أو من خلال DMA إلى SPI. التقنيات معروفة. حتى أنني ، في وقت واحد ، كنت أتحكم شخصيًا في خط مكون من ستة عشر صمامًا ثنائيًا عبر SPI. لكن النفقات العامة رائعة. يشغل بت واحد للبيانات في مصابيح LED 8 بتات في الذاكرة لحالة PWM ومن 3 إلى 4 بتات (اعتمادًا على برودة PLL في وحدة التحكم) لـ SPI. على الرغم من وجود عدد قليل من مصابيح LED ، فهذا ليس مخيفًا ، ولكن إذا كان هناك ، على سبيل المثال ، بضع مئات ، فإن 200 * 24 = 4800 بت = 600 بايت من البيانات المفيدة يجب تخزينها فعليًا في مخزن مؤقت بسعة تزيد عن 4 كيلوبايت لخيار PWM أو أكثر من 2 كيلو بايت لـ SPI- خيارات. للإشارة الديناميكية للمخازن المؤقتة ، يجب أن يكون هناك العديد ، و STM32F103 لديه ذاكرة وصول عشوائي لكل شيء حول كل شيء 20 كيلوبايت. لا يعني أننا واجهنا مهمة غير قابلة للتحقيق ، ولكن سبب التحقق مما إذا كان يمكن تنفيذ ذلك على PSoC دون الحاجة إلى إنفاق ذاكرة وصول عشوائي إضافية ، أمر مهم للغاية.
مراجع نظرية
أولاً ، دعنا نكتشف أي نوع من الوحوش مثل UDB وكيف يتعاملون معه. أفلام تعليمية رائعة من الشركة المصنعة للتحكم ستساعد في ذلك.
يجب أن تبدأ في المشاهدة
من هنا ، ثم في نهاية كل فيديو سيكون هناك رابط للمسلسل التالي. خطوة بخطوة ، ستكتسب المعرفة الأساسية وتنظر في المثال المتعارف عليه "العداد". حسنًا ، ونظام التحكم في إشارات المرور.
حول نفس الشيء ، ولكن مقطعة إلى قطع صغيرة ، يمكنك أن
ترى هنا . لم يتم تشغيل الفيديو الخاص بي ، ولكن يمكن تنزيله وعرضه محليًا. من بين أمور أخرى ، هناك أيضًا مثال قانوني لتطبيق PWM.
الحلول النهائية
من أجل عدم إعادة اختراع العجلة (والعكس صحيح - لتعلم المنهجية من تجربة شخص آخر) ، بحثت عن طريق الشبكة بحثًا عن حلول جاهزة للتحكم في مصابيح LED RGB. الحل الأكثر شيوعًا هو StripLightLib.cylib. ولكن لسنوات عديدة لديه الآن خطط لإضافة دعم إضافة DMA. لكني أريد تجربة حل لا يعتمد على المعالج المركزي. أريد أن أبدأ العملية وننسى ذلك ، مع التركيز على إعداد الإطار التالي.
تم العثور على الحل الذي يلبي رغباتي على
https://github.com/PolyVinalDistillate/PSoC_DMA_NeoPixel .
يتم تنفيذ كل شيء هناك على UDB (لكن مصابيح LED هي مجرد عذر ، والهدف هو تعلم UDB). هناك دعم لـ DMA. ومن الواضح أن المشروع منظم بشكل جميل.
مشاكل الحل المختار كأساس
كيف يتم "البرامج الثابتة" في مشروع PSoC_DMA_NeoPixel ، يمكن لأي شخص رؤيته بعد قراءة المقال. سيؤدي ذلك إلى إصلاح المواد. في الوقت الحالي ، سأقول فقط أنني قمت أولاً بتبسيط منطق البرامج الثابتة الأصلية دون تقليل الموارد المستهلكة (ولكن أصبح من السهل فهمها). ثم بدأ في تجربة استبدال منطق الأوتوماتون ، الذي وعد بكسب في الموارد ، لكنه واجه مشكلة خطيرة. وهكذا قرر - لا يتم القضاء عليه! وبدأت الشكوك الغامضة تعذبني: هل واجه المؤلف الإنجليزي نفس المشكلة؟ يومض عرضه بشكل جميل للغاية مع مصابيح LED. ولكن ماذا لو استبدلنا الحشوة الجميلة بـ "جميع الوحدات" ولم نتحكم في المخرجات ليس بأعيننا ، ولكن باستخدام راسم الذبذبات؟
لذا ، بأكبر قدر ممكن من الفظاعة (يمكنك حتى القول "بوحشية") نقوم بتشكيل البيانات:
memset (pPixelArray,0xff,sizeof(pPixelArray));
وهنا نرى مثل هذه الصورة على مرسمة الذبذبات:

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

يختلف العرض لكل بتة ثامنة.
بشكل عام ، هذا المثال كحل مستقل ليس مناسبًا ، ولكن كمصدر للإلهام - مثالي تمامًا. أولاً ، عدم إمكانية تشغيله غير مرئي بالعين (لا تزال مصابيح LED ساطعة ، والعين لا ترى أنها تلمع بنصف الحد الأقصى) ، لكن الشفرة منظمة بشكل جيد ، ومن الجميل أن تأخذها كأساس. ثانيًا ، يوفر هذا المثال مساحة لإيجاد طرق للتبسيط ، وثالثًا ، يجعلك تفكر في كيفية إصلاح العيب. الشيء نفسه هو فهم العتاد! مرة أخرى ، بعد قراءة المقال ، أوصي بمحاولة تحليل المثال الأصلي ، مع إدراك كيفية عمله.
جزء عملي
الآن نبدأ في الممارسة. نحن نختبر الجوانب الرئيسية لتطوير البرامج الثابتة لـ UDB. فكر في العلاقة والتقنيات الأساسية. للقيام بذلك ، افتح
إصداري من المشروع . يخزن الكتلة اليسرى معلومات حول ملفات العمل. بشكل افتراضي ، علامة التبويب
المصدر مفتوحة. المصدر الرئيسي للمشروع هو ملف
main.c. في الواقع ، لا توجد ملفات عمل أخرى في مجموعة
Source Files .

تحتوي مجموعة
Source Generated على وظائف المكتبة. من الأفضل عدم تعديلها. بعد كل تغيير في "البرامج الثابتة" لـ UDB ، سيتم إعادة إنشاء هذه المجموعة. إذن ، أين وصف رمز UDB في هذا المثال؟ لرؤيتها ، تحتاج إلى التبديل إلى علامة التبويب
المكونات :

قام مؤلف المشروع الأصلي بعمل مجموعة مكونة من مستويين. في المستوى العلوي تقع دائرة
NeoPixel_v1_2.cysch . هذا يمكن رؤيته من المخطط الرئيسي:

المكون كما يلي:

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

"البرامج الثابتة" UDB
افتح هذا المكون (ملف بامتداد
.cyudb ). الرسم المفتوح ببساطة ضخم. نبدأ في فهم ما هو.

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

من مقاطع الفيديو التدريبية ، لم يكن واضحًا تمامًا بالنسبة لي كيف ترتبط حالة الجهاز بـ ALU. يستخدم المؤلفون الاتصال كمسألة بالطبع ، لكني ، كمبتدئ ، لم أتمكن من رؤيته على الفور. دعنا نلقي نظرة سريعة عليها بالتفصيل. يوضح الشكل أعلاه أن حالة
الخمول مشفرة بالقيمة 1'b0. سيكون 3'b000 أكثر صحة ، ولكن المحرر سيعيد كل شيء بنفس الطريقة. يتم وصف مدخلات كتلة
Datapath على النحو التالي :

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

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

أنا أعلم بالفعل من هذا الإدخال أن هذا NOP عاديا. ولكن يمكنك النقر عليه مرتين وقراءة النسخة الكاملة:

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

إذا قمت بالنقر المزدوج عليها ، سيتم الكشف عن التفاصيل:

فيما يلي القائمة الكاملة للأعلام التي يمكن عرضها:

بعد تحديد العلم المطلوب ، يجب عليك تسميته. من الآن فصاعدا ، النظام لديه علم. كما ترون ، فإن علامة
NoData هي اسم حالة سلسلة
F0 block (فارغة) . أي إشارة إلى عدم وجود بيانات في المخزن المؤقت للإدخال. آه
! NoData ، على التوالي ،
انعكاسها . علامة على توفر البيانات. بمجرد دخول البيانات إلى FIFO (برمجيًا أو باستخدام DMA) ، سيتم مسح العلم (وانعكاسها) ، وفي دورة الساعة التالية ، يخرج الآلي من حالة الخمول ويدخل حالة
GetData .

كما ترون ، سوف يخرج الأوتوماتيكي من هذه الحالة دون قيد أو شرط بعد دخوله في دورة ساعة واحدة بالضبط. لا توجد إجراءات مذكورة على الرسم البياني الانتقالي لهذه الحالة. ولكن يجب أن تنظر دائمًا إلى ما ستفعله ALU. رمز الحالة هو 1'b1 ، أي 3'b001. ننظر إلى العنوان المقابل في ALU:

هناك شيء. لعدم وجود خبرة في قراءة ما هو مكتوب هنا ، افتحه بالنقر المزدوج على الخلية المقابلة:

ويترتب على ذلك أن ALU نفسها لا تزال لا تقوم بأي إجراءات. ولكن محتويات FIFO0 ، أي البيانات القادمة من البرنامج أو كتلة DMA ، سيتم وضعها في سجل A0. وبالنظر إلى المستقبل ، سأقول أن A0 يستخدم كسجل نوبة ، يخرج منه البايت في شكل تسلسلي. التسجيل A1 سيضع قيمة التسجيل D1. بشكل عام ، عادة ما يتم ملء جميع سجلات D في البرنامج قبل بدء تشغيل الجهاز. بعد ذلك ، عند فحص واجهة برمجة التطبيقات ، سنرى أنه يتم وضع عدد علامات الساعة في هذا السجل ، والذي يحدد مدة البت الثالث. لذا في A0 ، انخفضت القيمة المحولة ، وفي A1 ، كانت مدة جزء البداية من البت. وفي الإيقاع التالي ، ستدخل الماكينة بالتأكيد في الحالة
الثابتة 1 .

كما يوحي اسم الدولة ، يتم إنشاء الثابت 1 هنا. لنلق نظرة على وثائق LED. هذه هي الطريقة التي يجب أن تنتقل بها الوحدة:

وهنا - صفر:

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

وتحت الدولة على الرسم البياني يوجد مثل هذا النص:

لا تندهش من رمز التساوي. هذه هي ميزات المحرر. في رمز Verilog الناتج (الذي يتم إنشاؤه تلقائيًا بواسطة نفس النظام) سيكون هناك سهم:
Constant1 : begin CurrentBit <= (1); if (( CycleTimeout ) == 1'b1) begin MainState <= Setup1 ; end end
القيمة المتداخلة في هذا الزناد هي ناتج مجموعتنا الكاملة:

أي ، عندما دخلت الآلة إلى حالة
Constant1 ، سيحصل ناتج الكتلة التي نقوم بتطويرها على واحدة. الآن دعونا نرى كيف تمت برمجة ALU للعنوان 3'b010:

نكشف هذا العنصر:

يتم طرح الوحدة 1 من السجل A1. تقع قيمة خرج ALU في السجل A1. أعلاه ، اعتبرنا أن A1 هو عداد ساعة يستخدم لتعيين مدة نبض الإخراج. دعني أذكرك أنه تم تمهيده من D1 في الخطوة الأخيرة.
ما هو شرط الخروج من الدولة؟
CycleTimeOut . يوصف بين المخرجات على النحو التالي:

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

ننظر إلى تعليمات ALU عند 3'b011. سأفتحه على الفور:

يبدو أن ALU ليس لديها إجراءات. عملية NOP. ولا يحصل إخراج ALU في أي مكان. لكن الأمر ليس كذلك. إجراء مهم للغاية هو تحويل البيانات في ALU. والحقيقة هي أن لقمة الحمل بين المخرجات متصلة بسلسلة
ShiftOut الخاصة بنا:

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

يبدأ إرسال الجزء الثاني من البت - القيمة الفورية هي 0 أو 1.
نعود إلى تعليمات ALU. بالإضافة إلى ما قيل بالفعل ، من الواضح أن محتويات السجل D1 سيتم وضعها مرة أخرى في السجل A1 من أجل قياس مدة الثلث الثاني من النبض مرة أخرى.
حالة
DataStage تشبه إلى حد كبير حالة
Constant1 . يقوم الأوتوماتيكي بطرح واحد من A1 ويدخل الحالة التالية عندما يصل إلى الصفر. دعني أريها هكذا:

ومثل هذا:

ثم تأتي حالة
Setup2 ، التي نعرف جوهرها بالفعل.

في هذه الحالة ،
يتم إعادة تعيين مشغل
CurrentBit إلى صفر (حيث سيتم إرسال الثلث الثالث من النبض ، وجزء التوقف ، وهو دائمًا صفر). تقوم ALU بتحميل محتويات D1 إلى A1. يمكنك حتى رؤيته في ملاحظة قصيرة بعينك المدربة:

تتطابق حالة
Constant0 تمامًا مع حالات
Constant1 و
DataStage . اطرح الوحدة من A1. عندما تصل القيمة إلى الصفر ،
اخرج إلى حالة
ShiftData :


حالة
ShiftData أكثر تعقيدًا. في التعليمات المقابلة لـ ALU ، يتم تنفيذ الإجراءات التالية:

يتم تحويل السجل A0 بمقدار 1 بت ، ويتم إرجاع النتائج إلى A0. في A1 ، يتم وضع محتويات D1 مرة أخرى لبدء قياس ثلث البداية لبت البيانات التالي.
من الأفضل النظر في أسهم الإخراج مع مراعاة الأولويات ، والتي نقر عليها نقرة مزدوجة على حالة
ShiftData .

إذا لم يتم إرسال البت الأخير (حول كيفية تكوين هذا العلم ، أقل قليلاً) ، فإننا ننقل واحدة للبت التالي من البايت الحالي.
إذا تم إرسال البت الأخير ولا توجد بيانات في FIFO ، فإننا ننتقل إلى حالة الخمول.
أخيرًا ، إذا تم إرسال البت الأخير ، ولكن هناك بيانات في FIFO ، ننتقل إلى اختيار وإرسال البايت التالي.
الآن عن عداد البت. هناك بطاريتان فقط في ALU: A0 و A1. وهي مشغولة بالفعل بسجل المناوبات وعداد التأخير ، على التوالي. لذلك ، يتم استخدام عداد بت خارجيًا.

انقر عليه مرتين:

القيمة عند التمهيد هي ستة. يتم تحميله باستخدام علامة
LoadCounter الموضحة في قسم المتغير:

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

من هذا المستوى ، هناك تحكم في كل من وحدة مكتبة DMA وجميع الأجزاء المضمنة في جزء UDB. لتنفيذ واجهة برمجة التطبيقات ، أضاف مؤلف الأصل ملفات الرأس والبرامج:

شكل الجسم لهذه الملفات يجعلك حزينًا. اللوم كله هو حب مطوري PSoC Designer لـ "المحترفين". ومن هنا جاءت أسماء وحدات الماكرو والكيلومتر الرهيبة. تنظيم الصف في C ++ سيكون مفيدًا هنا. لقد تحققنا على الأقل من هذا عند تنفيذ RTOS MAX: فقد ظهر بشكل جميل ومريح. ولكن يمكنك هنا أن تجادل كثيرًا ، ولكن سيتعين عليك استخدام ما تم تخفيضه إلينا من الأعلى. سأعرض باختصار فقط شكل وظيفة API التي تحتوي على وحدات الماكرو هذه:
volatile void* `$INSTANCE_NAME`_Start(unsigned int nNumberOfNeopixels, void* pBuffer, double fSpeedMHz) {
يجب قبول قواعد اللعبة. الآن أنت تعرف من أين تستمد الإلهام من تطوير وظائفك (من الأفضل القيام بذلك في المشروع الأصلي). وأنا أفضل التحدث عن التفاصيل ، مع أخذ الخيار الذي تمت معالجته بالفعل بواسطة المولد.
بعد إنشاء الكود (كما هو موضح أدناه) ، سيتم تخزين هذا الملف هنا:

وسيكون العرض قابلاً للقراءة تمامًا بالفعل. هناك وظيفتان حتى الآن. الأول يهيئ النظام ، والثاني يبدأ نقل البيانات من المخزن المؤقت إلى خط LED.
تؤثر التهيئة على جميع أجزاء النظام. يتم تهيئة عداد السبعة بتات ، وهو جزء من نظام UDB:
NP_Neo_BITCNT_Start();
هناك حساب ثابت يجب تحميله في سجل D1 (أذكر أنه يحدد مدة كل من البتات الثالثة):
unsigned char fCyclesOn = (unsigned char)(0.35/(1.0/(fSpeedMHz))); CY_SET_REG8(NP_Neo_DPTH_D1_PTR, fCyclesOn+1);
يستغرق إعداد كتلة DMA معظم هذه الوظيفة. يتم استخدام المخزن المؤقت كمصدر ، ويتم استخدام FIFO0 من كتلة UDB كمستقبل (NP_Neo_DPTH_F0_PTR في سجل الكيلومتر). كان المؤلف جزءًا من هذا الإعداد في وظيفة نقل البيانات. ولكن ، في رأيي ، فإن إجراء جميع الحسابات من أجل كل عملية إرسال مضيعة للغاية. خاصة عندما تفكر في أن أحد الإجراءات داخل الوظيفة يبدو ضخمًا جدًا.
الوظيفة الثانية على خلفية الأولى هي قمة الاقتضال. فقط يتم استدعاء الأول في مرحلة التهيئة ، عندما تكون متطلبات الأداء مجانية تمامًا. أثناء التشغيل ، من الأفضل عدم إضاعة دورات المعالج على أي شيء غير ضروري:
void NP_Update() { if(NP_g_pFrameBuffer) { CyDmaChEnable(NP_g_nDMA_Chan, 1); } }
من الواضح أنه لا توجد وظائف كافية للعمل مع العديد من المخازن المؤقتة (لتوفير التخزين المؤقت المزدوج) ، ولكن بشكل عام ، فإن مناقشة وظيفة API خارج نطاق المقالة. الآن الشيء الرئيسي هو إظهار كيفية إضافة دعم البرامج إلى البرامج الثابتة المطورة. الآن نحن نعرف كيف نفعل ذلك.
توليد المشروع
لذلك ، جزء البرامج الثابتة بأكمله جاهز ، تتم إضافة واجهة برمجة التطبيقات ، ماذا تفعل بعد ذلك؟ حدد عنصر القائمة
Build-> إنشاء تطبيق .

إذا سارت الأمور على ما يرام ، يمكنك فتح علامة التبويب "
النتائج" والاطلاع على الملف بامتداد
rpt .

يوضح مقدار موارد النظام التي ذهبت إلى تنفيذ البرامج الثابتة.


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

الخلاصة
آمل أنه من هذا المثال ، تعلم القراء شيئًا جديدًا ومثيرًا للاهتمام حول العمل العملي مع كتل UDB. حاولت التركيز على مهمة محددة (التحكم في LED) ، وكذلك على منهجية التصميم ، حيث كان عليّ فهم بعض الجوانب التي كانت واضحة للمتخصصين. حاولت تمييزها بينما ذكريات المهمة جديدة. بالنسبة للمشكلة التي تم حلها ، تبين أن الرسوم البيانية للتوقيت بالنسبة لي ليست مثالية تمامًا مثل تلك الخاصة بمؤلف التطوير الأصلي ، ولكنها تتناسب تمامًا مع التسامح المحدد في وثائق LED ، وكانت موارد النظام أقل بكثير.
في الواقع ، هذا ليس سوى جزء من المعلومات غير القياسية الموجودة. على وجه الخصوص ، قد يبدو من معظم المواد أن UDB يعمل بشكل جيد فقط مع البيانات التسلسلية ، ولكن هذا ليس كذلك. تم العثور على ملاحظة التطبيق ، والتي توضح باختصار كيف يمكنك القيادة والبيانات المتوازية. يمكننا النظر في أمثلة محددة بناءً على هذه المعلومات (على الرغم من أنه من المستحيل أن تطغى على FX2LP ، وحدة تحكم أخرى من Cypress: PSoC لديها سرعة ناقل USB أقل).
رأسي يدور الأفكار حول كيفية حل مشكلة "وميض" طابعة ثلاثية الأبعاد ، والتي لطالما عذبتني. هناك ، المقاطعات التي تخدم محركات السائر تلتهم فقط نسبة جنونية من وقت وحدة المعالجة المركزية. بشكل عام ، تحدثت كثيرًا عن المقاطعات ووقت المعالج في
مقال حول RTOS MAX . هناك تقديرات أنه لخدمة المحركات السائر ، من الممكن أخذ جميع الأكواخ المؤقتة بالكامل إلى UDB ، مما يترك للمعالج مهمة حسابية بحتة دون خوف من أنه لن يكون لديه الوقت للقيام بذلك في فترة زمنية مخصصة.
لكن هذه الأشياء لا يمكن تفسيرها إلا إذا كان الموضوع مثيرًا للاهتمام.