قم بتشغيل العرض على STM32 عبر LTDC ... على التسجيلات

تحياتي! في الآونة الأخيرة ، يحتاج المشروع إلى تشغيل شاشة تحتوي على واجهة LVDS. لتنفيذ المهمة ، تم تحديد وحدة تحكم STM32F746 ، لأنه لقد عملت معه بالفعل كثيرًا ولديه وحدة LTDC ، والتي تتيح لك العمل مباشرة مع الشاشة بدون وحدة تحكم. في هذه الحالة ، يتم تنفيذ وحدة التحكم بالفعل داخل وحدة التحكم الدقيقة. أيضًا ، كانت الحجة الأخيرة هي أنه كان هناك تصحيح أخطاء STM32F746-Disco على هذا الحجر ، والذي كان لدي في متناول اليد ، مما يعني أنه يمكنني بدء العمل في المشروع دون انتظار اللوحة والمكونات وما إلى ذلك ليأتي إلي.

اليوم سأخبرك بكيفية تشغيل وحدة LTDC ، بالعمل مع السجلات (CMSIS). HAL والمكتبات الأخرى لا تحب ولا تستخدم لأسباب دينية ، ولكن هذا أيضًا مهم. سترى أن رفع الأجهزة الطرفية المعقدة على السجلات أمر بسيط مثل SPI العادي. مثيرة للاهتمام؟ ثم دعنا نذهب!



1. القليل عن LTDC


هذه الوحدة الطرفية هي في الأساس وحدة تحكم ، والتي تقف عادة على جانب الشاشة ، على سبيل المثال ، SSD1963 وما شابه ذلك. إذا نظرنا إلى هيكل LTDC ، فسنرى أن هذا هو في الواقع ناقل متوازي مع 24 بت + مسرع رسومات الأجهزة + مجموعة من البيانات في ذاكرة الوصول العشوائي ، والتي هي في الواقع مخزن مؤقت للعرض (مخزن مؤقت للإطار).



عند الإخراج ، لدينا ناقل متوازي عادي ، يحتوي على 24 بت من الألوان (8 بت لكل لون من نموذج RGB) ، وخطوط التزامن ، وشاشة تشغيل / إيقاف ، وساعة بكسل. هذا الأخير ، في الواقع ، هو إشارة ساعة يتم من خلالها تحميل وحدات البكسل في الشاشة ، أي إذا كان لدينا تردد 9.5 ميجا هرتز ، فيمكننا في غضون ثانية واحدة تحميل 9.5 مليون بكسل. هذا من الناحية النظرية ، بالطبع ، في الممارسة ، تكون الأرقام أكثر تواضعا إلى حد ما بسبب التوقيت وأشياء أخرى.

للحصول على مقدمة أكثر تفصيلاً لـ LTDC ، أنصحك بقراءة بعض المستندات:

  1. نظرة عامة على قدرات LTDC في F4 ، في كل شيء لدينا F7 هو نفسه
  2. ملاحظة التطبيق 4861. "وحدة تحكم العرض LCD-TFT (LTDC) على وحدات MCU STM32"

2. ماذا علينا أن نفعل؟


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



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

  • تفعيل LTDC قطع مسافة السباق
  • ضبط نظام الساعة وتردد إخراج البيانات (ساعة بكسل)
  • قم بتكوين منافذ الإدخال / الإخراج (GPIO) للعمل مع LTDC
  • قم بإعداد توقيت لنموذج العرض الخاص بنا
  • اضبط قطبية الإشارات. فعلت بالفعل بشكل افتراضي
  • حدد لون خلفية الشاشة. لن نراه بعد ، يمكنك تركها "عند الأصفار"
  • قم بتعيين الحجم الفعلي للمنطقة المرئية من الشاشة لطبقة معينة
  • حدد تنسيق الألوان: ARGB8888 ، RGB 888 ، RGB565 ، إلخ.
  • حدد عنوان الصفيف الذي سيكون بمثابة إطار مؤقت
  • تشير إلى كمية البيانات في سطر واحد (الطول في العرض)
  • تشير إلى عدد الأسطر (ارتفاع العرض)
  • قم بتضمين الطبقة التي نعمل معها
  • تمكين وحدة LTDC

