Archiv für das Tag 'STM32'

Erster Kontakt mit dem GPS Receiver => NMEA Daten mit dem µC einlesen (NMEADll)

19. März 2012

GPS Receiver stellen ihre aktuellen Daten normalerweise mit Hilfe des NMEA-0183 Protokolls über eine einfache UART Schnittstelle der Außenwelt zur Verfügung. Manche Receiver bieten oft auch noch proprietäre Protokolle mit erweiterten Funktionen zur Verfügung. Auf diese möchte ich aber hier nicht eingehen. Für mich reicht der Standard völlig aus. In diesem Artikel beschreibe ich also jetzt wie man auf unterster Ebene NMEA Datensätze mit einem Mikrocontroller, in meinem Fall ein STM32, einlesen und auf Gültigkeit prüfen kann. In meinem Projekt, dass es auch hier zum Download gibt, heißt das Software Modul „nmeadll.c„.

Struktursicht

Um erst mal einen groben Überblick über die Software zu bekommen ist eine Struktursicht immer ein guter Startpunkt, da man hier sieht wie sich ein Modul in den Rest der Software einfügt. Die Struktursicht zum nmeadll-Modul sieht wie folgt aus:

Wie zu erkennen ist, wird der GPS-Receiver direkt an einen der USARTs des Controllers angeschlossen. Darüber liegt dann noch eine Softwareschicht namens „huart.c“ die eine Abstraktion der Hardware (HAL = hardware abstraction layer), bzw. des USARTs, darstellt. Damit wird die Software leichter portierbar, d.h. man kann einfach auf einen anderen Controller wechseln und muss nur das Modul huart austauschen. Danach sollte alles wieder funktionieren (soweit die Theorie ;-)). Über dem HAL liegt dann auch schon das nmeadll-Modul welches die herinkommenden NMEA Datensätze einliest und auf Gültigkeit prüft. An der Stelle ist wichtig, dass hier nur der Datensatz an sich geprüft wird und nicht dessen Inhalt. Die Überprüfung des Dateninhalts liegt in der Verantwortung des übergeordneten SW-Layers der hier mit „gps“ gekennzeichnet ist und in der Software als Task implementiert wurde. Zu guterletzt ist noch erkennbar, dass der NMEA Data Link Layer noch Services des RTOS und verschiedene Utilities verwendet. Dazu aber später mehr.

Das NMEA-0183 Protokoll

Empfangene NMEA Daten haben immer das folgende Format:

D.h. ein Frame beginnt immer mit einem $-Zeichen gefolgt von den beiden Buchstaben „GP“ was für GPS-Receiver steht. Grundsätzlich können hier auch andere zwei Buchstaben stehen aber bei GPS-Receivern sind es immer die Buchstaben GP. Im Anschluss an diese beiden Buchstaben folgen dann drei weitere Buchstaben die den Datensatz kennzeichnen. Im Beispiel oben wäre das z.B. „GSV“. Hierüber lässt sich der Aufbau des empfangenen Datensatzes ermitteln. Daran anschließend folgen dann jew. durch ein Komma getrennt die einzelnen Daten bis zum nächsten ‚*‘-Zeichen was das Ende des Datenfeldes markiert und das Checksummenfeld abgrenzt. Die Checksumme befindet sich in den folgenden beiden Zeichen. Sie wird durch eine einfache XOR-Verknüpfung über alle Datenbytes inkl. des Identifiers gebildet (s.o.). Das Ergebnis wird abschließend noch in einen String konvertiert, d.h. jedes Nibble des Bytes wird in einen Character umgewandelt sodass das Ergebnis wieder direkt lesbar ist. Abgeschlossen wird jeder NMEA Frame mit den beiden nicht lesbaren Zeichen 0x0D und 0x0A, d.h. <CR>+<LF>. Ein NMEA Datensatz kann so inkl. aller Zeichen maximal 82 Zeichen lang sein.

Wie am obigen Beispiel zu sehen ist, lässt sich ein Datensatz auch sehr gut mit Hilfe eines Terminal-Programms auswerten. Das hat mir oft auch sehr bei der Implementierung geholfen.

