Anwendung von Arm Mbed OS. Feinabstimmung

LNDC1


Nach der Verwendung des Arm Mbed-Betriebssystems konnte die LED blinken . Es war Zeit, andere wichtige Dienste zu testen und zu konfigurieren. Folgendes wird beschrieben:


  • Mbed- Konfigurationstechnologie
  • Warum ist es schwierig, in normalem RTOS zu C ++ zu wechseln ?
  • So speichern Sie Speicher in RTOS
  • Wie sind Interrupts in Mbed OS organisiert ?
  • Dann ist es bequem, Mbed OS zu debuggen
  • So entfernen Sie eine zusätzliche Ebene der SDK- Abstraktion

Wir sind weiterhin mit der Technologie der Programmierung von Mikrocontrollern der MKE18F- Familie mit ARM Mbed OS vertraut



Mbed-Konfigurationstechnologie


Eine wichtige Komponente des Mbed-Projekts ist ein automatisches Konfigurations- und Montagesystem, sowohl online als auch offline , d.h. lokal auf dem Computer des Benutzers. Es wird empfohlen, .json-Dateien mit speziellen Namen zu konfigurieren. Anschließend konvertieren die Skripte im Python- Projekt diese Dateien in Header-Dateien, Arbeitsbereichsdateien der vom Benutzer ausgewählten IDE, Linker-Befehlsdateien und andere unterstützende Dateien.


Das Problem der beschriebenen Methode liegt jedoch in der Deckkraft aus Sicht der Quelltexte, da es für uns sehr schwierig ist zu verfolgen, wo und was sich das Konfigurationssystem in den Quellen ändert. Andererseits gibt es in unserem Fall kein Motiv, die Fähigkeit des Projekts zu unterstützen, automatisch auf verschiedene IDEs übertragen zu werden.


Daher wurde beschlossen, diesen Ansatz aufzugeben. Wie in einem früheren Artikel erwähnt, wurde es einfach zu einem Online-Projekt für die IDE-IAR geformt. Im IDE-Arbeitsbereich wurde ein unstrukturierter Stapel von Dateien abgerufen, dann wurden sie systematisiert und unnötige verworfen. Infolgedessen müssen Sie die Konfiguration nicht mehr über JSON- Dateien durchführen, und es gibt nur drei bestimmte Stellen, an denen sich Parameter befinden, die sich auf die Konfiguration von Mbed auswirken :


  • IDE- Compileroptionen
  • Linker MKE18F512xxx16_flash.icf Batchdatei
  • Header-Datei mbed_config.h

In der Datei mbed_config.h können Sie ungefähr 130 Definitionen zählen, was zunächst sehr ärgerlich ist. Glücklicherweise beziehen sich die meisten von ihnen auf Stapel von drahtlosen Protokollen, die derzeit im Projekt nicht verwendet werden. Der Einfachheit halber wurden die Einträge so sortiert, dass die tatsächlichen oben platziert wurden. Anfangs befinden sich die Definitionen für verschiedene Module in der Datei mbed_config.h zufällig, aber nach dem Sortieren sieht es folgendermaßen aus:


Öffnen
#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 

Funktionen des Übergangs zu C ++ für RTOS


Die API der obersten Ebene in Mbed ist in C ++ geschrieben , daher muss diese Sprache im Anwendungscode verwendet werden. Aber es gibt Nuancen, die Sie kennen müssen.
Die Verwendung von C ++ für RTOS auf kleinen eingebetteten Systemen ist noch relativ selten. Das Problem hierbei ist, dass erfolgreiche RTOS- Projekte mehrere Plattformen anstreben, während C ++ im Vergleich zu C hohe Anforderungen an das Plattformressourcenmanagement stellt . Der Grund ist der Wunsch, die Details der Ressourcenverwaltung auf niedriger Ebene vor dem Benutzer zu verbergen. Es geht hauptsächlich um Speicherressourcen. Konstruktoren, Destruktoren, Threads, Ausnahmen mit automatischer Zerstörung, Vorlagen von Datenstrukturobjekten usw. verwenden implizite Operationen mit dynamischem Speicher. Die RAM- Ressource in kleinen Systemen ist jedoch sehr begrenzt. RAM ist die knappste Ressource in solchen Systemen und insbesondere in RTOS . In RTOS wird jeder Aufgabe ein Stapel zugewiesen, der Entwickler kann seine genaue Größe nicht im Voraus vorhersagen und wählt daher mit einem Rand. Das Vorhandensein von RTOS mit einem Dutzend Aufgaben erfordert daher sofort eine RAM- Größe von 10 bis 30 kB. Für verschiedene Parser und Protokolle (HTTP, HTML ...) und Dateisysteme wird viel Speicher benötigt. Wenn eine Anzeige verwendet wird, werden die Anforderungen an freien RAM weiter erhöht.


