
ذات مرة صادفت مقالة ممتازة ( tyk ) - أظهر فيها المؤلف بوضوح الفرق بين استخدام وظائف Arduino والعمل مع السجلات. تمت كتابة الكثير من المقالات ، سواء تمدح أردوينو ويجادل بأن هذا كله تافه وعمومًا للأطفال ، لذلك لن نكرر ذلك ، ولكن حاول معرفة سبب النتائج التي حصل عليها مؤلف هذه المقالة. وبنفس القدر من الأهمية ، سنفكر فيما يمكننا القيام به. أي شخص مهتم ، أسأل تحت القط.
الجزء 1 "الأسئلة"
نقلاً عن مؤلف هذا المقال:
اتضح فقدان الأداء في هذه الحالة - 28 مرة. بالطبع ، هذا لا يعني أن اردوينو يعمل بشكل أبطأ 28 مرة ، لكن أعتقد أنه من أجل الوضوح ، هذا هو أفضل مثال على ما لا يعجبه أردوينو.
نظرًا لأن المقالة قد بدأت للتو ، فلن نفهمها بعد ، ولكننا سنتجاهل الجملة الثانية ونفترض أن سرعة وحدة التحكم تساوي تقريبًا تردد تبديل الدبوس. على سبيل المثال نحن نواجه مهمة جعل المولد بأعلى تردد لما لدينا. أولاً ، دعنا نرى مدى سوء كل شيء.
سنكتب برنامجًا بسيطًا لـ arduino (في الواقع ، مجرد نسخ وميض).
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, 1);
خياطة في وحدة تحكم. نظرًا لعدم وجود راسم ذبذبات ، ولكن محلل منطقي صيني فقط ، يلزم تكوينه بشكل صحيح. الحد الأقصى لتردد المحلل هو 24 ميجاهرتز ، وبالتالي ، يجب أن يكون متوازنًا مع تردد وحدة التحكم - مضبوطًا على 16 ميجاهرتز. نحن ننظر ...

... لفترة طويلة. نحن نحاول أن نتذكر ما تعتمد عليه سرعة وحدة التحكم - بالضبط ، التردد. نحن ننظر في arduino.cc . سرعة الساعة هي 16 ميجاهرتز ، وهنا لدينا 145.5 كيلوهرتز. ماذا تفعل دعونا نحاول حلها في الجبين. على نفس موقع arduino.cc ننظر إلى باقي اللوحة:
- ليوناردو - غير مناسب - يوجد أيضًا 16 ميجاهرتز
- ميجا - أيضا - 16 ميجا هرتز
- 101 - ستفعل - 32 ميجا هرتز
- DUE - حتى أفضل - 84 ميجاهرتز
يمكن افتراض أنه إذا قمت بزيادة تردد وحدة التحكم مرتين ، فإن تردد وميض LED سيزداد أيضًا مرتين ، وإذا كان بمقدار 5 ، ثم 5 مرات.

لم نحصل على النتائج المرجوة. والمولد أقل وأقل مثل المتعرج. نفكر أكثر - الآن ، على الأرجح ، اللغة سيئة. يبدو أنه مع c ، c ++ ، ولكنه صعب (وفقًا لتأثير Dunning-Krueger ، لا يمكننا أن ندرك أننا نكتب بالفعل في c ++) ، لذلك نحن نبحث عن بدائل. يقودنا بحث قصير إلى BASCOM-AVR (لم يتم إخبارنا عنها هنا ) ، قم بتعيين ، اكتب الرمز:
$Regfile="m328pdef.dat" $Crystal=16000000 Config Portb.5 = Output Do Toggle Portb.5 Loop
نحصل على:

