تطبيق Arm Mbed OS. ضبط دقيق

LNDC1


بعد استخدام Arm Mbed OS ، كان من الممكن وميض LED ، لقد حان الوقت لاختبار وتكوين الخدمات الهامة الأخرى. فيما يلي وصف:


  • تكنولوجيا تكوين Mbed
  • لماذا يصعب التبديل إلى C ++ في نظام RTOS العادي
  • كيفية حفظ الذاكرة في RTOS
  • كيف يتم تنظيم المقاطعات في Mbed OS
  • مما هو مناسب لتصحيح Mbed OS
  • كيفية التخلص من طبقة إضافية من تجريد SDK

نواصل معرفتنا بتكنولوجيا برمجة الميكروكونترولر لعائلة MKE18F باستخدام ARM Mbed OS



تكنولوجيا تكوين Mbed


أحد المكونات المهمة لمشروع Mbed هو نظام التكوين والتجميع التلقائي ، سواء عبر الإنترنت أو في وضع عدم الاتصال ، أي محليًا على جهاز كمبيوتر المستخدم. يقترح تكوين عن طريق تحرير ملفات .json بأسماء خاصة. بعد ذلك ، تقوم البرامج النصية في مشروع Python بتحويل هذه الملفات إلى ملفات رأس ، وملفات مساحة عمل IDE المحددة من قبل المستخدم ، وملفات أوامر linker ، وملفات دعم أخرى.


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


لذلك ، تقرر التخلي عن هذا النهج. كما ورد في مقال سابق ، تم تشكيله ببساطة في مشروع عبر الإنترنت لـ IDE IAR ، تم الحصول على مجموعة غير منظمة من الملفات في مساحة عمل IDE ، ثم تم تنظيمها وتم التخلص منها غير ضروري. ونتيجة لذلك ، لم تعد بحاجة إلى إجراء التهيئة عبر ملفات .json وهناك ثلاثة أماكن محددة فقط حيث توجد المعلمات التي تؤثر على تكوين Mbed :


  • خيارات مترجم IDE
  • Linker MKE18F512xxx16_flash.icf ملف دفعي
  • ملف الرأس mbed_config.h

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


