Aplikasi Arm Mbed OS. Penyetelan yang bagus

LNDC1


Setelah menggunakan Arm Mbed OS, dimungkinkan untuk mengedipkan LED , tiba saatnya untuk menguji dan mengkonfigurasi layanan penting lainnya. Berikut ini dijelaskan:


  • Teknologi Konfigurasi Mbed
  • Mengapa sulit untuk beralih ke C ++ di RTOS biasa
  • Bagaimana cara menyimpan memori dalam RTOS
  • Bagaimana interupsi diorganisasikan dalam Mbed OS
  • Daripada itu nyaman untuk men-debug OS Mbed
  • Bagaimana menyingkirkan lapisan tambahan abstraksi SDK

Kami terus berkenalan dengan teknologi pemrograman mikrokontroler keluarga MKE18F menggunakan ARM Mbed OS



Teknologi Konfigurasi Mbed


Komponen penting dari proyek Mbed adalah konfigurasi otomatis dan sistem perakitan, baik online maupun offline , mis. secara lokal di komputer pengguna. Diusulkan untuk mengonfigurasi dengan mengedit file .json dengan nama khusus. Kemudian, skrip dalam proyek Python mengubah file-file ini menjadi file header, file ruang kerja dari IDE yang dipilih pengguna, file perintah linker, dan file pendukung lainnya.


Tetapi masalah dari metode yang dijelaskan adalah dalam opacity dari sudut pandang teks sumber, karena sangat sulit bagi kita untuk melacak di mana dan apa perubahan sistem konfigurasi dalam sumber. Di sisi lain, dalam kasus kami tidak ada motif untuk mendukung kemampuan proyek untuk ditransfer secara otomatis ke berbagai IDE.


Karena itu, diputuskan untuk meninggalkan pendekatan ini. Seperti yang dinyatakan dalam artikel sebelumnya, itu hanya dibentuk menjadi proyek online untuk IDE IAR , tumpukan file yang tidak terstruktur diperoleh di ruang kerja IDE, kemudian mereka sistematis dan tidak perlu dibuang. Akibatnya, Anda tidak perlu lagi melakukan konfigurasi melalui file .json dan hanya ada tiga tempat khusus di mana parameter yang mempengaruhi konfigurasi Mbed berada :


  • Opsi penyusun IDE
  • File batcher MKE18F512xxx16_flash.icf
  • File tajuk mbed_config.h

Dalam file mbed_config.h , Anda dapat menghitung sekitar 130 definisi, yang pada awalnya sangat menjengkelkan. Tetapi untungnya, sebagian besar dari mereka berhubungan dengan tumpukan protokol nirkabel yang saat ini tidak digunakan dalam proyek. Untuk kenyamanan, entri diurutkan sehingga yang sebenarnya ditempatkan di atas. Awalnya, definisi untuk berbagai modul dalam file mbed_config.h terletak secara acak, tetapi setelah mengurutkannya terlihat seperti ini:


Buka
#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 

Fitur transisi ke C ++ sebagaimana diterapkan ke RTOS


API tingkat atas di Mbed ditulis dalam C ++ , jadi bahasa ini harus digunakan dalam kode aplikasi. Namun ada nuansa yang perlu Anda ketahui.
Menggunakan C ++ untuk RTOS pada sistem embedded kecil masih relatif jarang. Masalahnya di sini adalah bahwa proyek RTOS yang berhasil berusaha menjadi multi-platform, sementara C ++ menempatkan tuntutan tinggi pada manajemen sumber daya platform dibandingkan dengan C. Alasannya adalah keinginan untuk menyembunyikan dari pengguna rincian manajemen sumber daya tingkat rendah. Ini terutama tentang sumber daya memori. Konstruktor, destruktor, utas, pengecualian dengan penghancuran otomatis, templat objek struktur data, dll. Menggunakan operasi tersirat dengan memori dinamis. Tetapi sumber daya RAM dalam sistem kecil sangat terbatas. RAM adalah sumber daya yang paling langka dalam sistem seperti itu dan terutama di RTOS . Dalam RTOS, setiap tugas dialokasikan tumpukan, pengembang tidak dapat memprediksi ukuran persisnya di muka dan karenanya memilih dengan margin. Dengan demikian, kehadiran RTOS dengan selusin tugas segera membutuhkan ukuran RAM 10 hingga 30 kB. Banyak memori diperlukan untuk berbagai parser dan protokol (HTTP, HTML ...) dan sistem file. Jika layar digunakan, persyaratan untuk RAM bebas semakin meningkat.


