Всем привет.
В этой статье я расскажу, как мы делали sip-телефон на базе stm32f4-discovery с помощью своей встраиваемой ОС Embox. Характеристики stm32f4-discovery (144MHz, 192Kb RAM, 1Mb ROM) могут вызывать сомнения о возможности такой реализации. Нам самим было интересно, получится ли? В качестве ответа можно посмотреть видео, а в самой статье — технические подробности.
ПО этого устройства состоит из софт-телефона PJSIP и уже упомянутой ОС Embox. PJSIP отвечает за поддержку sip-протокола, декодирование звука. Embox предоставляет PJSIP стандартную библиотеку, библиотеку pthread, сетевой стек, драйвера для вывода аудио и остальной набор функций ОС. Для этого используется POSIX-интерфейс, что позволяет практически без изменений перенести широкий класс приложений с unix-подобных ОС на встраиваемые системы. Перенос PJSIP на ОС Embox прошел ожидаемо легко, а вот с запуском PJSIP на конкретной аппаратной платформе возникли некоторые трудности. О них — дальше.
Для тех, кто хочет повторить наш успех, мы подготовили небольшую инструкцию. Вам потребуется stm32f4discovery + stm32dis-bb, подключение по com-порту к ней (есть на плате расширения), хост с линуксом, ethernet соединение между платой и хостом.
Нужно взять github.com/embox/pjsip и перейти в этот каталог. Здесь лежат скрипты сборки, приложения и файлы, которые мы использовали для подготовки к демонстрации.
Первый простой шаг заключается в запуске Embox'а на discovery. В качестве формата образа мы используем ELF.
Можно взять предварительно собранный образ, файл embox.
Либо можно собрать Embox самостоятельно. Для этого:
После прошивки нужно подключиться по последовательному порту к discovery, подключить эмулятор терминала, выставить настройки 115200 8n1. После запуска система выведет немного статусной информации, отобразит и выполнит команды стартового скрипта и предоставит шелл-интерфейс. Далее предполагается, что все команды нужно выполнять в консоли discovery.
Далее, нужно собрать и запустить хост-клиент. Возвращаемся на консоль на хосте.
Нам очень хотелось сделать на обычной встраиваемой платформе что-то необычное. Было понятно, что платформа должна быть очень популярной и дешёвой, поэтому выбрали stm32f4-discovery — не 8-разрядный контроллер, но и не гигагерцовая RaspberryPi или Beaglebone. Стали смотреть, что есть на discovery: помимо ethernet обратили внимание на возможность вывода звука.
А тут сошлись звёзды: коллеги “по цеху” разрабатывали sip-телефон на OMAP137 с Линуксом. И писать специальный софт там было какое-то мучение: bitbake, который с минуту думает о том, что и как ему собирать, рекомендации чистить проект по любому поводу, а первая сборка занимает с несколько часов.
Поэтому мы для себя устроили challenge: SIP-устройство, максимально похожее по функциональности на телефон, на существенно более слабом железе.
Дополнительным пунктом в этом разделе стал комментарий Indemsys, в котором он спрашивал про софт, который есть только на Линуксе и нет на контроллерах (голом железе или классических RTOS).
Первый этап — выбор стороннего ПО для поддержки sip-протокола и сопутствующей передачи аудио. Первым в результатах выдачи google оказался www.pjsip.org, который умеет всё нужное, позволяет обработку топором и напильником в широких масштабах, переносим и имеет низкие требования к объему ОЗУ (заявляются 150кб).
Для начала нужно создать файлы описания сборки. Для тех, кто слышит о Embox в первый раз: у нас используется своя система сборки (статья на хабре), которая оперирует понятием “модуль”. Для облегчения сборки стороннего ПО мы ввели расширение, которое поддерживает автоматическое скачивание, применение патчей, конфигурацию, сборку и установку.
Библиотека PJSIP будет как раз модулем. Первый зафиксированный коммит на эту тему. Здесь можно оценить, как описание сборки отличается от сборки для хост системы(никак) и какие модификации понадобились в PJSIP: отключаем плюсовые приложения для простоты; сообщаем об отсутствии SOCK_RDM.
В дальнейшем эти файлы конечно менялись, но основа осталась той же. Дополнительно понадобились объявления макро-значений для уменьшения объема структур в памяти, отключения кодеков, и т.д. Современный вид описаний можно оценить под спойлером.
Для демонстрации мы изначально использовали пример под названием streamutil, который умеет передавать только медиа-поток.
После того как мы добавили скрипты сборки и немного помахали напильником, streamutil стал останавливаться с сообщением об отсутствующем устройстве для вывода звука. С нуля воспроизведение делать было не нужно, производитель поставляет демо-приложения для проигрывания wav-файлов с флешки. Этот код и послужил основой для будущей реализации.
С другой стороны, стоял вопрос, с помощью какого интерфейса будет осуществляться взаимодействие: можно разработать звуковой плагин для PJSIP или использовать один из уже существующих для популярных звуковых API. Среди них оказался portaudio, заметно более простой чем, например, ALSA API. Мы приступили к реализации драйвера с этим интерфейсом.
В двух словах интерфейс описывается так: регистрируем callback, который будет вызываться при необходимости, когда буфер звуковой карты будет близок к опустошению. Замечательный простой API не регламентирует, в каком контексте будет вызываться callback, и в реальных системах для этого запускается отдельный поток, который спит большую часть времени и выполняет callback в нужный момент времени.
С такой схемой возникают проблемы типа “дырявая абстракция”: PJSIP явно предполагает отдельный поток. Попытки вызвать callback по сигналу приводили к deadlock’у. Пришлось делать как во “взрослых” системах, с потоками. В конце этого этапа драйвер стал выглядеть так.
В будущем, мы собираемся по возможности отказаться от использования обычных потоков в пользу легких потоков. Главным отличием лёгкого потока является отсутствие собственного стека, вычисления ведутся на стеке последнего выполнявшегося потока. К легким потокам предоставляется pthread-совместимый интерфейс с доступными средствами синхронизации. В теории, это позволит использовать их в качестве реализации portaudio-потока, и тем самым сократить потребление памяти.
Основной проблемой, с которой мы боролись, была нехватка динамической памяти.
Здесь надо сделать ремарку про динамическое выделение в нашей системе. Мы используем pool’ы — зарезервированные участки статической памяти для фиксированного количества объектов фиксированного же размера. Способ не самый гибкий и порой заставляющий писать некоторое количество boilerplate кода, зато взамен получаем возможность контролировать расход памяти и отлавливать потенциальную нехватку памяти на этапе компиляции. На самом деле, конфигурировать размер pool’ов гораздо проще, чем может показаться.
Во-первых, они задаются в глобальном файле конфигурации, где описаны все остальные параметры системы, такие как: подсистемы, команды, политики управления, например, планировщиком. Размер этого файла редко превышает 100 строк, правда это число может сильно зависеть от состава системы.
Во-вторых, размеры pool’ов часто являются следствиями естественных требований, например, в нашей stm32f4discovery может быть только 2 сетевых адаптера: набортный ethernet и loopback. Удивительно, но такую определённость можно наблюдать в других подсистемах: число открытых файлов, приоритетов планировщика, количества обработчиков irq, многое другое — всё это хорошо поддаётся анализу во встроенной системе.
Возвращаясь к PJSIP, он использует собственную библиотеку для динамического выделения памяти, которая, в свою очередь, реализована поверх стандартных вызовов malloc/free. Предсказать потребность streamtuil в объеме динамической памяти достаточно трудно. Но так как вся куча монопольно используется streamutil, мы можем контролировать её размер и расширять за счёт системных pool’ов.
Одним из самых больших pool’ов в системе были буфера сетевых пакетов. Тут я сделал небольшой хак: изменил максимальный размер пакета со стандартных 1514 байт до 214, зато увеличил максимальное количество пакетов. Дело в том, что тестирование показало: от/к streamutil ходят пакеты не более 214 байт, поэтому такой трюк сработал.
Нам понадобилось несколько итераций увеличения кучи, но после них streamutil стал выдавать чистый звук.
Понятно, что streamutil — демо-приложение, мало похожее на нужный телефон. Нужно было взять что-то более функциональное. Хороший кандидат — pjsua, референсный sip-телефон на PJSIP. Модифицировали скрипты сборки, начали запускать. Не хватает памяти. Окей, утянули размеры pool’ов ещё туже. Не хватает памяти. Тут нас начали терзать смутные сомнения, а возможно ли в принципе удовлетворить запросы pjsua?
Первая попытка это выяснить заключалась в трассировке запросов pjsua к malloc/free на хосте. Но, оказалось, стандартная библиотека слишком активно использует динамическое выделение для собственных нужд (например, форматирование даты в строку), поэтому вывод трассировки был сильно захламлён.
Мы пошли другим путем: в своей стандартной библиотеке мы не используем malloc/free, поэтому трасса будет гораздо более информативной. Мы добавили трассировку в библиотеку выделения памяти, затем собрали embox с pjsua для beagleboard и запустили его на qemu. Результаты нас несколько расстроили, получилось чуть больше 200кб только на кучу, без учёта фрагментации и статической памяти pjsua. Такой объем в stm32f4 не поместится. Возможных выходов было два: серьёзным образом модифицировать pjsua, либо искать более легкую альтернативу.
К счастью нашлась альтернатива — демо-приложение simpleua. Трассировка динамического выделения simpleua показала максимальный размер живой кучи около 110кб. Если сложить с .data (~4кб) и .bss (~25кб), то остаётся достаточно места под основной стек и потоки, буфера сетевых пакетов и аудио-интерфейса.
В процессе реализации пришлось добавить поддержку нескольких регионов памяти для кучи, увеличить размер пакета (требуется 810 байт) и уменьшить их количество.
Результат вы видели на видео.
Статья написана сразу по успешному запуску. Мы не успели отловить возможные баги, поэтому, если встретите их, вы предупреждены. А если серьезно, мы будем рады любым отзывам и комментариям. Спасибо за внимание!
В этой статье я расскажу, как мы делали sip-телефон на базе stm32f4-discovery с помощью своей встраиваемой ОС Embox. Характеристики stm32f4-discovery (144MHz, 192Kb RAM, 1Mb ROM) могут вызывать сомнения о возможности такой реализации. Нам самим было интересно, получится ли? В качестве ответа можно посмотреть видео, а в самой статье — технические подробности.
Как это работает
ПО этого устройства состоит из софт-телефона PJSIP и уже упомянутой ОС Embox. PJSIP отвечает за поддержку sip-протокола, декодирование звука. Embox предоставляет PJSIP стандартную библиотеку, библиотеку pthread, сетевой стек, драйвера для вывода аудио и остальной набор функций ОС. Для этого используется POSIX-интерфейс, что позволяет практически без изменений перенести широкий класс приложений с unix-подобных ОС на встраиваемые системы. Перенос PJSIP на ОС Embox прошел ожидаемо легко, а вот с запуском PJSIP на конкретной аппаратной платформе возникли некоторые трудности. О них — дальше.
Для тех, кто хочет повторить наш успех, мы подготовили небольшую инструкцию. Вам потребуется stm32f4discovery + stm32dis-bb, подключение по com-порту к ней (есть на плате расширения), хост с линуксом, ethernet соединение между платой и хостом.
Нужно взять github.com/embox/pjsip и перейти в этот каталог. Здесь лежат скрипты сборки, приложения и файлы, которые мы использовали для подготовки к демонстрации.
Первый простой шаг заключается в запуске Embox'а на discovery. В качестве формата образа мы используем ELF.
Можно взять предварительно собранный образ, файл embox.
Либо можно собрать Embox самостоятельно. Для этого:
- склонируйте github.com/embox/embox
- загрузите правильные конфиги, выполнив
make confload-platform/pjsip/stm32f4discovery
- соберите, выполнив
make
- целевой образ находится по пути build/base/bin/embox
После прошивки нужно подключиться по последовательному порту к discovery, подключить эмулятор терминала, выставить настройки 115200 8n1. После запуска система выведет немного статусной информации, отобразит и выполнит команды стартового скрипта и предоставит шелл-интерфейс. Далее предполагается, что все команды нужно выполнять в консоли discovery.
- Eсли дефолтный IP-адрес вас не устраивает, следует выполнить
route del 192.168.1.0 dev eth0 ifconfig eth0 (ip-адрес) netmask (маска) up" route add (подсеть) netmask (маска) eth0
- выполните pjsip_simpleua
Далее, нужно собрать и запустить хост-клиент. Возвращаемся на консоль на хосте.
- скачайте www.pjsip.org/release/2.4/pjproject-2.4.tar.bz2 и положите в текущий каталог (pjsip)
- для сборки хост-библиотеки PJSIP, скомандуйте
./build_pjproject.sh
- Для того, чтобы собрать непосредственно хост-клиент, скомандуйте
make
- запустите
simpleua sip:test@(ip-адрес платы)
Зачем это?
Нам очень хотелось сделать на обычной встраиваемой платформе что-то необычное. Было понятно, что платформа должна быть очень популярной и дешёвой, поэтому выбрали stm32f4-discovery — не 8-разрядный контроллер, но и не гигагерцовая RaspberryPi или Beaglebone. Стали смотреть, что есть на discovery: помимо ethernet обратили внимание на возможность вывода звука.
А тут сошлись звёзды: коллеги “по цеху” разрабатывали sip-телефон на OMAP137 с Линуксом. И писать специальный софт там было какое-то мучение: bitbake, который с минуту думает о том, что и как ему собирать, рекомендации чистить проект по любому поводу, а первая сборка занимает с несколько часов.
Поэтому мы для себя устроили challenge: SIP-устройство, максимально похожее по функциональности на телефон, на существенно более слабом железе.
Дополнительным пунктом в этом разделе стал комментарий Indemsys, в котором он спрашивал про софт, который есть только на Линуксе и нет на контроллерах (голом железе или классических RTOS).
Подготовка
Первый этап — выбор стороннего ПО для поддержки sip-протокола и сопутствующей передачи аудио. Первым в результатах выдачи google оказался www.pjsip.org, который умеет всё нужное, позволяет обработку топором и напильником в широких масштабах, переносим и имеет низкие требования к объему ОЗУ (заявляются 150кб).
Для начала нужно создать файлы описания сборки. Для тех, кто слышит о Embox в первый раз: у нас используется своя система сборки (статья на хабре), которая оперирует понятием “модуль”. Для облегчения сборки стороннего ПО мы ввели расширение, которое поддерживает автоматическое скачивание, применение патчей, конфигурацию, сборку и установку.
Библиотека PJSIP будет как раз модулем. Первый зафиксированный коммит на эту тему. Здесь можно оценить, как описание сборки отличается от сборки для хост системы
В дальнейшем эти файлы конечно менялись, но основа осталась той же. Дополнительно понадобились объявления макро-значений для уменьшения объема структур в памяти, отключения кодеков, и т.д. Современный вид описаний можно оценить под спойлером.
Модули PJSIP
package third_party.pjproject
@Build(stage=2,script="$(EXTERNAL_MAKE)")
@BuildDepends(third_party.STLport.core)
module core {
depends embox.net.lib.getifaddrs
depends embox.compat.posix.pthreads
depends embox.compat.posix.pthread_key
depends embox.compat.posix.pthread_rwlock
depends embox.compat.posix.semaphore
depends embox.compat.posix.fs.fsop
depends embox.compat.posix.idx.select
depends embox.compat.posix.net.getaddrinfo
depends embox.compat.posix.net.gethostbyname
depends embox.compat.posix.util.gethostname
depends embox.compat.posix.proc.pid
depends embox.compat.posix.proc.exit
depends embox.compat.libc.stdio.fseek
depends embox.compat.libc.time
depends third_party.STLport.core
}
@AutoCmd
@Cmd(name="streamutil", help="", man="")
@BuildDepends(core)
@Build(stage=2,script="true")
module streamutil {
source "^BUILD/extbld/third_party/pjproject/core/install/streamutil.o"
depends core
}
@AutoCmd
@Cmd(name="pjsua", help="", man="")
@BuildDepends(core)
@Build(stage=2,script="true")
module pjsua {
source "^BUILD/extbld/third_party/pjproject/core/install/pjsua.o"
depends core
}
@AutoCmd
@Cmd(name="pjsip_simpleua", help="", man="")
@BuildDepends(core)
@Build(stage=2,script="true")
module simpleua {
source "^BUILD/extbld/third_party/pjproject/core/install/simpleua.o"
depends core
}
Описание сборки PJSIP
PKG_NAME := pjproject
PKG_VER := 2.2.1
PKG_SOURCES := http://www.pjsip.org/release/$(PKG_VER)/$(PKG_NAME)-$(PKG_VER).tar.bz2
PKG_MD5 := 6ed4bb7750c827dc1d881e209a3b62db
include $(EXTBLD_LIB)
PKG_PATCHES := pjproject.patch simpleua_default_loglevel.patch mutex_loglevel_increase.patch kmalloc.patch
DISABLE_FEATURES := l16-codec ilbc-codec speex-codec speex-aec gsm-codec g722-codec g7221-codec #g711-codec
PJPROJECT_ROOT := $(ROOT_DIR)/third-party/pjproject
PJSIP_CPPFLAGS := -I$(PJPROJECT_ROOT)/include $(EMBOX_DEPS_CPPFLAGS) -I$(SRC_DIR)/compat/cxx/include
BUILD_ROOT := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VER)
$(CONFIGURE) :
export EMBOX_GCC_LINK=full; cd $(BUILD_ROOT) && ( ./aconfigure CC=$(EMBOX_GCC) CXX=$(EMBOX_GXX) CPPFLAGS="$(PJSIP_CPPFLAGS)" --host=$(AUTOCONF_TARGET_TRIPLET) --target=$(AUTOCONF_TARGET_TRIPLET) --prefix=/ $(DISABLE_FEATURES:%=--disable-%) --with-external-pa; echo export CFLAGS+="$(PJSIP_CPPFLAGS)" > user.mak; echo export CXXFLAGS+="$(PJSIP_CPPFLAGS)" >> user.mak; )
cp ./config_site.h $(BUILD_ROOT)/pjlib/include/pj/config_site.h
touch $@
$(BUILD) :
cd $(BUILD_ROOT) && ( make -j1 dep; make -j1 MAKEFLAGS='$(EMBOX_IMPORTED_MAKEFLAGS)'; )
touch $@
$(INSTALL) :
for f in $(BUILD_ROOT)/pjsip-apps/bin/samples/$(AUTOCONF_TARGET_TRIPLET)/*; do cp $$f $(PKG_INSTALL_DIR)/$$(basename $$f).o; done
for f in $(BUILD_ROOT)/pjsip-apps/bin/*-$(AUTOCONF_TARGET_TRIPLET); do fn=$$(basename $$f); cp $$f $(PKG_INSTALL_DIR)/$${fn%-$(AUTOCONF_TARGET_TRIPLET)}.o; done
touch $@
Звук
Для демонстрации мы изначально использовали пример под названием streamutil, который умеет передавать только медиа-поток.
После того как мы добавили скрипты сборки и немного помахали напильником, streamutil стал останавливаться с сообщением об отсутствующем устройстве для вывода звука. С нуля воспроизведение делать было не нужно, производитель поставляет демо-приложения для проигрывания wav-файлов с флешки. Этот код и послужил основой для будущей реализации.
С другой стороны, стоял вопрос, с помощью какого интерфейса будет осуществляться взаимодействие: можно разработать звуковой плагин для PJSIP или использовать один из уже существующих для популярных звуковых API. Среди них оказался portaudio, заметно более простой чем, например, ALSA API. Мы приступили к реализации драйвера с этим интерфейсом.
В двух словах интерфейс описывается так: регистрируем callback, который будет вызываться при необходимости, когда буфер звуковой карты будет близок к опустошению. Замечательный простой API не регламентирует, в каком контексте будет вызываться callback, и в реальных системах для этого запускается отдельный поток, который спит большую часть времени и выполняет callback в нужный момент времени.
С такой схемой возникают проблемы типа “дырявая абстракция”: PJSIP явно предполагает отдельный поток. Попытки вызвать callback по сигналу приводили к deadlock’у. Пришлось делать как во “взрослых” системах, с потоками. В конце этого этапа драйвер стал выглядеть так.
В будущем, мы собираемся по возможности отказаться от использования обычных потоков в пользу легких потоков. Главным отличием лёгкого потока является отсутствие собственного стека, вычисления ведутся на стеке последнего выполнявшегося потока. К легким потокам предоставляется pthread-совместимый интерфейс с доступными средствами синхронизации. В теории, это позволит использовать их в качестве реализации portaudio-потока, и тем самым сократить потребление памяти.
Нехватка памяти
Основной проблемой, с которой мы боролись, была нехватка динамической памяти.
Здесь надо сделать ремарку про динамическое выделение в нашей системе. Мы используем pool’ы — зарезервированные участки статической памяти для фиксированного количества объектов фиксированного же размера. Способ не самый гибкий и порой заставляющий писать некоторое количество boilerplate кода, зато взамен получаем возможность контролировать расход памяти и отлавливать потенциальную нехватку памяти на этапе компиляции. На самом деле, конфигурировать размер pool’ов гораздо проще, чем может показаться.
Во-первых, они задаются в глобальном файле конфигурации, где описаны все остальные параметры системы, такие как: подсистемы, команды, политики управления, например, планировщиком. Размер этого файла редко превышает 100 строк, правда это число может сильно зависеть от состава системы.
Во-вторых, размеры pool’ов часто являются следствиями естественных требований, например, в нашей stm32f4discovery может быть только 2 сетевых адаптера: набортный ethernet и loopback. Удивительно, но такую определённость можно наблюдать в других подсистемах: число открытых файлов, приоритетов планировщика, количества обработчиков irq, многое другое — всё это хорошо поддаётся анализу во встроенной системе.
Возвращаясь к PJSIP, он использует собственную библиотеку для динамического выделения памяти, которая, в свою очередь, реализована поверх стандартных вызовов malloc/free. Предсказать потребность streamtuil в объеме динамической памяти достаточно трудно. Но так как вся куча монопольно используется streamutil, мы можем контролировать её размер и расширять за счёт системных pool’ов.
Одним из самых больших pool’ов в системе были буфера сетевых пакетов. Тут я сделал небольшой хак: изменил максимальный размер пакета со стандартных 1514 байт до 214, зато увеличил максимальное количество пакетов. Дело в том, что тестирование показало: от/к streamutil ходят пакеты не более 214 байт, поэтому такой трюк сработал.
Нам понадобилось несколько итераций увеличения кучи, но после них streamutil стал выдавать чистый звук.
SIP-телефон
Понятно, что streamutil — демо-приложение, мало похожее на нужный телефон. Нужно было взять что-то более функциональное. Хороший кандидат — pjsua, референсный sip-телефон на PJSIP. Модифицировали скрипты сборки, начали запускать. Не хватает памяти. Окей, утянули размеры pool’ов ещё туже. Не хватает памяти. Тут нас начали терзать смутные сомнения, а возможно ли в принципе удовлетворить запросы pjsua?
Первая попытка это выяснить заключалась в трассировке запросов pjsua к malloc/free на хосте. Но, оказалось, стандартная библиотека слишком активно использует динамическое выделение для собственных нужд (например, форматирование даты в строку), поэтому вывод трассировки был сильно захламлён.
Мы пошли другим путем: в своей стандартной библиотеке мы не используем malloc/free, поэтому трасса будет гораздо более информативной. Мы добавили трассировку в библиотеку выделения памяти, затем собрали embox с pjsua для beagleboard и запустили его на qemu. Результаты нас несколько расстроили, получилось чуть больше 200кб только на кучу, без учёта фрагментации и статической памяти pjsua. Такой объем в stm32f4 не поместится. Возможных выходов было два: серьёзным образом модифицировать pjsua, либо искать более легкую альтернативу.
К счастью нашлась альтернатива — демо-приложение simpleua. Трассировка динамического выделения simpleua показала максимальный размер живой кучи около 110кб. Если сложить с .data (~4кб) и .bss (~25кб), то остаётся достаточно места под основной стек и потоки, буфера сетевых пакетов и аудио-интерфейса.
В процессе реализации пришлось добавить поддержку нескольких регионов памяти для кучи, увеличить размер пакета (требуется 810 байт) и уменьшить их количество.
Результат вы видели на видео.
Вывод
Статья написана сразу по успешному запуску. Мы не успели отловить возможные баги, поэтому, если встретите их, вы предупреждены. А если серьезно, мы будем рады любым отзывам и комментариям. Спасибо за внимание!
Комментарии (5)
iliasam
08.06.2015 09:29А в каком формате передается звук?
antonkozlov Автор
08.06.2015 11:37Проверил, PCM. Странно, должен был быть g711. Попробую запустить g711/g712.
antonkozlov Автор
08.06.2015 12:19+1Так, глупость какую-то сморозил. g711 и есть PCM, конкретно PCMU/8000. Сейчас используется он.
iliasam
Насколько я понял, микрофон на stm32f4discovery не использовали?
antonkozlov Автор
Из-за того, что делали в свободное время, да, микрофон к сожалению не поддерживаем.
А перед тем, как приступать к микрофону, хотелось получить сначала какой-нибудь отклик на такой девайс. И потом уже пилить микрофон.
Я видел, что просто микрофон запустить легко. А вот можно ли запустить воспроизведение и запись одновременно, и какого типа гарнитуру для этого использовать ещё не исследовал.