Здравейте на всички.
Преди известно време ние за това как успяхме да стартираме SIP телефон на STM32F4-Discovery с 1MB ROM и 192KB RAM) на база . Тук е важно да се спомене, че тази версия беше минимална и свързваше два телефона директно без сървър и предаване на глас само в една посока. Затова решихме да пуснем по-пълноценен телефон със свързване през сървър и предаване на глас в двете посоки, като в същото време се опитваме да се вместим в възможно най-малкия размер памет.

За телефона беше решено да изберем приложението simple_pjsua в рамките на библиотеката PJSIP. Това е минимално приложение, което може да се регистрира на сървър, да приема и отговаря на обаждания. По-долу ще дам описание как да го стартираме на STM32F7-Discovery.
Как да стартираме
- Конфигурираме Embox
make confload-platform/pjsip/stm32f7cube - В файла conf/mods.config задаваме необходимия SIP акаунт.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")където server — това е SIP сървър (например, sip.linphone.org), username и парола — името на потребителя и паролата за акаунта.
- Събиране на Embox с команда направи. За фърмуера на платката имаме информация на и в .
- Стартираме в конзолата на Embox командата “simple_pjsua_imported”
00:00:12.870 pjsua_acc.c ....SIP outbound статус за acc 0 не е активен 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: регистрация успешна, статус=200 (Успешна регистрация) 00:00:12.911 pjsua_acc.c ....Keep-alive таймерът стартира за acc 0, цел:91.121.209.194:5060, интервал:15s - Накрая, остана да свържем колонки или слушалки в аудио изхода и да говорим в двата малки MEMS микрофона близо до екрана. Обаждаме се от линукс чрез приложението simple_pjsua, pjsua. Или можем да използваме и друго, като linphone.
Всичко това е описано на нашия .
Как стигнахме до това
И така, първоначално възникна въпросът за избора на хардуерна платформа. Тъй като беше ясно, че STM32F4-Discovery няма да е подходящ по отношение на паметта, избрахме STM32F7-Discovery. Тя има 1MB флаш и 256KB RAM (+ 64 специална бърза памет, която също ще използваме). Също не е много за обаждания през сървър, но решихме да опитаме да се вместим.
Условно разделихме задачата на няколко етапа:
- Стартиране на PJSIP на QEMU. Это было удобно для отладки, плюс у нас там уже была поддержка кодека AC97.
- Запис на глас и възпроизвеждане на QEMU и на STM32.
- Портирование на приложението simple_pjsua от PJSIP. То позволява да се регистрирате на SIP сървър и да провеждате разговори.
- Разгръщане на собствен сървър на база Asterisk и тестване на него, след което да опитаме външни, като sip.linphone.org
Звук в Embox работи чрез Portaudio, който се използва и в PISIP. На QEMU се появиха първите проблеми — WAV файловете се възпроизвеждаха добре на 44100 Hz, но на 8000 Hz нещо определено не беше наред. Оказа се, че става дума за настройката на честотата — по подразбиране в хардуера тя беше 44100 и ние не можехме да я променим софтуерно.
Тук вероятно си струва да поясним как изобщо става възпроизвеждането на звук. На звуковата карта може да се установи указател към част от паметта, от която да се възпроизвежда или записва на предварително установена честота. След като буферът приключи, се генерира прекъсване, и изпълнението продължава от следващия буфер. Но тези буфери трябва да бъдат запълвани навреме, докато се възпроизвежда предишния. С този проблем ще се сблъскаме по-късно и на STM32F7.
След това наехме сървър и разгряхме Asterisk на него. Тъй като отстраняването на грешки изискваше много, а не ми се говореше в микрофона, решихме да направим автоматично възпроизвеждане и запис. За това ипачвахме simple_pjsua, за да можем да подменим файлове вместо звукови устройства. В PJSIP това се прави доста лесно, тъй като те имат понятие за порт, който може да бъде както устройство, така и файл. И тези портове могат да се свързват гъвкаво с други портове. Можете да видите кода в нашия pjsip. . В резултат, схемата беше следната. На сървъра Asterisk създадох два акаунта — за Linux и за Embox. След това на Embox се изпълнява командата simple_pjsua_imported, Embox се регистрира на сървъра, след което от Linux се обаждаме на Embox. В момента на свързването проверяваме на сървъра Asterisk, че връзката е установена, и след известно време трябва да чуем звука от Linux в Embox, а в Linux запазваме файла, който се възпроизвежда от Embox.
След като това проработи на QEMU, преминахме към портироването на STM32F7-Discovery. Първият проблем — не успяхме да се вместим в 1 Мб ROM без включена оптимизация на компилатора “-Os” за размера на образа. Затова включихме “-Os”. След това с пач отключихме поддръжката за C++, тъй като тя е необходима само за pjsua, а ние ползваме simple_pjsua.
След като успяхме да помещим simple_pjsua, решихме, че сега имаме шансове да го стартираме. Но първо трябваше да се справим с записването и възпроизвеждането на глас. Въпросът е — къде да записваме? Избрахме външна памет — SDRAM (128 Мб). Можете да опитате това сами:
Ще създаде стерео WAV с честота 16000 Hz и продължителност 10 секунди:
record -r 16000 -c 2 -d 10000 -m C0000000
Възпроизвеждаме:
play -m C0000000
Тук възникнаха две проблема. Първата е свързана с кодека — използва се WM8994, в който има понятие за слот, а слотовете са 4 на брой. По подразбиране, ако не се настройва, възпроизвеждането на аудио става във всички четири слота. Следователно, при честота 16000 Hz получавахме 8000 Hz, а за 8000 Hz възпроизвеждането просто не работеше. Когато избрахме само слотовете 0 и 2, всичко заработи както трябва. Още една проблема беше аудио интерфейсът в STM32Cube, в който аудио изходът работи чрез SAI (Serial Audio Interface) синхронно с аудио входа (не вниквах в детайлите, но става така, че те делят общ часовник и при инициализация на аудио изхода, представлява се по някакъв начин с аудио входа). Тоест, не може да ги стартира поотделно, затова направихме следното — аудио входът и изходът всегда работят (включително генерират прекъсвания). Но когато в системата няма възпроизвеждане, просто подлагаме на аудио изхода празен буфер, а когато започва възпроизвеждането, честно започваме да го запълваме.
След това се сблъскахме с факта, че звукът при запис на глас беше много тих. Това се случва, защото MEMS микрофоните на STM32F7-Discovery не работят особено добре на честоти под 16000 Hz. Затова настроихме на 16000 Hz, дори и да постъпи 8000 Hz. За целта обаче беше нужно да добавим софтуерно преобразуване от една честота в друга.
След това се наложи да увеличим размера на купчината, разположена в RAM. По нашите изчисления, pjsip изискваше около 190 Кб, а ние имахме само около 100 Кб оставащи. Тук трябваше да използваме малко външна памет — SDRAM (около 128 Кб).
След всички тези корекции видях първите пакети между Линукс и Embox, и чух звука! Но звукът беше ужасен, никак не беше като на QEMU, нищо не можеше да се разбере. Тогава се замислихме каква може да бъде причината. Отладката показа, че Embox просто не успява да запълва/изхвърля аудио буферите. Докато pjsip обработва един кадър, успяваха да се случат 2 прекъсвания за завършване на обработката на буферите, което е твърде много. Първата мисъл за ускорение беше оптимизация на компилатора, но тя вече беше включена в PJSIP. Втората — хардуерно плаваща точка, за която говорихме в . Но как показа практиката, FPU не доведе до значително увеличаване на скоростта. Следващата стъпка беше определянето на приоритети на потоките. В Embox има различни стратегии за планиране, и активирах тази, която поддържа приоритети, като зададох на аудио потоките максимален приоритет. И това не помогна.
Следващата идея беше, че работим с външна памет и е добре да преместим структурите, до които достъпът се извършва изключително често. Проведох предварителен анализ кога и за какво simple_pjsua се заделя памет. Оказа се, че от 190 Кб първите 90 Кб са заделени за вътрешни нужди на PJSIP и достъпът до тях не е много често. След това, по време на входящо обаждане, се извиква функцията pjsua_call_answer, в която след това се заделят буфери за работа с входящите и изходящите рамки. Това беше още около 100 Кб. И тук постъпихме по следния начин. До момента на обаждането данните се разполагат във външната памет. Веднага щом има обаждане, веднага заменяме купчината с друга — в RAM. По този начин всички „горещи“ данни бяха пренесени в по-бърза и предсказуема памет.
В крайна сметка всичко това заедно позволи да стартираме simple_pjsua и да проведем разговор през собствения си сървър. А след това и през други сървъри, като sip.linphone.org.
Изводи
В крайна сметка успяхме да стартираме simple_pjsua передача на гласа в двете посоки през сървъра. Проблемът с допълнително изразходваните 128 Кб SDRAM може да бъде решен чрез използване на малко по-мощен Cortex-M7 (например, STM32F769NI с 512 Кб RAM), но все пак не сме се отказали от надеждата да успеем и с 256 Кб 🙂 Ще се радваме, ако някой се заинтересува, а още по-добре — опита. Всички изходни кодове, както обикновено, са налични в нашия .
Източник: habr.com
