Hello everyone.
Nekaj časa nazaj smo o tem, kako nam je uspelo zagnati SIP telefon na STM32F4-Discovery s 1 MB ROM in 192 KB RAM na osnovi . Treba je povedati, da je bila ta različica minimalna in je povezovala dva telefona neposredno brez strežnika ter prenašala zvok le v eno smer. Zato smo se odločili zagnati bolj popoln telefon z zvonjenjem preko strežnika, prenosom glasu v obe smeri, a pri tem ostati v čim manjšem pomnilniku.

Za telefon smo se odločili za aplikacijo simple_pjsua v okviru knjižnice PJSIP. To je minimalna aplikacija, ki se zna registrirati na strežniku, sprejemati in odgovarjati na klice. Spodaj takoj prinašam opis, kako to zagnati na STM32F7-Discovery.
Kako zagnati
- Konfiguriramo Embox
make confload-platform/pjsip/stm32f7cube - V datoteki conf/mods.config določimo želeni SIP račun.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")kjer strežnik — to je SIP strežnik (na primer, sip.linphone.org), username in password — uporabniško ime in geslo računa.
- Zberemo Embox z ukazom make. O programski opremi imamo na in v .
- Zagnamo v konzoli Embox ukaz “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 succes 00:00:12.911 pjsua_acc.c ....Keep-alive timer started for acc 0, destination:91.121.209.194:5060, interval:15s - Nazadnje ostane samo še, da v audio izhod priklopimo zvočnike ali slušalke, ter govorimo v dva majhna MEMS mikrofona ob zaslonu. Kličemo z Linuxa preko aplikacije simple_pjsua, pjsua. Ali pa lahko uporabimo tudi katero koli drugo, na primer linphone.
Vse to je opisano na našem .
Kako smo do tega prišli
Torej, sprva je bilo vprašanje izbire strojne platforme. Ker je bilo jasno, da STM32F4-Discovery ne bo dovolj za pomnilnik, je bila izbrana STM32F7-Discovery. Ima 1 MB flasha in 256 KB RAM (+ 64 posebne hitre pomnilniške enote, ki jo bomo tudi uporabili). Tudi to ni ravno veliko za klice preko strežnika, a smo se odločili poskusiti stlačiti.
Določili smo nalogo in jo razdelili na več faz:
- Zagon PJSIP na QEMU. To je bilo priročno za odpravljanje napak, poleg tega smo tam že imeli podporo za AC97 kodek.
- Posnetek in predvajanje glasu na QEMU in na STM32.
- Portiranje aplikacije simple_pjsua iz okvirja PJSIP. Omogoča registracijo na SIP strežniku in klicanje.
- Postaviti svoj lasten strežnik na osnovi Asteriska in testirati nanj, nato pa poskusiti tudi zunanje, kot je sip.linphone.org.
Zvok v Emboxu deluje prek Portaudio, ki se uporablja tudi v PISIP-u. Na QEMU so se pojavili prvi problemi - WAV datoteke so se dobro predvajale na 44100 Hz, medtem ko na 8000 Hz očitno ni bilo vse v redu. Izkazalo se je, da je bila težava v nastavitvi frekvence - privzeto je imela strojna oprema 44100 Hz, kar programersko nismo spremenili.
Tu je verjetno treba nekoliko pojasniti, kako sploh poteka predvajanje zvoka. Zvočni kartici lahko določimo kazalec na kos pomnilnika, iz katerega je treba predvajati ali snemati pri vnaprej določeni frekvenci. Ko se buferski prostor konča, se sproži prekinitev, in izvajanje se nadaljuje z naslednjim buffrom. Dejstvo je, da je te buferje potrebno vnaprej napolniti, medtem ko se predvaja prejšnji. S to težavo se bomo še srečali pri STM32F7.
Nato smo najeti strežnik in na njem namestili Asterisk. Ker smo potrebovali veliko odpravljati napake, govorjenje v mikrofon pa ni bilo posebno privlačno, smo morali omogočiti samodejno predvajanje in snemanje. Za to smo popravili simple_pjsua tako, da smo lahko namestili datoteke namesto zvočnih naprav. V PJSIP to stori precej enostavno, saj imajo koncept vrata, ki je lahko tako naprava kot datoteka. In ta vrata lahko fleksibilno povežemo z drugimi vrati. Kodo si lahko ogledate v našem pjsipu. . Tako je bila shema naslednja. Na strežniku Asterisk sem ustvaril dva računa - za Linux in za Embox. Nato na Emboxu izvedemo ukaz simple_pjsua_imported, Embox se registrira na strežniku, nato pa kličemo na Embox iz Linuxa. Ob vzpostavitvi povezave preverimo na strežniku Asterisk, da je vse povezano, in po nekem času moramo v Emboxu slišati zvok iz Linuxa, medtem ko v Linuxu shranimo datoteko, ki se predvaja iz Embox.
Potem ko je to delovalo na QEMU, smo prešli na prenos na STM32F7-Discovery. Prvi problem - nismo se prilegali v 1 MB ROM brez vključenega optimizacijskega nastavka kompilatorja “-Os” za velikost slike. Zato smo vključili “-Os”. Nadalje smo s popravkom onemogočili podporo za C++, saj je ta potrebna le za pjsua, mi pa uporabljamo simple_pjsua.
Potem ko smo se stisnili simple_pjsua, smo ugotovili, da imamo sedaj možnosti, da to zagnamo. Ampak najprej smo se morali lotiti snemanja in predvajanja glasu. Vprašanje - kam snemati? Izbrali smo zunanji pomnilnik - SDRAM (128 MB). Poskusite to sami:
Ustvari stereo WAV s frekvenco 16000 Hz in dolžino 10 sekund:
record -r 16000 -c 2 -d 10000 -m C0000000
Predvajamo:
play -m C0000000
Tukaj smo naleteli na dva problema. Prvi je bil z kodirnikom — uporablja se WM8994, v katerem obstaja koncept slotov, teh slotov pa je štiri. Tako je privzeto, če tega ne nastavimo, predvajanje zvoka poteka v vseh štirih slotih. Zato smo pri frekvenci 16000 Hz dobili 8000 Hz, za 8000 Hz pa predvajanje preprosto ni delovalo. Ko smo izbrali le slote 0 in 2, je delovalo, kot bi moralo. Drugi problem je bil avdio vmesnik v STM32Cube, kjer avdio izhod deluje preko SAI (Serial Audio Interface) sinhrono z avdio vhodom (nisem raziskoval podrobnosti, ampak se zdi, da si delita skupno uro in ob inicializaciji avdio izhoda se nekako poveže avdio vhod). Torej, zagnati jih ločeno ni bilo mogoče, zato smo naredili tole — avdio vhod in avdio izhod vedno delujeta (vključno z generiranjem prekinitev). Toda ko v sistemu nič ne predvajamo, preprosto ponudimo avdio izhodu prazen pomnilnik, ko pa se začne predvajanje, ga pošteno začnemo polniti.
Nato smo se srečali s tem, da je bil zvok pri snemanju glasu zelo tih. To se dogaja, ker MEMS mikrofoni na STM32F7-Discovery slabo delujejo na frekvencah pod 16000 Hz. Zato nastavimo 16000 Hz, tudi če pride 8000 Hz. Za to pa je bilo potrebno dodati programsko pretvorbo ene frekvence v drugo.
Nato smo morali povečati velikost kupčka, ki se nahaja v RAM-u. Po naših izračunih je pjsip zahteval okoli 190 kB, mi pa smo imeli le okoli 100 kB. Tukaj smo morali uporabiti nekoliko zunanjega pomnilnika — SDRAM (približno 128 kB).
Po vseh teh popravkih sem videl prve pakete med Linuxom in Emboxom ter slišal zvok! Ampak zvok je bil strašen, povsem drugačen kot na QEMU, ničesar ni bilo mogoče razbrati. Takrat smo se začeli spraševati, kje bi lahko bil problem. Odpravljanje napak je pokazalo, da Embox enostavno ne uspe napolniti/izprazniti avdio pomnilnikov. Medtem ko pjsip obdeluje en okvir, se zgodijo dve prekinitev o končanem obdelovanju pomnilnikov, kar je preveč. Prva misel za pospešitev je bila optimizacija prevajalnika, vendar je bila ta že vključena v PJSIP. Druga je bila strojni program za plavajočo vejico, o tem smo govorili v . Vendar pa praksa kaže, da FPU ni prinesel pomembnega povečanja hitrosti. Naslednji korak je bilo določanje prioritet tokov. V Emboxu obstajajo različne strategije načrtovanja, in vključil sem tisto, ki podpira prioritete, ter dodelil največjo prednost avdio tokovom. Tudi to ni pomagalo.
Naslednja ideja je bila, da delamo z zunanjim pomnilnikom in bi bilo smiselno premakniti strukture, ki jih pogosto uporabljamo, tja. Opravil sem predhodno analizo, kdaj in za kaj simple_pjsua se dodeljuje pomnilnik. Izkazalo se je, da od 190 Kb prvih 90 Kb dodeljenih za notranje potrebe PJSIP ni pogosto uporabljeno. Nato med prihajajočim klicem pride do klica funkcije pjsua_call_answer, v kateri so nato dodeljeni pomnilniki za delo s prihajajočimi in odhodnimi okviri. To je bilo še dodatnih približno 100 Kb. In tukaj smo naredili naslednje. Pred klicem podatke shranimo v zunanji pomnilnik. Takoj, ko pride klic — zamenjamo kup s drugim — v RAM. Tako so vsi 'vroči' podatki preneseni v hitrejši in bolj predvidljiv pomnilnik.
Na koncu je vse to skupaj omogočilo zagon simple_pjsua in klicanje preko svojega strežnika. Nato pa tudi preko drugih strežnikov, kot je sip.linphone.org.
Zaključki
Na koncu nam je uspelo zagnati simple_pjsua s prenosom glasu v obe smeri preko strežnika. Problem z dodatno porabljenimi 128 Kb SDRAM se lahko reši z uporabo nekoliko močnejšega Cortex-M7 (na primer, STM32F769NI z 512 Kb RAM), vendar pri tem še nismo obupali, da bi se uvrstili tudi v 256 Kb 🙂 Veseli bomo, če bo kdo zainteresiran, še bolje — če bo poskusil. Vse izvorne kode, kot običajno, so na našem .
Izvor: habr.com
