STM32 الجزء الأول: الأساسيات

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

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

قائمة بكل ما تحتاجه:

  1. Ubuntu 16+ آلة افتراضية أو أيا كان
  2. مترجم الذراع - تنزيل في developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
  3. مصحح ومبرمج openocd - لن تتمكن من التنزيل من الرابط ، نقوم بجمعه من المصادر ، يتم إرفاق التعليمات

    git clone https://git.code.sf.net/p/openocd/code openocd
  4. محرر نص ترضيك

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

ماذا نحتاج:

  1. Makefile
  2. Linker.ld
  3. الأولي ج

لنبدأ بالفقرة الأخيرة Init.c. بادئ ذي بدء ، يجب أن يقوم عضو الكنيست الخاص بنا بتحميل العنوان "مؤشر المكدس" هو مؤشر لعنوان الذاكرة المستخدم لإرشادات PUSH و POP. أوصي بشدة أن تدرس هاتين التوجيهتين بدقة ، لأنني لن أشرح بالتفصيل جميع التعليمات. كيفية تنفيذ ذلك ، انظر أدناه.

extern void *_estack;
void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack
}

الآن دعونا نلقي نظرة على هذا المثال. تعني كلمة Extern أن الرمز خارجي ، وقد أعلنا عن هذا الرمز في ملف Linker.ld ، وسنعود إليه لاحقًا.

 __attribute__((section(".isr_vector"), used))

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

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

سوف أعطي مثالا على ذلك. يتم إعطاء المكدس يبدأ بـ 0x20010000 ورمز البرنامج هو 0x0800008. ثم يمكن كتابة الصفيف باسم

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    0x20010000,
    0x08000008
}

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

الآن سنتوقف ونولي اهتمامًا بكيفية وضع هذا الصفيف في القسم الذي نحتاجه. يتم ذلك عن طريق "ld" أو رابط. يجمع هذا البرنامج برنامجنا بأكمله معًا ويتم استخدام البرنامج النصي Linker.ld لهذا الغرض. أعطي مثالا وأقوم بتحليله.

MEMORY{
	ROM_AXIM (rx) : ORIGIN = 0x08000000, LENGTH = 1M
	RAM_DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 64K
}

_estack = LENGTH(RAM_DTCM) + ORIGIN(RAM_DTCM);

SECTIONS{
	.isr_vector : {
	KEEP(*(.isr_vector))
	} >ROM_AXIM</code>
}

دعنا نرى ما يحدث هنا. تحدد الذاكرة أقسام الذاكرة ، بينما تحدد الأقسام الأقسام. هنا نرى _stack لدينا وحقيقة أنها تساوي مجموع بداية الذاكرة وطولها ، أي نهاية ذاكرتنا. يتم أيضًا تعريف قسم .isr_vector الذي نضع فيه الصفيف. >ROM_AXIMفي نهاية قسمنا يعني أنه يجب وضع هذا القسم في قسم الذاكرة الذي يبدأ بـ 0x08000000 كما هو مطلوب بواسطة ميكروناتنا.

نضع المصفوفة حيث يكون من الضروري الآن نحتاج إلى نوع من التعليمات حتى يعمل ميكروننا. هنا هو init.c المعزز:

extern void *_estack;

void Reset_Handler();

void *vectors[] __attribute__((section(".isr_vector"), used)) = {
    &_estack,
    &Reset_Handler
};

void __attribute__((naked, noreturn)) Reset_Handler()
{
    while(1);
}

كما ذكرت سابقًا ، يجب أن يكون العنوان الثاني مؤشرًا للوظيفة أو التعليمات الأولى. ومرة أخرى ، السمات ، ولكن كل شيء بسيط ، فالوظيفة لا ترجع أي قيم وتتبع أي ABI عند المدخل ، أي عارية "عارية". في مثل هذه الوظائف ، يتم دفع المجمع المعتاد.

حان الوقت لتجميع الكود الخاص بنا ، ومعرفة ما تحت الغطاء. لن نلمس ملف Makefile في الوقت الحالي.

arm-none-eabi-gcc -c init.c -o init.o -mthumb

arm-none-eabi-gcc -TLinker.ld -o prog.elf init.o -Wl,--gc-sections -nostartfiles -nodefaultlibs -nostdlib

وهنا نرى الكثير من المفهوم. مرتب:

  1. -ترجمة ملف armv7 ، الذي يستخدم تعليمات الإبهام فقط
  2. -TLinker.ld تحديد البرنامج النصي رابط. افتراضيًا ، يتم تجميعها للتنفيذ في بيئة Linux
  3. -Wl ، - gc-section -nostartfiles -nodefaultlibs -nostdlib إزالة كافة المكتبات القياسية ، وملفات تهيئة البيئة C ، وجميع المكتبات المساعدة الأخرى مثل الرياضيات.

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

objcopy -O ihex prog.elf prog.bin

hexdump prog.bin

ثم سنرى النتيجة. "00000000: 00 01 00 02 09 00 00 08" والتي ستمثل نجاحنا. هذه هي مقالتي الأولى ولم أتمكن من الكشف عن جميع المواد والجوهر بالكامل ، لذلك في المرة التالية سأصف الآليات بمزيد من التفصيل وسوف نكون أصدقاء قادرين على الانتقال إلى برنامج لن يومض ، ولكن تكوين ساعة المعالج والحافلات.

All Articles