لقد كتب الكثير عن وحدات الماكرو في المجمع. وفي الوثائق وفي المقالات المختلفة. لكن في معظم الحالات ، يتعلق الأمر إما بقائمة بسيطة من التوجيهات مع وصف موجز لوظائفها ، أو لمجموعة من الأمثلة المتباينة من وحدات الماكرو الجاهزة.
الغرض من هذه المقالة هو وصف طريقة محددة لبرمجة لغة التجميع لإنشاء التعليمة البرمجية الأكثر بساطة وقراءة باستخدام وحدات الماكرو. لن تصف المقالة بناء جملة الأوامر والتوجيهات الفردية. تم تقديم وصف مفصل بالفعل من قبل الشركة المصنعة . سوف نركز على كيفية استخدام هذه الفرص لحل مشاكل محددة.
في وقت واحد ، جربت ATMEL وطورت مجموعة من وحدات التحكم الدقيقة ذات الثماني بتات ذات بنية عالية الجودة وبسيطة ، لكن في نفس الوقت نظام قيادة قوي للغاية. ولكن ، كما تعلم ، لا يوجد حد للكمال ، وبعض التعليمات المستخدمة بشكل متكرر ليست كافية. لحسن الحظ ، يمكن لمجمّع الماكرو ، بلطف ومجاني تمامًا المقدم من الشركة المصنعة ، تبسيط التعليمات البرمجية بشكل كبير من خلال استخدام التوجيهات. قبل المتابعة مباشرةً إلى وحدات الماكرو ، سنقوم بتنفيذ بعض الخطوات الأولية
تعريف الثوابت
.EQU FOSC = 16000000 .EQU CLK8 = 0
يسمح لك هذان التعريفان بالتخلص من "الأرقام السحرية" في وحدات الماكرو ، حيث يتم حساب قيم السجلات بناءً على تردد المعالج وحالة انصهار القاسم المحيطي. التعريف الأول هو تردد الكريستال المعالج في هيرتز ، والثاني هو حالة مقسم التردد المحيطي.
تسجيل التسمية
.DEF TempL = r16 .DEF TempH = r17 .DEF TempQL = r18 .DEF TempQH = r19 .DEF AL = r0 .DEF AH = r1 .DEF AQL = r2 .DEF AQH = r3
وهناك حاجة مكررة إلى حد ما لأول وهلة سجلات التسمية التي يمكن استخدامها في وحدات الماكرو. هناك حاجة إلى أربعة سجلات فقط لـ Temp إذا كنا نتعامل مع قيم 32 بت (على سبيل المثال ، في عمليات ضرب رقمين 16 بت). إذا كنا متأكدين من أن سجلي تخزين مؤقتين يكفيان لاستخدامنا في وحدات الماكرو ، فلا يمكن تحديد TempQL و TempQH . هناك حاجة إلى تعريفات لـ A لوحدات الماكرو باستخدام عمليات الضرب. لم يعد AQ ضروريًا إذا لم نستخدم حساب 32 بت مع وحدات الماكرو الخاصة بنا.
وحدات الماكرو لتنفيذ أوامر بسيطة
الآن وبعد أن اكتشفنا تسمية السجلات ، سنبدأ في تنفيذ الأوامر المفقودة ونبدأ بمحاولة تبسيط الأوامر الموجودة. مجمع AVR لديه ميزة واحدة حرج. بالنسبة إلى المدخلات والمخرجات ، فإن أول 64 منفذًا تستخدم أوامر الإدخال / الإخراج ، والأجزاء المتبقية من lds / sts . من أجل عدم النظر إلى الوثائق في كل مرة بحثًا عن الأمر الضروري لمنفذ معين ، سننشئ مجموعة من الأوامر العامة التي ستحل محل القيم الضرورية بشكل مستقل.
.MACRO XOUT .IF @0<64 out @0,@1 .ELSE sts @0,@1 .ENDIF .ENDMACRO .MACRO XIN .IF @1<64 in @0,@1 .ELSE lds @0,@1 .ENDIF .ENDMACRO
لكي يعمل الاستبدال بشكل صحيح ، يتم استخدام الترجمة الشرطية في الماكرو. في الحالة التي يكون فيها عنوان المنفذ أقل من 64 ، يتم تنفيذ القسم الشرطي الأول ، وإلا يتم تنفيذ القسم الثاني. تكرّر وحدات الماكرو الخاصة بنا وظائف الأوامر القياسية تمامًا للعمل مع منافذ الإدخال / الإخراج ، وبالتالي ، للإشارة إلى أن فريقنا لديه ميزات متقدمة ، نضيف بادئة التسمية القياسية X.
أحد الأوامر الأكثر شيوعًا التي لا تتوفر في المجمّع ، ولكنها مطلوبة باستمرار ، هو أمر كتابة الثوابت في سجلات إدخال الإخراج. سيبدو تطبيق الماكرو لهذا الأمر هكذا
.MACRO OUTI ldi TempL,@1 .IF @0<64 out @0, TempL .ELSE sts @0, TempL .ENDIF .ENDMACRO
في هذه الحالة ، فإن الاسم في الماكرو ، لكي لا ينتهك منطق تسمية الأمر ، يضيف إلى الاسم القياسي postfix I ، الذي يستخدمه المطور للإشارة إلى أوامر العمل مع الثوابت. في هذا الماكرو ، نستخدم سجل TempL المحدد مسبقًا للتشغيل .
في بعض الحالات ، لا يلزم تسجيل واحد ، ولكن الزوج بأكمله يخزن قيمة 16 بت. إنشاء ماكرو جديد لكتابة قيمة 16 بت إلى زوج من سجلات الإدخال / الإخراج
.MACRO OUTIW ldi TempL,HIGH(@1) .IF @0<64 out @0H, TempL .ELSE sts @0H, TempL .ENDIF ldi TempL,LOW(@1) .IF @0<64 out @0L, TempL .ELSE sts @0L, TempL .ENDIF .ENDMACRO
في هذا الماكرو ، نستخدم الدالتين LOW و HIGH المضمنة لاستخراج البايتات المنخفضة والعالية من قيمة 16 بت. في اسم الماكرو ، أضف البادئات I و W إلى الأمر للإشارة إلى أنه في هذه الحالة ، يعمل الأمر مع قيمة 16 بت (كلمة).
لا يتم تحميل أزواج التسجيل في البرامج ، على سبيل المثال ، لإعداد مؤشرات على الذاكرة. دعونا إنشاء مثل هذا الماكرو
.MACRO ldiw ldi @0L, LOW(@1) ldi @0H, HIGH(@1) .ENDMACRO
في هذا الماكرو ، نستخدم حقيقة أن التسمية القياسية للسجلات والمنافذ في الشركة المصنعة تتضمن postfix L للأقل و postfix H للجزء العلوي من قيمة البايت المزدوجة. إذا اتبعت هذه القاعدة عند تسمية المتغيرات الخاصة بك ، فسيعمل الماكرو بشكل صحيح ، بما في ذلك معهم. يكمن جمال وحدات الماكرو أيضًا في حقيقة أنها توفر بديلاً بسيطًا ، وبالتالي ، في حالة كون المعامل الثاني رقمًا ، وفي حالة ما إذا كان هذا هو اسم الملصق ، سيعمل الماكرو بشكل صحيح.
وحدات الماكرو لتنفيذ الأوامر المعقدة.
عندما يتعلق الأمر بعمليات أكثر تعقيدًا ، لا يتم استخدام وحدات الماكرو بشكل عام ، مفضلة الروتين. ومع ذلك ، في هذه الحالات ، يمكن لوحدات الماكرو جعل الحياة أسهل وجعل الشفرة أكثر قابلية للقراءة. في هذه الحالة ، يأتي التجميع الشرطي للإنقاذ. قد يبدو نهج البرمجة كالتالي:
نضع كل إجراءاتنا في ملف منفصل ، والذي سنقوم بتسمية ، على سبيل المثال ، Library.inc . سيكون لكل روتين فرعي في هذا الملف النموذج التالي
_sub0: .IFDEF __sub0 ; ----- ----- ret .ENDIF
في هذه الحالة ، يعني وجود تعريف __sub0 أنه يجب تضمين الروتين الفرعي في الكود الناتج. خلاف ذلك ، يتم تجاهله.
بعد ذلك ، في ملف منفصل Macro.inc ، نعرّف وحدات ماكرو النموذج
.MACRO SUB0 .IFNDEF __sub0 .DEF __sub0 .ENDIF ; --- call _sub0 .ENDMACRO
عند استخدام هذا الماكرو ، نتحقق من تعريف __sub0 ، وإذا كان مفقودًا ، فإننا نقوم بإجراء التحديد. نتيجة لذلك ، يؤدي استخدام ماكرو إلى إلغاء تأمين إدراج رمز فرعي في ملف الإخراج. في حالة استخدام الإجراءات الروتينية في وحدات الماكرو ، سوف يأخذ رمز البرنامج الرئيسي النموذج التالي
.INCLUDE “Macro.inc” ;---- ---- .INCLUDE “Library.inc”
كمثال ، نعطي تطبيق ماكرو لتقسيم أعداد صحيحة غير موقعة 8 بت. نحافظ على منطق الشركة المصنعة ونضع النتيجة في AL (r0) ، وبقية التقسيم في AH (r1) . سوف الروتين الفرعي تبدو كما يلي
_div8u: .IFDEF __ div8u ;AH - ;AL ;TempL - ;TempH - ;TempQL - clr AL; clr AH; ldi TempQL,9 d8u_1: rol TempL dec TempQL brne d8u_2 ret d8u_2: rol A sub AH, TempH brcc d8u_3 add AH,TempH clc rjmp d8u_1 d8u_3: sec rjmp d8u_1 .ENDIF
سيكون تعريف الماكرو لاستخدام هذا الروتين كما يلي
.MACRO DIV8U .IFNDEF __div8u .DEF __div8u .ENDIF mov TempL, @0 mov TempH, @1 call _div8u .ENDMACRO
إذا رغبت في ذلك ، يمكنك إضافة نسخة للعمل مع ثابت
.MACRO DIV8UI .IFNDEF __div8u .DEF __div8u .ENDIF mov TempL, @0 ldi TempH, @1 call _div8u .ENDMACRO
نتيجة لذلك ، يعد استخدام عملية القسمة في نص البرنامج أمرًا بسيطًا
DIV8U r10, r11 ; r0 = r10/r11 r1 = r10 % r11 DIV8UI r10, 35 ; r0 = r10/35 r1 = r10 % 35
باستخدام الترجمة الشرطية ، يمكننا وضع جميع الإجراءات التي قد تكون مفيدة لنا في Library.inc . في هذه الحالة ، فقط تلك التي تم استدعاؤها مرة واحدة على الأقل ستظهر في رمز الإخراج. إيلاء الاهتمام لموقف تسمية الدخول. ناتج التسمية خارج حدود الشرط يرجع إلى المترجم. إذا وضعت الملصق في نص الكتلة الشرطية ، فقد يلقي المترجم خطأ. وجود علامات غير مستخدمة في التعليمات البرمجية ليس مخيفًا ، لأن وجود أي عدد من العلامات لا يؤثر على النتيجة.
وحدات الماكرو المحيطية
إحدى العمليات التي يصعب القيام بها دون استخدام وثائق الشركة المصنعة هي تهيئة الأجهزة الطرفية. حتى مع استخدام تسميات ذاكري للسجلات والبتات من الكود ، قد يكون من الصعب فهم أي وضع تم تكوين الجهاز ، خاصةً أنه في بعض الأحيان يتم تكوين الوضع من خلال مجموعة من قيم البتات لسجلات مختلفة. دعونا نرى كيف يمكن استخدام وحدات الماكرو مع مثال USART .
لنبدأ مع ماكرو تهيئة الوضع غير المتزامن.
.MACRO USART_INIT ; speed, bytes, parity, stop-bits .IF CLK8 == 0 .SET DIVIDER = FOSC/16/@0-1 .ELSE .SET DIVIDER = FOSC/128/@0-1 .ENDIF ; Set baud rate to UBRR0 outi UBRR0H, HIGH(DIVIDER) outi UBRR0L, LOW(DIVIDER) ; Enable receiver and transmitter .SET UCSR0B_ = (1<<RXEN0)|(1<<TXEN0) outi UCSR0B, UCSR0B_ .SET UCSR0C_ = 0 .IF @2 == 'E' .SET UCSR0C_ |= (1<<UPM01) .ENDIF .IF @2 == 'O' .SET UCSR0C_ |= (1<<UPM00) .ENDIF .IF @3== 2 .SET UCSR0C_ |= (1<<USBS0) .ENDIF .IF @1== 6 .SET UCSR0C_ |= (1<<UCSZ00) .ENDIF .IF @1== 7 .SET UCSR0C_ |= (1<<UCSZ01) .ENDIF .IF @1== 8 .SET UCSR0C_ = UCSR0C_ |(1<<UCSZ01)|(1<<UCSZ00) .ENDIF .IF @1== 9 .SET UCSR0C_ |= (1<<UCSZ02)|(1<<UCSZ01)|(1<<UCSZ00) .ENDIF ; Set frame format outi UCSR0C,UCSR0C_ .ENDMACRO
سمح لنا استخدام الماكرو باستبدال تهيئة سجلات إعداد USART بقيم كانت غير مفهومة دون قراءة الوثائق بسطر يمكن حتى لأولئك الذين واجهوا وحدة التحكم هذه أولاً التعامل معها. في هذا الماكرو ، أصبح من الواضح أخيرًا سبب تحديد ثوابت التردد والمقسوم عليها. حسنًا ، تجدر الإشارة إلى أنه على الرغم من الكود المثير للإعجاب للماكرو نفسه ، فإن المظهر الناتج سيكون له نفس المظهر كما لو كنا نكتب التهيئة بالطريقة المعتادة.
لإنهاء استخدام USART ، إليك بعض وحدات الماكرو الصغيرة
.MACRO USART_SEND_ASYNC outi UDR0, @0 .ENDMACRO
لا يوجد سوى سطر واحد ، ولكن استخدام هذا الماكرو سيتيح لك رؤية مكان عرض البرنامج للبيانات في USART بشكل أفضل . إذا افترضنا أن نعمل في وضع متزامن دون استخدام المقاطعات ، فمن الأفضل استخدام الماكرو أدناه بدلاً من USART_SEND_ASYNC
.MACRO USART_SEND USART_Transmit: xin TempL, UCSR0A sbrs TempL, UDRE0 rjmp USART_Transmit outi UDR0, @0 .ENDMACRO
في هذه الحالة ، لا يمكننا تمكين فحص شغل المنافذ وعرض البيانات إلا عندما يكون المنفذ مجانيًا. من الواضح أن هذا النهج للعمل مع الأجهزة الطرفية سيعمل على أي جهاز ، وليس فقط من أجل USART .
مقارنة بين البرامج دون استخدام وحدات الماكرو.
دعونا نلقي نظرة على مثال صغير ومقارنة الكود المكتوب دون استخدام وحدات الماكرو مع الكود الذي يتم استخدامه فيه. على سبيل المثال ، خذ برنامجًا يعرض الكلاسيكية "Hello world!" إلى المحطة عبر الأجهزة UART .
RESET: ldi r16, high(RAMEND) out SPH,r16 ldi r16, low(RAMEND) out SPL,r16 USART_Init: out UBRR0H, r17 out UBRR0L, r16 ldi r16, (1<<RXEN0)|(1<<TXEN0) out UCSRnB,r16 ldi r16, (1<<USBS0)|(3<<UCSZ00) out UCSR0C,r16 ldi ZL, LOW(STR<<1) ldi ZH, HIGH(STR<<1) LOOP: lpm r16, Z+ or r16,r16 breq END USART_Transmit: in r17, UCSR0A sbrs r17, UDRE0 rjmp USART_Transmit out UDR0,r16 rjmp LOOP END: rjmp END STR: .DB “Hello world!”,0
وهنا هو نفس البرنامج ، ولكن مكتوب باستخدام وحدات الماكرو
.INCLUDE “macro.inc” .EQU FOSC = 16000000 .EQU CLK8 = 0 RESET: ldiw SP, RAMEND; USART_INIT 19200, 8, "N", 1 ldiw Z, STR<<1 LOOP: lpm TempL, Z+ test TempL breq END USART_SEND TempL rjmp LOOP END: rjmp END STR: .DB “Hello world!”,0
في هذا المثال ، استخدمنا وحدات الماكرو الموضحة أعلاه ، والتي سمحت لنا بتبسيط رمز البرنامج بشكل كبير وجعله أكثر قابلية للفهم. سيكون الكود الثنائي في كلا البرنامجين متطابقين تمامًا.
استنتاج
يمكن أن يؤدي استخدام وحدات الماكرو إلى تقليل رمز المجمّع في البرنامج بدرجة كبيرة ، مما يجعله أكثر قابلية للفهم وقراءة. التحويل البرمجي الشرطي يسمح لك بإنشاء أوامر عامة ومكتبات الإجراءات دون إنشاء رمز الإخراج المكرر. وكعيب ، يمكن للمرء أن يشير إلى متواضع للغاية بمعايير اللغات الرفيعة المستوى التي تم تعيينها للعمليات والقيود المسموح بها عند إعلان البيانات "إلى الأمام". لا يسمح هذا التقييد ، على سبيل المثال ، بالكتابة عن طريق وحدات ماكرو أمر عالمي كامل للانتقالات jmp / rjmp وينفخ رمز الماكرو نفسه بشكل كبير عند تنفيذ المنطق المعقد.