Perpustakaan lingkungan pengembangan seperti IAR dilengkapi dengan manajer memori dinamis yang baik, tetapi mereka dirancang untuk lingkungan runtime single-threaded. Agar mereka mulai bekerja di RTOS, Anda perlu menulis kode tambahan. Proses ini disebut retargeting .
Dalam RTOS yang ditulis dalam C, penargetan ulang umumnya tidak dilakukan. Karena tidak ada operasi implisit dengan memori dinamis di tingkat bahasa, semua operasi dilakukan secara eksplisit dengan memanggil versi malloc dan fungsi bebas thread-safe mereka sendiri. Programmer memiliki kontrol penuh atas operasi dengan memori dinamis dan dapat dengan mudah menerapkan semua langkah yang mungkin untuk menyimpannya.


Dalam kasus C ++, jika kita ingin menggunakan semua fitur dari bahasa ini, kita harus melakukan penargetan ulang . Tetapi penargetan ulang di setiap lingkungan pengembangan adalah proses yang murni individual. Ini menyulitkan pengembang RTOS .


Gambar di bawah ini adalah contoh struktur panggilan dengan penargetan ulang. Fungsi __write , __lseek , __read mungkin tidak diimplementasikan oleh pengguna, tetapi kemudian fungsinya tetap pada kebijaksanaan IDE . Dan tentunya printf dan scanf tidak akan multithreaded.



Mbed adalah salah satu dari sedikit jika bukan satu-satunya RTOS yang menyediakan kode sumber dengan penargetan ulang yang sudah dilakukan untuk triad alat pengembangan terkenal: GCC , IAR , Keil
Terlepas dari semua hal di atas, Anda dapat menemukan artikel tentang porting RTOS ke C ++ tanpa melakukan retargeting, misalnya, menyelesaikan masalah dengan hanya mengganti beberapa fungsi standar umum dengan Anda sendiri. Ini mungkin berhasil, tetapi kemudian programmer harus menyadari berbagai pembatasan implisit dan tidak berdokumen saat menggunakan konstruksi C ++ di IAR (hanya konstruktor statis, periksa semua templat untuk yang baru , pengecualian pengecualian, dll.). Sudah akan sulit untuk memanggil C ++ . Mbed, sebagai sistem yang mudah digunakan, menghilangkan banyak keterbatasan ini, mendekati Arduino dalam kesederhanaan.


Selain semua versi terbaru IAR, ada kesulitan untuk beralih ke C11 dan C ++ 14, seperti yang dijelaskan di sini - https://www.iar.com/support/resources/articles/exploring-c11-and-c14/


Bagaimana interupsi diorganisasikan dalam Mbed


Anehnya, tidak mungkin menemukan kelas atau fungsi atau sesuatu yang cocok untuk mengatur layanan interupsi di Mbed API. Anda hanya dapat menemukan kelas InterruptIn , yang dimaksudkan hanya untuk port eksternal.


Jawaban atas pertanyaan semacam itu harus dicari dalam CMSIS-RTOS , yaitu di Lapisan Akses Periferal Inti CMSIS Cortex-M4 . Makro didefinisikan di sana:


  • NVIC_SetVector - mengatur rutinitas layanan interupsi (ISR) untuk vektor yang diberikan
  • NVIC_SetPriority - Menetapkan prioritas untuk vektor interupsi yang ditentukan.
  • NVIC_EnableIRQ - memungkinkan panggilan interupsi pada vektor yang diberikan

Inilah cara organisasi interupsi dari pengatur waktu SysTick diinisialisasi:


  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()); //    . 

Prioritas tugas mbed dan prioritas interupsi tidak boleh dikacaukan.


Jika prioritas tidak ditetapkan, maka secara default diatur ke maksimum.
Dari penangan interupsi yang ditugaskan dengan cara ini, Anda dapat memanggil layanan RTOS apa pun yang tidak menimbulkan harapan. Yaitu mengirimkan bendera, semafor, pesan, kotak surat, dll. Layanan dipanggil, bagaimanapun, bukan dari ISR itu sendiri, tetapi dengan memanggil perangkat lunak interupsi dengan mengatur bit PENDSVSET dalam Kontrol Interupsi dan State Register ( ICSR ) dari blok kontrol Sistem ( SCB ) dari kernel Cortex-M . Yaitu setelah penyelesaian interrupt handler saat ini, jika tidak ada interupsi prioritas lainnya, handler sistem akan dipanggil oleh vektor PendSV di mana layanan akan dilakukan.


Bagaimana cara kerja Mbed dengan memori dinamis?