Bibliotheken von Entwicklungsumgebungen wie IAR sind mit guten dynamischen Speichermanagern ausgestattet, sie sind jedoch für eine Single-Threaded-Laufzeitumgebung konzipiert. Damit sie unter RTOS arbeiten können, müssen Sie zusätzlichen Code schreiben. Dieser Vorgang wird als Retargeting bezeichnet .
In in C geschriebenem RTOS wird im Allgemeinen kein Retargeting durchgeführt. Da es auf Sprachebene keine impliziten Operationen mit dynamischem Speicher gibt, werden alle Operationen explizit ausgeführt, indem ihre eigenen thread-sicheren Versionen der Funktionen malloc und free aufgerufen werden. Der Programmierer hat die volle Kontrolle über Vorgänge mit dynamischem Speicher und kann problemlos alle möglichen Maßnahmen zum Speichern anwenden.


Wenn wir in C ++ alle Funktionen dieser Sprache nutzen möchten, müssen wir ein Retargeting durchführen . Das Retargeting in jeder Entwicklungsumgebung ist jedoch ein rein individueller Prozess. Dies macht RTOS- Entwicklern das Leben schwer.


Die folgende Abbildung zeigt ein Beispiel für eine Anrufstruktur mit Retargeting. Die Funktionen __write , __lseek , __read werden möglicherweise nicht vom Benutzer implementiert, ihre Funktionalität liegt jedoch im Ermessen der IDE . Und sicherlich werden printf und scanf nicht multithreaded.



Mbed ist eines der wenigen, wenn nicht das einzige RTOS , das Quellcodes mit bereits durchgeführtem Retargeting für eine Triade bekannter Entwicklungstools bereitstellt: GCC , IAR , Keil
Trotz alledem finden Sie Artikel zum Portieren von RTOS nach C ++, ohne ein Retargeting durchzuführen. Sie können beispielsweise das Problem lösen, indem Sie einfach einige gängige Standardfunktionen durch Ihre eigenen ersetzen. Dies mag funktionieren, aber dann muss der Programmierer verschiedene implizite und nicht dokumentierte Einschränkungen kennen, wenn er C ++ - Konstrukte in IAR verwendet (nur statische Konstruktoren, alle Vorlagen auf neue prüfen, Ausnahmen verwerfen usw.). Es wird bereits schwierig sein, C ++ aufzurufen. Mbed als benutzerfreundliches System beseitigt viele dieser Einschränkungen und nähert sich Arduino in seiner Einfachheit.


Zusätzlich zu allen neueren Versionen von IAR gibt es Schwierigkeiten beim Wechsel zu C11 und C ++ 14, die hier beschrieben werden - https://www.iar.com/support/resources/articles/exploring-c11-and-c14/


Wie sind Interrupts in Mbed organisiert?


Seltsamerweise ist es nicht möglich, eine Klasse oder Funktion oder etwas zu finden, das zum Organisieren des Interrupt-Dienstes in der Mbed-API geeignet ist. Sie finden nur die InterruptIn- Klasse, die nur für externe Ports vorgesehen ist.


Die Antworten auf solche Fragen sollten in CMSIS-RTOS gesucht werden, und zwar in der CMSIS Cortex-M4 Core Peripheral Access Layer . Dort werden Makros definiert:


  • NVIC_SetVector - Legt die Interrupt-Serviceroutine (ISR) für einen bestimmten Vektor fest
  • NVIC_SetPriority - Legt die Priorität für den angegebenen Interruptvektor fest.
  • NVIC_EnableIRQ - Ermöglicht Interrupt-Aufrufe für einen bestimmten Vektor