مخيف وكنت خائفة ، ولكن تبين أن العمل لمدة 20 دقيقة مع جميع الإجراءات. هناك مهمة ، الخطة مخططة وتبقى فقط لتحقيقها.

3. إنشاء نظام الساعة


العنصر الأول الذي نحتاجه لإرسال إشارة ساعة إلى وحدة LTDC ، يتم ذلك عن طريق الكتابة إلى سجل RCC:

RCC->APB2ENR |= RCC_APB2ENR_LTDCEN; 

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

 RCC->CR |= RCC_CR_HSEON; while (!(RCC->CR & RCC_CR_HSERDY)); 

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

 FLASH->ACR |= FLASH_ACR_LATENCY_5WS; 

الآن ، للحصول على التردد المطلوب ، سأقسم 25 ميجاهرتز من الإدخال إلى 25 وأحصل على 1 ميجاهرتز. بعد ذلك ، في PLL فقط أضرب في 432 ، لأنه في المستقبل يوجد مقسم تردد بحد أدنى / 2 وتحتاج إلى تطبيق ضعف التردد عليه. بعد ذلك ، نقوم بتوصيل مدخل PLL بمرنان الكوارتز الخاص بنا (HSE):

 RCC->PLLCFGR |= RCC_PLLCFGR_PLLM_0 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLM_4; RCC->PLLCFGR |= RCC_PLLCFGR_PLLN_4 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLN_7 | RCC_PLLCFGR_PLLN_8; RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC; 

الآن قم بتمكين PLL وانتظر العلم الجاهز:

 RCC->CR |= RCC_CR_PLLON; while((RCC->CR & RCC_CR_PLLRDY) == 0){} 

نقوم بتعيين إخراج PLL الخاص بنا كمصدر لتردد النظام وننتظر العلم الجاهز:

 RCC->CFGR |= RCC_CFGR_SW_PLL; while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_1) {} 

يؤدي هذا إلى إنهاء الإعداد العام للساعة وننتقل إلى تعيين تردد الساعة (PLLSAI) لعرضنا (ساعة بكسل). يتم أخذ إشارة PLLSAI وفقًا لورقة البيانات بعد المقسم / 25 ، أي عند الإدخال لدينا 1 ميجاهرتز. نحتاج إلى الحصول على تردد حوالي 9.5 ميجاهرتز ، لهذا نضرب تردد 1 ميجاهرتز في 192 ، ثم باستخدام مقسومين على 5 و 4 نحصل على القيمة المطلوبة PLLSAI = 1 ميجاهرتز * 192/5/4 = 9.6 ميجاهرتز:

 RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIN_6 | RCC_PLLSAICFGR_PLLSAIN_7; RCC->PLLSAICFGR |= RCC_PLLSAICFGR_PLLSAIR_0 | RCC_PLLSAICFGR_PLLSAIR_2; RCC->DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_0; RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_1; 

كخطوة أخيرة ، نقوم بتمكين PLLSAI للعرض وننتظر العلم الجاهز للعمل:

 RCC->CR |= RCC_CR_PLLSAION; while ((RCC->CR & RCC_CR_PLLSAIRDY) == 0) {} 

هذا يكمل الإعداد الأساسي لنظام الساعة ، حتى لا ننسى ثم لا نتألم ، دعنا نتيح تسجيل الوقت على جميع منافذ الإدخال / الإخراج (GPIO). ليس لدينا طاقة البطارية ، على الأقل لتصحيح الأخطاء ، لذلك لا نحفظ:

 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOJEN; RCC->AHB1ENR |= RCC_AHB1ENR_GPIOKEN; 

4. تكوين منافذ الإدخال / الإخراج (GPIO)


يعد إعداد gpio أمرًا بسيطًا للغاية - لدينا جميع أرجل حافلة LTDC ليتم تهيئتها كمخرجات بديلة وعلى تردد عالٍ. للقيام بذلك ، في الدليل المرجعي في الصفحة 201 لدينا هذه النصيحة:



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



