www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Modulares Firmware-update möglich?


Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,
es gibt ja die Möglichkeit ein Firmware-Update mittels Bootloader 
durchzuführen. Dabei wird aber die Firmware komplett neu (bis auf den 
Bootloader) in den internen Flash geschrieben.

Ist es möglich Firmware auch modular im internen Flash auszutauschen?
Ich stelle mir das Ganze so vor:
-Hauptmodul (enthält Firmware-Updater)
-Tabelle mit Funktionszeigern
-Funktionsmodul 1
-Funktionsmodul 2
-Datenmodul 1
-Datenmodul 2
-Bootloader

Die Funktions- und Datenmodule liegen mit ausreichend Platz 
zwischeneinander (für Änderungen der Module) im Flash an festen 
Adressen. Die Adressen werden im Linkerskript festgelegt.
Alle Funktionen in den Funktionsmodulen können mittels Funktionszeigern 
vom Hauptmodul aufgerufen werden. Funktionsaufrufe innerhalb der 
Funktionsmodule sollten ohne Funktionszeiger funktionieren.

Wenn ein Funktions- oder Datenmodul ausgetauscht werden soll, dann 
befindet sich das Hauptmodul in der Firmware-Update-Funktion.
Diese überschreibt den internen Flash mit dem neuen Modul aus dem 
externen Flash. Danach wird ein Software-Reset ausgeführt um 
Inkonsistenzen zu verhindern.

Ich habe bisher nirgends ein Konzept für so etwas gefunden. Kann ich 
also davon ausgehen, dass ein modulares Firmware-Update aufgrund 
bestimmter Dinge unpraktikabel bzw. unmöglich ist?

Antworten die über ein "geht"/ "geht nicht" hinausgehen würde ich toll 
finden. Ich denke dass eine evtl. angestossene Diskussion auch Anderen 
ein besseres Verständnis über die Funktionsweise von Mikrocontrollern 
geben würde.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Geht schon, macht im allgemeinen nur keinen Sinn, einzelne Module 
auszutauschen. Der Aufwand dafür ist viel höher, als wenn die ganze 
Software neu geflasht werden soll.
Warum baust du die einzelnen Module nicht am PC in der benötigten 
Kombination zusammen und flashst genau diese? ...dann brauchst du auch 
keinen Freiraum zwischen den Modulen auszusparen, sparst dir die 
Funktionszeigertabelle, es gibt keine Inkonsistenzen... Damit sind alle 
deine Nachteile ausgeräumt. :-)

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian B. wrote:

> Antworten die über ein "geht"/ "geht nicht" hinausgehen würde ich toll
> finden.

Ich hätte erstmal ne Frage: was soll das überhaupt bringen?

Außer nem Haufen Nachteilen sehe ich keinerlei Vorteile.
Funktionen können nicht mehr direkt aufgerufen werden, sondern über 
einen Dispatcher.
Variablen können nicht mehr direkt angelegt werden, sondern nur über 
malloc (mit der Möglichkeit des Fehlschlags!).

Es kostet also mehr SRAM, Flash und CPU-Zeit.
Von den Huddeleien mit dem Linker ganz zu schweigen.


Peter

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter:
Peter Dannegger wrote:
> Ich hätte erstmal ne Frage: was soll das überhaupt bringen?
> Außer nem Haufen Nachteilen sehe ich keinerlei Vorteile.

Vorteile aus meiner Sicht:
-die Zeit bis das System wieder normal reagieren kann wird verkürzt
-ich hätte immer ein funktionierendes System, wenn ich das alte 
Funktionsmodul vor dem Kopiervorgang irgendwo temporär speichere
-die Menge der zu übertragenenen Daten wird reduziert, was einen schon 
evtl. stark belasteten Bus entlastet,
-wenn beispielsweise das Update über GPRS (packetorientiert) für viele 
tausend Geräte gemacht wird kann das auch ein paar Taler sparen und die 
Server-Seite entlasten
(ca. 1 cent/ 1 kB, anstelle von 200 kB nur 20 kB Code übertragen macht 
eine Einsparung von 1,80 EUR/ Update)

> Funktionen können nicht mehr direkt aufgerufen werden, sondern über
> einen Dispatcher.

