Microcontroller für das IoT - Teil 4

Mein kleines Betriebssystem: Multi-Threading mit dem STM32F429

Tam Hanna

@Shutterstock / Alfazet Chronicles

Die bisherigen Beispiele unserer Artikelserie über Mikrocontroller-Programmierung waren insofern „dozil“, als dass sie sich auf das Vorführen einzelner Peripheriegeräte beschränkten. Nebenläufigkeit kam nur deshalb vor, weil die Aufgabe teilweise aus mehreren Teilen bestand. Praktische Embedded-Systeme sind meist wesentlich komplexer. So müsste die Steuerung einer Kaffeemaschine gleichzeitig auf den Brühkopf, die Milchpumpe und das Display samt Knöpfen achten: mehrere Aufgaben mit unterschiedlichen Prioritäten, bei deren Bewältigung man sich schon mal nach Thread und Co. sehnt.

Serie: Microcontroller für das Internet der Dinge

Multi-Threading mit dem STM32F429

Sowohl Windows als auch Linux sind für derartige Multi-Threading-Aufgaben nicht wirklich geeignet. Erstens ist ihre Reaktionsgeschwindigkeit viel zu langsam, zweitens sorgt der enorme Ressourcenbedarf für teure und langsam arbeitende Zielsysteme. Als RTOS beziehungsweise Echtzeitbetriebssystem bezeichnete Plattformen sind auf die Bedürfnisse von Mikrocontrollern optimierte Betriebssysteme, die ihren Entwicklern neben Threading oft auch eine HAL zur Erleichterung von Portierungen zwischen einzelnen Controllerfamilien anbieten. Es steht außer Frage, dass ein qualifizierter Embedded-Entwickler seine Steuerungen auch ohne die Hilfe einer solchen Plattform realisieren kann. In Zeiten immer schneller werdender Produktzyklen muss man diese Vorgehensweise aus wirtschaftlicher Sicht allerdings anzweifeln: Projekte auf Basis eines RTOS kommen in den meisten Fällen wesentlich schneller ans Ziel.

ChibiOS herbei

Wer Google nach Echtzeitbetriebssystemen befragt, findet sich mit einer geradezu erschlagenden Produktvielfalt konfrontiert. Wir wollen in den folgenden Schritten auf ChibiOS setzen – ein teilweise quelloffenes Betriebssystem, das seinen Nutzern eine Vielzahl von Hilfsmitteln und Optionen zur Verfügung stellt.

Da das manuelle Integrieren von Echtzeitbetriebssystemen in vorhandene Lösungen und Toolchains erfahrungsgemäß in Arbeit ausartet – der Autor scheiterte einmal nach zwölf Stunden Bastelei an ChibiOS – beginnen wir unsere Erprobungen mit dem Herunterladen der Entwicklungsumgebung ChibiStudio. Als Hostplattform dient dieses Mal Windows 8.1 – die abermalige Installation des STM32F429-Discovery-Boards besprechen wir erst an gegebener Stelle.

Nach der Extraktion in das Verzeichnis C:\ChibiStudio – 7Zip muss unter Windows 8.1 aus dem Installationsordner gestartet werden – sollten Sie das Vorhandensein einer 32-Bit-Java-VM überprüfen: Zum Zeitpunkt der Drucklegung funktioniert ChibiStudio mit 64-Bit-VMs nicht.

Klicken Sie danach auf die Verknüpfung Chibi Studio GCC 4.9, um die IDE anzuwerfen. Nach dem Start präsentiert der Project Explorer eine Liste deaktivierter C-Projekte, die diverse Features vorführen und bei Bedarf als Projektskelett fungieren können.

Sprechen Sie STM?

ChibiStudio ist vollständig: Mit dem Entpacken des Archivs haben Sie – bis auf den zum Ansprechen des Evaluationsboards benötigten Treiber – alles auf der Maschine, was zur Kompilation eines MCU-Projekts erforderlich ist.

Öffnen Sie danach das Projekt RT-STM32F429-DISCOVERY. Es handelt sich dabei um ein kleines Projektskelett, das die für den STM32F429 notwendigen Adaptierungen mitbringt und zudem einige Hardwarebausteine initialisiert. Echtzeitbetriebssysteme verlangen normalerweise nach einem bestimmten Einsprungspunkt – im Fall von ChibiOS beginnt man folgendermaßen:

  