كما ترون ، فإن منطق الجدول هو الأبسط: نجد الوظيفة التي نحتاجها ، في حالتنا LTDC B0 ، ثم ننظر إلى GPIO قيد التشغيل (PE4 ، على سبيل المثال) وفي الجزء العلوي نرى عدد الوظيفة البديلة التي سنستخدمها لتكوين (AF14 معنا). لتهيئة مخرجاتنا كمخرجات دفع سحب بوظيفة بديلة ، LTDC B0 ، نحتاج إلى كتابة الكود التالي:

 GPIOE->MODER &= ~GPIO_MODER_MODER4; GPIOE->MODER |= GPIO_MODER_MODER4_1; GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4_1; GPIOE->AFR[0] &= ~GPIO_AFRL_AFRL4_0; GPIOE->AFR[0] |= GPIO_AFRL_AFRL4_1 | GPIO_AFRL_AFRL4_2 | GPIO_AFRL_AFRL4_3; 

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

 GPIOK->MODER &= ~GPIO_MODER_MODER3; GPIOK->MODER |= GPIO_MODER_MODER3_0; 

هذا الإعداد مخصص لإخراج PK3 ، والذي يشغل الإضاءة الخلفية ويطفئها. بالمناسبة ، يمكنك أيضًا دفعه لضبط السطوع بسلاسة. بالنسبة إلى PI12 ، التي تتضمن شاشة عرض (DISP) ، فكل شيء هو نفسه. السرعة على هذين الدبابيس منخفضة افتراضيًا ، لأن بعض الإجراءات عالية التردد ليست مطلوبة منها.

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

5. التوقيتات وإعداداتها


التوقيتات من وجهة نظر مادية هي تأخيرات عادية. أعتقد أنك لاحظت مرارًا وتكرارًا انحرافات مختلفة لنوع التأخير (1) عندما نظرت إلى أمثلة التعليمات البرمجية على شاشات العرض مع وحدات تحكم SPI / I2C مشابهة لـ ILI9341. هناك ، هناك حاجة إلى تأخير بحيث يكون لدى وحدة التحكم ، على سبيل المثال ، وقت لقبول الأمر ، وتنفيذه ، ثم القيام بشيء ما باستخدام البيانات. في حالة LTDC ، كل شيء هو نفسه تقريبًا ، فقط لن نقوم بعمل عكازات ولماذا لا - وحدة التحكم الدقيقة الخاصة بنا نفسها قادرة على تكوين التوقيت اللازم في الأجهزة. لماذا هم على شاشة لا يوجد فيها جهاز تحكم؟ نعم ، من الأساسي أنه بعد ملء السطر الأول من وحدات البكسل ، انتقل إلى السطر التالي وعد إلى بدايته. ويرجع ذلك إلى تقنية إنتاج الشاشات ، وبالتالي فإن كل طراز عرض محدد له توقيتاته الخاصة.

لمعرفة القيم التي نحتاجها ، انتقل إلى موقع ويب ST وابحث في الرسم التخطيطي للوحة تصحيح أخطاء STM32F746-Disco . هناك يمكننا أن نرى أن العرض هو RK043FN48H-CT672B وأن وثائقه متاحة ، على سبيل المثال ، هنا . نحن مهتمون أكثر بالجدول الوارد في الصفحة 13 في القسم 7.3.1:



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

انتقل إلى الإعدادات. بادئ ذي بدء ، لكي لا أبقي هذه القيم في رأسي ، سأرتبها في شكل تعريفات:

 #define DISPLAY_HSYNC ((uint16_t)30) #define DISPLAY_HBP ((uint16_t)13) #define DISPLAY_HFP ((uint16_t)32) #define DISPLAY_VSYNC ((uint16_t)10) #define DISPLAY_VBP ((uint16_t)2) #define DISPLAY_VFP ((uint16_t)2) 

