STM32 الجزء 2: التهيئة

البرمجة تفكك شيئًا كبيرًا ومستحيلًا إلى شيء صغير وحقيقي جدًا.


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

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

لذا فلنبدأ. في مقال سابق ، ركضت بسرعة على النقاط الأولى. كان هذا هو ملف startup.c الخاص بنا ، والذي كان مسؤولًا عن متجهين (مكدس ، Reset_Handler) وقليلاً عن البرنامج النصي للرابط. سنكمل اليوم كود التهيئة الخاص بنا ، ونقوم بفك الرابط لقطع الغيار ومعرفة كيفية عمل كل شيء.


extern void *_estack;

void Reset_Handler();

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

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


إذا لم يكن هذا الرمز واضحًا لأي شخص ، فيمكنك القراءة عنه هنا (المقالة ليست نافورة ، ولكن حاولت شرحها).

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

(بالنسبة لأولئك الذين ما زالوا لا يعرفون ما هو المتجه ، فهذا مؤشر إلى عنوان ، يُعرف أيضًا باسم "المؤشر").

نكمل الكود الخاص بنا ، وبعد ذلك سأشرح ما فعلت.

extern void *_estack;

void Reset_Handler();
void Default_Handler();

void NMI_Handler()   __attribute__((weak, alias("Default_Handler")));
void HardFault_Handler()   __attribute__((weak, alias("Default_Handler")));
//     

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

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

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


أضفت هنا وظيفة جديدة Default_Handler ()؛ الذي سيتعامل مع كل المقاطعات ، بالتأكيد. ولكن أيضًا بمساعدة ، __attribute__((weak, alias("Default_Handler")));لاحظت أنه إذا تم الإعلان عن وظيفة تحمل نفس الاسم ، فسيكون معالج هذا المقاطعة. بعبارة أخرى ، الآن هو مجرد لقب لـ Default_Handler. حتى نتمكن من إضافة جميع المتجهات في القائمة من الدليل المرجعي الخاص بك. يتم ذلك بحيث إذا قررت فجأة استخدام مقاطعة ، ولكن نسيت إنشاء معالج دالة ، فسيتم استدعاء Default_Handler المعتاد.

أعتقد أنه في هذه المرحلة مع المتجهات يمكنك الانتهاء والانتقال إلى تهيئة القطاعات وإلى الرابط. للبدء ، دعنا نقوم بتحديث startup.c مرة أخرى بإضافة تهيئة قطاعات البيانات و bss إلى نص Reset_Handler بالإضافة إلى العديد من المتغيرات الخارجية.

extern void *_sidata, *_sdata, *_edata, *_sbss, *_ebss;

void __attribute__((naked, noreturn)) Reset_Handler()
{


	void **pSource, **pDest;
	for (pSource = &_sidata, pDest = &_sdata; pDest != &_edata; pSource++, pDest++)
		*pDest = *pSource;

	for (pDest = &_sbss; pDest != &_ebss; pDest++)
		*pDest = 0;

	while(1);
}


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

MEMORY{
	ROM(rx) : ORIGIN = 0x08000000, LENGTH = 1M
	SRAM (rwx):     ORIGIN = 0x20010000, LENGTH = 240K
}

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

SECTIONS{
	.isr_vector : {
	KEEP(*(.isr_vector))
	} >ROM
	
	.text : {
        . = ALIGN(4);
        *(.text)
    } >ROM
	
	_sidata = LOADADDR(.data);
        .data : {
		. = ALIGN(4);
		_sdata = .;
		*(.data)
		. = ALIGN(4);
		_edata = .;
	} >SRAM AT>ROM
	
	.bss : {
		. = ALIGN(4);
		_sbss = .;
		*(.bss)
		. = ALIGN(4);
		_ebss = .;
	} >SRAM
}


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


        _sidata = LOADADDR(.data);
        .data : {
		. = ALIGN(4);
		_sdata = .;
		*(.data)
		. = ALIGN(4);
		_edata = .;
	} >SRAM AT>ROM


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

يتم أيضًا تضمين _sdata و _edata ونهاية القسم. ولكن في نهاية القسم هناك ما نحتاجه.SRAM AT>ROM. يخبر هذا الخط رابطنا أن القسم موجود في ذاكرة الوصول العشوائي (SRAM) ولكن يتم تحميل هذا القسم في ذاكرة ROM أو ذاكرة فلاش. نعود الآن إلى متغير _sidata ووظيفة LOADADDR. ستعيد لنا هذه الوظيفة قيمة ستكون مساوية لعنوان قسم التحميل. أي ، أشرنا إلى أن القسم تم تحميله في ذاكرة فلاش ، لكننا نستخدمه في ذاكرة الوصول العشوائي ، ومن أجل نقل هذا القسم ، نحتاج إلى معرفة عنوانه في ذاكرة فلاش ، وهو ما ترجعه وظيفة LOADADDR. وفي الوقت نفسه ، تشير المتغيرات _sdata و _edata إلى موقع القسم في ذاكرة الوصول العشوائي ، مما يتيح لنا الفرصة لتحميل قسم البيانات في الجزء الصحيح من الذاكرة. دعني أذكرك بأن هذا هو ما يتعامل معه كود التهيئة الذي استكملناه أعلاه.

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

.text (ORIGIN(ROM_ITCM) + SIZEOF(.isr_vector)): {
        . = ALIGN(4);
        *(.text)
    } AT>ROM_AXIM


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

في هذه المرحلة ، يتم تحميل الأقسام عند الضرورة وتهيئتها. لدينا قاعدة عمل لمزيد من كتابة التعليمات البرمجية. في المقالة التالية ، سنكتب برنامج التشغيل الأول ونومض مصباح LED.

شكرا لكم جميعا على اهتمامكم ونجاحكم في مساعيكم.

All Articles