افتح
#ifndef __MBED_CONFIG_DATA__ #define __MBED_CONFIG_DATA__ // Configuration parameters #define MBED_CONF_RTOS_PRESENT 1 // set by library:rtos #define MBED_ALL_STATS_ENABLED 1 //#define DEVICE_SLEEP=1       SLEEP      #define MBED_CONF_APP_MAIN_STACK_SIZE 1024 #define MBED_CONF_APP_TIMER_THREAD_STACK_SIZE 512 #define MBED_CONF_APP_IDLE_THREAD_STACK_SIZE 512 #define MBED_CONF_PLATFORM_DEFAULT_SERIAL_BAUD_RATE 3000000 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_ALL_THREADS_INFO 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_FILENAME_CAPTURE_ENABLED 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_HIST_ENABLED 0 // set by library:platform #define MBED_CONF_PLATFORM_ERROR_HIST_SIZE 4 // set by library:platform #define MBED_CONF_PLATFORM_FORCE_NON_COPYABLE_ERROR 0 // set by library:platform #define MBED_CONF_PLATFORM_MAX_ERROR_FILENAME_LEN 16 // set by library:platform #define MBED_CONF_PLATFORM_POLL_USE_LOWPOWER_TIMER 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_BAUD_RATE 3000000 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_BUFFERED_SERIAL 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_CONVERT_NEWLINES 1 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_CONVERT_TTY_NEWLINES 0 // set by library:platform #define MBED_CONF_PLATFORM_STDIO_FLUSH_AT_EXIT 1 // set by library:platform #define MBED_CONF_DRIVERS_UART_SERIAL_RXBUF_SIZE 256 // set by library:drivers #define MBED_CONF_DRIVERS_UART_SERIAL_TXBUF_SIZE 256 // set by library:drivers #define MBED_CONF_EVENTS_PRESENT 1 // set by library:events #define MBED_CONF_EVENTS_SHARED_DISPATCH_FROM_APPLICATION 0 // set by library:events #define MBED_CONF_EVENTS_SHARED_EVENTSIZE 256 // set by library:events #define MBED_CONF_EVENTS_SHARED_HIGHPRIO_EVENTSIZE 256 // set by library:events #define MBED_CONF_EVENTS_SHARED_HIGHPRIO_STACKSIZE 1024 // set by library:events #define MBED_CONF_EVENTS_SHARED_STACKSIZE 1024 // set by library:events #define MBED_CONF_EVENTS_USE_LOWPOWER_TIMER_TICKER 0 // set by library:events #define MBED_CONF_CELLULAR_DEBUG_AT 0 // set by library:cellular #define MBED_CONF_CELLULAR_RANDOM_MAX_START_DELAY 0 // set by library:cellular #define MBED_CONF_CELLULAR_USE_APN_LOOKUP 1 // set by library:cellular #define MBED_CONF_FILESYSTEM_PRESENT 1 // set by library:filesystem #define MBED_CONF_KINETIS_EMAC_RX_RING_LEN 16 // set by library:kinetis-emac #define MBED_CONF_KINETIS_EMAC_TX_RING_LEN 8 // set by library:kinetis-emac #define MBED_CONF_LORA_ADR_ON 1 // set by library:lora #define MBED_CONF_LORA_APP_PORT 15 // set by library:lora #define MBED_CONF_LORA_APPLICATION_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_APPLICATION_KEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_APPSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_AUTOMATIC_UPLINK_MESSAGE 1 // set by library:lora #define MBED_CONF_LORA_DEVICE_ADDRESS 0x00000000 // set by library:lora #define MBED_CONF_LORA_DEVICE_EUI {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_DUTY_CYCLE_ON 1 // set by library:lora #define MBED_CONF_LORA_LBT_ON 0 // set by library:lora #define MBED_CONF_LORA_NB_TRIALS 12 // set by library:lora #define MBED_CONF_LORA_NWKSKEY {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // set by library:lora #define MBED_CONF_LORA_OVER_THE_AIR_ACTIVATION 1 // set by library:lora #define MBED_CONF_LORA_PHY EU868 // set by library:lora #define MBED_CONF_LORA_PUBLIC_NETWORK 1 // set by library:lora #define MBED_CONF_LORA_TX_MAX_SIZE 64 // set by library:lora #define MBED_CONF_LWIP_ADDR_TIMEOUT 5 // set by library:lwip #define MBED_CONF_LWIP_ADDR_TIMEOUT_MODE 1 // set by library:lwip #define MBED_CONF_LWIP_DEBUG_ENABLED 0 // set by library:lwip #define MBED_CONF_LWIP_DEFAULT_THREAD_STACKSIZE 512 // set by library:lwip #define MBED_CONF_LWIP_ENABLE_PPP_TRACE 0 // set by library:lwip #define MBED_CONF_LWIP_ETHERNET_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_IP_VER_PREF 4 // set by library:lwip #define MBED_CONF_LWIP_IPV4_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_IPV6_ENABLED 0 // set by library:lwip #define MBED_CONF_LWIP_MEM_SIZE 36560 // set by library:lwip[Freescale] #define MBED_CONF_LWIP_PPP_THREAD_STACKSIZE 768 // set by library:lwip #define MBED_CONF_LWIP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCP_ENABLED 1 // set by library:lwip #define MBED_CONF_LWIP_TCP_SERVER_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_TCPIP_THREAD_STACKSIZE 1200 // set by library:lwip #define MBED_CONF_LWIP_UDP_SOCKET_MAX 4 // set by library:lwip #define MBED_CONF_LWIP_USE_MBED_TRACE 0 // set by library:lwip #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL_MASK 0x7fff800 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_CHANNEL_PAGE 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_DEVICE_TYPE NET_6LOWPAN_ROUTER // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PANID_FILTER 0xffff // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PSK_KEY {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_PSK_KEY_ID 1 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_SEC_LEVEL 5 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_6LOWPAN_ND_SECURITY_MODE NONE // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_HEAP_SIZE 32500 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL 22 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_MASK 0x7fff800 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_CHANNEL_PAGE 0 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_COMMISSIONING_DATASET_TIMESTAMP 0x10000 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_EXTENDED_PANID {0xf1, 0xb5, 0xa1, 0xb2,0xc4, 0xd5, 0xa1, 0xbd } // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_ML_PREFIX {0xfd, 0x0, 0x0d, 0xb8, 0x0, 0x0, 0x0, 0x0} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_NETWORK_NAME "Thread Network" // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_PANID 0x0700 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_CONFIG_PSKC {0xc8, 0xa6, 0x2e, 0xae, 0xf3, 0x68, 0xf3, 0x46, 0xa9, 0x9e, 0x57, 0x85, 0x98, 0x9d, 0x1c, 0xd0} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_DEVICE_TYPE MESH_DEVICE_TYPE_THREAD_ROUTER // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_MASTER_KEY {0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_PSKD "ABCDEFGH" // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_SECURITY_POLICY 255 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_THREAD_USE_STATIC_LINK_CONFIG 1 // set by library:mbed-mesh-api #define MBED_CONF_MBED_MESH_API_USE_MALLOC_FOR_HEAP 0 // set by library:mbed-mesh-api #define MBED_CONF_NANOSTACK_CONFIGURATION nanostack_full // set by library:nanostack #define MBED_CONF_NANOSTACK_HAL_CRITICAL_SECTION_USABLE_FROM_INTERRUPT 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_DISPATCH_FROM_APPLICATION 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_THREAD_STACK_SIZE 6144 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_EVENT_LOOP_USE_MBED_EVENTS 0 // set by library:nanostack-hal #define MBED_CONF_NANOSTACK_HAL_NVM_CFSTORE 0 // set by library:nanostack-hal #define MBED_CONF_NSAPI_DEFAULT_MESH_TYPE THREAD // set by library:nsapi #define MBED_CONF_NSAPI_DEFAULT_STACK LWIP // set by library:nsapi #define MBED_CONF_NSAPI_DEFAULT_WIFI_SECURITY NONE // set by library:nsapi #define MBED_CONF_NSAPI_DNS_CACHE_SIZE 3 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_RESPONSE_WAIT_TIME 5000 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_RETRIES 0 // set by library:nsapi #define MBED_CONF_NSAPI_DNS_TOTAL_ATTEMPTS 3 // set by library:nsapi #define MBED_CONF_NSAPI_PRESENT 1 // set by library:nsapi #define MBED_CONF_PPP_CELL_IFACE_APN_LOOKUP 1 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_AT_PARSER_BUFFER_SIZE 256 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_AT_PARSER_TIMEOUT 8000 // set by library:ppp-cell-iface #define MBED_CONF_PPP_CELL_IFACE_BAUD_RATE 115200 // set by library:ppp-cell-iface #define MBED_CONF_TARGET_NETWORK_DEFAULT_INTERFACE_TYPE ETHERNET // set by target:K66F #define MBED_LFS_BLOCK_SIZE 512 // set by library:littlefs #define MBED_LFS_ENABLE_INFO 0 // set by library:littlefs #define MBED_LFS_INTRINSICS 1 // set by library:littlefs #define MBED_LFS_LOOKAHEAD 512 // set by library:littlefs #define MBED_LFS_PROG_SIZE 64 // set by library:littlefs #define MBED_LFS_READ_SIZE 64 // set by library:littlefs #define NSAPI_PPP_AVAILABLE 0 // set by library:lwip #define NSAPI_PPP_IPV4_AVAILABLE 1 // set by library:lwip #define NSAPI_PPP_IPV6_AVAILABLE 0 // set by library:lwip #define NVSTORE_ENABLED 1 // set by library:nvstore #define NVSTORE_MAX_KEYS 16 // set by library:nvstore // Macros #define _RTE_ // defined by library:rtos #define NS_USE_EXTERNAL_MBED_TLS // defined by library:nanostack #define UNITY_INCLUDE_CONFIG_H // defined by library:utest #endif 

ميزات الانتقال إلى C ++ كما تم تطبيقها على RTOS


واجهة برمجة التطبيقات ذات المستوى الأعلى في Mbed مكتوبة بلغة C ++ ، لذلك يجب استخدام هذه اللغة في رمز التطبيق. ولكن هناك فروق دقيقة تحتاج إلى معرفتها.
لا يزال استخدام C ++ لـ RTOS على الأنظمة المدمجة الصغيرة نادرًا نسبيًا. تكمن المشكلة هنا في أن مشاريع RTOS الناجحة تسعى جاهدة لأن تكون متعددة المنصات ، بينما يضع C ++ متطلبات عالية على إدارة موارد النظام الأساسي مقارنة بـ C. السبب هو الرغبة في إخفاء تفاصيل إدارة الموارد منخفضة المستوى عن المستخدم. يتعلق الأمر بشكل أساسي بموارد الذاكرة. يستخدم المُنشئون والمدمرون والخيوط والاستثناءات مع التدمير التلقائي وقوالب كائنات بنية البيانات وما إلى ذلك العمليات الضمنية مع الذاكرة الديناميكية. لكن موارد ذاكرة الوصول العشوائي في الأنظمة الصغيرة محدودة للغاية. ذاكرة الوصول العشوائي هي المورد الأكثر ندرة في هذه الأنظمة وخاصة في RTOS . في RTOS ، يتم تخصيص مكدس لكل مهمة ، ولا يمكن للمطور توقع حجمه بالضبط مقدمًا وبالتالي يختار بهامش. وبالتالي ، يتطلب وجود RTOS مع اثنتي عشرة مهمة على الفور حجم ذاكرة الوصول العشوائي من 10 إلى 30 كيلوبايت. هناك حاجة إلى الكثير من الذاكرة لمختلف الموزعين والبروتوكولات (HTTP ، HTML ...) وأنظمة الملفات. إذا تم استخدام شاشة ، يتم زيادة متطلبات ذاكرة الوصول العشوائي المجانية.


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


