Здравейте на всички.
Преди известно време ние разказахме как успяхме да стартираме SIP телефон на STM32F4-Discovery с 1 MB ROM и 192 KB 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")където сървер — това е SIP сървър (например, sip.linphone.org), username и password — името на потребителя и паролата за акаунта.
- Събираме Embox с командата , затова потребителите на Windows трябва да инсталират Cygwin или да се справят сами с Visual Studio и библиотеките.. За фърмуера на платката имаме информация на и в .
- Стартираме в конзолата на Embox командата “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 - Накрая, остава да свържем колонките или слушалки към аудио изхода и да говорим в два малки MEMS микрофона, разположени до дисплея. Обаждаме се от Linux чрез приложението simple_pjsua, pjsua. Или можем да използваме друго като linphone.
Всичко това е описано в нашия .
Как стигнахме до това
И така, първоначално възникна въпросът за избора на хардуерна платформа. Тъй като беше ясно, че STM32F4-Discovery не е подходящ за размера на паметта, избрахме STM32F7-Discovery. Тя разполага с 1 MB флаш памет и 256 KB RAM (+ 64 KB специална бърза памет, която също ще използваме). Не е много за обаждания през сървър, но решихме да опитаме.
Условно разделихме задачата на няколко етапа:
- Стартиране на PJSIP на QEMU. Това беше удобно за отстраняване на проблеми, плюс там вече имаме поддръжка за кодек AC97.
- Запис на глас и възпроизвеждане на QEMU и на STM32.
- Портирование на приложението simple_pjsua от PJSIP. То позволява регистрация на SIP сървъра и извършване на обаждания.
- Постепенно разгръщане на собствен сървър на база Asterisk и тестване на него, след което да пробваме външни, като sip.linphone.org
Звук в Embox работи чрез Portaudio, който се използва и в PISIP. На QEMU се появиха първите проблеми — WAV файлове на 44100 Hz се възпроизвеждаха добре, но на 8000 нещо явно не беше наред. Оказа се, че проблемът е в настройката на честотата — по подразбиране в хардуера тя беше 44100 и при нас това не се променяше програмно.
Тук вероятно е добре да поясня как всъщност става възпроизвеждането на звука. На звуковата карта може да се зададе указател на блок от памет, от който трябва да се възпроизвежда или записва на предварително зададена честота. След като буферът се изчерпи, се генерира прекъсване и изпълнението продължава от следващия буфер. Работата е там, че тези буфери трябва да бъдат запълнени навреме, докато се възпроизвежда предишният. С този проблем ще се срещнем и по-късно на STM32F7.
След това наехме сървър и разгръщахме Asterisk на него. Тъй като беше необходимо много отстраняване на грешки, а говоренето в микрофона не беше приятно, трябваше да направим автоматично възпроизвеждане и запис. Затова патчнахме simple_pjsua, така че да можем да подадем файлове вместо аудиоустройства. В PJSIP това се прави доста лесно, тъй като те имат понятието порт, който може да бъде както устройство, така и файл. И тези портове могат да бъдат гъвкаво свързвани с други портове. Може да видите кода в нашия pjsip. . В крайна сметка схемата беше следната. На сървъра Asterisk създадох два акаунта — за Linux и за Embox. След това на Embox се изпълнява командата simple_pjsua_imported, Embox се регистрира на сървъра, след което от Линукс се обаждаме на Embox. В момента на свързването проверяваме на сървъра Asterisk, че връзката е установена и след известно време трябва да чуем в Embox звука от Линукс, а в Линукс запазваме файла, който се възпроизвежда от 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) синхронно с аудио входа (не се задълбочавах в детайли, но се оказва, че те споделят общия clock и при инициализацията на аудио изхода по някакъв начин се свързва аудио входът). Няма как да ги стартираме поотделно, затова направихме следното — винаги работят (включително се генерират прекъсвания) аудио вход и аудио изход. Но когато в системата не се възпроизвежда нищо, просто подмятаме на аудио изхода празен буфер, а когато започне възпроизвеждането, честно го запълваме.
Следващият проблем, който срещнахме, беше, че звукът при запис на глас беше много тих. Това се случва, защото 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