والنتيجة أفضل بكثير ، بالإضافة إلى أننا حصلنا على المتعرج المثالي ، ولكن ... BASIC في 2018 ، على محمل الجد؟ ربما سنترك هذا في الماضي.
الجزء 2 "الإجابات"
يبدو أن الوقت قد حان للتوقف عن لعب الأحمق والبدء في الفهم (وتذكر أيضًا si و المجمع). ما عليك سوى نسخ الرمز "المفيد" من المقالة المذكورة في البداية إلى الحلقة ().
هنا ، أعتقد ، هناك حاجة إلى تفسير: سيتم كتابة جميع التعليمات البرمجية في مشروع اردوينو ، ولكن في بيئة Atmel Studio 7.0 (يوجد أداة تفكيك ملائمة هناك) ، ستكون لقطات الشاشة منه.
void setup() { DDRB |= (1 << 5); // PB5 } void loop() { PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON }
النتيجة:

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

ويرجع ذلك إلى تشغيل المقاطعات من المؤقت المسؤول عن المللي (). لذا ما سنفعله هو ببساطة قطع الاتصال. نحن نبحث عن ISR (وظيفة معالج المقاطعة). نجد:
ISR(TIMER0_OVF_vect) { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = timer0_millis; nsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } timer0_fract = f; timer0_millis = m; timer0_overflow_count++; }
الكثير من الكود الغير مفيد لنا يمكنك تغيير وضع تشغيل المؤقت أو تعطيل المقاطعة ، ولكن هذا غير ضروري لأغراضنا ، لذا فقط قم بتعطيل جميع المقاطعات باستخدام الأمر cli (). ما عليك سوى إلقاء نظرة على رمزنا:
PORTB &= ~(1 << 5); //OFF PORTB |= (1 << 5); //ON
عدد كبير جدًا من عوامل التشغيل ، يتم تقليلها إلى مهمة واحدة.
PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON
نعم ، والتبديل إلى الحلقة () يتطلب الكثير من الأوامر ، نظرًا لأن هذه وظيفة إضافية في الحلقة الرئيسية.
int main(void) { init();
لذا قم بعمل حلقة لا نهائية في الإعداد (). نحصل على ما يلي:
void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF PORTB = 0b11111111; //ON } }

61 نانوثانية هو الحد الأقصى المطابق لتردد وحدة التحكم. هل ممكن أسرع؟ المفسد - لا. دعونا نحاول أن نفهم لماذا - لهذا نقوم بتفكيك الكود الخاص بنا:

كما يتبين من الشاشة ، من أجل الكتابة إلى المنفذ 1 أو 0 ، يتم إنفاق دورة ساعة واحدة بالضبط ، ولكن الانتقال الذي لا يمكن إكماله في أقل من دورة ساعة واحدة (يتم تنفيذ RJMP في دورتين للساعة ، وعلى سبيل المثال ، JMP ، في ثلاث ) ونحن على وشك الوصول - من أجل الحصول على متعرج ، تحتاج إلى زيادة الوقت الذي يتم فيه إعطاء 0 من خلال إجراءين. أضف إلى أمري nop المجمعين الذين لا يفعلون شيئًا سوى دورة ساعة واحدة:
void setup() { cli(); DDRB |= (1 << 5); // PB5 while (1) { PORTB = 0b00000000; //OFF asm("nop"); asm("nop"); PORTB = 0b11111111; //ON } }

الجزء 3 "الاستنتاجات"
لسوء الحظ ، كل ما فعلناه كان عديم الفائدة على الإطلاق من الناحية العملية ، لأنه لم يعد بإمكاننا تنفيذ أي رمز. أيضًا ، في 99.9٪ من الحالات ، تكون ترددات تبديل المنفذ كافية لأي غرض. نعم ، وإذا كنا بحاجة حقًا إلى إنشاء متعرج سلس ، فيمكنك أن تأخذ stm32 مع dma أو رقاقة توقيت خارجية مثل NE555. هذه المقالة مفيدة لفهم أجهزة mega328p و arduino بشكل عام.
ومع ذلك ، الكتابة في سجلات قيم 8 بت PORTB = 0b11111111;
أسرع بكثير من digitalWrite(13, 1);
ولكن سيتعين عليك الدفع مقابل ذلك من خلال عدم القدرة على نقل الرمز إلى لوحات أخرى ، لأن أسماء السجلات قد تختلف.
بقي سؤال واحد فقط: لماذا لم يؤد استخدام الأحجار بشكل أسرع إلى نتائج؟ الجواب بسيط للغاية - في الأنظمة المعقدة ، يكون تردد gpio أقل من التردد الأساسي. ولكن يمكن رؤية مدى الانخفاض وكيفية تعيينه دائمًا في ورقة البيانات على وحدة تحكم معينة.
استشهد المنشور بمقالات: