Application de Arm Mbed OS. Réglage fin

LNDC1


Après avoir utilisé Arm Mbed OS, il était possible de faire clignoter une LED , il était temps de tester et de configurer d'autres services importants. Ce qui suit est décrit:


  • Technologie de configuration Mbed
  • Pourquoi est-il difficile de passer au C ++ dans RTOS normal
  • Comment économiser de la mémoire dans RTOS
  • Comment les interruptions sont-elles organisées dans Mbed OS
  • Qu'il est pratique de déboguer le système d'exploitation Mbed
  • Comment se débarrasser d'une couche supplémentaire d'abstraction SDK

Nous continuons notre connaissance de la technologie de programmation des microcontrôleurs de la famille MKE18F utilisant ARM Mbed OS



Technologie de configuration Mbed


Un élément important du projet Mbed est le système de configuration et d'assemblage automatiques , en ligne et hors ligne , c'est-à-dire localement sur l'ordinateur de l'utilisateur. Il est proposé de configurer en éditant des fichiers .json avec des noms spéciaux. Ensuite, les scripts du projet Python convertissent ces fichiers en fichiers d'en-tête, fichiers d'espace de travail de l'IDE sélectionné par l'utilisateur, fichiers de commandes de l'éditeur de liens et autres fichiers de prise en charge.


Mais le problème de la méthode décrite réside dans l'opacité du point de vue des textes sources, car il nous est très difficile de suivre où et ce que le système de configuration change dans les sources. D'un autre côté, dans notre cas, il n'y a aucune raison de soutenir la capacité du projet à être automatiquement transféré vers différents IDE.


Il a donc été décidé d'abandonner cette approche. Comme indiqué dans un article précédent, il a simplement été transformé en un projet en ligne pour l' IDE IAR , un tas non structuré de fichiers a été obtenu dans l'espace de travail IDE, puis ils ont été systématisés et inutiles ont été jetés. Par conséquent, vous n'avez plus besoin d'effectuer de configuration via des fichiers .json et il n'y a que trois endroits spécifiques où se trouvent les paramètres affectant la configuration de Mbed :


  • Options du compilateur IDE
  • Linker MKE18F512xxx16_flash.icf fichier batch
  • Fichier d'en-tête mbed_config.h

Dans le fichier mbed_config.h , vous pouvez compter environ 130 définitions, ce qui est très ennuyeux au début. Mais heureusement, la plupart d'entre eux concernent des piles de protocoles sans fil qui ne sont pas actuellement utilisés dans le projet. Pour plus de commodité, les entrées ont été triées de façon à ce que les entrées réelles soient placées en haut. Initialement, les définitions des différents modules du fichier mbed_config.h sont situées de manière aléatoire, mais après le tri, cela ressemble à ceci:


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

Caractéristiques de la transition vers C ++ appliquées à RTOS


L'API de niveau supérieur dans Mbed est écrite en C ++ , donc ce langage doit être utilisé dans le code de l'application. Mais il y a des nuances que vous devez connaître.
L'utilisation de C ++ pour RTOS sur de petits systèmes embarqués est encore relativement rare. Le problème ici est que les projets RTOS réussis s'efforcent d'être multi-plateforme, tandis que C ++ impose des exigences élevées sur la gestion des ressources de la plate-forme par rapport à C. La raison en est le désir de cacher à l'utilisateur les détails de la gestion des ressources de bas niveau. Il s'agit principalement de ressources mémoire. Les constructeurs, destructeurs, threads, exceptions avec destruction automatique, modèles d'objets de structure de données, etc. utilisent des opérations implicites avec la mémoire dynamique. Mais la ressource de RAM dans les petits systèmes est très limitée. La RAM est la ressource la plus rare dans ces systèmes et en particulier dans RTOS . Dans RTOS, chaque tâche se voit attribuer une pile, le développeur ne peut pas prédire sa taille exacte à l'avance et choisit donc avec une marge. Ainsi, la présence de RTOS avec une dizaine de tâches nécessite immédiatement une taille de RAM de 10 à 30 ko. Beaucoup de mémoire est nécessaire pour les différents analyseurs et protocoles (HTTP, HTML ...) et les systèmes de fichiers. Si un écran est utilisé, les besoins en RAM libre sont encore augmentés.


Les bibliothèques d'environnements de développement tels que IAR sont équipées de bons gestionnaires de mémoire dynamique, mais elles sont conçues pour un environnement d'exécution monothread. Pour qu'ils puissent commencer à travailler dans RTOS, vous devez écrire du code supplémentaire. Ce processus est appelé reciblage .
Dans RTOS écrit en C, le reciblage n'est généralement pas effectué. Puisqu'il n'y a pas d'opérations implicites avec de la mémoire dynamique au niveau du langage, toutes les opérations sont effectuées explicitement en appelant leurs propres versions thread-safe du malloc et des fonctions libres . Le programmeur a un contrôle total sur les opérations avec la mémoire dynamique et peut facilement appliquer toutes les mesures possibles pour l'enregistrer.


Dans le cas de C ++, si nous voulons utiliser toutes les fonctionnalités de ce langage, nous devrons faire du reciblage . Mais le reciblage dans chaque environnement de développement est un processus purement individuel. Cela rend la vie difficile aux développeurs RTOS .


La figure ci-dessous est un exemple de structure d'appel avec reciblage. Les fonctions __write , __lseek , __read peuvent ne pas être implémentées par l'utilisateur, mais leur fonctionnalité reste à la discrétion de l' IDE . Et certainement printf et scanf ne seront pas multithread.



Mbed est l' un des rares sinon le seul RTOS qui fournit des codes source avec un reciblage déjà fait pour une triade d'outils de développement bien connus: GCC , IAR , Keil
Malgré tout ce qui précède, vous pouvez trouver des articles sur le portage de RTOS vers C ++ sans effectuer de reciblage, par exemple, en résolvant le problème en remplaçant simplement certaines fonctions standard courantes par les vôtres. Cela peut fonctionner, mais le programmeur doit alors être conscient de diverses restrictions implicites et non documentées lors de l'utilisation de constructions C ++ dans IAR (uniquement les constructeurs statiques, vérifier tous les modèles pour les nouveaux , éliminer les exceptions, etc.). Il sera déjà difficile d'appeler C ++ . Mbed, en tant que système convivial, supprime bon nombre de ces limitations, abordant Arduino en toute simplicité.


En plus de toutes les versions récentes de IAR, il est difficile de passer à C11 et C ++ 14, comme décrit ici - https://www.iar.com/support/resources/articles/exploring-c11-and-c14/


Comment les interruptions sont-elles organisées dans Mbed


Curieusement, il ne sera pas possible de trouver une classe ou une fonction ou quelque chose qui convienne pour organiser le service d'interruption dans l'API Mbed. Vous ne pouvez trouver que la classe InterruptIn , qui est destinée uniquement aux ports externes.


Les réponses à ces questions doivent être recherchées dans CMSIS-RTOS , à savoir dans la couche d'accès périphérique CMSIS Cortex-M4 . Les macros y sont définies:


  • NVIC_SetVector - définit la routine de service d'interruption (ISR) pour un vecteur donné
  • NVIC_SetPriority - Définit la priorité du vecteur d'interruption spécifié.
  • NVIC_EnableIRQ - autorise les appels d'interruption sur un vecteur donné

Voici comment l'organisation d'interruption du minuteur SysTick est initialisée:


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

Les priorités de tâche Mbed et les priorités d'interruption ne doivent pas être confondues.


Si la priorité n'est pas attribuée, elle est par défaut définie sur maximum.
À partir des gestionnaires d'interruption affectés de cette manière, vous pouvez appeler tous les services RTOS qui ne provoquent pas d'attentes. C'est-à-dire transmettre des drapeaux, des sémaphores, des messages, des boîtes aux lettres, etc. Les services ne sont toutefois pas appelés à partir de l' ISR lui-même, mais en appelant des interruptions logicielles en définissant le bit PENDSVSET dans le contrôle d'interruption et le registre d'état ( ICSR ) du bloc de contrôle système ( SCB ) du noyau Cortex-M . C'est-à-dire une fois le gestionnaire d'interruptions en cours terminé, s'il n'y a pas d'autres interruptions prioritaires, le gestionnaire système sera appelé par le vecteur PendSV où le service sera exécuté.


Comment Mbed fonctionne-t-il avec la mémoire dynamique?


La mémoire dynamique, ou bien le tas ou le tas, est un composant requis lors de la programmation en C ++ . Dans notre projet Mbed sous IAR , la taille de cette zone mémoire est déterminée dans le fichier de paramètres de l'éditeur de liens MKE18F512xxx16_flash.icf en écrivant dans la variable __size_heap__ . Nous définissons la taille pour qu'elle occupe toute la mémoire libre restante. Nous savons combien de mémoire libre reste du fichier .map après la compilation, c'est-à-dire le dimensionnement du tas est un processus itératif.


Appel de constructeurs statiques d'objets C ++


Un problème important lors de l'utilisation de C ++ est où et comment les constructeurs d'objets globaux sont appelés. Même dans RTOS affirmant du sérieux, par exemple, MAX , cela est ignoré, c'est-à-dire laissé au hasard. Là, les concepteurs sont engagés dans une bibliothèque d'environnement de développement monothread standard avec le mécanisme d'allocation de mémoire monothread habituel. Mais après le début, les RTOS communs créent leur propre mécanisme de gestion de la mémoire dynamique, et la mémoire occupée par les objets globaux reste oubliée. C'est un trou dans nos efforts pour économiser la mémoire et tout contrôler.


Mbed a abordé cette question beaucoup plus sérieusement. Là pour chaque environnement de développement a sa propre approche. Dans l' IAR, cela se fait comme suit:


  • le flux d'initialisation est intercepté par le code utilisateur avant même que les concepteurs appellent
  • les méthodes de blocage des flux de l'API RTOS sont substituées dans la bibliothèque standard
  • les fonctions standard new , delete , malloc , free ... sont redirigées vers les appels vers l' API de mémoire dynamique RTOS .

Mbed utilise des adaptateurs de bibliothèque IAR pour un fonctionnement multithread
Vous pouvez lire sur l'adaptation d'IAR pour le multithreading ici - http://supp.iar.com/FilesPublic/UPDINFO/005691/arm/doc/infocenter/DLIBThreadSupport.html
Mbed a adapté les verrous système et les verrous de flux de fichiers de la bibliothèque IAR . Ils sont implémentés dans le fichier mbed_boot.c et utilisent des mutex OS .


L'exécutable de la fonction __iar_program_start dans les toutes premières lignes du fichier startup_MKE18F16.s initialise la pile du système d'exploitation et la mémoire dynamique en appelant mbed_set_stack_heap


Dimensionnement des piles de tâches


La suppression des piles de tâches au minimum est l'option la plus intéressante pour économiser de la RAM .
Pour les tâches nécessitant moins de pile, différentes techniques sont utilisées. Par exemple, les fonctions de bibliothèque affectent fortement la pile pour la sortie et le formatage des lignes printf , sprintf , scanf , etc. Ils ont la possibilité d'allouer de grandes zones temporaires pour le stockage des données sur la pile. Si nous refusons d'utiliser ces fonctions dans la tâche, nous pouvons réduire la pile de tâches de quelques centaines d'octets.


Mbed OS au démarrage crée immédiatement trois tâches avec des noms: "main_thread" , "timer_thread" , "idle_thread" . La taille de pile par défaut pour eux a été déterminée par des macros dans le fichier d'en-tête mbed_rtx_conf.h . Nous avons déplacé les déclarations de ces piles vers le fichier de configuration mbed_config.h et réduit la taille des piles. Maintenant, les définitions ressemblent à ceci:


  • La pile de tâches "main_thread" est définie par la macro MBED_CONF_APP_MAIN_STACK_SIZE = 1024 octets
  • La pile de tâches timer_thread est définie par la macro MBED_CONF_APP_TIMER_THREAD_STACK_SIZE = 512 octets
  • La pile de tâches idle_thread est définie par la macro MBED_CONF_APP_IDLE_THREAD_STACK_SIZE = 512 octets

Contrôles d'utilisation de la mémoire Mbed


La mémoire dynamique ainsi que la pile sont des ressources qui nécessitent une attention constante. Pour voir combien de mémoire dynamique est utilisée et quelle est l'intensité des demandes, combien de pile reste pour chaque tâche et quel était le pic de chargement de la pile dans Mbed, il existe des compteurs spéciaux. Par défaut, ils sont désactivés par la directive de compilation conditionnelle; pour les activer, vous devez déclarer MBED_ALL_STATS_ENABLED . Lorsque la définition est déclarée, vous devez écrire votre propre procédure pour afficher des informations à l'utilisateur. Nous avons écrit une procédure spéciale pour la sortie des statistiques vers l'émulateur de terminal VT100, qui sera discutée plus tard.