هناك ميزة مثيرة للاهتمام. عرض نبض التوقيت ، الذي يسمى DISPLAY_HSYNC ، له قيمة في الجدول فقط لتردد ساعة البكسل 5 ميجاهرتز ، ولكن ليس لـ 9 و 12 ميجاهرتز. يجب تحديد هذا التوقيت لشاشتك ، لقد حصلت على هذه القيمة 30 ، عندما كانت مختلفة في الأمثلة من ST. في البداية ، إذا كان لديك خطأ في إعداده ، فسيتم تحويل الصورة إما إلى اليسار أو إلى اليمين. إذا كان على اليمين ، فإننا نخفض التوقيت ؛ وإذا كان على اليسار ، نزيده. في الواقع ، إنها تؤثر على أصل المنطقة المرئية ، والتي سنراها لاحقًا. فقط ضع في اعتبارك ، وستساعد الصورة التالية من الصفحة 24 من AN4861 لدينا على فهم هذه الفقرة بأكملها:



تجريد صغير مناسب هنا. لدينا منطقتان للعرض: مرئي وعام. تحتوي المنطقة المرئية على أبعاد بدقة معلنة تبلغ 480 × 272 بكسل ، والمنطقة الإجمالية هي المرئية + توقيتاتنا ، والتي يوجد منها 3 على كل جانب. من المفيد أيضًا أن نفهم (هذا لم يعد تجريدًا) أن علامة نظام واحدة هي 1 بكسل ، لذا فإن المساحة الإجمالية هي 480 بكسل + HSYNC + HBP + HFP.

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

لتعيين التوقيتات ، جعلت من نفسي "ورقة غش" صغيرة للمستقبل داخل المشروع ، كما ستساعدك على فهم الرقم المحدد وأين تكتبه:

 /* *************************** Timings for TFT display********************************** * * HSW = (DISPLAY_HSYNC - 1) * VSH = (DISPLAY_VSYNC - 1) * AHBP = (DISPLAY_HSYNC + DISPLAY_HBP - 1) * AVBP = (DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) * AAH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) * TOTALW = (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1) * TOTALH = (DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP - 1) * */ 

من أين أتت "ورقة الغش" هذه ... أولاً ، لقد رأيت "صيغة" مماثلة بضع فقرات من قبل. ثانيًا ، انتقل إلى الصفحة 56 من AN4861 لدينا:



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

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

 LTDC->SSCR |= ((DISPLAY_HSYNC - 1) << 16 | (DISPLAY_VSYNC - 1)); LTDC->BPCR |= ((DISPLAY_HSYNC+DISPLAY_HBP-1) << 16 | (DISPLAY_VSYNC+DISPLAY_VBP-1)); LTDC->AWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP - 1) << 16 | (DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1)); LTDC->TWCR |= ((DISPLAY_WIDTH + DISPLAY_HSYNC + DISPLAY_HBP + DISPLAY_HFP -1)<< 16 |(DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP + DISPLAY_VFP - 1)); 

وهذا كل شيء مع التوقيت! في هذا القسم ، يمكنك فقط تكوين لون الخلفية. لديّ باللون الأسود بشكل افتراضي ، لذا فهو مكتوب بالصفر. إذا كنت تريد تغيير لون طبقة الخلفية (الخلفية) ، فيمكنك كتابة أي قيمة بالتساوي ، على سبيل المثال ، 0xFFFFFFFF وملء كل شيء باللون الأبيض:

 LTDC->BCCR = 0; 

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

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



6. الجزء الأخير من إعداد LTDC


تنقسم إعدادات LTDC إلى قسمين: الأول شائع في وحدة LTDC بالكامل ويقع في مجموعة تسجيل LTDC ، والثاني يتم تكوينه في إحدى طبقتين وهو في مجموعة LTDC_Layer1 و LTDC_Layer2 .

قمنا بإعداد الإعدادات العامة في الفقرة السابقة ، وهذه تتضمن تعيين التوقيتات وطبقة الخلفية. ننتقل الآن إلى تعيين الطبقات وتتطلب قائمتنا الحجم الحقيقي للمنطقة المرئية للطبقة ، والتي يتم وصفها في شكل 4 إحداثيات (x0 ، y0 ، x1 ، y2) ، والتي تسمح لنا بالحصول على أبعاد المستطيل. قد يكون حجم الطبقة المرئية أقل من دقة الشاشة ، ولا يزعج أحد لجعل حجم الطبقة 100 لكل 100 بكسل. لضبط حجم المنطقة المرئية ، اكتب الرمز التالي:

 LTDC_Layer2->WHPCR |= (((DISPLAY_WIDTH + DISPLAY_HBP + DISPLAY_HSYNC - 1) << 16) | (DISPLAY_HBP + DISPLAY_HSYNC)); LTDC_Layer2->WVPCR |= (((DISPLAY_HEIGHT + DISPLAY_VSYNC + DISPLAY_VBP - 1) << 16) |(DISPLAY_VSYNC + DISPLAY_VBP)); 

كما ترى ، كل شيء كما هو الحال مع التوقيت. تتكون نقاط البداية (x0 ، y0) للمنطقة المرئية من مجموع توقيتين: HSYNC + HBP و VSYNC + VBP. لحساب إحداثيات نقطة النهاية (x1 ، y1) ، تتم إضافة العرض والارتفاع بالبكسل إلى بيانات القيمة.

الآن تحتاج إلى تكوين تنسيق البيانات المستلمة. يتم الحصول على أقصى جودة عند استخدام تنسيق ARGB8888 ، ولكن في نفس الوقت نحصل على أقصى قدر من الذاكرة المشغولة. يشغل بكسل واحد 32 بت أو 4 بايت ، مما يعني أن الشاشة بأكملها تأخذ 4 * 480 * 272 = 522،240 بايت ، أي نصف ذاكرة الفلاش الخاصة بنا ليست أضعف وحدة تحكم. لا تخف - توصيل SDRAM الخارجي وذاكرة الفلاش عبر QSPI يحل مشاكل الذاكرة ولا توجد قيود على هذا التنسيق ، نحن نفرح بجودة عالية. إذا كنت تريد توفير مساحة أو إذا كانت شاشتك لا تدعم تنسيق 24 بت ، فسيتم استخدام نماذج أكثر ملاءمة لهذا ، على سبيل المثال ، RGB565. تنسيق شائع جدًا لكل من الشاشات والكاميرات ، والأهم من ذلك عند استخدامه ، 1 بكسل يأخذ فقط 5 + 6 + 5 = 16 بت أو 2 بايت. وفقًا لذلك ، ستكون كمية الذاكرة التي تشغلها الطبقة أقل مرتين. بشكل افتراضي ، تحتوي وحدة التحكم بالفعل على تنسيق ARGB8888 وقد تم تكوينه كما يلي:

 LTDC_Layer2->PFCR = 0; 

إذا كنت بحاجة إلى تنسيق مختلف عن ARGB8888 ، فانتقل إلى الصفحات 533 و 534 في الدليل المرجعي وحدد التنسيق المطلوب من القائمة أدناه:



الآن قم بإنشاء صفيف وتمرير عنوانه إلى LTDC ، سيتحول إلى مخزن مؤقت للإطار وسيكون "انعكاسًا" لطبقتنا. على سبيل المثال ، تحتاج إلى ملء البكسل الأول في الصف الأول باللون الأبيض ، لذلك تحتاج فقط إلى كتابة قيمة اللون (0xFFFFFFFF) إلى العنصر الأول من هذا الصفيف. هل تحتاج إلى ملء البكسل الأول في الصف الثاني؟ ثم نكتب أيضًا قيمة اللون في العنصر بالرقم (480 + 1). 480 - قم بعمل فاصل أسطر ، ثم أضف الرقم في السطر الذي نحتاجه.

يبدو هذا الإعداد كما يلي:

 #define DISPLAY_WIDTH ((uint16_t)480) #define DISPLAY_HEIGHT ((uint16_t)272) const uint32_t imageLayer2[DISPLAY_WIDTH * DISPLAY_HEIGHT]; LTDC_Layer2->CFBAR = (uint32_t)imageLayer2; 

