Anbindung eingebetteter Sensoren: Abfrage von ADC-Treibern

Wie Embedded-Entwicklungsingenieure eine Schnittstelle in modernen Embedded-Anwendungen erstellen können, die Details zur Treiberimplementierung auf niedriger Ebene vom Anwendungscode trennt. Diese Schnittstelle bietet eine Architekturabstraktion, die die Skalierbarkeit und Portabilität von Anwendungscode verbessert, indem sie die Abhängigkeit von Hardware verringert.

Wir werden uns nun mit einigen verschiedenen Möglichkeiten befassen, wie Embedded-Entwicklungsingenieure ADC-Treiber implementieren können, basierend auf den Techniken, die in 3 Techniken zum Entwerfen von Mikrocontroller-Treibern besprochen werden. In diesem Artikel werden wir die Verwendung von Abfragetechniken genauer untersuchen und die Unterschiede zwischen blockierenden und nicht blockierenden Treibern diskutieren.

Blockieren oder nicht blockieren, das ist hier die Frage

Bei der Entwicklung eines Treibers für einen Mikrocontroller müssen Embedded-Entwicklungsingenieure entscheiden, ob ihr Treiber blockierend oder nicht blockierend sein soll. Ein blockierender Treiber unterbricht im Wesentlichen die Codeausführung, bis der Treiber seine Aufgabe abgeschlossen hat. Beispielsweise ist eine typische Implementierung von printf, die einem UART zugeordnet ist, blockierend.

Wenn Sie beispielsweise einen Anruf tätigen:

printf(„Hallo Welt!“);

Embedded-Entwicklungsingenieure wissen, dass keine der Codezeilen nach dieser Anweisung ausgeführt wird, bis die gesamte „Hello World!“-Anweisung erscheint. Die Anweisung wurde aus dem UART ausgedruckt. „Hello, World!“ enthält 12 Bytes (96 Bits), aber wie lange die Anweisung blockiert, hängt von der UART-Baudrate ab. Für einen UART, der für 1 Mbit/s konfiguriert ist, würden Sie etwa 96 Mikrosekunden erwarten. Für einen UART, der für 9600 Bit/s konfiguriert ist, würden Sie etwa 10.000 Mikrosekunden erwarten! Dies variiert stark je nach Konfiguration der Hardware und kann in Fällen, in denen der UART-Treiber als blockierender Treiber konfiguriert ist, die Programmausführung stark beeinträchtigen.

Ein nicht blockierender Treiber ist ein Treiber, der die Programmausführung nicht stoppt, während der Treiber seine Aufgabe erledigt. Beispielsweise können die printf- und UART-Treiber im vorherigen Beispiel so konfiguriert werden, dass die Anwendung nicht blockiert, sondern weiterhin ausgeführt werden kann, während jedes Byte aus dem UART übertragen wird. Unter den richtigen Umständen kann dies die Anwendung effizienter machen, erfordert jedoch zusätzliche Einstellungen, z. B. die Verwendung von Interrupts, DMA oder zumindest eines Sendepuffers.

Die Entscheidung, wie Sie Ihren Treiber entwerfen, hängt von Ihrer Anwendung und Hardware ab. Wenn der UART beispielsweise für 1 Mbit/s konfiguriert ist, bringt das Schreiben eines nicht blockierenden Treibers unter Effizienzgesichtspunkten möglicherweise keinen großen Vorteil und kann tatsächlich mehr Probleme verursachen, als durch eine Erhöhung der Programmkomplexität zu lösen. Wenn die Anwendung jedoch 9600 Bit/s erfordert und der Anwendungscode 10 ms lang blockiert ist, kann die Verwendung eines nicht blockierenden Treibers die Programmeffizienz erheblich verbessern, und das Risiko anderer Probleme mit der Zeitkomplexität ist viel geringer und besser beherrschbar.

Übersicht über den eingebetteten ADC-Treiber

Es ist wichtig zu beachten, dass ich nicht alle Schritte, die zum Schreiben eines vollständigen ADC-Treibers erforderlich sind, in einem Blog durchgehen kann. Ich könnte leicht einen 20-seitigen Aufsatz darüber schreiben oder ein ganzes Webinar halten, wahrscheinlich nicht alle Details, aber wir können uns zumindest einige der Kernthemen ansehen.

Es gibt verschiedene Möglichkeiten, ADC-Treiber zu organisieren, aber die Art und Weise, wie ich sie gerne organisiere, erfordert drei Komponenten:

Low-Level-Treiber

Anwendungscode

Konfigurationsmodul