En plus des outils fournis par OS, l'environnement de développement IAR dans les versions récentes ajoute une nouvelle fonctionnalité - les canaris empilés . Lisez à leur sujet ici . Les problèmes généraux de protection de pile contre le débordement sont discutés ici et ici .


Outils de débogage et d'analyse de code Mbed


Une étude vraiment approfondie du travail de Mbed sur la nouvelle plate-forme n'est possible qu'en utilisant le débogueur JTAG / SWD .



Les sources Mbed regorgent de macros à plusieurs niveaux et d'instructions de compilation conditionnelle. En regardant simplement la source, vous ne pouvez pas dire quelles fonctions fonctionnent et lesquelles ne fonctionnent pas, où le programme s'exécute et où il ne fonctionne pas. La seule solution consiste à connecter le débogueur et à analyser pas à pas le chemin d'exécution du programme. Déjà au stade du portage, il est presque impossible de se passer d'un débogage étape par étape.


Je recommanderais les versions de débogueurs Segger de J-Link Pro et J-Link Ultra . Ils se distinguent par une bande passante élevée, plusieurs fois supérieure à celle des modèles bon marché courants. Pour les systèmes de débogage en temps réel difficile, cela est important. Lors du suivi d'événements rapides, ces débogueurs sont moins sujets aux débordements et nécessitent moins d'itérations de débogage. En plus du débogage étape par étape, ils sont capables de consigner les interruptions, de conserver des statistiques sur les interruptions, y compris leurs durées d'exécution, de prendre en charge les technologies de débogage RTT et ITM , de détecter les exceptions matérielles et d'autres fonctions.


Vous trouverez ci-dessous une vue de la fenêtre du débogueur IAR lorsque vous utilisez l'adaptateur J-Link.



Cela ne vaut pas la peine d’être sauvé, 90% du temps de développement est consacré au débogage. Bien que les traceurs Segger J-Trace, plus chers, n'offrent plus un grand avantage, car la série MKE18F n'a pas d'interface de traceur spéciale.


Le deuxième outil de débogage, bien sûr, est les E / S via UART . Mbed dispose d'au moins quatre technologies différentes pour accéder à un canal de données série via UART ,


C’est:


  • redirection via la classe DirectSerial des fonctions d'E / S C / C ++ standard
  • class RawSerial
  • série de classe
  • classe UARTSerial

Une variété assez inhabituelle. Les quatre peuvent être utilisés simultanément pour une sortie vers le même port. Certains d'entre eux sont plus fonctionnels, certains le sont moins, certains sont documentés et d'autres non. À l'avenir, dans notre projet, nous utiliserons RawSerial , la classe la moins gourmande en ressources.


Combien de minuteries perdons-nous en portant Mbed


Pour faire clignoter la LED, la fonction d' attente de l'API Mbed OS a été utilisée. Le dernier article parlait des problèmes de portage. Mais ce n'est pas tout, sauf que Mbed garde un compteur de son travail (il peut être lu par la fonction mbed_stats_cpu_get ) et il y a une API timer . Ces services utilisent les fonctions de bas niveau du fichier lp_ticker.c . Dans ce fichier, le temporisateur LPTMR du kit périphérique Kinetis est utilisé pour organiser le décompte du temps. Pendant le processus de portage, ce fichier a été modifié pour correspondre aux méthodes d'horloge utilisées dans le microcontrôleur MKE18F512VLL16 .


Ainsi, le port Mbed capture complètement deux modules de compteur - PIT et LPTMR et le temporisateur principal SysTick . Il faut s'en souvenir lors de la planification des ressources pour une tâche appliquée.


Caractéristiques du Bootstrap MKE18F


La famille de puces MKE18F possède une ROM intégrée avec un chargeur de démarrage via des interfaces série: UART, CAN, SPI, I2C . Mais il est prévu d'utiliser notre chargeur de démarrage sécurisé sur notre carte, de sorte que le travail d'un chargeur de démarrage normal n'est pas souhaitable.
Dans ce cas, les puces Kinetis doivent faire attention au contenu de la zone Program Flash à 0x040D . Il y a une constante stockée qui détermine l'ordre de démarrage. Dans notre cas, la constante 0x7B doit y être écrite, indiquant le démarrage à partir de la mémoire Flash , et non à partir de la ROM et désactivant la fonction NMI sur la sortie externe. Avec une valeur différente pour cette constante, le programme peut se bloquer dans le chargeur de démarrage intégré appelé accidentellement à partir de la ROM.