Memori dinamis, atau heap atau heap, adalah komponen yang diperlukan saat pemrograman dalam C ++ . Dalam proyek Mbed kami di bawah IAR , ukuran area memori ini ditentukan dalam file pengaturan MKE18F512xxx16_flash.icf linker dengan menulis ke variabel __size_heap__ . Kami mengatur ukuran sehingga mengambil semua memori bebas yang tersisa. Kita tahu berapa banyak memori bebas yang tersisa dari file .map setelah kompilasi, mis. ukuran tumpukan adalah proses berulang.


Memanggil konstruktor statis objek C ++


Masalah penting ketika menggunakan C ++ adalah di mana dan bagaimana konstruktor objek global dipanggil. Bahkan dalam RTOS mengklaim keseriusan, misalnya, MAX , ini diabaikan, mis. dibiarkan kebetulan. Di sana, desainer terlibat dalam perpustakaan lingkungan pengembangan single-threaded standar dengan mekanisme alokasi memori single-threaded biasa. Tetapi setelah awal, RTOS umum membuat mekanisme mereka sendiri untuk mengelola memori dinamis, dan memori yang ditempati oleh objek global tetap dilupakan. Ini adalah lubang dalam upaya kami untuk menghemat memori dan mengendalikan segalanya.


Mbed mendekati masalah ini dengan lebih serius. Di sana untuk setiap lingkungan pengembangan memiliki pendekatan sendiri. Di IAR, ini dilakukan seperti ini:


  • aliran inisialisasi dicegat oleh kode pengguna bahkan sebelum desainer memanggil
  • metode memblokir aliran dari RTOS API diganti ke perpustakaan standar
  • fungsi standar baru , hapus , malloc , gratis ... dialihkan ke panggilan ke API memori dinamis RTOS .

Mbed menggunakan adaptor perpustakaan IAR untuk operasi multi-utas
Anda dapat membaca tentang mengadaptasi IAR untuk multithreading di sini - http://supp.iar.com/FilesPublic/UPDINFO/005691/arm/doc/infocenter/DLIBThreadSupport.html
Mbed telah mengadaptasi kedua kunci sistem dan kunci aliran file dari perpustakaan IAR . Mereka diimplementasikan dalam file mbed_boot.c dan menggunakan mutex OS .


Fungsi __iar_program_start dijalankan di baris pertama file startup_MKE18F16.s menginisialisasi tumpukan OS dan memori dinamis dengan memanggil mbed_set_stack_heap


Ukuran tumpukan tugas


Memangkas tumpukan tugas hingga minimum adalah opsi paling menarik untuk menghemat RAM .
Untuk tugas-tugas yang membutuhkan tumpukan lebih sedikit, teknik yang berbeda digunakan. Misalnya, fungsi perpustakaan sangat memengaruhi tumpukan untuk menghasilkan dan memformat printf , sprintf , garis scanf , dll. Mereka memiliki kemampuan untuk mengalokasikan area sementara yang besar untuk penyimpanan data pada stack. Jika kita menolak untuk menggunakan fungsi-fungsi ini dalam tugas, kita dapat mengurangi tumpukan tugas dengan beberapa ratus byte yang baik.


OS Mbed saat startup segera membuat tiga tugas dengan nama: "main_thread" , "timer_thread" , "idle_thread" . Ukuran tumpukan default untuk mereka ditentukan oleh makro di file header mbed_rtx_conf.h . Kami memindahkan deklarasi tumpukan ini ke file konfigurasi mbed_config.h dan mengurangi ukuran tumpukan. Sekarang definisi terlihat seperti ini:


  • Tumpukan tugas "main_thread" ditentukan oleh MBED_CONF_APP_MAIN_STACK_SIZE makro = 1024 byte
  • Tumpukan tugas timer_thread ditentukan oleh MBED_CONF_APP_TIMER_THREAD_STACK_SIZE makro = 512 byte
  • Tumpukan tugas idle_thread didefinisikan oleh MBED_CONF_APP_IDLE_THREAD_STACK_SIZE makro = 512 byte

Kontrol Penggunaan Memori Mbed


Memori dinamis serta tumpukan adalah sumber daya yang membutuhkan perhatian terus-menerus. Untuk melihat berapa banyak memori dinamis yang digunakan dan berapa intensitas permintaan untuk itu, berapa banyak tumpukan yang tersisa untuk setiap tugas dan berapa beban puncak tumpukan di Mbed ada penghitung khusus. Secara default, mereka dinonaktifkan oleh arahan kompilasi bersyarat, untuk mengaktifkannya, Anda harus mendeklarasikan MBED_ALL_STATS_ENABLED . Ketika definisi tersebut dideklarasikan, Anda perlu menulis prosedur Anda sendiri untuk menampilkan informasi kepada pengguna. Kami menulis prosedur khusus untuk mengeluarkan statistik ke emulator terminal VT100, yang akan dibahas nanti.


Selain alat yang disediakan oleh OS, lingkungan pengembangan IAR dalam versi terbaru menambahkan kenari ditumpuk fitur baru. Baca tentang mereka di sini . Masalah perlindungan tumpukan umum dari overflow dibahas di sini dan di sini .


Alat Debugging dan Analisis Kode Mbed


Studi yang benar-benar mendalam tentang pekerjaan Mbed pada platform baru hanya dimungkinkan menggunakan debugger JTAG / SWD .



Sumber mbed penuh dengan makro bertingkat dan instruksi kompilasi bersyarat. Hanya dengan melihat sumbernya Anda tidak bisa mengatakan fungsi mana yang berfungsi dan mana yang tidak, di mana program berjalan dan di mana tidak. Satu-satunya jalan keluar adalah menghubungkan debugger dan langkah demi langkah untuk menganalisis jalur eksekusi program. Sudah pada tahap porting, hampir tidak mungkin dilakukan tanpa langkah demi langkah debugging.


Saya akan merekomendasikan versi debugger Segger dari J-Link Pro dan J-Link Ultra . Mereka dibedakan oleh bandwidth tinggi, beberapa kali lebih tinggi dari model murah yang umum. Untuk sistem debugging dengan waktu nyata yang sulit, ini penting. Saat melacak acara cepat, debugger seperti itu cenderung untuk meluap dan membutuhkan iterasi debugging yang lebih sedikit. Selain debugging langkah-demi-langkah, mereka dapat mencatat interupsi, menjaga statistik interupsi, termasuk waktu eksekusi, mendukung teknologi debugging RTT dan ITM , menangkap pengecualian perangkat keras dan fungsi lainnya.


Di bawah ini adalah tampilan jendela debugger IAR saat bekerja melalui adaptor J-Link.



Itu tidak layak untuk disimpan, 90% dari waktu pengembangan dihabiskan untuk debugging. Meskipun pelacak Segger J-Trace yang lebih mahal tidak lagi memberikan keuntungan besar, karena seri MKE18F tidak memiliki antarmuka pelacak khusus.


Alat debugging kedua , tentu saja, adalah I / O via UART . Mbed memiliki setidaknya empat teknologi berbeda untuk mengakses saluran data serial melalui UART ,


Ini adalah:


  • pengalihan melalui kelas DirectSerial dari fungsi C / C ++ I / O standar
  • kelas RawSerial
  • serial kelas
  • kelas UARTerial

Variasi yang tidak biasa. Keempatnya dapat digunakan secara bersamaan untuk output ke port yang sama. Beberapa dari mereka lebih fungsional, ada yang kurang, ada yang didokumentasikan, dan ada yang tidak. Di masa depan, dalam proyek kami, kami akan menggunakan RawSerial , sebagai kelas yang paling hemat sumber daya.


Berapa banyak penghitung waktu yang kita hilangkan dengan porting Mbed


Untuk mengedipkan LED, fungsi tunggu dari Mbed OS API digunakan. Artikel terakhir berbicara tentang masalah porting itu. Tapi ini tidak semua, kecuali Mbed menyimpan penghitung pekerjaannya (bisa dibaca oleh fungsi mbed_stats_cpu_get ) dan ada timer API . Layanan ini menggunakan fungsi tingkat rendah dari file lp_ticker.c . Dalam file ini, timer LPTMR dari kit perangkat Kinetis digunakan untuk mengatur jumlah waktu. Selama proses porting, file ini diedit agar sesuai dengan metode pencatatan jam kerja yang digunakan dalam mikrokontroler MKE18F512VLL16 .


Dengan demikian, port Mbed sepenuhnya menangkap dua modul penghitung - PIT dan LPTMR dan timer inti SysTick . Ini harus diingat ketika merencanakan sumber daya untuk tugas yang diterapkan.


Fitur Bootstrap MKE18F


Rangkaian chip MKE18F memiliki ROM bawaan dengan bootloader melalui antarmuka serial: UART, CAN, SPI, I2C . Tetapi direncanakan untuk menggunakan bootloader aman kami di papan kami, sehingga pekerjaan bootloader reguler tidak diinginkan.
Dalam hal ini, chip Kinetis perlu memperhatikan isi area Program Flash pada 0x040D . Ada yang disimpan konstan yang menentukan urutan boot. Dalam kasus kami, konstanta 0x7B harus ditulis di sana , menunjukkan mulai dari memori Flash , dan bukan dari ROM dan menonaktifkan fungsi NMI pada output eksternal. Dengan nilai berbeda untuk konstanta ini, program dapat membeku di bootloader bawaan yang secara tidak sengaja dipanggil dari ROM.


Penting juga untuk diingat bahwa menulis ke memori Flash pengontrol hanya dimungkinkan pada frekuensi inti 120 MHz dan tidak lebih tinggi, mis. Dalam mode HRUN , perekaman Flash tidak dimungkinkan.


Aktifkan Watchdog


Papan pengontrol kami ditujukan untuk penggunaan industri, yang berarti Anda tidak dapat melakukannya tanpa Watchdog .


Awalnya, makro DISABLE_WDOG dalam file system_MKE18F16.h diatur untuk menonaktifkan watchdog . Untuk memperbaiki situasi, makro ini dihapus dan inisialisasi WDOG sendiri diimplementasikan.


Inisialisasi Watchdog dilakukan dalam fungsi SystemInit . Penghitung pengawas diperbarui dalam tugas IDLE .


Pendekatan semacam itu mensyaratkan bahwa tugas dengan prioritas lebih tinggi tidak secara eksklusif menangkap prosesor selama lebih dari 100 ms . Tetapi ini dapat dengan mudah terjadi, misalnya, ketika mengeluarkan data yang besar ke terminal. Oleh karena itu, di mana pun kami memecah prosedur panjang dalam tugas dengan prioritas lebih besar dari IDLE menjadi fragmen pendek diselingi dengan jeda menggunakan fungsi menunggu .


Masalah driver dari pengiriman SDK


Driver SDK diawali dengan fsl yang terletak di direktori NXP_MKE18F_drivers dan merupakan sejenis lapisan abstraksi periferal. Mereka seharusnya memfasilitasi pemrograman yang sulit untuk mempelajari peripheral, tetapi sayangnya minimal didokumentasikan. Sebaliknya, dokumentasi mereka terbatas pada komentar di header fungsi. Ada kebingungan untuk siapa mereka kemudian ditulis, dan bagaimana mereka dapat membebaskan kita dari mempelajari manual di pinggiran mikrokontroler. Jawaban saya tidak mungkin. Driver hanya memudahkan transfer program ke mikrokontroler yang berbeda dalam keluarga yang sama. Tetapi agar dapat menerapkannya secara efektif, Anda perlu menangani dokumentasi periferal dengan sangat baik. Dengan demikian, driver SDK memecahkan masalah yang agak pribadi dari pengembang driver itu sendiri, jauh dari kebutuhan pengguna yang mulai mempelajari Kinetis .


Driver juga dirancang untuk bekerja pada semua chip keluarga yaitu bersifat universal dan karena itu jenuh dengan makro bersyarat dan memeriksa bahwa untuk setiap chip tertentu tidak membawa fungsi yang berguna.


Driver masih dapat membantu dalam beberapa cara untuk lebih memahami cara menangani periferal, tetapi setelah memahami driver, Anda dapat dengan aman mengabaikannya.
Dalam hal ini, tidak mengherankan bahwa dalam proyek ini, akses ke periferal yang tidak terpengaruh oleh port Mbed langsung melewati pengemudi.


Namun, mungkin ada kekhawatiran tentang ketergantungan perangkat tertentu pada ketersediaan driver SDK. Di sini Anda harus memeriksa kode sumber driver. Bahaya utama ketergantungan berasal dari pemisahan fungsi driver DMA . Untuk mencegah hal ini, Anda perlu melacak dan melarang layanan Mbed reguler dari menggunakan DMA saat bekerja dengan periferal. Jika DMA tetap tidak tersentuh oleh driver SDK , maka hampir semua yang tidak berlaku untuk 2 modul timer yang disebutkan ( PIT dan LPTMR ) dan UART debug dapat dibuang atau diabaikan dari direktori NXP_MKE18F_drivers .


Bahaya yang lebih kecil, tetapi juga signifikan, dapat datang dari memprioritaskan periferal yang terlibat melalui driver SDK. Misalnya, Anda perlu tahu prioritas apa yang ditetapkan untuk interupsi SysTick, debug UART, dan timer. Dalam situasi di mana prioritas mereka sama atau lebih besar dari prioritas pinggiran yang digunakan dalam manajemen waktu nyata, ini dapat menyebabkan penurunan kualitas manajemen.


Ingatlah bahwa port Mbed untuk MKE18F memulai UART dan penghitung waktu memotong tanpa memprioritaskan, mis. mereka mendapatkan level maksimum secara default.

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


All Articles