Gundfunktionen

Der NMEA Data Link Layer stell die folgenden Funktionen zur Verfügung:

  • Einlesen der NMEA Daten in den internen Speicher
  • Prüfen der NMEA Daten auf Gültigkeit
    • Synchronisation auf einen laufenden Datenstrom
    • Formatprüfung
    • Checksumme
  • Verwerfen von ungültigen Datensätzen
  • Speichern von mehreren gültigen Datensätzen bis zur Verarbeitung
  • Bereitstellen der Datensätze für die höher liegende Softwareschicht

Interface

Das Interface des Data Link Layers ist in der folgenden Abbildung dargestellt:

Wie zu erkennen ist, besteht die Schnittstelle zur höheren SW-Schicht aus nur vier Funktionen. Zunächst muss der Data Link Layer mit der Funktion NMEAD_vInitNMEADll() intialisiert werden. Hier werden dann alle internen Datenstruktureninitialisiert, der USART konfiguriert und ein Interrupt Handler installiert (USART Receive Interrupt) in dem das Protokoll verarbeitet wird. Diese Funktion heißt NMEAD_vNMEAProtcolRxEvent(). Auf Applkationsebene spielt sich dann alles mit Hilfe der Funktionen NMEAD_pucGetNMEAFrame() und NMEAD_vReleaseFrame() ab. Über GetFrame kann ein neuer NMEA Datensatz angefordert werden. Hier ist besonders, dass diese Funktion die aufrufende Funktion so lange blockiert bis ein neuer Datensatz empfangen wurde (siehe auch hier). Mit ReleaseFrame wird der Speicher eines Datensatzes wieder freigegeben und kann somit wieder mit neuen Daten überschrieben werden.

Einbindung in das RTOS

Die Kommunikation mit der höheren Softwareschicht ist in der folgenden Abbildung dargestellt. Hier ist auch erkennbar, wie das Betriebssystem in die Kommunikation eingebunden ist.

In obiger Abbildung ist erkennbar, dass jedes empfangene Byte vom GPS-Receiver einen Receive-Interrupt des USARTs auslöst. In diesem Interrupt (Funktion NMEAD_vNMEAProtcolRxEvent()) wird das NMEA Protokoll verarbeitet und die Daten gespeichert. Sollte ein kompletter Frame empfangen worden sein, so posted der Interrupt Handler eine Semaphore die also mit jedem vollständig empfangenen Frame um eins inkrementiert wird. Der GPS Task kann nun auf Task-Ebene die Funktion NMEAD_pucGetNMEAFrame() aufrufen. Diese Funktion fragt wiederum die Semaphore ab und blockiert so lange bis der Interrupt Handler einen neuen Datensatz eingelesen hat (Semaphore Post). Wurde ein Datensatz empfangen, so wird der Task fortgesetzt. Mit diesem Konzept ist somit sichergestellt, dass nur dann Rechenleistung verwendet wird, wenn auch Daten zu verarbeiten sind.

Pufferkonzept

Die folgende Abbildung zeigt das Pufferkonzept des Data Link Layers:

Die NMEA Daten werden im internen Speicher in Form eines Ringpuffers gespeichert. Der Ringpuffer ist dabei Datensatzweise organisiert, d.h. jedes Ringelement kann einen kompletten, bis zu 82 Zeichen langen, NMEA Datensatz speichern. Dieses Konzept ist zwar nicht ganz speicherschonend aber es ermöglicht eine einfache Verarbeitung auf der Protokollebene als auch auf der höher liegenden Verarbeitungsebene, da hier die Daten einfach in einem linearen Array liegen. Es muss also nicht speziell auf Speicherumbrüche geachtet werden.

Protokoll Statemachine

Die Statemachine des Protokollhandlers ist wie folgt organisiert:

Die Statemachine besitzt als Entry-State den Zustand Synch. Dieser Zustand synchronisiert den Protokoll-Handler also zunächst auf einen laufenden Datenstrom indem er auf das Ende eines NMEA Datensatzes wartet und bis dahin alle empfangenen Zeichen verwirft. Nach der Synchronisation wird in den Idle-State gewechselt wo der Automat auf einen neuen Datensatz wartet der immer mit dem ‚$‘-Zeichen beginnen muss. Wurde ein Datensatz erkannt, so wird dieser im State ReadFrame eingelesen und gespeichert. Das Datenfeld endet mit dem ‚*‘-Zeichen. Wird dieses Zeichen empfangen, so wechselt der Automat in den State WaitEOF wo die Checksumme ausgewertet wird und noch die letzten Zeichen (<CR> + <LF>) eingelesen werden. Weiterhin gibt es immer noch den direkten Wechsel zum Synch-Zustand, der immer dann durchgeführt wird wenn die maximale Frame-Länge überschritten wird.

Fehlerbehandlung

Grundsätzlich muss mit den folgenden Fehlern gerechnet werden:

  • Falsches Frame-Format, d.h.
    • Ein Frame beginnt nicht mit dem ‚$‘-Zeichen
    • Das Datenfeld eines Frames endet nicht mit dem ‚*‘-Zeichen
    • Die Checksumme stimmt nicht
    • Der Frame endet nicht mit <CR> + <LF>
  • Es wird auf einen laufenden Datenstrom geschaltet
  • Ein laufender Datenstrom wird unterbrochen

Das Frameformat wird von Data Link Layer komplett auf Interrupt-Ebene geprüft. Sollte das Format eines Frames nicht passen, so synchronisiert sich der Protokoll-Handler wieder neu auf den Datenstrom indem er auf das <LF>-Zeichen wartet. Auch das Aufschalten auf einen laufenden Datenstrom wird über die Synchronisation erschlagen. Ein abreißender Datenstrom wird von Protokoll Handler nicht direkt abgefangen. Sollte dieser Fall jedoch eintreten, so sollte generell die Formatprüfung, speziell die Checksummenprüfung, fehlschlagen und somit eine Neusynchronisation erzwungen werden. Alle Fehler die bei der Formatprüfung erkannt werden führen dazu, dass ein eingelesener Datensatz verworfen wird.

Erstes Release meines SportTrackers

14. März 2012

Heute habe ich mich entschieden schon mal ein wenig von meinem Source Code für den SportTracker zu veröffentlichen. Das ganze ist zwar derzeit noch nicht wirklich fertig, d.h. es läuft alles nur im Debugger ohne eine richtige Benutzerschnittstelle, aber es sind schon einige Funktionen realisiert. Das sind z.B.:

  • Parsen der NMEA Frames eines GPS Receivers
    • Extrahieren der aktuellen Position (Lat/Lon)
    • Extrahieren der aktuellen Zeit sowie des Datums
    • Extrahieren der Höhe
  • Ansteuerung einer SD Card über das SDIO Interface
  • Integration eines FAT File Systems (ElmChans FatFS)
  • Einfache Tracker Funktion mit Speicherung eines Tracks auf einer SD Karte im GPX-Format
  • Tastenentprellung

Wie gesagt läuft das Ganze noch nicht zusammen und ist derzeit auch noch sehr schlecht dokumentiert. Das Konzept des NMEA Parsers habe ich dabei an meinem alten Code für den AVR angelehnt.

Zu finden ist der Code hier:  SportTracker Code

Viel Spaß damit…

µC/OS-II Port für den Cortex-M3

4. September 2011

Wie ich in meinem letzten Artikel schon geschrieben habe möchte ich mit meinem SportTracker Projekt einige für mich neue Dinge ausprobieren. Unter anderem möchte ich auch mal ein wenig mit Echtzeitbetriebssystemen experimentieren. Das Betriebssystem meiner Wahl ist hierbei das uC/OS-II von Micrium. Jetzt höhre ich natürlich schon die ersten Stimmen: „Warum gerade das?“, „Wieso so ein kommerzieller Mist? FreeRTOS ist doch frei und kostenlos“, usw., usw.. Die Argumente sind wahrscheinlich auch zum großen Teil richtig, aber…