Il est également important de se rappeler que l’écriture dans la mémoire flash du contrôleur n’est possible qu’à une fréquence centrale de 120 MHz et non plus, c’est-à-dire En mode HRUN , l'enregistrement Flash n'est pas possible.


Activer le chien de garde


La carte de notre contrôleur est destinée à un usage industriel, ce qui signifie que vous ne pouvez pas vous passer de Watchdog .


Initialement, la macro DISABLE_WDOG dans le fichier system_MKE18F16.h a été définie pour désactiver le chien de garde . Pour corriger la situation, cette macro a été effacée et sa propre initialisation de WDOG a été implémentée.


L'initialisation de Watchdog se fait dans la fonction SystemInit . Le compteur de surveillance est mis à jour dans la tâche IDLE .


Une telle approche nécessite que les tâches de priorité plus élevée ne capturent pas exclusivement le processeur pendant plus de 100 ms . Mais cela peut facilement se produire, par exemple, lors de la sortie de grands vidages de données vers le terminal. Par conséquent, partout où nous interrompons de longues procédures dans des tâches avec une priorité supérieure à IDLE en courts fragments entrecoupés de pauses à l'aide de la fonction d' attente .


Problème de pilotes lié à la livraison du SDK


Les pilotes SDK sont préfixés par fsl situé dans le répertoire NXP_MKE18F_drivers et constituent une sorte de couche d'abstraction périphérique. Ils sont censés faciliter la programmation de périphériques difficiles à apprendre, mais malheureusement peu documentés. Au contraire, leur documentation se limite aux commentaires dans les en-têtes de fonction. Il y a une confusion pour qui alors ils sont écrits et comment ils peuvent nous libérer de l'étude des manuels à la périphérie du microcontrôleur. Ma réponse n'est pas possible. Les pilotes facilitent simplement le transfert de programmes vers différents microcontrôleurs au sein de la même famille. Mais pour les appliquer efficacement, vous devez très bien gérer la documentation périphérique. Ainsi, les pilotes du SDK résolvent un problème plutôt privé des développeurs des pilotes eux-mêmes, loin des besoins des utilisateurs qui commencent à apprendre Kinetis .


Les pilotes sont également conçus pour fonctionner sur toutes les puces de la famille, c'est-à-dire être universel et donc saturé de macros conditionnelles et vérifie que pour chaque puce particulière ne porte aucune fonction utile.


Les pilotes peuvent toujours aider d'une certaine manière à mieux comprendre comment gérer les périphériques, mais après avoir compris le pilote, vous pouvez l'ignorer en toute sécurité.
À cet égard, il n'est pas surprenant que dans ce projet, l'accès aux périphériques non affectés par le port Mbed passe directement par le pilote.


Cependant, il peut y avoir des préoccupations concernant les dépendances d'un périphérique particulier sur la disponibilité des pilotes SDK. Ici, vous devez examiner le code source des pilotes. Le principal danger des dépendances provient de la séparation des fonctions des pilotes DMA . Pour éviter cela, vous devez suivre et interdire aux services Mbed réguliers d'utiliser DMA lorsque vous travaillez avec des périphériques. Si DMA reste intact par les pilotes du SDK , alors presque tout ce qui ne s'applique pas aux 2 modules de temporisation mentionnés ( PIT et LPTMR ) et l'UART de débogage peuvent être supprimés ou ignorés du répertoire NXP_MKE18F_drivers .


Un danger moindre, mais également important, peut provenir de la priorité des périphériques impliqués via les pilotes du SDK. Par exemple, vous devez savoir quelle priorité est affectée aux interruptions SysTick, au débogage des UART et aux temporisateurs. Dans une situation où leur priorité est égale ou supérieure à la priorité de la périphérie utilisée dans la gestion en temps réel, cela peut conduire à une dégradation de la qualité de la gestion.


N'oubliez pas que le port Mbed pour le MKE18F initie les interruptions UART et timer sans priorisation, c.-à-d. ils obtiennent le niveau maximum par défaut.

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


All Articles