في حالة C ++ ، إذا أردنا استخدام جميع ميزات هذه اللغة ، فسيتعين علينا إعادة الاستهداف . لكن إعادة الاستهداف في كل بيئة تطوير عملية فردية بحتة. هذا يجعل الحياة صعبة لمطوري RTOS .


الشكل أدناه هو مثال على هيكل المكالمة مع إعادة التوجيه. قد لا يتم تنفيذ وظائف __ الكتابة ، __lseek ، __read من قبل المستخدم ، ولكن بعد ذلك تظل وظائفها وفقًا لتقدير IDE . وبالتأكيد لن يكون printf و scanf متعدد مؤشرات الترابط.



يعد Mbed أحد أنظمة RTOS القليلة إن لم يكن الوحيدة التي توفر مصادر مع إعادة التوجيه بالفعل لثلاثة من أدوات التطوير المعروفة: GCC ، IAR ، Keil
على الرغم من كل ما سبق ، يمكنك العثور على مقالات حول نقل RTOS إلى C ++ دون إجراء إعادة توجيه ، على سبيل المثال ، حل المشكلة عن طريق استبدال بعض الوظائف القياسية الشائعة بوظائفك الخاصة. قد يعمل هذا ، ولكن بعد ذلك يحتاج المبرمج إلى أن يكون على دراية بالعديد من القيود الضمنية وغير الموثقة عند استخدام بنيات C ++ في IAR (فقط المنشئات الثابتة ، والتحقق من جميع القوالب بحثًا عن استثناءات جديدة ، وإلغاء الاستثناءات ، وما إلى ذلك). سيكون من الصعب بالفعل استدعاء C ++ . يزيل Mbed ، كنظام سهل الاستخدام ، العديد من هذه القيود ، ويقترب من Arduino بكل بساطة.


بالإضافة إلى جميع الإصدارات الأخيرة من IAR ، هناك صعوبات في التحول إلى C11 و C ++ 14 ، الموصوف هنا - https://www.iar.com/support/resources/articles/exploring-c11-and-c14/


كيف يتم تنظيم المقاطعات في Mbed


ومن الغريب أنه لن يكون من الممكن العثور على فئة أو وظيفة أو شيء مناسب لتنظيم خدمة المقاطعة في واجهة برمجة تطبيقات Mbed. يمكنك العثور فقط على فئة InterruptIn ، والمخصصة فقط للمنافذ الخارجية.


يجب البحث عن إجابات لهذه الأسئلة في CMSIS-RTOS ، وبالتحديد في CMSIS Cortex-M4 Core Peripheral Access Layer . يتم تعريف وحدات الماكرو هناك:


  • NVIC_SetVector - تعيين روتين خدمة المقاطعة (ISR) لناقل معين
  • NVIC_SetPriority - يحدد أولوية متجه المقاطعة المحدد.
  • NVIC_EnableIRQ - يسمح بمقاطعة المكالمات على ناقلات معينة

إليك كيفية تهيئة منظمة المقاطعة من مؤقت SysTick :


  NVIC_SetVector(mbed_get_m0_tick_irqn(), (uint32_t)SysTick_Handler); //   mbed_get_m0_tick_irqn       SysTick. //        SysTick_IRQn    MKE18F16.h //  SysTick_Handler   . NVIC_SetPriority(mbed_get_m0_tick_irqn(), 0xFF); // 0xFF    .      .   NVIC    MKE18F16   16  . ..   4-  . NVIC_EnableIRQ(mbed_get_m0_tick_irqn()); //    . 