Ich habe mich für uC/OS-II entschieden, weil ich die Dokumentation des Betriebssystems und auch den Code des Systems sehr gut finde. D.h. ich habe mir schon vor einiger Zeit das Buch von Herrn Labrosse gekauft und ich finde, dass er die Grundfunktionen von Embedded Betriebssystemen wirklich gut erklärt. Ähnliches gilt natürlich auch für FreeRTOS, aber aus meiner Sicht ist uC/OS-II verständlicher und übersichtlicher geschrieben.

Nachdem ich mich also für uC/OS-II entschieden hatte war es nun in einem nächsten Schritt natürlich wichtig, dass es auch eine Portierung für den von mir geplanten Controller, den STM32, sowie meine Entwicklungsumgebung, Crossworks for ARM, gibt. Und da waren sie auch schon wieder, meine Probleme. Auf der Micrium Seite gab es natürlich nur Ports für die kommerziellen Toolchains von Keil und IAR und nicht für den GCC. Es blieb mir also nichts anderes übrig, als eine Anpassung für den GCC zu schreiben. Diese Anpassung gibts jetzt hier zum herunterladen. Basis meines Ports für den GCC Compiler war ein Port für die Toolchain von Keil. Im Prinzip habe ich auch nur die Asssembler Datei „os_cpu_a.asm“ entsprechend anpassen müssen. Wobei sich die Anpassungen nicht auf den Code an sich bezogen, sondern vielmehr auf die Anpassung der generellen Struktur der Datei. D.h. die Schlüsselwörter zur Definition von Sections, externen Variablen und Funktionen unterscheiden sich z.B. geringfügig zwischen den beiden Toolchains. Ein wichtiger Punkt bei der Portierung war dann noch, dass der GCC zu Beginn einige ASM-Befehle des Ports nicht kannte obwohl diese eindeutig im STM32 Programming Manual definiert waren. Dieses Problem konnte ich dann aber nach ein wenig googlen lösen. Beim GCC ist es nämlich erforderlich die Schlüsselworte „.syntax unified“ voranzustellen damit der Assembler sich nicht verrennt. Ein Glück das die meisten Probleme schonmal ein anderer hatte :-).

Also dann, zu guter letzt fehlt nur noch mein Port für den GCC:

uCOS_Port_GCC_STM32

Viel Spass damit…

PS: Bevor ich’s noch vergesse. Der GCC ist auch für mich noch eine neue Toolchain. Es ist also möglich, dass noch Fehler im Code enthalten sind. Die Grundfunktionen habe ich zwar getestet aber die Probleme stecke ja bekanntlich im Detail. Wer also einen Fehler findet, kann ihn mir gerne melden :-). Einen Finderlohn kann ich leider nicht zahlen ….

Elektronik + Laufen = Spaß^2

1. September 2011

Häh? Was meint er denn damit schon wieder? Ganz einfach:

Wenn man zwei Hobbies miteinander kombinieren kann ist das auf jeden Fall eine gute Sache, sprich „Spaß hoch zwei“.

Genau das habe ich mit meinem kommenden Bastelprojekt vor. Und zwar möchte ich einen Sport Tracker bauen. Was genau meine ich damit? Im Prinzip möchte ich einen normalen GPS Tracker bauen, der natürlich wie jeder andere Tracker auch in bestimmten Zeitabständen seine aktuelle Position speichert. Ergänzen möchte ich das Ganze jedoch um verschiedene Funktionen die mich bei meinem Lauftraining unterstützen. Das sind zum Beispiel Funktionen wie:

  • Pulsmesser
  • Angabe von Pulsbereichen
  • Sonderfunktionen für Intervalltrainings, Tempoläufe und lange Läufe
  • Verbindung zum PC
  • gpx Unterstützung
  • GPS Mausfunktion
  • usw.

Jetzt kann man natürlich sagen das man das alles schon fix und fertig kaufen kann. Stimmt! Mein Garmin Forerunner 305 leistet mir jetzt auch schon über zwei Jahre gute Dienste. Aber die Sinnfrage macht besonders bei Hobbyprojekten nur selten Sinn ;-). Insgesamt geht es mir viel mehr darum bestimmte Dinge einfach mal auszuprobieren. Besonders mit einem Tracker gibt es hierzu besonders vielfältige Möglichkeiten. Also dann, auf los gehts…