So wird die Interrupt-Organisation vom SysTick- Timer initialisiert:


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

Mbed-Aufgabenprioritäten und Interrupt-Prioritäten sollten nicht verwechselt werden.


Wenn keine Priorität zugewiesen ist, wird diese standardmäßig auf Maximum gesetzt.
Über die auf diese Weise zugewiesenen Interrupt-Handler können Sie alle RTOS- Dienste aufrufen, die keine Erwartungen hervorrufen. Das heißt, Senden von Flags, Semaphoren, Nachrichten, Postfächern usw. Dienste werden jedoch nicht vom ISR selbst aufgerufen, sondern durch Aufrufen von Software-Interrupts durch Setzen des PENDSVSET- Bits im Interrupt Control and State Register ( ICSR ) des System Control Block ( SCB ) des Cortex-M- Kernels. Das heißt, Wenn nach Abschluss des aktuellen Interrupt- Handlers keine anderen Prioritäts-Interrupts vorhanden sind, wird der System-Handler vom PendSV- Vektor aufgerufen , in dem der Dienst ausgeführt wird.


Wie arbeitet Mbed mit dynamischem Speicher?


Dynamischer Speicher oder Heap oder Heap ist eine erforderliche Komponente beim Programmieren in C ++ . In unserem Mbed- Projekt unter IAR wird die Größe dieses Speicherbereichs in der Einstellungsdatei des Linkers MKE18F512xxx16_flash.icf durch Schreiben in die Variable __size_heap__ bestimmt. Wir stellen die Größe so ein, dass der gesamte verbleibende freie Speicher belegt wird. Wir wissen, wie viel freier Speicher nach der Kompilierung aus der .map- Datei übrig bleibt , d. H. Die Größenanpassung von Heap ist ein iterativer Prozess.


Aufruf statischer Konstruktoren von C ++ - Objekten


Ein wichtiges Problem bei der Verwendung von C ++ ist, wo und wie globale Objektkonstruktoren aufgerufen werden. Selbst bei RTOS, die Ernsthaftigkeit beanspruchen, beispielsweise MAX , wird dies ignoriert, d.h. dem Zufall überlassen. Dort arbeiten Designer an einer Standardbibliothek für Single-Threaded-Entwicklungsumgebungen mit dem üblichen Single-Threaded-Speicherzuweisungsmechanismus. Nach dem Start erstellen die gemeinsamen RTOS jedoch einen eigenen Mechanismus zur Verwaltung des dynamischen Speichers, und der von globalen Objekten belegte Speicher bleibt vergessen. Dies ist eine Lücke in unseren Bemühungen, Speicherplatz zu sparen und alles zu kontrollieren.


Mbed ging dieses Problem viel ernsthafter an. Dort hat jede Entwicklungsumgebung ihren eigenen Ansatz. In der IAR geschieht dies folgendermaßen:


  • Der Initialisierungsstrom wird vom Benutzercode abgefangen, noch bevor die Designer ihn aufrufen
  • Methoden zum Blockieren von Flows aus der RTOS-API werden in die Standardbibliothek eingesetzt
  • Standardfunktionen new , delete , malloc , free ... werden an Aufrufe der RTOS Dynamic Memory API umgeleitet.

Mbed verwendet IAR- Bibliotheksadapter für den Multithread-Betrieb
Informationen zum Anpassen von IAR für Multithreading finden Sie hier - http://supp.iar.com/FilesPublic/UPDINFO/005691/arm/doc/infocenter/DLIBThreadSupport.html
Mbed hat sowohl Systemsperren als auch Dateistreamsperren der IAR- Bibliothek angepasst. Sie sind in der Datei mbed_boot.c implementiert und verwenden Betriebssystem-Mutexe .


Die in den ersten Zeilen der Datei startup_MKE18F16.s ausführbare Funktion __iar_program_start initialisiert den Betriebssystemstapel und den dynamischen Speicher durch Aufrufen von mbed_set_stack_heap