يجب عدم الخلط بين أولويات المهام المحددة وأولويات المقاطعة.


إذا لم يتم تعيين الأولوية ، يتم تعيينها افتراضيًا إلى الحد الأقصى.
من معالجات المقاطعة المعينة بهذه الطريقة ، يمكنك استدعاء أي من خدمات RTOS التي لا تسبب التوقعات. على سبيل المثال نقل الأعلام ، الإشارات ، الرسائل ، صناديق البريد ، إلخ. ومع ذلك ، لا يتم استدعاء الخدمات من ISR نفسها ، ولكن عن طريق استدعاء مقاطعات البرامج عن طريق تعيين بت PENDSVSET في التحكم في المقاطعة وسجل الدولة ( ICSR ) لكتلة التحكم في النظام ( SCB ) من Cortex-M kernel. على سبيل المثال بعد الانتهاء من معالج المقاطعة الحالي ، إذا لم تكن هناك مقاطعات أخرى ذات أولوية ، فسيتم استدعاء معالج النظام بواسطة ناقل PendSV حيث سيتم تنفيذ الخدمة.


كيف يعمل Mbed مع الذاكرة الديناميكية؟


الذاكرة الديناميكية ، أو كومة أو كومة أخرى ، هي مكون مطلوب عند البرمجة في C ++ . في مشروع Mbed الخاص بنا تحت IAR ، يتم تحديد حجم مساحة الذاكرة هذه في ملف إعدادات رابط MKE18F512xxx16_flash.icf عن طريق الكتابة إلى المتغير __size_heap__ . قمنا بتعيين الحجم بحيث يشغل كل المساحة الخالية المتبقية. نحن نعلم مقدار الذاكرة الفارغة المتبقية من ملف .map بعد التجميع ، أي كومة التحجيم هي عملية تكرارية.


استدعاء المنشئات الثابتة لكائنات C ++


من المشكلات المهمة عند استخدام C ++ مكان وكيفية استدعاء مُنشئي الكائنات العامة. حتى في RTOS التي تدعي الجدية ، على سبيل المثال ، MAX ، يتم تجاهل هذا ، أي غادر للصدفة. هناك ، ينخرط المصممون في مكتبة قياسية لبيئة التطوير أحادية الترابط مع آلية تخصيص الذاكرة المفردة الأحادية المعتادة. ولكن بعد البداية ، ينشئ RTOS المشترك آليته الخاصة لإدارة الذاكرة الديناميكية ، وتظل الذاكرة التي تشغلها الكائنات العالمية منسية. هذه فجوة في جهودنا لتوفير الذاكرة والتحكم في كل شيء.


تناول Mbed هذه القضية بجدية أكبر. هناك لكل بيئة تطوير لها نهجها الخاص. في IAR ، يتم ذلك على النحو التالي:


  • يتم اعتراض دفق التهيئة بواسطة رمز المستخدم حتى قبل استدعاء المصممين
  • يتم استبدال طرق منع التدفقات من RTOS API في المكتبة القياسية
  • الوظائف القياسية الجديدة ، الحذف ، malloc ، مجانية ... يتم إعادة توجيهها إلى المكالمات إلى واجهة برمجة تطبيقات الذاكرة الديناميكية RTOS .

يستخدم Mbed محولات مكتبة IAR لعملية متعددة الخيوط
يمكنك أن تقرأ عن تكييف IAR لتعدد المقالات هنا - http://supp.iar.com/FilesPublic/UPDINFO/005691/arm/doc/infocenter/DLIBThreadSupport.html
قام Mbed بتكييف كل من أقفال النظام وأقفال دفق الملفات لمكتبة IAR . يتم تنفيذها في ملف mbed_boot.c ويستخدمون كائنات نظام التشغيل .


تعمل وظيفة __iar_program_start القابلة للتنفيذ في الأسطر الأولى من ملف startup_MKE18F16.s على تهيئة مكدس نظام التشغيل والذاكرة الديناميكية عن طريق استدعاء mbed_set_stack_heap


تحجيم مكدسات المهام


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


يقوم Mbed OS عند بدء التشغيل بإنشاء ثلاث مهام على الفور بأسماء: "main_thread" و "timer_thread" و "idle_thread" . تم تحديد حجم المكدس الافتراضي لهم بواسطة وحدات الماكرو في ملف الرأس mbed_rtx_conf.h . قمنا بنقل إعلانات هذه المكدسات إلى ملف التكوين mbed_config.h وقمنا بتقليل حجم المكدسات. الآن تبدو التعريفات كما يلي:


  • يتم تعريف مكدس المهام "main_thread" بواسطة الماكرو MBED_CONF_APP_MAIN_STACK_SIZE = 1024 بايت
  • يتم تعريف مكدس المهام timer_thread بواسطة الماكرو MBED_CONF_APP_TIMER_THREAD_STACK_SIZE = 512 بايت
  • يتم تعريف حزمة المهام idle_thread بواسطة الماكرو MBED_CONF_APP_IDLE_THREAD_STACK_SIZE = 512 بايت

ضوابط استخدام ذاكرة Mbed


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


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


أدوات تصحيح الأخطاء البرمجية وتحليلها


لا يمكن إجراء دراسة متعمقة حقًا لعمل Mbed على النظام الأساسي الجديد إلا باستخدام مصحح أخطاء JTAG / SWD .



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


أوصي بإصدارات Segger debuggers من J-Link Pro و J-Link Ultra . تتميز بنطاق ترددي مرتفع ، عدة مرات أعلى من النماذج الرخيصة الشائعة. بالنسبة لأنظمة تصحيح الأخطاء في الوقت الحقيقي الصعب ، هذا مهم. عند تتبع الأحداث السريعة ، يكون هؤلاء المصححون أقل عرضة للتدفقات ويتطلبون تكرارات أقل لتصحيح الأخطاء. بالإضافة إلى تصحيح الأخطاء خطوة بخطوة ، يمكنهم تسجيل المقاطعات ، والاحتفاظ بإحصائيات المقاطعات ، بما في ذلك فترات التنفيذ ، ودعم تقنيات تصحيح أخطاء RTT و ITM ، والتقاط استثناءات الأجهزة وغيرها من الوظائف.


فيما يلي عرض لإطار المصحح IAR عند العمل من خلال محول J-Link.



لا يستحق التوفير ، يتم إنفاق 90٪ من وقت التطوير على تصحيح الأخطاء. على الرغم من أن متتبعات Segger J-Trace الأكثر تكلفة لم تعد تقدم ميزة كبيرة ، نظرًا لأن سلسلة MKE18F لا تحتوي على واجهة تتبع خاصة.


أداة التصحيح الثانية ، بالطبع ، هي I / O عبر UART . لدى Mbed أربع تقنيات مختلفة على الأقل للوصول إلى قناة بيانات تسلسلية عبر UART ،


هذا:


  • إعادة التوجيه من خلال فئة DirectSerial من وظائف الإدخال / الإخراج القياسية C / C ++
  • فئة RawSerial
  • مسلسل فئة
  • فئة UARTSerial

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


كم عدد الموقتات التي نخسرها عن طريق ترقية Mbed


وميض LED ، تم استخدام وظيفة الانتظار من Mbed OS API . تحدثت المقالة الأخيرة عن مشاكل نقلها. ولكن هذا ليس كل شيء ، باستثناء أن Mbed يحتفظ بعكس عمله (يمكن قراءته بواسطة وظيفة mbed_stats_cpu_get ) وهناك واجهة برمجة تطبيقات للمؤقت . تستخدم هذه الخدمات الدوال ذات المستوى المنخفض من ملف lp_ticker.c . في هذا الملف ، يتم استخدام جهاز ضبط الوقت LPTMR من مجموعة الأجهزة الطرفية Kinetis لتنظيم عدد الوقت. أثناء عملية النقل ، تم تعديل هذا الملف ليتناسب مع طرق تسجيل الوقت المستخدمة في متحكم MKE18F512VLL16 .


وبالتالي ، يلتقط منفذ Mbed وحدتين مضادتين تمامًا - PIT و LPTMR وجهاز ضبط الوقت الأساسي SysTick . يجب تذكر ذلك عند تخطيط الموارد لمهمة تطبيقية.


ميزات التمهيد MKE18F


تحتوي عائلة رقائق MKE18F على ROM مدمج مع محمل إقلاع عبر واجهات تسلسلية: UART و CAN و SPI و I2C . ولكن من المخطط استخدام محمل الإقلاع الآمن الخاص بنا على السبورة ، لذا فإن عمل محمل الإقلاع العادي غير مرغوب فيه.
في هذه الحالة ، تحتاج رقائق Kinetis إلى الانتباه إلى محتويات منطقة برنامج Flash عند 0x040D . يتم تخزين ثابت يحدد ترتيب التمهيد. في حالتنا ، يجب كتابة الثابت 0x7B هناك ، للإشارة إلى البداية من ذاكرة فلاش ، وليس من ROM وتعطيل وظيفة NMI على الإخراج الخارجي. مع قيمة مختلفة لهذا الثابت ، قد يتجمد البرنامج في أداة تحميل التشغيل المضمنة التي يتم استدعاؤها عن طريق الخطأ من ROM.


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


تنشيط الوكالة الدولية للطاقة


تم تصميم لوحة التحكم الخاصة بنا للاستخدام الصناعي ، مما يعني أنه لا يمكنك الاستغناء عن الوكالة الدولية للطاقة .


في البداية ، تم تعيين الماكرو DISABLE_WDOG في ملف system_MKE18F16.h لتعطيل المراقبة . لتصحيح الوضع ، تم مسح هذا الماكرو وتم تنفيذ التهيئة الخاصة به لـ WDOG .


تتم تهيئة Watchdog في وظيفة SystemInit . يتم تحديث عداد المراقبة في مهمة IDLE .


يتطلب مثل هذا النهج ألا تقوم المهام ذات الأولوية الأعلى بالتقاط المعالج حصريًا لأكثر من 100 مللي ثانية . لكن هذا يمكن أن يحدث بسهولة ، على سبيل المثال ، عند إخراج مقالب بيانات كبيرة إلى المحطة. لذلك ، في كل مكان نقوم بتقسيم الإجراءات الطويلة في المهام ذات الأولوية الأكبر من IDLE إلى أجزاء قصيرة تتخللها فترات توقف مؤقت باستخدام وظيفة الانتظار .


إصدار برامج التشغيل من تسليم SDK


تكون برامج تشغيل SDK مسبوقة بـ fsl الموجود في دليل NXP_MKE18F_drivers وهي نوع من طبقة التجريد المحيطي. من المفترض أن تسهل البرمجة التي يصعب تعلمها ، ولكن للأسف يتم توثيقها بشكل طفيف. بدلاً من ذلك ، تقتصر وثائقهم على التعليقات في رؤوس الوظائف. هناك حيرة لمن يكتبون بعد ذلك ، وكيف يمكنهم أن يحررونا من دراسة كتيبات على محيط وحدة التحكم الدقيقة. جوابي ليس من المستحيل تجعل برامج التشغيل من السهل نقل البرامج إلى وحدات تحكم دقيقة مختلفة داخل نفس العائلة. ولكن من أجل تطبيقها بشكل فعال ، تحتاج إلى التعامل بشكل جيد للغاية مع الوثائق الطرفية. وبالتالي ، فإن برامج تشغيل SDK تحل مشكلة خاصة إلى حد ما لمطوري برامج التشغيل أنفسهم ، بعيدًا عن احتياجات المستخدمين الذين بدأوا في تعلم Kinetis .


تم تصميم السائقين أيضًا للعمل على جميع شرائح الأسرة ، أي يكون عالميًا وبالتالي مشبعًا بوحدات ماكرو شرطية ويتحقق من أن كل شريحة معينة لا تحمل أي وظيفة مفيدة.


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


ومع ذلك ، قد تكون هناك مخاوف بشأن تبعيات جهاز طرفي معين على توفر برامج تشغيل SDK. هنا يجب عليك فحص الكود المصدري للسائقين. يأتي الخطر الرئيسي للتبعيات من فصل وظائف محركات DMA . لمنع هذا ، تحتاج إلى تتبع وحظر خدمات Mbed العادية من استخدام DMA عند العمل مع الأجهزة الطرفية. إذا ظلت DMA على حالها من قبل برامج تشغيل SDK ، فكل شيء تقريبًا لا ينطبق على وحدتي المؤقت المذكورة ( PIT و LPTMR ) ويمكن التخلص من تصحيح UART أو تجاهله من دليل NXP_MKE18F_drivers .


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


تذكر أن المنفذ Mbed لـ MKE18F يبدأ مقاطعات UART والمؤقت دون تحديد الأولويات ، أي يحصلون على الحد الأقصى بشكل افتراضي.

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


All Articles