Ich schätze mal, dass Du Deine Funktion "u8 execute(u8 func_no)" aus dem 
Thread "Absolute Funktionsadressen in Tabelle ablegen" (Nr. 66866, 
Posting am 11.04.2007) als Dispatcher bezeichnest.
Das es pro Funktionsmodul so eine "Dispatcher"- Funktion geben muß 
leuchtet mir ein.
Wenn ich aus dem Hauptmodul eine Funktion in einem Funktionsmodul 
aufrufen möchte, dann rufe ich die azugehörige "Dispatcher"-Funktion mit 
dem Argument "Index von Funktions-Adress-Array's" auf, welches 
beispielsweise die Funktion Read_XYZ im Funktionsmodul adressiert.
D.h. der Dispatcher hat ein Array in dem die Adressen der Funktionen 
stehen die von außerhalb aufgerufen werden sollen.
Richtig so?

> Variablen können nicht mehr direkt angelegt werden, sondern nur über
> malloc (mit der Möglichkeit des Fehlschlags!).

Ich stimme auch überein, dass man Variablen nur noch mit malloc() 
anlegen darf, da bei jedem neuen "Make" die Variablen vermutlich neue
Adressen im RAM erhalten. Die alten Module konnen also 
Speicherkollisionen mit den neuen Modulen haben.

> Von den Huddeleien mit dem Linker ganz zu schweigen.

Da sehe ich auch Probleme:
1)
Kann ich dem Linker beibringen, dass die Dispatcher-Funktion von Modul 1 
an Adresse X im Flash liegen soll und die restlichen Funktionen von 
Modul 1 darauf folgen sollen?
Ich weiß nur dass man dem Linker sagen kann wo Code und wo Daten im 
Flash liegen sollen.
2)
Können lokale Funktionen innerhalb eines Funktionsmoduls einander ohne 
Pointer aufrufen? Da sollte es doch keine Probleme geben oder?

@Gast:
Gast wrote:
> Warum baust du die einzelnen Module nicht am PC in der benötigten
> Kombination zusammen und flashst genau diese?

Ich müßte doch trotzdem immer noch den kompletten Code neu flashen. Oder 
habe ich das falsch verstanden?

Bitte erschlagt mich nicht, wenn ich etwas übersehen oder falsch 
verstanden habe ;-)

Gruß
Christian