Aufgabenstapel dimensionieren


Das Reduzieren von Aufgabenstapeln auf ein Minimum ist die attraktivste Option zum Speichern von RAM .
Für Aufgaben, die weniger Stapel erfordern, werden verschiedene Techniken verwendet. Beispielsweise wirken sich Bibliotheksfunktionen stark auf den Stapel zum Ausgeben und Formatieren von printf- , sprintf- , scanf- Zeilen usw. aus. Sie können große temporäre Bereiche für die Datenspeicherung auf dem Stapel zuweisen. Wenn wir uns weigern, diese Funktionen in der Aufgabe zu verwenden, können wir den Aufgabenstapel um einige gute hundert Bytes reduzieren.


Mbed OS erstellt beim Start sofort drei Aufgaben mit Namen: "main_thread" , "timer_thread" , "idle_thread" . Die Standardstapelgröße für sie wurde durch Makros in der Header-Datei mbed_rtx_conf.h bestimmt . Wir haben die Deklarationen dieser Stapel in die Konfigurationsdatei mbed_config.h verschoben und die Größe der Stapel reduziert. Jetzt sehen die Definitionen folgendermaßen aus:


  • Der Taskstapel "main_thread" wird durch das Makro MBED_CONF_APP_MAIN_STACK_SIZE = 1024 Byte definiert
  • Der Taskstapel timer_thread wird durch das Makro MBED_CONF_APP_TIMER_THREAD_STACK_SIZE = 512 Byte definiert
  • Der Taskstapel idle_thread wird durch das Makro MBED_CONF_APP_IDLE_THREAD_STACK_SIZE = 512 Byte definiert

Mbed Memory Usage Controls


Der dynamische Speicher sowie der Stapel sind Ressourcen, die ständige Aufmerksamkeit erfordern. Um zu sehen, wie viel dynamischer Speicher verwendet wird und wie intensiv Anforderungen an ihn sind, wie viel Stapel für jede Aufgabe übrig bleibt und wie hoch der Spitzenstapel in Mbed war, gibt es spezielle Zähler. Standardmäßig sind sie durch die Anweisung zur bedingten Kompilierung deaktiviert . Um sie zu aktivieren, müssen Sie MBED_ALL_STATS_ENABLED deklarieren. Wenn die Definition deklariert ist, müssen Sie Ihre eigene Prozedur schreiben, um dem Benutzer Informationen anzuzeigen. Wir haben ein spezielles Verfahren für die Ausgabe von Statistiken an den VT100-Terminalemulator geschrieben, das später erläutert wird.


Zusätzlich zu den vom Betriebssystem bereitgestellten Tools bietet die IAR-Entwicklungsumgebung in neueren Versionen eine neue Funktion - gestapelte Kanarienvögel . Lesen Sie hier darüber . Allgemeine Stapelschutzprobleme vor Überlauf werden hier und hier behandelt .


Tools zum Debuggen und Analysieren von Mbed-Code


Eine wirklich gründliche Untersuchung der Arbeit von Mbed auf der neuen Plattform ist nur mit dem JTAG / SWD- Debugger möglich.



Mbed-Quellen sind voll von mehrstufigen Makros und Anweisungen zur bedingten Kompilierung. Wenn Sie sich nur die Quelle ansehen, können Sie nicht sagen, welche Funktionen funktionieren und welche nicht, wo das Programm ausgeführt wird und wo nicht. Der einzige Ausweg besteht darin, den Debugger anzuschließen und Schritt für Schritt den Programmausführungspfad zu analysieren. Bereits in der Portierungsphase ist es fast unmöglich, auf ein schrittweises Debuggen zu verzichten.


Ich würde Segger- Debugger-Versionen von J-Link Pro und J-Link Ultra empfehlen. Sie zeichnen sich durch eine hohe Bandbreite aus, die um ein Vielfaches höher ist als bei herkömmlichen billigen Modellen. Für das Debuggen von Systemen mit harter Echtzeit ist dies wichtig. Bei der Verfolgung schneller Ereignisse sind solche Debugger weniger anfällig für Überläufe und erfordern weniger Debugging-Iterationen. Zusätzlich zum schrittweisen Debuggen können sie Interrupts protokollieren, Statistiken über Unterbrechungen, einschließlich ihrer Ausführungsdauer, führen, RTT- und ITM- Debugging-Technologien unterstützen, Hardware-Ausnahmen und andere Funktionen abfangen.


