Hallo zusammen.
Vor einiger Zeit haben wir darüber gesprochen, wie es uns gelungen ist, ein SIP-Telefon auf dem STM32F4-Discovery mit 1 MB ROM und 192 KB RAM zu starten, basierend auf . Man muss sagen, dass diese Version minimal war und zwei Telefone direkt ohne Server verband und die Sprachübertragung nur in eine Richtung erfolgte. Daher haben wir beschlossen, ein vollständigeres Telefon zu starten, welches die Sprachübertragung über einen Server und in beide Richtungen ermöglicht, dabei jedoch so wenig Speicherplatz wie möglich benötigt.

Für das Telefon haben wir die Anwendung simple_pjsua aus der PJSIP-Bibliothek ausgewählt. Dies ist eine minimalistische Anwendung, die sich beim Server registrieren, Anrufe annehmen und beantworten kann. Unten werde ich gleich eine Beschreibung geben, wie man dies auf dem STM32F7-Discovery startet.
So starten Sie
- Konfigurieren Sie Embox
make confload-platform/pjsip/stm32f7cube - In der Datei conf/mods.config legen wir das benötigte SIP-Konto fest.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")wobei server — dies ist der SIP-Server (zum Beispiel sip.linphone.org), Benutzername und password — der Benutzername und das Passwort für das Konto.
- Kompilieren Sie Embox mit dem Befehl make. Informationen zum Flashen des Boards finden Sie auf auch in .
- Starten Sie in der Embox-Konsole den Befehl “simple_pjsua_imported”
00:00:12.870 pjsua_acc.c ....SIP-Ausgangsstatus für acc 0 ist nicht aktiv 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: Registrierung erfolgreich, Status=200 (Registrierung erfolgreich 00:00:12.911 pjsua_acc.c ....Keep-alive-Timer für acc 0 gestartet, Ziel: 91.121.209.194:5060, Intervall: 15s - Nun müssen noch die Lautsprecher oder Kopfhörer an den Audioausgang angeschlossen werden, während man in zwei kleine MEMS-Mikrofone spricht, die sich neben dem Display befinden. Wir telefonieren von Linux aus über die Anwendung simple_pjsua, pjsua. Oder man kann auch jede andere Anwendung wie linphone verwenden.
All dies ist auf unserer .
Wie wir dazu gekommen sind
Zunächst stellte sich die Frage nach der Auswahl der Hardware-Plattform. Da klar war, dass STM32F4-Discovery aufgrund des Speichers nicht geeignet ist, wurde STM32F7-Discovery ausgewählt. Sie verfügt über 1 MB Flash-Speicher und 256 KB RAM (+ 64 KB speziellen schnellen Speicher, den wir ebenfalls verwenden werden). Auch nicht viel für Anrufe über den Server, aber wir beschlossen, es zu versuchen.
Wir haben die Aufgabe grob in mehrere Phasen unterteilt:
- Start von PJSIP auf QEMU. Dies war bequem für das Debugging, außerdem hatten wir dort bereits Unterstützung für den AC97-Codec.
- Aufnahme und Wiedergabe von Sprache auf QEMU und auf STM32.
- Portierung der Anwendung simple_pjsua aus dem PJSIP-Paket. Sie ermöglicht die Registrierung bei einem SIP-Server und das Telefonieren.
- Richten Sie Ihren eigenen Server auf Basis von Asterisk ein und testen Sie daran. Anschließend können Sie externe Dienste wie sip.linphone.org ausprobieren.
Der Sound in Embox funktioniert über Portaudio, das auch in PISIP verwendet wird. Bei QEMU traten die ersten Probleme auf — WAV-Dateien wurden bei 44100 Hz gut wiedergegeben, während bei 8000 Hz offensichtlich etwas nicht stimmte. Es stellte sich heraus, dass es an den Frequenzeinstellungen lag — standardmäßig war diese in der Hardware auf 44100 eingestellt und konnte bei uns softwareseitig nicht geändert werden.
Hier sollte ich wahrscheinlich etwas klarstellen, wie die Tonwiedergabe eigentlich funktioniert. Der Soundkarte kann ein Zeiger auf einen bestimmten Speicherbereich zugewiesen werden, aus dem entweder wiedergegeben oder mit einer vorher festgelegten Frequenz aufgenommen werden soll. Nachdem der Puffer erschöpft ist, wird eine Unterbrechung ausgelöst und die Ausführung geht mit dem nächsten Puffer weiter. Das Problem dabei ist, dass diese Puffer rechtzeitig gefüllt werden müssen, während der vorherige abgespielt wird. Mit diesem Problem werden wir uns später noch auf dem STM32F7 auseinandersetzen.
Als Nächstes haben wir einen Server gemietet und Asterisk darauf installiert. Da wir viel zu konfigurieren hatten und nicht ständig ins Mikrofon sprechen wollten, benötigten wir eine Möglichkeit für automatisches Abspielen und Aufnehmen. Daher haben wir simple_pjsua so angepasst, dass wir Dateien anstelle von Audiogeräten verwenden können. In PJSIP ist das recht einfach, da es dort den Begriff Port gibt, der sowohl ein Gerät als auch eine Datei sein kann. Diese Ports lassen sich flexibel miteinander verbinden. Den Code dazu finden Sie in unserem pjsip. Das Ergebnis war folgendes: Auf dem Asterisk-Server habe ich zwei Konten erstellt – eines für Linux und eines für Embox. Danach wird auf Embox der Befehl ausgeführt simple_pjsua_imported, Embox registriert sich beim Server, und anschließend tätigen wir von Linux einen Anruf an Embox. Während der Verbindung überprüfen wir auf dem Asterisk-Server, dass die gesamte Verbindung hergestellt wurde, und nach einer kurzen Zeit sollten wir in Embox den Ton von Linux hören. In Linux speichern wir die Datei, die aus Embox abgespielt wird.
Nachdem es auf QEMU funktionierte, haben wir mit dem Portieren auf das STM32F7-Discovery begonnen. Das erste Problem war, dass wir ohne aktivierte Compiler-Optimierung „-Os“ nicht in den 1 MB ROM passten. Daher haben wir „-Os“ aktiviert. Außerdem haben wir durch einen Patch die C++-Unterstützung deaktiviert, da wir sie nur für pjsua benötigen und wir simple_pjsua verwenden.
Nachdem wir es untergebracht haben, simple_pjsua, waren wir der Meinung, dass die Chancen, es jetzt zu starten, bestehen. Aber zuerst mussten wir klären, wo wir Sprache aufnehmen und wiedergeben. Die Frage war – wohin aufnehmen? Wir haben uns für externen Speicher – SDRAM (128 MB) entschieden. Sie können es selbst ausprobieren:
Es wird eine Stereo-WAV-Datei mit einer Frequenz von 16000 Hz und einer Dauer von 10 Sekunden erstellt:
record -r 16000 -c 2 -d 10000 -m C0000000
Wird wiedergegeben:
play -m C0000000
Hier gab es zwei Probleme. Das erste mit dem Codec – der WM8994, der das Konzept von Slots beinhaltet, und es gibt insgesamt vier dieser Slots. Standardmäßig, wenn das nicht konfiguriert wird, erfolgt die Audiowiedergabe über alle vier Slots. Daher erhielten wir bei einer Frequenz von 16000 Hz 8000 Hz, was bedeutete, dass die Wiedergabe bei 8000 Hz einfach nicht funktionierte. Nachdem wir nur die Slots 0 und 2 ausgewählt hatten, funktionierte es wie gewünscht. Ein weiteres Problem war das Audio-Interface in STM32Cube, bei dem der Audioausgang über das SAI (Serial Audio Interface) synchron mit dem Audioeingang arbeitet (ich habe mich nicht im Detail damit beschäftigt, aber es scheint, als ob sie sich eine gemeinsame Taktfrequenz teilen, und bei der Initialisierung des Audioausgangs wird irgendwie der Audioeingang damit verknüpft). Das bedeutet, dass man sie nicht separat starten kann, deshalb haben wir folgendes gemacht – sowohl der Audioeingang als auch der Audioausgang arbeiten immer (einschließlich der generierten Interrupts). Wenn jedoch im System nichts abgespielt wird, geben wir einfach einen leeren Puffer an den Audioausgang weiter, und wenn die Wiedergabe gestartet wird, beginnen wir ehrlich, ihn zu füllen.
Wir sind dann auf das Problem gestoßen, dass der Ton bei der Sprachaufnahme sehr leise war. Dies liegt daran, dass MEMS-Mikrofone auf dem STM32F7-Discovery bei Frequenzen unter 16.000 Hz nicht optimal funktionieren. Daher stellen wir 16.000 Hz ein, selbst wenn nur 8.000 Hz ankommen. Dafür mussten wir allerdings eine Softwarekonvertierung der Frequenz vornehmen.
Anschließend mussten wir die Heap-Größe im RAM erhöhen. Nach unseren Berechnungen benötigte pjsip etwa 190 KB, aber wir hatten nur noch rund 100 KB übrig. Hierfür mussten wir etwas externen Speicher – SDRAM (etwa 128 KB) – verwenden.
Nach all diesen Anpassungen sah ich die ersten Pakete zwischen Linux und Embox und hörte den Ton! Aber der Klang war schrecklich, ganz anders als bei QEMU, man konnte nichts verstehen. Da fragten wir uns, woran das liegen könnte. Die Fehlersuche ergab, dass Embox einfach nicht schnell genug die Audio-Puffer füllen und entladen kann. Während pjsip ein Frame bearbeitet, fanden bereits zwei Interrupts zum Abschluss der Pufferverarbeitung statt, was zu viel war. Der erste Gedanke zur Beschleunigung war die Optimierung des Compilers, aber diese war bereits in PJSIP aktiviert. Zweitens – die Hardware-Floating-Point-Operation, über die wir berichtet haben. . Allerdings hat sich in der Praxis gezeigt, dass die FPU keinen wesentlichen Geschwindigkeitsvorteil gebracht hat. Der nächste Schritt bestand darin, die Prioritäten der Streams festzulegen. In Embox gibt es verschiedene Planungsstrategien, und ich habe die aktiviert, die Prioritäten unterstützt, und den Audio-Streams die höchste Priorität zugewiesen. Das hat ebenfalls nicht geholfen.
Die nächste Idee war, dass wir mit externem Speicher arbeiten und es sinnvoll wäre, häufig verwendete Strukturen dorthin zu verschieben. Ich habe eine vorläufige Analyse durchgeführt, wann und für was simple_pjsua Speicher zugewiesen wird. Dabei stellte sich heraus, dass von 190 KB die ersten 90 KB für interne Bedürfnisse von PJSIP reserviert sind und nicht sehr häufig darauf zugegriffen wird. Während eines eingehenden Anrufs wird die Funktion pjsua_call_answer aufgerufen, in der dann die Puffer für die Verarbeitung von eingehenden und ausgehenden Frames zugewiesen werden. Das waren nochmals etwa 100 KB. Und hier haben wir folgendes getan: Bis zum Zeitpunkt des Anrufs haben wir die Daten im externen Speicher abgelegt. Sobald der Anruf kommt, wechseln wir sofort den Heap auf einen anderen – in den RAM. So wurden alle „heißen“ Daten in den schnelleren und vorhersehbareren Speicher verlagert.
Insgesamt hat all dies zusammen das Starten ermöglicht simple_pjsua und über Ihren Server zu telefonieren. Und dann auch über andere Server wie sip.linphone.org.
Fazit
Es gelang schließlich, simple_pjsua die Sprachübertragung in beide Richtungen über den Server zu realisieren. Das Problem mit den zusätzlich benötigten 128 KB SDRAM kann durch einen etwas leistungsstärkeren Cortex-M7 (wie z.B. den STM32F769NI mit 512 KB RAM) gelöst werden, aber wir haben die Hoffnung, auch mit 256 KB auszukommen 🙂 Wir würden uns freuen, wenn sich jemand dafür interessiert, oder noch besser — es ausprobiert. Der gesamte Quellcode ist wie gewohnt in unserem .
Quelle: habr.com