int main(void) {

   thread_t *shelltp = NULL;


 halInit();

 chSysInit();

Diese Vorgehensweise mag auf Entwickler, die Windows und/oder Linux gewohnt sind, verstörend wirken: Die meisten Echtzeitbetriebssysteme verschmelzen Applikationscode und Betriebssystem zu einem Monolithen. Im Fall von ChibiOS liegen die Betriebssystemressourcen im Unterordner /os.

Unser vorliegendes Beispiel enthält zwei Threads, die die auf dem Evaluationsboard verbauten LEDs zum Blinken bringen. Ihre Initialisierung erfolgt in main – achten Sie darauf, dass die Methode nach dem Aufruf von cySysInit() zu einem „normalen“ Thread degradiert wird:

  

chThdCreateStatic(waThread1, sizeof(waThread1),

                  NORMALPRIO + 10, Thread1, NULL);

chThdCreateStatic(waThread2, sizeof(waThread2),

                  NORMALPRIO + 10, Thread2, NULL);

Im Rahmen der Initialisierung eines neuen Threads ist – neben der Deklaration einer Working Area – auch ein numerischer Wert erforderlich, der die Wichtigkeit der in ihm abgebildeten Aufgabe beschreibt. ChibiOS geht dabei nach dem Schema „größer ist wichtiger“ vor. Je höher der Wert des Chars, desto mehr Rechenzeit wird dem Thread zugewiesen.

Die eigentliche Implementierung der beiden LED-Blinker ist, bis auf die anderen Flags, identisch, weshalb wir uns an dieser Stelle auf das Abdrucken einer der beiden Methoden beschränken. Als ersten Akt ist die Deklaration einer Working Area zuständig: Sie legt fest, wie viel Stackspeicher der Thread für die Erledigung seiner Aufgaben benötigt.

THD_FUNCTION zeichnet Funktionen aus, die als „Basis“ für einen Thread dienen können. ChibiOS orientiert sich dabei an C-Runnables: Der Lebenszyklus eines Threads endet, sobald die Methode retourniert. Unser Beispiel umgeht dieses Problem durch die Nutzung einer Endlosschleife (Listing 1).

static THD_WORKING_AREA(waThread1, 128);

static THD_FUNCTION(Thread1, arg) {



  (void)arg;

  chRegSetThreadName("blinker1");

  while (true) {

     palClearPad(GPIOG, GPIOG_LED4_RED);

     chThdSleepMilliseconds(500);

     palSetPad(GPIOG, GPIOG_LED4_RED);

     chThdSleepMilliseconds(500);

   }

}

Beim Aufbau der Thread-Payload sollten Entwickler darauf achten, dass die Subroutinen nur dann zuverlässig funktionieren, wenn sie Wartezeiten an das Betriebssystem delegieren. Unser Beispiel löst dies durch Aufruf von chThdSleepMilliseconds;. Die Methode informiert ChibiOS darüber, dass der Thread für die nächsten x Millisekunden keine sinnvolle Arbeit erledigen kann.

Abstrahiere die Hardware

ChibiOS ist stark modular aufgebaut: Wer das Betriebssystem herunterlädt, bekommt in Wirklichkeit drei mehr oder weniger unabhängige Module, die sich zudem in der später besprochenen Lizenzierungsweise unterscheiden.

Auf Seiten des Kernels dürfen Sie zwischen RT und NIL wählen. NIL steht für Null. Der Name ist Programm: Das Produkt nimmt nur 1 KB ROM in Beschlag und ist komplett statisch, bietet aber mit Semaphoren und Ähnlichem trotzdem einige Komfortfeatures. Das hier verwendete RT benötigt mehr Codespeicher, bietet aber auch wesentlich mehr Funktionen. Weitere Informationen zum Funktionsumfang der beiden Kernel kann man auf der Webseite von ChibiOS herunterladen. Auf den meisten heutigen MCUs dürfte der zusätzliche Speicherbedarf von RT nicht wesentlich ins Gewicht fallen.

Eine Ebene darüber findet sich eine – streng optionale – Hardwareabstraktionsschicht. Wer die HAL von ChibiOS verwendet, kann seine Applikation leichter auf andere Controllerfamilien portieren: Eine MCU aus dem Hause Atmel dürfte mit der von STMicroelectronics bereitgestellten HAL nur wenig anfangen können.

Die wichtigste Datei ist hierbei halconf.h: Sie enthält eine Vielzahl von Deklarationen, die einzelne Module nach Bedarf ein- und ausschalten. Unser Beispiel aktiviert die als PAL bezeichnete Ansteuerungsmöglichkeit für GPIO-Ports, während andere Module wie der ADC deaktiviert bleiben:

#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)

#define HAL_USE_PAL                 TRUE

#endif

. . .

#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)

#define HAL_USE_ADC                 FALSE

#endif

Plattformspezifische Anpassungen der HAL finden sich unter anderem in der Datei board.c, die im Rahmen des Kompilationsprozesses eingebunden wird. Eine genauere Beschreibung der Erzeugung einer eigenen HAL würde den Rahmen dieses Artikels sprengen. Es ist (auch aus Zeitgründen) empfehlenswerter, eigene Schaltungen an einer vom jeweiligen RTOS schon unterstützten Planare auszurichten.

Auf die Hardware!

Nach diesen einleitenden Schritten wollen wir unser im letzten Teil der Serie verwendetes Evaluationsboard abermals zum Leben erwecken. Der STM32F429 ist eine vergleichsweise preiswerte Planare, die neben einem betriebsbereiten Mikroprozessor samt Kommandogerät auch einen LCD-Bildschirm und sogar etwas zusätzliches RAM mitbringt.

Kommerziell oder doch nicht?

Nach der Veröffentlichung des letzten Teils der Serie meldeten sich einige Leser und wiesen mich darauf hin, dass das Verbot der kommerziellen Nutzung des STM32F429-DISCO-Boards innerhalb der EU unwirksam sei, weil es nicht auf der Außenseite der Verpackung erkennbar ist.

Der Autor hält diese Vorgehensweise – bei allem Respekt vor der rechtlichen Korrektheit – für hochgradig gefährlich. Die praktische Erfahrung aus jahrelangem Dienst lehrt, dass der Chiphersteller immer am längeren Hebel sitzt: Neben Problemen bei der Einreise in die USA könnte sich STMicroelectronics bei ausreichender Abnahmemenge durch die absichtliche Lieferung einer fehlerhaften Tranche „bedanken“.

Ob der vergleichsweise geringen Verbreitung von STM-Evaluationsboards enthält Windows keinen Treiber für diese Gerätegattung. Öffnen Sie diese URL in einem Browser Ihrer Wahl und laden Sie die dort angebotene Software durch Klick auf den in Abbildung 1 gezeigten Downloadbutton herunter.

Abb. 1: STMicroelectronics liefert mit seiner Website ein Antibeispiel in Sachen Usability

Extrahieren Sie das Archiv an eine beliebige Stelle im Dateisystem, und stoßen Sie die Installation des Treibers durch Ausführung von stlink_winusb_install.bat an. Im nächsten Schritt folgt die hier bereitstehende ST-LINK Utility, die weitere zum Debugging erforderliche Module installiert.

Überprüfen Sie danach, ob die beiden „Discovery-Jumper“ der Platine gesteckt sind – fehlen sie, so ist der im Evaluationsboard integrierte Brenner nicht mit dem Hauptprozessor verbunden.

Stecken Sie die Platine dann an die Workstation an: der Hardwareerkennungsprozess nimmt mitunter eine oder zwei Minuten in Anspruch. Nach der erfolgreichen Integration in den Gerätebaum ist die Planare zur Programmausführung bereit – kehren Sie in ChibiStudio zurück, und klicken Sie auf den kleinen nach unten zeigenden Pfeil neben dem Debug-Symbol.

Unser Projektskelett wird von Chibi mit einer betriebsbereiten Debugger-Konfiguration ausgestattet, die das Kompilat normalerweise automatisch auf den Prozessrechner deployt. Kommt es dabei zu Problemen nach der Bauart „The Program is not being run“, so liegt ein Fehler im Bereich der Konfiguration von OpenOCD vor. Dieses Problem lässt sich in der Debug-Perspektive der IDE beheben. Klicken Sie auf das External-Tools-Menü, und wählen Sie die Option OpenOCD on ST-Link V2 aus. Im sich daraufhin öffnenden Dialog wird die Datei stm32f429discovery.cfg ausgewählt, die für gewöhnlich im Ordner C:\ChibiStudio\tools\openocd\scripts\st_board zu finden ist.

Die erfolgreiche Verbindung zwischen Prozessrechner und Workstation lässt sich an zwei Stellen überprüfen. Neben dem alterierenden Blinken der zweifarbigen COM-LED auf der Oberseite des Boards findet sich in der Konsole eine nach folgendem Schema aufgebaute CPU-Signatur:

 

. . .

Info : STLINK v2 JTAG v17 API v2 SWIM v0 VID 0x0483 PID 0x3748

Info : using stlink api v2

Info : Target voltage: 2.885362

Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints

Starten Sie den Debugging-Prozess daraufhin abermals neu, um sich am Blinken der LEDs zu erfreuen. Der am Anfang von main() befindliche Breakpoint ist Teil der Konfiguration – gewöhnen Sie sich an das Anklicken des Play-Buttons.

Funktionsgenerator, Marke Eigenbau

Bei der Reparatur und Erprobung von Schaltungen wünscht man sich mitunter ein Gerät, das durch eine Formel oder eine Tabelle beschriebene Wellenformen ausgibt. Elektroniker bezeichnen derartige Maschinen als „Arbitrary Waveform Generator“ – eine Geräteklasse, deren praktische Nutzbarkeit stark von der Qualität der Benutzerschnittstelle abhängt.

Während preiswerte chinesische Einheiten oder der weitverbreitete HP 33120 bei Betrachtung der Wellenformen nur wenig Anlass zur Kritik geben, ist die Benutzerführung oft alles andere als anspruchsvoll: Wer auf dem semigrafischen Display eines derartigen Generators eine Wellenform zusammenbastelt, wechselt bald freiwillig auf den PC.

Eigenbaulösungen sind an dieser Stelle schon aus dem Grund besser, weil sich ihre Benutzerschnittstelle an die Bedürfnisse des jeweiligen Anwenders anpassen lässt. Wer beispielsweise häufig mit „kombinatorischen“ Sinusschwingungen arbeitet, kann das GUI seines Generators an diese Situation anpassen.

Wir beginnen unsere Arbeiten an dieser Stelle mit der Realisierung einer Tabelle, die die abzubildende Sinusschwingung in Form einer Punktwolke vorhält. Der einfachste Weg zum Erfolg ist hierbei die Verwendung einer beliebigen Tabellenkalkulation. Zwecks leichterer Exportierbarkeit ist es empfehlenswert, das Arbeitsblatt horizontal aufzubauen.

Die Formel SIN(HW1)*0,8+SIN(HW1*5)*0,2 realisiert ein aus zwei ineinander schwingenden Sinusschwingungen bestehendes Wellensegment. Die Festlegung eines Wertebereichs von -1 bis +1 spart uns bei der Datenaufbereitung die eine oder andere Multiplikation (Abb. 2).

Abb. 2: LibreOffice Calc stellt die Wellenform grafisch dar

Exportieren Sie die Tabelle im nächsten Schritt – natürlich ohne Diagramm – in das .csv-Format, um einen nach folgendem Schema aufgebauten String zu erhalten:

 

0;0,025;0,05;. . .

0;0,0449328634;0,0894641273;0,1331982716;0,175751841;0,2167592413;0,255878258;0,2927952105;. . .

Für unseren Funktionsgenerator ist nur die zweite Zeile von Relevanz. Sie lässt sich mit einem kleinen Java-Programm oder mit geschickter Nutzung der Suchen-Ersetzen-Funktion eines Texteditors in ein fertiges C-Array umwandeln:

 

0,0.0449328634, . . . ,-0.014731195

An dieser Stelle sei explizit darauf hingewiesen, dass die in den Köpfen vieler Programmierer vorhandene Barriere zwischen „Code“ und „Nutzdaten“ in der Realität nicht existiert. Es spricht aus technischer Sicht nichts dagegen, die Werte von Look-up-Tabellen und ähnlichen Strukturen mit einem Generatorprogramm zu erzeugen. Wenn Sie sich damit Zeit sparen, wird sich der Kunde mit Sicherheit nicht beklagen.

Der generierte String kann sodann als statisches Objekt initialisiert werden. Der in ChibiOS enthaltene C-Compiler versteht die in C99 eingeführte Syntax zum „Vorladen“ eines Arrays mit Informationen:

 

static float samples[] = { 0,0.0449328634, . . . ,-0.014731195};



/*

* Application entry point.

*/

int main(void) {

thread_t *shelltp = NULL;

Echtzeitbetriebssysteme bieten oftmals mehrere Methoden zur Allokation von Speicher an. Im Fall von ChibiOS gibt es derer fünf, die sich in Sachen Flexibilität und Performance unterscheiden. Logischerweise sind statisch allozierte Variablen am sichersten, denn sie ändern ihren Aufenthaltsort während der Programmausführung nie.

Als Alternative dazu gibt es drei Systeme, die sich in den Bereichen deterministische Laufzeit und Aufrufbarkeit aus Interrupt-Serviceroutinen unterscheiden. Die Rolle der zweiten Information ist logisch – Ersteres ist immer dann interessant, wenn eine Steuerung harte Echtzeitkriterien erfüllen muss.

Da das genaue Verhalten der verschiedenen Speichermanager von Plattform zu Plattform unterschiedlich ist, verweisen wir an dieser Stelle auf die unter folgendem Link einsehbare Dokumentation. Falls Sie diese Schritte mit einem anderen Echtzeitbetriebssystem umsetzen wollen, bietet der Hersteller mit Sicherheit ähnliche Informationen an.

Spannung hoch!

Unser Evaluationsboard ist mit einem vollwertigen DAC-Konverter ausgestattet. Die mit Sicherheit nicht besonders schnell arbeitende Komponente ist für erste Gehversuche völlig ausreichend. Als ersten Akt müssen Sie die betreffenden Module in der HAL aktivieren. Öffnen Sie dazu die Datei halconf.h, und setzen Sie das im Snippet gezeigte Flag auf TRUE:

 
#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)

#define HAL_USE_DAC                 TRUE

#endif

Wechseln Sie im nächsten Schritt in mcuconf.h, und weisen Sie ChibiOS durch Setzen des betreffenden Flags zur Nutzung des ersten Kanals des DAC-ICs an:

 

/*

* DAC driver system settings.

*/

#define STM32_DAC_DUAL_MODE                 FALSE

#define STM32_DAC_USE_DAC1_CH1              TRUE

Ein Kompilationsversuch scheitert an dieser Stelle mit einem Verweis darauf, dass der betreffende Kanal im Prozessor nicht vorhanden sei. Dies liegt daran, dass der DAC-Treiber bisher noch nicht an die STM32F429-Plattform angepasst wurde.

C-erfahrene Entwickler fühlen sich an dieser Stelle mitunter zu Hackereien motiviert: Da andere STM-Derivate kein Problem bei der Kompilation aufweisen, könnte man ja sein Glück versuchen. Langfristig erweisen sich derartige Modifikationen an Echtzeitbetriebssystemen meist als undankbar. Ein während der Arbeit an diesem Artikel erschienenes Update von ChibiStudio brachte eine neue Version des OS mit, die die bisher gemachten Änderungen mit Freude überschreibt.

STMicroelectronics bietet eine HAL an, der Download des etwa 300 MB großen Archivs nimmt allerdings etwas Zeit in Anspruch. Deaktivieren Sie derweil die im vorigen Schritt eingeschaltete Flag HAL_USE_DAC wieder, um das Projekt zurück in einen kompilierbaren Zustand zu bringen.

Die für unseren Prozessor notwendige Hardwareabstraktionsschicht liegt im Unterverzeichnis Drivers\STM32F4xx_HAL_Driver\. Importieren Sie die Inhalte der /src– und der /inc-Ordner in Ihr Projekt. Öffnen Sie danach das Makefile, und fügen Sie die beiden neuen Verzeichnisse nach folgendem Schema ein:

 

CSRC = $(STARTUPSRC) \

      . . .

      usbcfg.c main.c \

      $(wildcard stmhal/Src/*.c)

. . .

INCDIR = $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) \

         $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \

          $(CHIBIOS)/os/hal/lib/streams $(CHIBIOS)/os/various \

          stmhal/Inc

Benennen Sie die Datei stm32f4xx_hal_conf_template.h im nächsten Schritt in stm32f4xx_hal_conf.h um, und deaktivieren Sie alle Includes außer der für den DAC notwendigen Zeilen:

 

#define HAL_DAC_MODULE_ENABLED

#define HAL_DMA_MODULE_ENABLED

#define HAL_CORTEX_MODULE_ENABLED

#define HAL_FLASH_MODULE_ENABLED

#define HAL_GPIO_MODULE_ENABLED

#define HAL_PWR_MODULE_ENABLED

#define HAL_RCC_MODULE_ENABLED

#define HAL_TIM_MODULE_ENABLED

Unter Drivers\CMSIS\Device\ST\STM32F4xx\Include finden sich die beiden Dateien stm32f4xx.h und stm32f429xx.h, die ebenfalls Aufnahme im Inc-Unterverzeichnis suchen. In _hal.h sind zwei kleine Ergänzungen notwendig, die die Kompilierbarkeit der Codebasis sicherstellen:

 
#include "stm32f4xx_hal_def.h"

#define STM32F429xx

#include "stm32f429xx.h"

Arbeiten Sie die weiteren Compilerfehler danach nacheinander ab, bis das Projekt als Ganzes kompilierbar wird. Als nächste Aufgabe müssen wir HAL und DAC im Rahmen der Initialisierung konfigurieren. Dabei kopieren wir die als float vorliegenden Werte in ein zweites Array, in dem sie in einem für den DAC bekömmlichen Format vorliegen (Listing 2).

  

static const float myRawSamples[]={0,0.0449328634,. . .,-0.014731195};

static int mySamples[252];

static int sampleCounter;

static int waitTime=200;

 int main(void) {

  thread_t *shelltp = NULL;



   // Samples aufbereiten

   for(sampleCounter=0;sampleCounter<252;sampleCounter++)

   {

mySamples[sampleCounter]=(int)(((float)(myRawSamples[sampleCounter]+1.0f))*1006.0f);

   }

   sampleCounter=0;

Echtzeitbetriebssysteme nutzen normalerweise einen oder mehrere Systemtimer zur Eventverarbeitung. Eine vollständige Initialisierung der STM32-HAL per HAL_Init(); stoppt ChibiOS. Danach werden Threads aufgrund fehlender Zeitgeberereignisse nicht mehr aufgerufen. Mindestens ebenso wichtig ist, dass die – aus dem vorigen Heft mit minimalen Anpassungen übernommene – Initialisierung des GPIO-Clusters unbedingt erst nach der Initialisierung der ChibiOS-HAL erfolgen muss (Listing 3).

 

  halInit();
    chSysInit();


  __GPIOA_CLK_ENABLE();
  __DAC_CLK_ENABLE();

  // DAC-Block
  GPIO_InitTypeDef GPIO_InitStruct;

  GPIO_InitStruct.Pin   = GPIO_PIN_4 | GPIO_PIN_5;
  GPIO_InitStruct.Mode  = GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull  = GPIO_NOPULL;
  // GPIOA wird von ChibiOS gestellt und ist für HAL unnütz
  HAL_GPIO_Init(((GPIO_TypeDef *) GPIOA_BASE), &GPIO_InitStruct);

  DacHandle.Instance = DAC;
  if(HAL_DAC_Init(&DacHandle) != HAL_OK)
  {  }

  sConfig.DAC_Trigger = DAC_TRIGGER_NONE; // Act ASAP
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // Power up pwr transistor
  if(HAL_DAC_ConfigChannel(&DacHandle, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {  }
  HAL_DAC_SetValue(&DacHandle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, 0);
  HAL_DAC_Start(&DacHandle, DAC_CHANNEL_1);

Die Ausgabe der einzelnen Samples erfolgt dann in einem der beiden Blinkthreads, während der andere für die periodische Anpassung der Ausgabegeschwindigkeit zuständig ist. Die Korpusse der beiden Threadmethoden sehen nun aus wie in Listing 4.

 
static THD_WORKING_AREA(waThread1, 128);
static THD_FUNCTION(Thread1, arg) {

  (void)arg;
  chRegSetThreadName("blinker1");
  while (true) {
   sampleCounter++;
   if(sampleCounter>=252)sampleCounter=0;
   HAL_DAC_SetValue(&DacHandle, DAC_CHANNEL_1, DAC_ALIGN_12B_R, mySamples[sampleCounter]);
   chThdSleepMicroseconds(waitTime);
  }
}

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg) {

  (void)arg;
  chRegSetThreadName("blinker2");
  while (true) {
    waitTime=200;
    chThdSleepMilliseconds(500);
    waitTime=800;
    chThdSleepMilliseconds(500);
  }
}

ChibiOS bietet eine Vielzahl weiterer Zeitgebermethoden an, die eine höhere Genauigkeit erreichen. Weitere Informationen hierzu finden sich in der Dokumentation.

Eine Frage der Messbarkeit

Das Verifizieren des weichen Übergangs zwischen verschiedenen Frequenzstufen stellt Oszillografen vor erhebliche Schwierigkeiten: Einfach darauflos Sampeln mit Hereinzoomen ist nicht gut, weil es uns interessanter didaktischer Möglichkeiten beraubt.

Oszillografen können Daten nur dann zuverlässig erfassen, wenn ein den Erfassungsbeginn anzeigendes Trigger-Signal vorliegt. Dessen Generierung erfolgt, je nach Gerätetyp, mehr oder weniger aufwendig: Ein Triggern auf absinkende Signalfrequenz ist bei keinem Hersteller verfügbar.

Bei genauer Überlegung fällt auf, dass das zu testende Gerät das Trigger-Signal eigentlich auch selbst generieren könnte. In unserem Funktionsgenerator könnten wir dieses Problem durch einen GPIO-Pin lösen, dessen Zustand bei jeder Frequenzänderung invertiert wird (Listing 5).

 

static THD_WORKING_AREA(waThread2, 128);
static THD_FUNCTION(Thread2, arg) {

  (void)arg;
  chRegSetThreadName("blinker2");
  while (true) {
    palClearPad(GPIOG, GPIOG_LED3_GREEN);
    waitTime=200;
    chThdSleepMilliseconds(500);
    palSetPad(GPIOG, GPIOG_LED3_GREEN);
    waitTime=800;
    chThdSleepMilliseconds(500);
  }
}

Wer einen Digital- oder Hybridspeicheroszillografen zur Hand hat, konfiguriert das Gerät an dieser Stelle in den „Single Shot“-Modus. Verbinden Sie danach Kanal 1 mit dem Ausgang des Generators, während Kanal 2 mit dem GPIO-Pin verbunden wird. Die Trigger-Einheit der meisten Oszillografen lässt sich unabhängig von den darzustellenden Kurven mit Signaleingängen verbinden. Verdrahten Sie sie mit Kanal 2, und setzen Sie die Datenposition in die Mitte des Bildschirms – der Lohn der Mühen ist ein übersichtliches Oszillogramm, das die Verifikation des weichen Übergangs ermöglicht (Abb. 3).

Abb. 3: Trick + Oszillograf = brauchbare Signaldarstellung

Open Source, aber nicht kostenlos

ChibiOS mag im Quellcode vorliegen – wer die beiden Mikrokernels einfach in ein kommerzielles Produkt einbindet, begeht dennoch eine Urheberrechtsverletzung. Sowohl RT als auch NIL stehen unter der GPL3. Ob des viralen Charakters der Lizenz müssen Sie den kompletten Code Ihrer Applikation offenlegen.

Zur Umgehung dieses Problems bieten sich zwei Lizenzschemata an. Bei Produkten mit vergleichsweise geringer Gesamtproduktionsmenge könnte die Free-Commercial-Lizenz ausreichen: Nach schriftlichem Antrag erlaubt der Entwickler die Nutzung seines Betriebssystems auf bis zu 500 Geräten. Für größere Serien ist eine kommerzielle Lizenz erforderlich. Die Preise richten sich hier normalerweise nach dem Verhandlungsgeschick des diensthabenden Juristen.

Fazit

Die Welt ist voller Echtzeitbetriebssysteme: Wer Google nach RTOS befragt, wird mit Dutzenden von Optionen versorgt. Wie in so vielen anderen Bereichen der Informationstechnik gilt auch hier das Prinzip des Analogieschlusses: Wer mit einem RTOS umgehen kann, hat beim Umstieg auf eine andere Plattform weniger Lernaufwand.

Unsere Reise durch die Welt der Mikrocontrollerprogrammierung ist damit noch nicht zu Ende. Im nächsten Teil der Serie werden wir das im Moment brachliegende LCD unseres Evaluationsboards abermals zum Leben erwecken und dabei mehr über GUI-Stacks lernen. Bis dahin wünscht der Autor viel Spaß bei der Erstellung komplexer, echtzeitbetriebssystembasierter Embedded-Systeme.

Geschrieben von
Tam Hanna
Tam Hanna
Tam Hanna befasst sich seit der Zeit des Palm IIIc mit der Programmierung und Anwendung von Handcomputern. Er entwickelt Programme für diverse Plattformen, betreibt Onlinenewsdienste zum Thema und steht unter tamhan@tamoggemon.com für Fragen, Trainings und Vorträge gern zur Verfügung.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.