Ahoj všichni.
Nějakou dobu zpátky jsme o tom, jak se nám podařilo spustit SIP telefon na STM32F4-Discovery s 1 MB ROM a 192 KB RAM) na základě . Je třeba říct, že ta verze byla minimální a spojuje dva telefony přímo bez serveru s přenosem hlasu pouze jedním směrem. Proto jsme se rozhodli spustit funkčnější telefon se spojeními přes server, přenosem hlasu oběma směry, ale přitom se vejít do co nejmenšího rozměru paměti.

Pro telefon bylo rozhodnuto vybrat aplikaci simple_pjsua v rámci knihovny PJSIP. Toto je minimální aplikace, která umí se registrovat na serveru, přijímat a odpovídat na hovory. Níže okamžitě uvedu popis, jak to spustit na STM32F7-Discovery.
Jak spustit
- Konfigurujeme Embox
make confload-platform/pjsip/stm32f7cube - V souboru conf/mods.config nastavíme potřebný SIP účet.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")kde server — to je SIP server (například sip.linphone.org), uživatelské jméno a heslo — uživatelské jméno a heslo pro účet.
- Sestavíme Embox příkazem make. O firmwaru desky máme informace na a v .
- Spustíme v konzoli Embox příkaz “simple_pjsua_imported”
00:00:12.870 pjsua_acc.c ....SIP outbound status for acc 0 is not active 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: registration success, status=200 (Registration success 00:00:12.911 pjsua_acc.c ....Keep-alive timer started for acc 0, destination:91.121.209.194:5060, interval:15s - Nakonec zbývá připojit sloupce nebo sluchátka k audio výstupu a mluvit do dvou malých MEMS mikrofonů vedle displeje. Voláme z Linuxu přes aplikaci simple_pjsua, pjsua. A nebo můžeme použít jakoukoliv jinou, jako je linphone.
To vše je popsáno na našem .
Jak jsme k tomu dospěli
Takže původně vyvstala otázka výběru hardwarové platformy. Protože bylo jasné, že STM32F4-Discovery nebude vyhovovat paměťovým nárokům, byla zvolena STM32F7-Discovery. Ta má 1 MB flash a 256 KB RAM (+ 64 KB speciální rychlé paměti, kterou také použijeme). Také to není mnoho pro volání přes server, ale rozhodli jsme se to zkusit.
Úkol jsme si prakticky rozdělili do několika etap:
- Spuštění PJSIP na QEMU. To bylo pohodlné pro ladění, navíc jsme tam měli podporu kodeku AC97.
- Záznam hlasu a přehrávání na QEMU a na STM32.
- Portace aplikace simple_pjsua z portfolia PJSIP. Umožňuje registraci na SIP serveru a volání.
- Rozběhnout vlastní server na bázi Asterisk a testovat na něm, poté zkusit externí, jako sip.linphone.org
Zvuk v Emboxu funguje přes Portaudio, které se používá i v PISIP. Na QEMU se objevily první problémy – WAV soubory se přehrávaly na 44100 Hz bez problémů, ale na 8000 Hz to evidentně nefungovalo správně. Ukázalo se, že problém byl v nastavení frekvence – výchozí hodnotou v hardware byla 44100, a v našem případě to nebylo programově změněno.
Tady je asi dobré trochu vysvětlit, jak vlastně probíhá přehrávání zvuku. Zvukové kartě lze nastavit určitou adresu paměti, ze které se má přehrávat nebo zapisovat při předem nastavené frekvenci. Jakmile se vyplní buffer, dojde k přerušení a vykonávání pokračuje od dalšího bufferu. V podstatě je potřeba předem naplnit tyto buffery, zatímco se přehrává ten předchozí. S tímto problémem se ještě setkáme na STM32F7.
Dále jsme si pronajali server a na něm rozjeli Asterisk. Protože bylo potřeba mnoho ladit a mluvit do mikrofonu se mi nechtělo, museli jsme zajistit automatické přehrávání a nahrávání. K tomu jsme upravili simple_pjsua tak, aby bylo možné nahradit audio zařízení soubory. V PJSIP se to dělá docela jednoduše, protože mají pojem port, který může být jak zařízení, tak soubor. Tyto porty lze flexibilně připojit k jiným portům. Kód si můžete prohlédnout v našem pjsipu. . Nakonec byla schémata následující. Na serveru Asterisk jsem založil dva účty – pro Linux a pro Embox. Poté na Emboxu provedeme příkaz simple_pjsua_imported, Embox se registruje na serveru, následně voláme z Linuxu na Embox. V okamžiku spojení zkontrolujeme na serveru Asterisk, že je celé spojení navázáno, a po nějaké době bychom měli v Emboxu slyšet zvuk z Linuxu, přičemž v Linuxu uložíme soubor, který se přehrává z Emboxu.
Jakmile to začalo fungovat na QEMU, přešli jsme k portování na STM32F7-Discovery. První problém – nevešli jsme se do 1 MB ROM bez zapnuté optimalizace překladače “-Os” kvůli velikosti obrazu. Proto jsme zapli “-Os”. Dále jsme patchem zakázali podporu C++, protože je potřebná jen pro pjsua, a my používáme simple_pjsua.
Poté, co jsme se vešli simple_pjsua, jsme dospěli k názoru, že šance na spuštění nyní existují. Nejprve jsme se však museli vyřešit problém s nahráváním a přehráváním hlasu. Otázka – kam nahrávat? Zvolili jsme externí paměť – SDRAM (128 MB). Můžete to zkusit sami:
Vytvoří stereo WAV s frekvencí 16000 Hz a délkou 10 sekund:
record -r 16000 -c 2 -d 10000 -m C0000000
Přehráváme:
play -m C0000000
Tady vznikly dva problémy. První s kodekem — používá se WM8994 a ten má takový pojem jako slot, a těch slotů je 4. Tak tedy, pokud to není nastaveno, při přehrávání audia dochází k přehrávání ve všech čtyřech slotech. Proto při frekvenci 16000 Hz jsme dostali 8000 Hz, a pro 8000 Hz přehrávání prostě nefungovalo. Když jsme zvolili pouze sloty 0 a 2, začalo to fungovat jak má. Dalším problémem byl audio interface v STM32Cube, ve kterém audio výstup funguje přes SAI (Serial Audio Interface) synchronně s audio vstupem (nezkoumal jsem detaily, ale vychází to tak, že sdílejí společný clock a při inicializaci audio výstupu se nějak navazuje na audio vstup). To znamená, že je nelze spustit samostatně, proto jsme udělali následující — audio vstup a audio výstup vždy pracují (včetně generování přerušení). Ale když se v systému nic nehraje, jednoduše posíláme výstupu prázdný buffer, a když začínáme přehrávání, poctivě jej začneme naplňovat.
Dále jsme se setkali s tím, že zvuk při nahrávání hlasu byl velmi tichý. To se děje, protože MEMS mikrofony na STM32F7-Discovery nějak špatně fungují na frekvencích pod 16000 Hz. Proto nastavujeme 16000 Hz, i když přijde 8000 Hz. K tomu bylo pravda potřeba přidat programovou konverzi jedné frekvence na druhou.
Dále bylo nutné zvýšit velikost haldy, která se nachází v RAM. Podle našich výpočtů pjsip vyžadoval přibližně 190 kB, a nám zbylo pouze asi 100 kB. Tady bylo nutné využít kousek externí paměti — SDRAM (přibližně 128 kB).
Po všech těchto opravách jsem viděl první pakety mezi Linuxem a Emboxem a slyšel zvuk! Ale zvuk byl hrozný, vůbec ne takový jako na QEMU, nic nebylo možné rozpoznat. Tak jsme začali přemýšlet, v čem by mohl být problém. Ladění ukázalo, že Embox prostě nestíhá plnit/vykládat audio buffery. Zatímco pjsip zpracovává jeden frame, se stihly objevit 2 přerušení o dokončení zpracování bufferů, což je příliš mnoho. První myšlenkou pro zrychlení byla optimalizace kompilátoru, ale ta byla už v PJSIP povolena. Druhá — hardwarová floating point, o které jsme mluvili v . Ale jak ukázala praxe, FPU nepřineslo významné zlepšení rychlosti. Dalším krokem bylo nastavení priorit pro toky. V Emboxu existují různé strategie plánování, a já jsem zapnul tu, která podporuje priority, a nastavil audiotokům co nejvyšší prioritu. To také nepomohlo.
Další myšlenka byla, že pracujeme s externí pamětí a bylo by dobré tam přesunout struktury, ke kterým dochází k velmi častému přístupu. Provedl jsem předběžnou analýzu toho, kdy a pro co simple_pjsua se paměť alokuje. Ukázalo se, že z 190 kB prvních 90 kB je vyhrazeno pro interní potřeby PJSIP a k nim se neprovádí příliš častý přístup. Dále během příchozího hovoru se volá funkce pjsua_call_answer, kde se následně alokují buffery pro práci s příchozími a odchozími rámci. To bylo dalších přibližně 100 kB. A tady jsme postupovali následovně. Před hovorem ukládáme data do externí paměti. Jakmile dojde k hovoru — okamžitě vyměníme haldu za jinou — v RAM. Tímto způsobem byla všechna „horká“ data přesunuta do rychlejší a předvídatelnější paměti.
V důsledku toho všeho společně se podařilo spustit simple_pjsua a zavolat přes svůj server. A poté i přes další servery jako sip.linphone.org.
Závěry
Nakonec se podařilo spustit simple_pjsua s přenosem hlasu oběma směry přes server. Problém s navíc spotřebovanými 128 kB SDRAM lze vyřešit použitím o něco výkonnějšího Cortex-M7 (například STM32F769NI s 512 kB RAM), ale přesto jsme ještě neztratili naději vejít se i do 256 kB 🙂 Budeme rádi, pokud se někdo zaujme, a ještě lépe — vyzkouší. Všechny zdrojové kódy, jak obvykle, jsou v našem .
Zdroj: habr.com