Autor: Jörg B. (manos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da Du anscheinend C verwenden willst, erzeugt man dadurch nicht für 
jedes Modul redundanten Overhead?

Autor: Norgan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Vorteile aus meiner Sicht:
> -die Zeit bis das System wieder normal reagieren kann wird verkürzt

Um wieviele Bruchteile von Sekunden?

> -ich hätte immer ein funktionierendes System, wenn ich das alte
> Funktionsmodul vor dem Kopiervorgang irgendwo temporär speichere

Nein, wenn du deine Sprungtabelle beim Update aufmischst nützt dir das 
alte gespeicherte Modul auch nichts mehr.

> -die Menge der zu übertragenenen Daten wird reduziert, was einen schon
> evtl. stark belasteten Bus entlastet,

Wenn du dir eine Unterbrechung durch ein Firmware-Update auf dem System 
nicht leisten kannst und Auszeiten so kritisch sind, dann denk mal 
drüber nach das gesamte System doppelt auszulegen. In einer 
Aktiv/Passiv- oder Aktiv/Aktiv-Konfiguration. Das ist viel Arbeit, aber 
immer noch besser als im Flash rumzupopeln.

> -wenn beispielsweise das Update über GPRS (packetorientiert) für viele
> tausend Geräte gemacht wird kann das auch ein paar Taler sparen und die
> Server-Seite entlasten
> (ca. 1 cent/ 1 kB, anstelle von 200 kB nur 20 kB Code übertragen macht
> eine Einsparung von 1,80 EUR/ Update)

Bessere Mobilfunk-Konditionen aushandeln. Gibt es für Großkunden.

Tausend Geräte a 200kb macht 200 MB. Das ist nicht die Welt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht solltest Du erstmal sagen, um welche CPU und um welche 
Codegrößen es sich überhaupt handelt.


Bei mir geht es z.B. um 8051/AVR mit 2kB..32kB Code.
Und da lade ich lieber alles komplett neu.


Man muß auch sehen, ob sich der zusätzliche Entwicklungsaufwand und die 
warscheinlich höhere Fehleranfälligkeit überhaupt rechnet.
Je komplexer ein System, umso mehr Fehler kann man ja beim Entwickeln 
machen.
Man sieht es ja sehr schön, wie es bei Windows an allen Ecken und Enden 
kracht und knirscht, nur weil unterschiedliche Programme 
unterschiedliche Versionen von irgendwelchen DLLs benötigen.
Dieses blöde DLL-Zeugs ist ja quasi so ein modulares Konzept (leider 
kein gutes).


Peter

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Norgan wrote:
> Nein, wenn du deine Sprungtabelle beim Update aufmischst nützt dir das
> alte gespeicherte Modul auch nichts mehr.

Ich hab eher daran gedacht, dass jedes Modul eine eigene Sprungtabelle 
hat und damit die Sprungtabelle vom alten Modul weiterhin stimmt (wenn 
ich das alte Modul wieder zurückschreibe wenn das neue Modul nicht 
funktioniert).

> Tausend Geräte a 200kb macht 200 MB. Das ist nicht die Welt.

Ok, die eine Seite ist dass man das Verhältnis Nutzen/Aufwand abschätzen 
muß...sehe ich ein.
Mich würde aber trotzdem brennend interessieren ob der oben umrissene 
Update-Mechnismus funktionieren kann. Um eben auch eigene Denkfehler zum 
Zusammenspiel Hard-/ Software bei uC's zu erkennen.

Gruß
Christian

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> Vielleicht solltest Du erstmal sagen, um welche CPU und um welche
> Codegrößen es sich überhaupt handelt.

Bei mir handelt es sich um einen Controller mit ARM7-Core, 512k Flash 
und 32k SRAM (+externer SDRAM und Flash). Bei dem internen Flash wird es 
langsam knapp, deshalb der externe Flash für Daten.

Jetzt kann man sofort sagen, bei soviel externem Flash und SDRAM sollte 
es Null Probleme geben ein sicheres komplettes Firmware-Update 
durchzuführen.
Anregungen dazu sind auch willkommen. Mich würde aber interessieren ob 
das o.g. Konzept funktionieren kann bzw. was man noch berücksichtigen 
muß.

Gruß Christian

PS: Ich hab schon verstanden, dass es irgendeinen Grund geben muß warum 
alle nur komplett die Firmware ersetzen, aber was ist der wirkliche 
Knackpunkt?

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg B. wrote:
> Da Du anscheinend C verwenden willst, erzeugt man dadurch nicht für
> jedes Modul redundanten Overhead?

Denkst Du da an die hier in Thread so bezeichnete Dispatcher-Funktion?
Oder was meinst Du?

Autor: Jörg B. (manos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich dachte an die in C eingebauten Funktionen zum Rechnen, Anzeigen oder 
ähnlichen die dann möglicherweise in jedem Modul neu vorhanden sind...

Autor: Pete (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Flatrate für Datenübertragung wäre vermutlich günstiger.

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg B. wrote:
> Ich dachte an die in C eingebauten Funktionen zum Rechnen, Anzeigen oder
> ähnlichen die dann möglicherweise in jedem Modul neu vorhanden sind...

Da ist was dran *grins.
Also darf nicht nur das Hauptmodul auf Funktionen in Modulen zugreifen, 
sondern es muß auch Funktionen des einen Moduls gestattet sein auf 
Funktionen in anderen Modulen zuzugreifen (mit den gleichen 
Mechanismen)?

Autor: Norgan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Nein, wenn du deine Sprungtabelle beim Update aufmischst nützt dir das
>> alte gespeicherte Modul auch nichts mehr.
>
> Ich hab eher daran gedacht, dass jedes Modul eine eigene Sprungtabelle
> hat und damit die Sprungtabelle vom alten Modul weiterhin stimmt (wenn
> ich das alte Modul wieder zurückschreibe wenn das neue Modul nicht
> funktioniert).

Du musst an einem Punkt die Sprungtabelle, welche die anderen Module 
benutzen um in das zu ändernde bzw. geänderte Modul einspringen zu 
können, neu schreiben.

Stromausfall mitten drin: Eine Hälfte zeigt schon in das neue Module, 
die andere noch in das alte Modul. Viel Spass. Programmierfehler mitten 
drin? Viel Spass. Inkompatible Module (neues Modul erwartet, dass 
bereits andere neue Module da sind, die sind es aber nicht, geänderte 
Sprungtabelle). Viel Spass.

Ja, auch das kann man mit mehr und trickreicherem Code regeln, z.B. ein 
Transaktions-Log im EEPROM mitschreiben, nur ist das nichts, was man mal 
so aus Jux macht weil es so cool ist. Zu sowas greift man nicht ohne 
Not.

Autor: Εrnst B✶ (ernst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorschlag:

Bau dein Programm in etwa wie eingangs beschrieben auf, einzelne 
Funktionen auf Flash-Blöcke alligned, mit Zwischenraum. Keine 
Funktionspointertabellen ö.ä. nötig.

Für ein Update kompilierst du alles neu, und machst ein diff zw. altem 
und neuem Hex.

Die Funktionen sollten dabei großteils an den selben Stellen bleiben, 
d.h. Funktionsaufrufe etc. bleiben gleich.

Jetzt musst du nur noch die geänderten Flash-Blöcke übertragen und 
flashen, hast aber trotzem ein 100%aktuelles Zielsystem.

Ohne die Zwischenräume + alignment verschiebt dir natürlich eine 
Änderung allen nachfolgenden Code...

Und wenns unabhängig von der Zielsystem-Softwareversion gehen soll, muss 
der Bootloader dort halt CRCs der vorhandenen Blöcke generieren und die 
mit dem Upload-Program austauschen...

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Norgan:
Ich sehe mich schon mit meinen 2 Hardware-Breakpoints verzeifeln *grins.
Durch das ständige Haareraufen eine Glatze bekommend...

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ernst Bachmann wrote:
> Für ein Update kompilierst du alles neu, und machst ein diff zw. altem
> und neuem Hex.

Das würde folgendes Bedeuten:
Ich ändere eine Funktion in Funktionsmodul_1, damit verschieben sich die 
Adressen der folgenden Funktionen in diesem Modul. Deshalb wird sich 
auch in allen Modulen, die irgendwelche Funktionen in Modul_1 aufrufen 
der Code ändern (da sich ja die Adressen geändert haben). Kann ich dann 
davon ausgehen, dass ich fast den kompletten Code neu flashen muß?

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

keine Ahnung, wie das in C handhabbar gehen könnte...

Am Anfang jedes Moduls ist eine Tabelle mit Sprüngen zu den im Modul 
befindlichen Funktionen.
Jeder Funktionsaufruf kennt nur den Beginn dieser Tabelle (Startadresse 
des Moduls) und den Offset.
Dann kann sich innerhalb eines Moduls ändern, was will.
Wenn man Reserve bei der Tabelle einplant, kann mach auch Funktionen 
hinzufügen, die dann natürlich nur von neuen Modulen benutzt werden 
können, die die Bedeutung der neuen Offstes kennen.

Das ging schon beim C64 so, war allerdings ASN... ;-)

Gruß aus Berlin
Michael

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Funktionsaufruf kennt nur den Beginn

Es sollte allerdings auch ein "Ende der Sprungtabelle" markiert sein, zB 
durch einen Nullpointer.
Weiterhin wäre es sinnvoll, wenn jedes Modul eine Art Versionsnummer 
hat, um sagen zu können, ob alle Funktionen unterstützt werden

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

kann man da doch einbauen. Anzahl der Funktionen an den Anfang, Version 
danach, dann die Liste. Das mitzutesten macht doch wenig Aufwand, 
allerdings bleibt bei einem µC-Konzept wohl sinnvoll nur, das bei 
Systemstart zu prüfen und dann gleich mit einem DeadEnd abzubrechen, 
wenn was nicht stimmt.

Wenn die Module noch FallBack-Lösungen enthalten sollen, ist wohl ein 
RealTime-OS, daß den Kram zur Laufzeit nachlädt, die bessere Wahl.

Gruß aus Berlin
Michael

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:
> Am Anfang jedes Moduls ist eine Tabelle mit Sprüngen zu den im Modul
> befindlichen Funktionen.

Ja.

> Jeder Funktionsaufruf kennt nur den Beginn dieser Tabelle (Startadresse
> des Moduls) ...

Ja.

>> ... und den Offset.

Meinst Du mit Offset den Abstand zwischen den Sprungadressen in der 
Tabelle?

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:
> Wenn die Module noch FallBack-Lösungen enthalten sollen, ist wohl ein
> RealTime-OS, daß den Kram zur Laufzeit nachlädt, die bessere Wahl.

Ein RTOS hätte vermutlich auch den Vorteil Funktionsmodule solange für 
andere sperren zu können bis sicher ist, dass diese vollständig geflasht 
wurden und die Sprungtabelle wieder aktuell ist.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was versprichst du dir von der Diskussion? Natürlich ist eine modulare 
Firmware realisierbar. Alles ist realisierbar wenn man genug Zit und 
Geld übrig hat. Die Frage sollte eher lauten: brauchst DU eine modulare 
Firmware? Falls nein, warum zerbrichst du dir darüber den Kopf?

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gast wrote:
> Was versprichst du dir von der Diskussion?

Hatte ich eig. oben schon erwähnt. Aus 2 Gründen:
1.) Um herauszubekommen ob man eine modulare Software auf diese oder 
eine andere Art und Weise realisieren kann.
2.) Um herauszufinden womit ich mich noch beschäftigen sollte, damit ich 
das nächste mal nicht so blöde Fragen im Forum stelle ;-)