Der Low-Level-Treiber ruft das Konfigurationsmodul während der Initialisierung ab und richtet die Hardware entsprechend der Konfiguration ein. Low-Level-Treiber stellen eine gemeinsame Hardware-Abstraktionsschicht (HAL) bereit, die dann vom Anwendungscode verwendet werden kann. ADC-HAL-Aufrufe sollten generisch sein, damit High-Level-Anwendungen die Hardware auf jede erforderliche Weise konfigurieren und sie wiederverwendbar und skalierbar machen können. Zu den ADC-HAL-Aufrufen, die ich in der Vergangenheit verwendet habe, gehören beispielsweise:

AdcError_t Adc_Init(const AdcConfig_t * Config);

AdcError_t Adc_StartConversion(void);

bool Adc_ConversionComplete(void);

void Adc_RegisterWrite(uint32_t const Address,uint32_t const Value);

uint32_t Adc_RegisterRead(uint32_t Adresse);

void Adc_CallbackRegister(AdcCallback_t const Function,TYPE(* CallbackFunction)(type));

Die ersten drei APIs bieten die folgenden Funktionen: ADC-Hardware initialisieren, Konvertierung starten und Konvertierungsstatus prüfen. Die letzten drei Funktionen sollen Erweiterungen auf Low-Level-Hardware ermöglichen. Wenn der HAL beispielsweise keine von der Anwendung benötigten Optionen wie die Konvertierung einzelner ADC-Kanäle bereitstellt, kann der HAL mit den Funktionen Adc_RegisterRead und Adc_RegisterWrite erweitert werden. Dies bietet Flexibilität basierend auf den Anwendungsanforderungen, ohne überwältigende APIs zu erstellen.

Schreiben Sie einen einfachen blockierenden ADC-Treiber

Wir können einen sehr einfachen ADC-Treiber auf die Hardwareschicht schreiben. Beispielsweise können wir eine einfache Funktion namens Adc_Sample erstellen, die die ADC-Hardware startet und dann alle Ergebnisse in einem Puffer speichert, auf den die Anwendung dann zugreifen kann. Ein Puffer, der einen analogen Zählwert speichert, muss nicht nur einen Wert speichern, sondern kann mehrere Werte speichern, die später entsprechend den Anforderungen der Anwendung gemittelt oder gefiltert werden können. Eine blockierende Version der Beispielfunktion könnte wie folgt aussehen:

Wie Sie in diesem Code sehen können, blockiert die While-Schleife die Ausführung, bis die ADC-Hardware ihre Konvertierung abschließt und den Wert dann im Anwendungspuffer speichert.

Schreiben Sie einen einfachen nicht blockierenden ADC-Treiber

Das Konvertieren eines blockierenden Treibers in nicht blockierenden Code ist unkompliziert, erfordert jedoch Änderungen am Anwendungscode einer höheren Ebene. Wenn eine Anwendung heute beispielsweise einen Sensor abtasten möchte, ruft ein Embedded-Entwickler Folgendes auf:

Adc_Sample();

In der nicht blockierenden Version muss der eingebettete Entwickler den Rückgabewert von Adc_Sample überprüfen, um festzustellen, ob das Beispiel vollständig und einsatzbereit ist. Dadurch kann das Beispiel im Hintergrund ausgeführt werden und der Anwendungscode kann mit dem folgenden Update unseres Treibercodes weiter ausgeführt werden:

abschließend

Wie wir in diesem Beitrag gesehen haben, gibt es mehrere Möglichkeiten, einen ADC zu schreiben, und je nach unseren Anforderungen kann die Implementierung blockierend oder nicht blockierend sein. Blockierende Treiber sind in der Regel einfacher und weniger vollständig als nicht blockierende Treiber, aber möglicherweise nicht so effizient. Nicht blockierende Treiber ermöglichen die Ausführung von anderem Code, während der Treiber ausgeführt wird. Der Anwendungscode muss jedoch weiterhin den Status einchecken, was in einer Polling-Implementierung selbst ineffizient wäre.

Das eingebettete Internet der Dinge muss viel lernen. Lernen Sie nicht die falsche Route und den falschen Inhalt, da dies zu einem Anstieg Ihres Gehalts führt!

Teilen Sie ein Datenpaket mit allen, etwa 150 G. Die darin enthaltenen Lerninhalte, persönlichen Schriften und Projekte sind relativ neu und vollständig! (Klicken Sie hier, um einen kleinen Assistenten zu finden, den Sie erhalten möchten)

Supongo que te gusta

Origin blog.csdn.net/m0_70911440/article/details/132160964
Recomendado
Clasificación