Den lwIP SNTP-Client mit ChibiOS verwenden

English Deutsch

Eine häufige Aufgabe bei eingebetteten Systemen ist die Verwendung der RTC zum Zeitstempeln von Ereignissen. Der Systemarchitekt muss jedoch einen Weg finden, die RTC-Zeit des Geräts mit einer externen Zeitquelle zu synchronisieren. Zusätzlich muss der Designer das Problem des Drifts von RTC-Uhren berücksichtigen, insbesondere bei langlebigen Geräten. Dieser Artikel diskutiert einen lwIP+SNTP-basierten Ansatz für STM32-Geräte mit dem ChibiOS-RTOS. Der lwIP-spezifische Teil dieses Artikels ist auch auf andere Mikrocontroller-Typen anwendbar.

Für hochpräzise oder langlebige Anwendungen muss auch der RTC-Uhren-Drift berücksichtigt werden. Je nach verwendeter Taktquelle kann die Taktfrequenz signifikant vom Nennwert abweichen.

Beim STM32F4 beispielsweise kann der RTC-Takt abgeleitet werden von: Dem HSE/HSI-Hauptoszillator Dem LSI-Oszillator * Dem LSE-Oszillator, d.h. einem 32,768 kHz externen Quarz.

In den meisten Anwendungen wird die RTC hauptsächlich verwendet, um eine gültige Zeit auch während Zeiträumen zu erhalten, in denen die primäre Stromversorgung nicht verfügbar ist. Aus diesem Grund enthalten diese Systeme eine Art Backup-Batterie (normalerweise eine CR2032), die die RTC betreibt. In diesem Fall kann man den RTC-Takt offensichtlich nicht von HSE/HSI ableiten, da diese Takte nicht aktiv sind, wenn die primäre Stromquelle nicht verfügbar ist.

Der LSI-Oszillator ist im Gerät kostenlos enthalten, hat jedoch ein Hauptproblem, dessen sich die meisten Benutzer nicht bewusst sind: Er hat exorbitante Toleranzwerte: Laut dem STM32F407-Datenblatt (Tabelle 34: LSI oscillator characteristics) reicht die tatsächliche Quarzfrequenz von 17 bis 47 kHz bei einem Nennwert von 32 kHz. Dies entspricht einer Toleranz von -53,1 / +46,8 Prozentpunkten oder mehr als 460000 ppm. Ich kann mir keine reale Anwendung vorstellen, die mit dieser Größenordnung der Toleranz umgehen kann. Obwohl die angegebene Toleranz über den gesamten Temperaturbereich von -40 bis +105° gilt, kann ich bestätigen, dass sie bei Raumtemperatur höher als +-25% ist.

Der LSE-Oszillator ist die einzige verbleibende Option. Denke also daran, einen 32,768 kHz-Quarz in dein Design einzubauen. Für diesen speziellen Verwendungszweck glaube ich, dass Quarzoszillatoren aufgrund ihres relativ hohen Stromverbrauchs nicht die richtige Wahl sind.

Es bleiben einige Hauptprobleme: Wie man anfänglich den aktuellen Zeitstempel auf das Gerät bekommt (hängt vom Bereitstellungsprozess ab und ist normalerweise kein Problem) und wie man das Gerät mit einer externen Taktquelle synchronisiert hält.

Letzteres ist besonders relevant, wenn mehrere Geräte synchron gehalten werden müssen und/oder eine nahezu monotone Taktquelle erforderlich ist. Selbst mit nur einem Prozent Uhren-Drift wird die RTC-Datumszeit über ein Jahr um mehr als drei Tage abweichen.

Für Anwendungen, die mit dem Internet oder einem anderen Computernetzwerk verbunden sind, bietet das lwIP contrib-Repository einen vollständigen SNTP-Client. SNTP ist eine weniger genaue Version des NTP-Protokolls, das dennoch mit jedem normalen NTP-Server verwendet werden kann.

Der Bibliothek fehlt jedoch die grundlegendste Dokumentation. Daher stelle ich ein Beispiel zur Verfügung, wie sie in Verbindung mit dem ChibiOS RTCD1-Treiber verwendet wird.