Autor: tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

@Christian: mal die ungeliebte ;-) Antwort nach Sender Erewan: Im 
Prinzip ja, aber ...

Machbar ist das. Aber zum Schluß bist Du bei einem dynamischen 
Modul-Loader a la den bekannten Betriebssystemen. Eine Sprungtabelle ist 
eine Vereinfachung, wird Dir aber vermutlich schnell a) Probleme 
bereiten und b) nicht mehr Deinen Ansprüchen gerecht. Da ich beruflich 
mit u.a. wirklich großen Embedded-Systemen zu tun habe, man dort auch 
daran gedacht hat, dies aber verworfen hat: laß es! Der 
Verwaltungsaufwand, sei es Code, sei es Sicherstellung, das nicht zum 
Schluß doch etwas Inkompatibles geladen wird und Dein System in eine 
Resetschleife gerät, ist schlicht zu hoch.

Insbesondere, wenn Du auf die Download-and-Flash-Zeit anspielst! (in der 
Regel dauert das Flash-Page-Löschen systembedingt am längsten!) Hier 
könntest Du ev. überlegen, das Du nur eine Differenz zum Vorgänger 
flasht. Was aber voraussetzt, das Du den Vorgänger kennst, ansonsten 
hättest Du unnötige Download-Zeit. Alternativ ginge auch ein 
Mechanismus, der "oben" und "unten" über eine Page eine Checksumme 
rechnet und nur im Differenzfall überträgt und flasht. Das könnte u.U. 
mehr sparen und in der Verwaltung des Code weniger aufwendig sein.