Unten sehen Sie eine Ansicht des IAR-Debugger-Fensters, wenn Sie über den J-Link-Adapter arbeiten.



Es lohnt sich nicht zu sparen, 90% der Entwicklungszeit wird für das Debuggen aufgewendet. Obwohl die teureren Segger J-Trace- Tracer keinen großen Vorteil mehr bieten, verfügt die MKE18F- Serie nicht über eine spezielle Tracer-Schnittstelle.


Das zweite Debugging-Tool ist natürlich E / A über UART . Mbed verfügt über mindestens vier verschiedene Technologien für den Zugriff auf einen seriellen Datenkanal über UART .


Das:


  • Umleitung durch die DirectSerial- Klasse der Standard- C / C ++ - E / A-Funktionen
  • Klasse RawSerial
  • Klasse seriell
  • Klasse UARTSerial

Eine ziemlich ungewöhnliche Vielfalt. Alle vier können gleichzeitig für die Ausgabe an denselben Port verwendet werden. Einige von ihnen sind funktionaler, andere weniger, andere dokumentiert und andere nicht. In Zukunft werden wir in unserem Projekt RawSerial als die am wenigsten ressourcenintensive Klasse verwenden.


Wie viele Timer verlieren wir durch die Portierung von Mbed


Zum Blinken der LED wurde die Wartefunktion der Mbed OS- API verwendet. Der letzte Artikel sprach über die Probleme der Portierung. Dies ist jedoch nicht alles, außer dass Mbed einen Zähler seiner Arbeit behält (er kann von der Funktion mbed_stats_cpu_get gelesen werden) und es eine Timer-API gibt . Diese Dienste verwenden die Funktionen auf niedriger Ebene aus der Datei lp_ticker.c . In dieser Datei wird der LPTMR- Timer aus dem Kinetis- Peripheriekit verwendet, um die Zeitzählung zu organisieren. Während des Portierungsprozesses wurde diese Datei so bearbeitet, dass sie den im Mikrocontroller MKE18F512VLL16 verwendeten Taktmethoden entspricht .


Somit erfasst der Mbed-Port zwei Zählermodule vollständig - PIT und LPTMR sowie den SysTick- Core- Timer . Dies sollte bei der Planung von Ressourcen für eine angewendete Aufgabe berücksichtigt werden.


MKE18F Bootstrap-Funktionen


Die MKE18F-Chipfamilie verfügt über ein integriertes ROM mit einem Bootloader über serielle Schnittstellen: UART, CAN, SPI, I2C . Es ist jedoch geplant, unseren sicheren Bootloader auf unserem Board zu verwenden, sodass die Arbeit eines normalen Bootloaders unerwünscht ist.
In diesem Fall müssen Kinetis- Chips bei 0x040D auf den Inhalt des Programm-Flash-Bereichs achten . Es ist eine Konstante gespeichert, die die Startreihenfolge bestimmt. In unserem Fall sollte dort die Konstante 0x7B geschrieben werden , die den Start vom Flash- Speicher und nicht vom ROM anzeigt und die NMI- Funktion am externen Ausgang deaktiviert. Bei einem anderen Wert für diese Konstante friert das Programm möglicherweise im integrierten Bootloader ein, der versehentlich vom ROM aufgerufen wurde.


Es ist auch wichtig zu bedenken, dass das Schreiben in den Flash-Speicher des Controllers nur bei einer Kernfrequenz von 120 MHz und nicht höher möglich ist, d. H. Im HRUN- Modus ist keine Flash- Aufnahme möglich.


Watchdog aktivieren


Die Platine unseres Controllers ist für den industriellen Einsatz vorgesehen, dh Sie können nicht auf Watchdog verzichten .