Der hier gezeigte Header ersetzt den sntp.h-Header, der mit der Bibliothek geliefert wird. Er enthält die Compile-Time-Konfiguration, die für die Arbeit mit dem RTCD1-Treiber erforderlich ist. Für dieses Beispiel habe ich den NTP-Server swisstime.ethz.ch gewählt, weil er von meinem Standort einen guten Ping hat. Ich empfehle, einen geeigneten Server aus dem NTP-Pool auszuwählen.

sntp_config.h
#ifndef __SNTP_H__
#define __SNTP_H__

#ifdef __cplusplus
extern "C" {
#endif

//Benutzerdefinierte Konfiguration
#include <hal.h>
#include <chrtclib.h>
#define SNTP_SERVER_ADDRESS "82.197.164.46" /* swisstime.ethz.ch */
#define SNTP_UPDATE_DELAY 90000 /* SNTP update every 90 seconds */
//ChibiOS RTC-Treiber
#define SNTP_SET_SYSTEM_TIME(sec) rtcSetTimeUnixSec(&RTCD1, (sec))
#define SNTP_GET_SYSTEM_TIME(sec, us) \
    do{uint64_t time = rtcGetTimeUnixUsec(&RTCD1);\
       (sec) = time / 1000000;\
       (us) = time % 1000000;}while(0)

void sntp_init(void);
void sntp_stop(void);

#ifdef __cplusplus
}
#endif

#endif /* __SNTP_H__ */

/*
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Simon Goldschmidt (lwIP raw API part)
 * Modified by Uli Koehler for ChibiOS (2014)
 */

Die Konfiguration ist recht einfach und sntp.c unterstützt einige recht nützliche Features, die hier nicht konfiguriert sind – vor allem die Unterstützung für mehrere NTP-Server.

Der zweite Schritt ist die ordnungsgemäße Konfiguration von lwIP. Dies ist kein Tutorial zu lwIP und ich nehme an, der Leser weiß bereits, wie der Stack funktioniert (wenn nicht, ist dieser Artikel wahrscheinlich nicht der beste Ausgangspunkt). Um Probleme mit signifikanten Abweichungen von idealen monotonen Uhren zu vermeiden, sendet dieses Beispiel alle 90 Sekunden SNTP-Anfragen und überschreibt damit den Standard von 60 Minuten. Welche besser geeignet ist, hängt vollständig von deiner Anwendung ab.

Es gibt zwei häufige Fallstricke, die auftreten können, beide befinden sich im lwipopts.h-Header:

Erstens verbraucht der SNTP-Client dauerhaft ein UDP-PCB. Stelle sicher, dass der MEMP_NUM_UDP_PCB-Wert entsprechend erhöht wird. Selbstverständlich musst du UDP aktivieren, damit SNTP funktioniert.

Zweitens ruft der SNTP-Client die Sendefunktion wiederholt über die lwIP sys_timeout()-Funktion auf. Die Standardoptionen für die Anzahl der unterstützten gleichzeitigen Timeouts sind recht begrenzt und werden wahrscheinlich nicht ausreichen. Ich empfehle, den MEMP_NUM_SYS_TIMEOUT-Wert um mindestens eins zu erhöhen.

Sobald diese Anforderungen erfüllt sind, rufe einfach

sntp_init_call.c
sntp_init();

nach der Initialisierung des lwIP-Threads und der Event-Schleife auf. Wenn deine Konfiguration korrekt ist, wird die RTC aktualisiert, wenn die SNTP-Antworten empfangen werden. NICHT sntp_init() wiederholt aufrufen, es ruft die Sendefunktion selbstständig über die lwIP-Event-Schleife auf.

Beachte, dass STM32F4-Geräte eine RTC mit Millisekunden-Genauigkeit haben. SNTP wird nur selten eine Genauigkeit von besser als 1 Millisekunde bieten. Daher ist der Millisekunden-RTC-Wert möglicherweise nicht signifikant.

Wenn die Anwendung abstürzt, ist es wahrscheinlich, dass einige lwIP-Limits überschritten wurden. In diesem Fall siehe diesen vorherigen TechOverflow-Artikel mit einer detaillierten Beschreibung, wie man ARM-Hardware-Breakpoints zu ChibiOS und LWIP-Assertions hinzufügt.


Check out similar posts by category: C/C++, Embedded