Schönen Tag noch,
Thomas

Autor: Christian Kito (cateye030)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
tom wrote:
> Hier könntest Du ev. überlegen, das Du nur eine Differenz zum Vorgänger
> flasht. Was aber voraussetzt, das Du den Vorgänger kennst, ansonsten
> hättest Du unnötige Download-Zeit. Alternativ ginge auch ein
> Mechanismus, der "oben" und "unten" über eine Page eine Checksumme
> rechnet und nur im Differenzfall überträgt und flasht.

Klingt logisch, wobei die Praxis eventuell zeigt, dass die 
Übersetzungsmechanismen (Compiler, Linker) an so ein Konzept angepaßt 
werden müssen (damit sich bei kleineren Änderungen nicht der ganze Code 
ändert). Oder gibt es solche Optionen schon???

Bei Datenblöcken könnte man es ja so machen:
Einzelne Datenblöcke am Ende vom Code. Aber mit ausreichend Platz 
zueinander. Um Sicherzustellen, dass sich die Adressen im Flash nicht 
ändern wenn man einen Datenblock verändert. Das würde auch ein 
modulweises Ersetzen den Datenblöcke ermöglichen bzw. einem Vorgehen 
nach Deinem Vorschlag noch mehr Wirkung verleihen.

Mein Eindruck ist jetzt, das der von Tom vorgeschlagene Weg am 
praktikabelsten ist. Wenn jetzt noch jemand sagt, das es die oben 
angesprochene Compiler-Option gibt, dann gebe ich endlich Ruhe ;-)

Gruß Christian

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.