في بعض الأحيان يساعد رمز شخص آخر حقًا في توصيل الحديد المحيطي بجهاز التحكم الدقيق. لسوء الحظ ، قد يكون تكييف كود شخص آخر مع مشروعك أكثر صعوبة من إعادة كتابته بنفسك ، خاصة عندما يتعلق الأمر بالأطر الضخمة مثل arduino أو mbed. رغبًا في توصيل شاشة LCD صينية استنادًا إلى ILI9341 بلوحة DISCOVERY STM32L476G ، شرع المؤلف في استخدام برنامج التشغيل المكتوب لـ mbed في المشروع التجريبي من ST دون تغيير سطر واحد في رمزه. ونتيجة لذلك ، كان من الممكن في نفس الوقت تسريع الشاشة إلى سرعات تحديث غير مسبوقة تبلغ 27 إطارًا في الثانية.
مقدمة عن المشكلة
تنتج شركة ST Microelectronics وحدات تحكم دقيقة مثيرة للاهتمام للغاية ، سواء من حيث القدرات أو السعر ، أو لوحات تحرير للتطوير السريع. ستتم مناقشة أحدها - STM32L476G DISCOVERY . إن إمكانات الحوسبة لهذه اللوحة مشجعة للغاية - ARM 32 بت مع أقصى تردد ساعة 80 ميجا هرتز يمكنه تنفيذ عمليات الفاصلة العائمة. في الوقت نفسه ، فهي قادرة على تقليل استهلاك الطاقة إلى الحد الأدنى والعمل على البطارية ، في انتظار الفرصة للقيام بشيء مفيد. قررت توصيل شاشة LCD ملونة صينية رخيصة بدقة 320 × 240 تعمل على واجهة SPI لهذا الجهاز. يتم وصف كيفية استخدامه مع mbed بالتفصيل هنا . Mbed- هذه بيئة برمجة عبر الإنترنت حيث يمكنك تجميع البرامج الثابتة الخاصة بك دون الحاجة إلى وجود مترجم على جهاز الكمبيوتر الخاص بك ، وبعد ذلك يمكنك تنزيله ووميضه ببساطة بنسخه على اللوحة المتوافقة مع mbed ، والذي يبدو وكأنه قرص قابل للإزالة عند توصيله بـ USB. كل هذا رائع ، ولكن هناك بعض المشاكل. أولاً ، ليست جميع اللوحات متوافقة مع mbed. ثانيًا ، هناك العديد من المشاريع الحالية التي لا تتوافق مع mbed على الإطلاق بأي شكل من الأشكال ، بما في ذلك البرامج المقدمة من ST. وأخيرًا ، لا يتوافق جميع المطورين مع mbed ، يجد البعض (على سبيل المثال ، مؤلف هذه السطور) عيوبًا أكثر من المزايا في هذه الأداة الرائعة. ما هي هذه العيوب ، سنناقش أدناه ، يكفي الآن أن نذكر ذلك بعد توصيل برنامج تشغيل الشاشة إلى المشروع التجريبي من ST وبعض التحسينات البسيطة ، بدأ العمل بشكل أسرع 10 مرات تقريبًا.تعلم كود السائق
حان الوقت لتنزيل رمز مصدر برنامج تشغيل العرض ودراسته. يتم تنظيم العمل مع المنافذ في mbed من خلال استدعاءات لطرق الفصل التي تمثل منافذ الإدخال / الإخراج. على سبيل المثال ، تطبق فئة DigitalOut الوصول إلى منفذ الإخراج. يؤدي تعيين مثيل لهذا الكائن صفر أو واحد إلى بدء كتابة البت المطابق لمنفذ الإخراج. تتم تهيئة فئة DigitalOut بواسطة النوع المسمى PinName ، الذي يتمثل غرضه الوحيد في التعرف على ساق المعالج. أحد العوائق الرئيسية لتطبيق DigitalOut والفئات الأخرى التي تنفذ I / O هو أن المنفذ تمت تهيئته في مُنشئ مثيل الفئة. يعد هذا أمرًا رائعًا لوميض LED إذا تم إنشاء مثيل لفئة DigitalOut على المكدس في الوظيفة الرئيسية. لكن تخيل أن لدينا الكثير من الحديد المختلف ، والذي يتم تبدئه عبر عدة وحدات.إذا جعلنا حالات من فئات فئات I / O ثابتة ، فإننا نفقد كل السيطرة على التهيئة ، حيث أنها ستحدث قبل الوظيفة الرئيسية وفي ترتيب عشوائي. تستخدم مكتبات ST (تسمى HAL - مستوى تجريد الأجهزة) نموذجًا مختلفًا وأكثر مرونة. يحتوي كل منفذ إدخال / إخراج على سياقه الخاص ومجموعة من الوظائف التي تعمل معه ، ولكن يمكن استدعاؤها بالضبط عند الضرورة. عادة ما يتم إنشاء سياقات المنفذ كمتغيرات ثابتة ، ولكن لا تحدث تهيئة تلقائية غير خاضعة للرقابة (مكتبات ST مكتوبة في C). أداة مفيدة للغاية تستحق الذكر أيضًا.تستخدم مكتبات ST (تسمى HAL - مستوى تجريد الأجهزة) نموذجًا مختلفًا وأكثر مرونة. يحتوي كل منفذ إدخال / إخراج على سياقه الخاص ومجموعة من الوظائف التي تعمل معه ، ولكن يمكن استدعاؤها بالضبط عند الضرورة. عادة ما يتم إنشاء سياقات المنفذ كمتغيرات ثابتة ، ولكن لا تحدث تهيئة تلقائية غير خاضعة للرقابة (تتم كتابة مكتبات ST في C). أداة مفيدة للغاية جديرة بالذكر أيضًا.تستخدم مكتبات ST (تسمى HAL - مستوى تجريد الأجهزة) نموذجًا مختلفًا وأكثر مرونة. يحتوي كل منفذ إدخال / إخراج على سياقه الخاص ومجموعة من الوظائف التي تعمل معه ، ولكن يمكن استدعاؤها تمامًا عند الضرورة. عادة ما يتم إنشاء سياقات المنفذ كمتغيرات ثابتة ، ولكن لا تحدث تهيئة تلقائية غير خاضعة للرقابة (تتم كتابة مكتبات ST في C). أداة مفيدة للغاية تستحق الذكر أيضًا.ولكن لا تحدث تهيئة تلقائية غير خاضعة للرقابة في هذه الحالة (تتم كتابة مكتبات ST بالحرف C). أداة مفيدة للغاية جديرة بالذكر أيضًا.ولكن لا تحدث تهيئة تلقائية غير خاضعة للرقابة في هذه الحالة (تتم كتابة مكتبات ST بالحرف C). أداة مفيدة للغاية جديرة بالذكر أيضًا.CubeMX ، الذي يمكنه إنشاء جميع رموز التهيئة اللازمة لمجموعة المنافذ التي تحتاجها ، بل ويسمح لك بإجراء تغييرات على هذه المجموعة من المنافذ لاحقًا دون التأثير على التعليمات البرمجية الخاصة بك. عيبه الوحيد هو عدم القدرة على الاستخدام مع المشاريع القائمة ، يجب أن تبدأ المشروع باستخدام هذه الأداة المساعدة.تستخدم مكتبة mbed نفس وظائف HAL من مكتبة ST لتهيئة موارد وحدة التحكم الدقيقة ، ولكن هذا يجعلها بدون تفكير في الأماكن. للتأكد من ذلك ، ما عليك سوى إلقاء نظرة على رمز التهيئة لمنفذ SPI (الذي نحتاجه للعمل مع الشاشة) في ملف spi_api.c. تبحث الدالة spi_init أولاً عن منفذ SPI مناسب من خلال الأرجل التي ستستخدمها ، ثم تستدعي وظيفة init_spi ، التي تقوم بالفعل بتهيئة المنفذ. في الوقت نفسه ، تستخدم جميع منافذ SPI الثلاثة المحتملة بنية سياق ثابتةstatic SPI_HandleTypeDef SpiHandle;
هذا في الأساس مثال كلاسيكي على استخدام المتغيرات العالمية بدلاً من المتغيرات المحلية. حتى مع الأخذ في الاعتبار حقيقة أن لدينا جوهرًا واحدًا للحوسبة ، فإن السياق العالمي غير محمي من الاستخدام المتزامن في أماكن مختلفة من التعليمات البرمجية ، وهناك انقطاعات ، بالإضافة إلى ازدحام المهام المتعددة.ربط المكتبة بمشروعك
لذلك لا أريد أن أكتب جميع التعليمات البرمجية على mbed. أحب أمثلة ST التي تأتي مع CubeMX أكثر من ذلك بكثير . لم أجد برنامج التشغيل النهائي لشاشة LCD الخاصة بمكتبات ST ، ولم أرغب في كتابته بنفسي. بقيت طريقة بديلة للحصول على المتعة بطريقة أو بأخرى - لتوصيل برنامج التشغيل المكتوب من أجل mbed ، بحيث لا يتطلب تغيير أي شيء. كل ما عليك فعله هو تنفيذ مكتبات mbed بطريقة بديلة. في الواقع ، المهمة أبسط مما يبدو ، نظرًا لجميع المكتبات الكبيرة ، يستخدم برنامج تشغيل LCD منفذ الإخراج و SPI فقط. بالإضافة إلى ذلك ، يحتاج إلى وظائف تشكيل التأخيرات وفئات الملفات والتدفقات. مع هذا الأخير ، كل شيء بسيط - لا نحتاجها ونستبدلها بسدادات لا تفعل شيئًا. من السهل كتابة وظائف التأخير - فهي موجودة في الملفwait_api.h . يتطلب تنفيذ دروس I / O نهجًا أكثر إبداعًا قليلاً. سنقوم بإصلاح عدم وجود مكتبات mbed ولا نقوم بتهيئة الأجهزة في المنشئ. سيتلقى المنشئ ارتباطًا بسياق المنفذ الموجود في مكان آخر ، وسيكون رمز التهيئة الخاص به مستقلاً تمامًا عن فئات الواجهة الخاصة بنا. هناك الطريقة الوحيدة لتمرير هذه المعلومات إلى المنشئ دون تغيير رمز برنامج التشغيل - من خلال PinName ، والذي بدلاً من مجرد سرد الأرجل ، سيخزن الآن مؤشرًا إلى المنفذ ، وعدد الساق ، واختيارًا إلى المؤشر للمورد (مثل SPI) الذي يتصل به هذا الرجل.class PinName {
public:
PinName() : m_port(0), m_pin(0), m_obj(0) {}
PinName(GPIO_TypeDef* port, unsigned pin, void* obj = 0)
: m_port(port), m_pin(pin), m_obj(obj)
{
assert_param(m_port != 0);
}
GPIO_TypeDef* m_port;
unsigned m_pin;
void* m_obj;
static PinName not_connected;
};
إن تنفيذ منفذ الإخراج تافه للغاية. لتحسين الأداء ، سنحاول استخدام وظائف HAL أقل ، والعمل مباشرة مع تسجيلات المنافذ قدر الإمكان ، بالإضافة إلى كتابة رمز مضمن ، والذي سيسمح للمترجم بتجنب استدعاءات الوظائف.class DigitalOut {
public:
DigitalOut(GPIO_TypeDef* port, unsigned pin)
: m_port(port), m_pin(pin)
{
assert_param(m_port != 0);
}
DigitalOut(PinName const& N)
: m_port(N.m_port), m_pin(N.m_pin)
{
assert_param(m_port != 0);
}
void operator =(int bit) {
if (bit) m_port->BSRR = m_pin;
else m_port->BRR = m_pin;
}
private:
GPIO_TypeDef* m_port;
unsigned m_pin;
};
رمز تنفيذ منفذ SPI ليس أكثر تعقيدًا ، يمكنك رؤيته هنا . نظرًا لأننا فصلنا تهيئة المنفذ عن رمز الواجهة ، فإننا نتجاهل طلبات تغييرات التكوين. يتم تذكر عمق بت الكلمة ببساطة. إذا كان المستخدم يريد إرسال كلمة من 16 بت ، وتم تكوين المنفذ على أنه 8 بت ، فإننا نحتاج فقط إلى إعادة ترتيب وحدات البايت ونقلها واحدة تلو الأخرى - لا يزال يتم وضع ما يصل إلى 4 بايت في مخزن المنفذ المؤقت. يتم جمع جميع الملفات اللازمة لتجميع برنامج التشغيل في الدليل المتوافق . يمكنك الآن توصيل ملفات برنامج التشغيل الأصلية بالمشروع وتجميعها. سنحتاج أيضًا إلى رمز يهيئ المنافذ ، وينشئ مثيلًا للسائق ويرسم شيئًا ذا معنى على الشاشة.رفع تردد التشغيل
إذا تم استخدام شاشة LCD لإخراج شيء ديناميكي ، فهناك رغبة طبيعية في جعل الاتصال به أسرع. أول ما يتبادر إلى الذهن هو زيادة تردد ساعة SPI ، الذي يضبطه السائق عند 10 ميجا هرتز ، لكننا نتجاهل رغباته ويمكننا ضبط أي شيء. اتضح أن الشاشة تعمل بشكل جيد وعلى تردد 40 ميجا هرتز - هذا هو أقصى تردد يمكن لمعالجنا بتردد ساعة 80 ميجا هرتز. لتقييم الأداء ، تم كتابة رمز بسيط يعرض صورة نقطية 100 × 100 بكسل في حلقة. ثم تم استنتاج النتيجة على الشاشة بأكملها (صورة نقطية تشغل الشاشة بأكملها ببساطة لا تتناسب مع الذاكرة). والنتيجة - 11 إطارًا في الثانية بعيدة جدًا عن الحد النظري البالغ 32 إطارًا في الثانية ، والذي يتم الحصول عليه إذا قمت بنقل 16 بت لكل بكسل بدون توقف. يصبح السبب واضحًا إذا نظرت إليهكود مصدر السائق . إذا كان بحاجة إلى إرسال سلسلة من وحدات البايت ، فإنه ببساطة ينقلها واحدة تلو الأخرى ، في أحسن الأحوال بتغليف 16 كلمة. يكمن سبب هذا التصميم غير الفعال في واجهة برمجة تطبيقات mbed. تحتوي فئة SPI على طريقة لإرسال صفيف من البيانات ، ولكن لا يمكن استخدامها إلا بشكل غير متزامن ، واستدعاء وظيفة الإعلام عند الانتهاء ، وفي سياق معالج المقاطعة. ليس من المستغرب أن القليل من الناس يستخدمون هذه الطريقة. لقد استكملت تنفيذي لفئة SPI بوظيفة ترسل مخزنًا مؤقتًا وتنتظر نهاية التحويل. بعد أن أضفت مكالمة إلى هذه الوظيفة إلى رمز نقل الصورة النقطية ، زاد الأداء إلى 27 إطارًا في الثانية ، وهو قريب جدًا بالفعل من الحد النظري.كود المصدر
تقع هنا . لتجميع تم استخدام IAR Embedded Workbench ARM 7.50.2. استنادًا إلى البرامج الثابتة للتعليمات البرمجية البرمجية من ST. وصف دبوس، وهذا مرتبط إلى LCD يمكن العثور عليها في ملف lcd.h .