بطريقة جيدة ، بعد تكوين LTDC ، تحتاج أيضًا إلى تكوين SDRAM لإزالة مُحَوِر الثابت والحصول على المخزن المؤقت للإطار في ذاكرة الوصول العشوائي ، لأن ذاكرة الوصول العشوائي الخاصة بـ MK ليست كافية حتى لطبقة واحدة ذات 4 بايت. على الرغم من أن هذا لا يضر لاختبار التكوين الصحيح للأجهزة الطرفية.

بعد ذلك ، تحتاج إلى تحديد قيمة طبقة ألفا ، أي الشفافية لطبقة Layer2 الخاصة بنا ، لذلك نكتب قيمة من 0 إلى 255 ، حيث 0 هي طبقة شفافة تمامًا ، 255 غير شفافة تمامًا ، وتكون مرئية بنسبة 100٪:

 LTDC_Layer2->CACR = 255; 

وفقًا لخطتنا ، من الضروري الآن تسجيل حجم منطقة العرض المرئية بالبايت ، لذلك نكتب القيم المقابلة في السجلات:

 LTDC_Layer2->CFBLR |= (((PIXEL_SIZE * DISPLAY_WIDTH) << 16) | (PIXEL_SIZE * DISPLAY_WIDTH + 3)); LTDC_Layer2->CFBLNR |= DISPLAY_HEIGHT; 

تبقى الخطوتان الأخيرتان ، وهما إدراج الطبقة 2 والوحدة الطرفية LTDC نفسها. للقيام بذلك ، اكتب البتات المقابلة:

 LTDC_Layer2->CR |= LTDC_LxCR_LEN; LTDC->GCR |= LTDC_GCR_LTDCEN; 

هذا يكمل تكوين وحدتنا ويمكنك العمل مع عرضنا!

7. القليل عن العمل مع LTDC


يأتي كل العمل مع الشاشة الآن فقط لكتابة البيانات إلى مصفوفة imageLayer2 ، ولها حجم 480 × 272 عنصرًا ، والذي يتوافق تمامًا مع الدقة والتلميحات في حقيقة بسيطة - 1 عنصر صفيف = 1 بكسل على الشاشة .

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

تعد رموز النص والخطوط والعناصر الأخرى صفائف بكسل بطبيعتها ، لذا لتنفيذها ، تحتاج إلى تنفيذ المنطق بنفسك ، ولكن يجب أن تبدأ بالوظيفة الأساسية - "إخراج البكسل". يجب أن يأخذ 3 حجج: الإحداثيات على طول X ، والإحداثيات على طول Y ، وبالتالي اللون الذي يتم فيه رسم بكسل معين. قد يبدو مثل هذا:

 typedef enum ColorDisplay { RED = 0xFFFF0000, GREEN = 0xFF00FF00, BLUE = 0xFF0000FF, BLACK = 0xFF000000, WHITE = 0xFFFFFFFF } Color; void SetPixel (uint16_t setX, uint16_t setY, Color Color) { uint32_t numBuffer = ((setY - 1) * DISPLAY_WIDTH) + setX; imageLayer2[numBuffer] = Color; } 

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

الملخص


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

إذا قارنت الكود الناتج بمحلول في HAL أو SPL ، فستلاحظ أن الكود المكتوب في التسجيلات أكثر إحكاما. بإضافة بعض التعليقات حيث تحتاجها ولفها في الوظائف ، نحصل على سهولة القراءة على الأقل مثل HAL / SPL ، وإذا كنت تتذكر أن الوثائق اليدوية المرجعية تسجل ، فإن استخدام CMSIS هو أكثر ملاءمة.

1) يمكن تنزيل المشروع مع مصادر في TrueSTUDIO هنا

2) بالنسبة لأولئك الذين هم أكثر راحة في النظر إلى GitHub

3) قم بتنزيل الأداة المساعدة لتحويل الصور إلى رمز محول صور LCD هنا

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


All Articles