Zu Beginn wurde das Makro DISABLE_WDOG in der Datei system_MKE18F16.h festgelegt, um den Watchdog zu deaktivieren. Um die Situation zu korrigieren, wurde dieses Makro gelöscht und eine eigene Initialisierung von WDOG implementiert.


Die Initialisierung von Watchdog erfolgt in der SystemInit- Funktion. Der Watchdog- Zähler wird in der IDLE- Task aktualisiert.


Ein solcher Ansatz erfordert, dass Aufgaben mit höherer Priorität den Prozessor nicht länger als 100 ms erfassen. Dies kann jedoch leicht passieren, wenn beispielsweise große Datenabbilder an das Terminal ausgegeben werden. Daher teilen wir langwierige Prozeduren in Aufgaben mit einer Priorität größer als IDLE mithilfe der Wartefunktion in kurze Fragmente auf, die mit Pausen durchsetzt sind.


Treiberproblem bei SDK-Lieferung


SDK- Treibern wird fsl im Verzeichnis NXP_MKE18F_drivers vorangestellt und ist eine Art periphere Abstraktionsschicht. Sie sollen das Programmieren von schwer zu erlernenden Peripheriegeräten erleichtern, sind aber leider nur minimal dokumentiert. Ihre Dokumentation beschränkt sich vielmehr auf Kommentare in Funktionsüberschriften. Es ist verwirrend, für wen sie dann geschrieben sind und wie sie uns vom Studium von Handbüchern an der Peripherie des Mikrocontrollers befreien können. Meine Antwort ist auf keinen Fall. Treiber erleichtern lediglich die Übertragung von Programmen auf verschiedene Mikrocontroller innerhalb derselben Familie. Um sie jedoch effektiv anwenden zu können, müssen Sie sich sehr gut mit der Dokumentation von Peripheriegeräten auseinandersetzen. Somit lösen die SDK- Treiber ein eher privates Problem der Entwickler der Treiber selbst, weit entfernt von den Bedürfnissen der Benutzer, die anfangen, Kinetis zu lernen.


Treiber sind auch so konzipiert, dass sie auf allen Chips der Familie funktionieren, d. H. universell sein und daher mit bedingten Makros gesättigt sein und prüfen, ob für jeden einzelnen Chip keine nützliche Funktion vorhanden ist.


Treiber können immer noch helfen, den Umgang mit Peripheriegeräten besser zu verstehen, aber nachdem Sie den Treiber verstanden haben, können Sie ihn ignorieren.
In diesem Zusammenhang sollte es nicht überraschen, dass in diesem Projekt der Zugriff auf die Peripheriegeräte, die nicht vom Mbed-Port betroffen sind, direkt über den Treiber erfolgt.


Es können jedoch Bedenken hinsichtlich der Abhängigkeiten eines bestimmten Peripheriegeräts von der Verfügbarkeit von SDK-Treibern bestehen. Hier müssen Sie den Quellcode der Treiber überprüfen. Die Hauptgefahr von Abhängigkeiten besteht in der Trennung der Funktionen von DMA- Treibern. Um dies zu verhindern, müssen Sie verfolgen und verhindern, dass reguläre Mbed-Dienste DMA verwenden, wenn Sie mit Peripheriegeräten arbeiten. Wenn DMA von den SDK- Treibern nicht berührt wird, kann fast alles, was nicht für die beiden genannten Timer-Module ( PIT und LPTMR ) und den Debug-UART gilt, aus dem Verzeichnis NXP_MKE18F_drivers gelöscht oder ignoriert werden.


Eine geringere, aber auch erhebliche Gefahr kann durch die Priorisierung der beteiligten Peripheriegeräte durch die SDK-Treiber entstehen. Beispielsweise müssen Sie wissen, welche Priorität SysTick-Interrupts, Debug-UARTs und Timern zugewiesen wird. In einer Situation, in der ihre Priorität gleich oder höher als die Priorität der im Echtzeitmanagement verwendeten Peripherie ist, kann dies zu einer Verschlechterung der Verwaltungsqualität führen.


Denken Sie daran, dass der Mbed- Port für den MKE18F UART- und Timer-Interrupts ohne Priorisierung initiiert, d. H. Sie erhalten standardmäßig die maximale Stufe.

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


All Articles