Mon projet peut être divisé en deux parties. Le premier est un téléphone, et je veux l'intégrer dans le moins de ressources possible. Le second est le développement d'une interface utilisateur minimale qui vous permet de recevoir un appel et de communiquer.
Téléphone à bord du STM32F769I-Discovery
Embox est un système d'exploitation configurable pour les systèmes embarqués. Une caractéristique distinctive est qu'il vous permet d'utiliser un logiciel Linux sans changer le code source sur des systèmes aux ressources limitées.
L'un des projets de téléphonie VOIP les plus populaires est PJSIP . Nous l'utiliserons à nos fins.
Construire PJSIP sous Linux
Vous devez d'abord télécharger, construire et exécuter la partie principale - PJSIP, une pile SIP open source. Téléchargez la dernière version . Pour le moment, il s'agit de la version 2.10.
Ensuite, vous devez créer le projet. C'est facile à faire pour votre système d'exploitation hôte. Dans mon cas, c'est Linux.
$ ./configure --prefix=~/pj_build
Ici, je n'ai spécifié aucune option sauf le préfixe, les chemins où les bibliothèques compilées et les fichiers d'en-tête seront installés. Cela est nécessaire pour analyser les éléments qui se retrouvent potentiellement dans notre microcontrôleur.
Puis nous exécutons
$ make dep $ make
Exécution de PJSIP sous Linux
Si tout s'est déroulé avec succès, nous avons compilé un PJSIP, ainsi qu'une application de démonstration.
Commençons quelque chose de simple mais fonctionnel. Nous avons besoin d'un appel dans les deux sens, prenez pjsip-apps / src / samples / simple_pjsua.c. Ceci est une application simple avec réponse automatique aux appels. Modifions l'exemple sélectionné simple_pjsua.c afin de spécifier le compte SIP. Les lignes suivantes en sont responsables:
#define SIP_DOMAIN "example.com"
#define SIP_USER "alice"
#define SIP_PASSWD "secret"
Nous reconstruisons et exécutons:
$ ./pjsip-apps/bin/samples/x86_64-unknown-linux-gnu/simple_pjsua
Quelque chose de similaire devrait apparaître:
15:21:22.181 pjsua_acc.c ....SIP outbound status for acc 0 is not active 15:21:22.181 pjsua_acc.c ....sip:bob@sip.linphone.org: registration success, status=200 (Registration successful), will re-register in 300 seconds 15:21:22.181 pjsua_acc.c ....Keep-alive timer started for acc 0, destination:91.121.209.194:5060, interval:15s
Vous pouvez désormais recevoir des appels entrants.
Construire PJSIP sur Embox
Mettons la même chose ensemble sur Embox. Tout d'abord, afin de ne pas vous soucier de la quantité de mémoire, nous allons faire un assemblage pour l'émulateur Qemu.
Embox dispose d'un mécanisme pour connecter des projets externes. Il vous permet de définir un lien pour télécharger des projets, d'appliquer des correctifs si nécessaire et de définir des règles pour trois étapes: configurer, construire, installer.
Pour utiliser ce mécanisme, il suffit d'indiquer que dans l'annotation '@Build' vous devez utiliser 'script = $ (EXTERNAL_MAKE)'.
@Build(stage=2,script="$(EXTERNAL_MAKE) PJSIP_ENABLE_CXX=false") @BuildArtifactPath(cppflags="-I$(abspath $(EXTERNAL_BUILD_DIR))/third_party/pjproject/core/install/include/") module core_c extends core { depends pjsip_dependencies }
Voici le Makefile utilisé pour porter l'assembly vers Embox:
PKG_NAME := pjproject
PKG_VER := 2.10
PKG_SOURCES := https://github.com/pjsip/pjproject/archive/$(PKG_VER).tar.gz
PKG_MD5 := 13e5c418008ae46c4ce0c1e27cdfe9b5
include $(EXTBLD_LIB)
PKG_PATCHES := pjproject-$(PKG_VER).patch \
sha256_error_fix-$(PKG_VER).patch \
addr_resolv_sock-$(PKG_VER).patch
…
DISABLE_FEATURES := \
l16-codec \
ilbc-codec \
speex-codec \
speex-aec \
gsm-codec \
g722-codec \
g7221-codec \
libyuv \
libwebrtc
$(CONFIGURE) :
export EMBOX_GCC_LINK=full; \
cd $(BUILD_ROOT) && ( \
./configure \
CC=$(EMBOX_GCC) \
CXX=$(EMBOX_GXX) \
--host=$(AUTOCONF_TARGET_TRIPLET) \
--target=$(AUTOCONF_TARGET_TRIPLET) \
--prefix=$(PJSIP_INSTALL_DIR) \
$(DISABLE_FEATURES:%=--disable-%) \
--with-external-pa; \
)
touch $@
$(BUILD) :
cd $(BUILD_ROOT) && ( \
$(MAKE) dep; \
$(MAKE) MAKEFLAGS='$(EMBOX_IMPORTED_MAKEFLAGS)'; \
)
touch $@
$(INSTALL) :
...
Comme vous pouvez le voir, ce sont les mêmes configure, make dep, make que pour Linux. Bien sûr, lors de la configuration, nous indiquons que vous devez utiliser la compilation croisée (--host, --target, CC, CXX) pour la plate-forme cible.
De plus, vous pouvez remarquer une autre différence. Nous spécifions --with-external-pa, c'est-à-dire que nous disons que pour l'audio, vous devez utiliser les pilotes d'Embox. Les pilotes audio d'Embox fournissent l'interface portaudio, également disponible sous Linux.
Comme vous pouvez le voir, nous avons désactivé la construction des bibliothèques libyuv et libwebrtc. Nous désactiverons également tous les codecs audio inutiles à l'avance, à l'exception de PCMA / PCMU. Nous vérifions l'exactitude de la configuration sous Linux:
$ ./configure \
--prefix=$PREFIX \
--disable-l16-codec \
--disable-ilbc-codec \
--disable-speex-codec \
--disable-speex-aec \
--disable-gsm-codec \
--disable-g722-codec \
--disable-g7221-codec \
--disable-libyuv \
--disable-libwebrtc
$ make dep && make
Pour un travail plus facile avec l'application simple_pjsua, déplaçons le code vers Embox. A partir des modifications, nous transférerons simplement le paramétrage des paramètres du compte SIP du code C-shny vers le fichier 'simple_pjsua_sip_account.inc', que nous mettrons dans les fichiers de configuration. Autrement dit, pour créer une application avec un compte différent, il vous suffit de modifier ce fichier. Le contenu reste le même:
#define SIP_DOMAIN <sip_domain>
#define SIP_USER <sip_user>
#define SIP_PASSWD <sip_passwd>
Exécutez l'application simple_pjsua comme précédemment sous Linux. Si cela fonctionne, PJSIP est configuré correctement. Ces options de configuration sont ensuite facilement portées vers le Makefile sur Embox.
Makefile final sous le spoiler
PKG_NAME := pjproject
PKG_VER := 2.10
PKG_SOURCES := https://github.com/pjsip/pjproject/archive/$(PKG_VER).tar.gz
PKG_MD5 := 13e5c418008ae46c4ce0c1e27cdfe9b5
include $(EXTBLD_LIB)
PKG_PATCHES := pjproject-$(PKG_VER).patch \
sha256_error_fix-$(PKG_VER).patch \
addr_resolv_sock-$(PKG_VER).patch
ifeq ($(PJSIP_ENABLE_CXX),false)
PKG_PATCHES += pjsua2_disable-$(PKG_VER).patch
endif
DISABLE_FEATURES := \
l16-codec \
ilbc-codec \
speex-codec \
speex-aec \
gsm-codec \
g722-codec \
g7221-codec \
libyuv \
libwebrtc \
#g711-codec
BUILD_ROOT := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VER)
PJSIP_INSTALL_DIR := $(EXTERNAL_BUILD_DIR)/third_party/pjproject/core/install
$(CONFIGURE) :
export EMBOX_GCC_LINK=full; \
cd $(BUILD_ROOT) && ( \
./configure \
CC=$(EMBOX_GCC) \
CXX=$(EMBOX_GXX) \
--host=$(AUTOCONF_TARGET_TRIPLET) \
--target=$(AUTOCONF_TARGET_TRIPLET) \
--prefix=$(PJSIP_INSTALL_DIR) \
$(DISABLE_FEATURES:%=--disable-%) \
--with-external-pa; \
)
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) :
cd $(BUILD_ROOT) && $(MAKE) install
# Remove AUTOCONF_TARGET_TRIPLET from file names to use them in Mybuild
for f in $(PJSIP_INSTALL_DIR)/lib/*-$(AUTOCONF_TARGET_TRIPLET).a; do \
fn=$$(basename $$f); \
cp $$f $(PJSIP_INSTALL_DIR)/lib/$${fn%-$(AUTOCONF_TARGET_TRIPLET).a}.a; \
done
# Copy binaries and
# remove AUTOCONF_TARGET_TRIPLET from file names to use them in Mybuild
for f in $(BUILD_ROOT)/pjsip-apps/bin/samples/$(AUTOCONF_TARGET_TRIPLET)/*; do \
cp $$f $(PJSIP_INSTALL_DIR)/$$(basename $$f).o; \
done
for f in $(BUILD_ROOT)/pjsip-apps/bin/*-$(AUTOCONF_TARGET_TRIPLET); do \
fn=$$(basename $$f); \
cp $$f $(PJSIP_INSTALL_DIR)/$${fn%-$(AUTOCONF_TARGET_TRIPLET)}.o; \
done
touch $@
Final Mybuild sous le spoiler
package third_party.pjproject module pjsip_dependencies { 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.posix.time.time depends embox.kernel.thread.thread_local_heap depends embox.driver.audio.portaudio_api } @DefaultImpl(core_c) abstract module core { } @Build(stage=2,script="$(EXTERNAL_MAKE) PJSIP_ENABLE_CXX=false") @BuildArtifactPath(cppflags="-I$(abspath $(EXTERNAL_BUILD_DIR))/third_party/pjproject/core/install/include/") module core_c extends core { depends pjsip_dependencies } /* Currently not used. It will be used for PJSUA2 if required. */ @Build(stage=2,script="$(EXTERNAL_MAKE) PJSIP_ENABLE_CXX=true") @BuildArtifactPath(cppflags="-I$(abspath $(EXTERNAL_BUILD_DIR))/third_party/pjproject/core/install/include/") @BuildDepends(third_party.STLport.libstlportg) module core_cxx extends core { depends pjsip_dependencies depends third_party.STLport.libstlportg } @BuildDepends(core) @Build(stage=2,script="true") static module libpjsip { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpjsip.a", "libpjsip-simple.a", "libpjsip-ua.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpjsua { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpjsua.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpjlib_util { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpjlib-util.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpj { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpj.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpjmedia { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpjmedia.a", "libpjmedia-codec.a", "libpjmedia-audiodev.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpjnath { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libpjnath.a" @NoRuntime depends core } @BuildDepends(core) @Build(stage=2,script="true") static module libpj_third_party { @AddPrefix("^BUILD/extbld/third_party/pjproject/core/install/lib") source "libresample.a", "libsrtp.a" @NoRuntime depends core } @BuildDepends(libpjsua) @BuildDepends(libpjsip) @BuildDepends(libpjmedia) @BuildDepends(libpj) @BuildDepends(libpjlib_util) @BuildDepends(libpjnath) @BuildDepends(libpj_third_party) @Build(stage=2,script="true") static module libpj_all { @NoRuntime depends libpjsua, libpjsip, libpjmedia, libpj, libpjlib_util, libpjnath, libpj_third_party } @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" } @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 }
Vous pouvez désormais recevoir des appels entrants, mais sur Embox.
Lancement de PJSIP sur STM32F769I-Discovery
Il reste à changer la configuration Embox de PJSIP pour QEMU à la configuration pour une carte spécifique - STM32F769I-Discovery. Pour configurer Embox, vous avez besoin de plusieurs composants:
- Fichier d'indicateurs de compilation (build.conf).
- Fichier de l'éditeur de liens décrivant la mémoire disponible et la manière dont l'image finale (lds.conf) y sera localisée.
- Fichier de configuration des modules Embox (mods.conf).
- Configuration PJSIP.
Les deux premiers points sont généralement faciles à comprendre. Ce sont des options de compilateur et de lieur, et elles changent rarement d'un projet à l'autre pour le même tableau. Sauf peut-être pour les drapeaux de compilation. Les principaux travaux de définition des caractéristiques du système final seront effectués dans les troisième et quatrième paragraphes.
Tout d'abord, examinons la configuration d'Embox. Quelle est la différence ici par rapport à l'exécution sous Linux? Sous Linux, nous avions une quantité de mémoire presque infinie, nous ne nous soucions pas du nombre de tâches, de la quantité de mémoire allouée, etc. Maintenant, nous n'avons que 2 Mo de ROM et 512 Mo de RAM, hors mémoire externe. En conséquence, il est nécessaire de définir le nombre de ressources dont nous avons besoin pour des besoins spécifiques.
Par exemple, PJSIP s'exécute sur son propre thread. Pour chaque nouvelle connexion, il y a un autre flux. Et un fil de plus pour travailler avec l'audio. Ainsi, même avec une connexion, nous avons besoin d'au moins 3 threads. Ensuite, nous voulons ajouter DHCP - nous sélectionnons un autre flux. Au total déjà 4. Tout cela est naturellement transféré dans la configuration:
include embox.kernel.thread.core(thread_pool_size=5,thread_stack_size=12000)
Nous avons défini les piles à une taille fixe. Vous pouvez demander des choses différentes. Tout dépend de la tâche.
Ensuite, sélectionnez le nombre de packages requis:
include embox.net.skbuff(amount_skb=28) include embox.net.skbuff_data(amount_skb_data=28)
Définissez la taille du tas (d'où malloc () fonctionne):
include embox.mem.heap_bm include embox.mem.static_heap(heap_size=0x3C000)
Sinon, la configuration reste la même que sur QEMU.
Connaître la taille du tas
La principale question qui se pose lors de l'élaboration d'une configuration est de savoir comment choisir les paramètres nécessaires? Par exemple, pourquoi le tas est 0x3C000, le nombre de paquets réseau est 28 et la pile est 12Kb? J'adopte souvent l'approche suivante. La première étape consiste à gérer les piles et le tas. Un tas de choses peuvent être explorées pour commencer sous Linux en utilisant Valgrind. Vous pouvez utiliser le profileur Valgrind-Massif pour cela. Il fonctionne sur des «instantanés» à certains moments et montre quelle fonction a demandé combien de mémoire.
Lancez valgrind avec notre application:
$ valgrind --tool=massif --time-unit=B --massif-out-file=pjsip.massif ./pjsip-apps/bin/samples/x86_64-unknown-linux-gnu/simple_pjsua
Après avoir exécuté l'application, nous visualisons les données à l'aide du massif-visualizer:
$ massif-visualizer pjsip.massif
Ici, vous pouvez voir que la mémoire est dépensée non seulement sur PJSIP, mais aussi sur la bibliothèque standard, ainsi que sur libasound (c'est le son hôte - ALSA). PJSIP lui-même demande les dimensions de la sous-parcelle rouge inférieure. C'est au sommet de 600 Kb, et pendant la connexion environ 320 Kb. Notre carte a 512 Ko de RAM. Par conséquent, nous essayons de configurer PJSIP, réduisant la consommation de mémoire ...
J'ai fait la configuration suivante:
#define PJ_LOG_USE_STACK_BUFFER 0
#define PJ_LOG_MAX_LEVEL 6
#define PJ_POOL_DEBUG 0
#define PJ_HAS_POOL_ALT_API 0
/* make PJSUA slim */
#define PJSUA_MAX_ACC 3
#define PJSUA_MAX_CALLS 1
#define PJSUA_MAX_VID_WINS 0
#define PJSUA_MAX_BUDDIES 1
#define PJSUA_MAX_CONF_PORTS 4
#define PJSUA_MAX_PLAYERS 1
#define PJSUA_MAX_RECORDERS 1
/* Changing to #if 0 will increase memory consumption
* but insreases communication speed. */
#if 1
/* This sample derived from pjlib/include/pj/config_site_sample.h: */
#define PJ_OS_HAS_CHECK_STACK 0
#define PJ_ENABLE_EXTRA_CHECK 0
#define PJ_HAS_ERROR_STRING 0
#undef PJ_IOQUEUE_MAX_HANDLES
#define PJ_IOQUEUE_MAX_HANDLES 8
#define PJ_CRC32_HAS_TABLES 0
#define PJSIP_MAX_TSX_COUNT 15
#define PJSIP_MAX_DIALOG_COUNT 15
#define PJSIP_UDP_SO_SNDBUF_SIZE 4000
#define PJSIP_UDP_SO_RCVBUF_SIZE 4000
#define PJMEDIA_HAS_ALAW_ULAW_TABLE 0
#endif
Copiez-le dans PJSIP dans le fichier pjlib / include / pj / config_site.h. Nous reconstruisons et courons. Analyse du résultat:
Au sommet, il est déjà d'environ 300 Ko, qui peuvent tenir sur une carte.
Ensuite, j'ai mis un tas d'environ 300 Ko dans Embox et défini les pools de débogage pour voir si quelque chose déborde (notez qu'en conséquence, la taille du tas a été réduite à 240 Ko). Le débogage des pools est activé avec l'option:
#define PJ_POOL_DEBUG 1
dans le même pjlib / include / pj / config_site.h.
D'accord, il ne reste plus qu'à configurer les piles de threads et le nombre de paquets réseau. Ici, vous devez répartir correctement les ressources restantes. Par exemple, s'il y a trop peu de paquets réseau, le son "s'étouffera" simplement. Si vous allouez trop de packages, il ne restera plus rien pour les piles. La priorité est bien sûr les piles. Si la pile tourne mal, tout est parti.
Ainsi, nous commençons avec la taille de pile maximale possible, puis nous la réduisons jusqu'à ce que le logiciel fonctionne sous charge. Si nous attrapons des dommages à la pile, nous nous arrêtons. Nous mettons les interruptions sur une pile séparée pour minimiser l'imprévisibilité. Autrement dit, votre pile est uniquement pour votre programme.
@Runlevel(0) include embox.arch.arm.armmlib.exception_entry(irq_stack_size=1024)
Après cela, nous attribuons les ressources restantes aux paquets réseau. Comme je l'ai mentionné ci-dessus, nous en avons eu 28.
Tout, la première partie a été réalisée avec succès. L'application Simple_pjsia s'exécute avec succès sur STM32F769I-Discovery dans la mémoire interne de 512 Ko.
Nous finalisons le téléphone SIP. Nous ajoutons l'interface utilisateur.
Après avoir lancé avec succès la version de la console, vous devez en quelque sorte ajouter l'interface utilisateur. Par souci de simplicité, nous supposerons qu'il comprend les éléments suivants. Lorsque l'application démarre, il devrait y avoir une sorte d'inscription explicative à l'écran. Par exemple, «PJSIP DEMO». S'il y a un appel entrant, l'écran affiche la provenance de l'appel et deux boutons avec des icônes apparaissent - «Accepter», «Refuser». L'appel peut être accepté ou rejeté. Si l'appel est accepté, la conversation commence, les informations de contact sur l'abonné s'affichent et un bouton reste à l'écran - «Hang». Si l'appel a été initialement rejeté - tout est trivial ici - nous revenons à l'image initiale avec «PJSIP DEMO».
Voici un exemple de ce à quoi cela devrait ressembler.
Développement d'un prototype sous Linux
Comme Embox avait déjà un support pour Nuklear, j'ai décidé d'utiliser ce projet. Bien que nous ayons déjà une version console de travail du téléphone sur le microcontrôleur, il est important ici qu'il soit beaucoup plus facile de modifier l'interface utilisateur sous Linux, comme c'était déjà le cas avec le paramètre PJSIP ci-dessus.
Pour ce faire, prenons deux exemples. Le premier exemple est simple_pjsua de PJSIP. Le deuxième exemple est demo / x11_rawfb / de Nuklear. Maintenant, notre tâche est de les faire fonctionner ensemble sous Linux.
La première chose que j'ai faite a été de remplacer la réponse automatique aux appels PJSIP par un événement externe (comme une pression sur un bouton). Ensuite, j'ai écrit la logique sur Nuklear.
Au cours du processus, il s'est avéré que pour une raison quelconque, les icônes n'étaient pas dessinées à l'intérieur des boutons. Dans l'image ci-dessous, vous pouvez voir les icônes de téléphone à l'intérieur des boutons vert et rouge. Ce sont des images ordinaires, sur lesquelles tout est 100% transparent à l'exception du récepteur téléphonique. Dans le même temps, au départ, seuls des carrés blancs ont été dessinés à leur place. Il s'agissait de l'implémentation du plugin rawfb. Apparemment, ce n'est pas très populaire, donc seul le curseur y est dessiné. J'ai ajouté du code qui copie simplement le contenu de l'image dans la bonne région de mémoire Nuklear.
En conséquence, après une journée de travail sur le projet, j'ai obtenu ce qui suit:
Sachant que le STM32F76I-Discovery a une taille d'écran de 800x480, et en QEMU 800x600, j'ai immédiatement défini les dimensions requises dans Nuklear, afin qu'il soit plus facile de naviguer dans la création d'étiquettes et de boutons dynamiques. Le code résultant est le suivant:
if (nk_begin(ctx, "Demo", nk_rect(0, 0, WIN_WIDTH, WIN_HEIGHT),
NK_WINDOW_NO_SCROLLBAR)) {
int answer_pressed = 0, decline_pressed = 0;
if (!draw_mouse) {
nk_style_hide_cursor(ctx);
}
nk_layout_row_static(ctx,
(WIN_HEIGHT - CALL_BTN_HEIGHT - 2 * CALL_INFO_TEXTBOX_HEIGHT - WIN_HEIGHT / 4), 15, 1);
nk_layout_row_dynamic(ctx, CALL_INFO_TEXTBOX_HEIGHT, 1);
nk_style_set_font(ctx, &rawfb_fonts[RAWFB_FONT_DEFAULT]->handle);
switch (call_info->state) {
case CALL_INACTIVE:
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 56;
nk_label(ctx, "PJSIP demo", NK_TEXT_CENTERED);
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 32;
break;
case CALL_INCOMING:
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 32;
nk_label(ctx, "Incoming call from:", NK_TEXT_CENTERED);
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 38;
nk_label(ctx, call_info->incoming, NK_TEXT_CENTERED);
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 32;
break;
case CALL_ACTIVE:
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 32;
nk_label(ctx, "Active call:", NK_TEXT_CENTERED);
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 38;
nk_label(ctx, call_info->remote_uri, NK_TEXT_CENTERED);
rawfb_fonts[RAWFB_FONT_DEFAULT]->handle.height = 32;
break;
}
if (call_info->state != CALL_INACTIVE) {
nk_layout_row_static(ctx, (WIN_WIDTH - 9 * 4) / 9, (WIN_WIDTH - 9 * 4) / 9, 9);
switch (call_info->state) {
case CALL_INCOMING:
nk_spacing(ctx, 2);
demo_nk_accept_btn(ctx, im_accept, &answer_pressed);
nk_spacing(ctx, 3);
demo_nk_decline_btn(ctx, im_decline, &decline_pressed);
nk_spacing(ctx, 2);
break;
case CALL_ACTIVE:
nk_spacing(ctx, 4);
demo_nk_decline_btn(ctx, im_decline, &decline_pressed);
nk_spacing(ctx, 4);
break;
default:
break;
}
}
if (answer_pressed && call_info->state == CALL_INCOMING) {
demo_pj_answer();
}
if (decline_pressed) {
demo_pj_hang();
}
}
nk_end(ctx);
Lancement à bord
Il reste à transférer le projet d'abord à QEMU, puis au conseil d'administration. Nous avons déjà tout prêt pour la version console, nous allons donc simplement transférer la nouvelle application de Linux. Pour ce faire, créez simplement un fichier Mybuild dans le système de construction Embox:
@AutoCmd @Cmd(name="sip_nuklear", help="", man="") @BuildDepends(third_party.pjproject.libpj_all) @BuildDepends(third_party.lib.nuklear) @Build(stage=2) module sip_nuklear { @InitFS source "icons/phone-accept-80.png", "icons/phone-decline-80.png", "fonts/Roboto-Regular.ttf" source "main.c" source "nuklear_main.c" @IncludePath("$(CONF_DIR)") @DefineMacro("PJ_AUTOCONF=1") source "pjsua.c" @NoRuntime depends third_party.pjproject.libpj_all @NoRuntime depends third_party.lib.nuklear depends embox.driver.input.core depends rawfb_api }
Comme vous pouvez le voir, les sources sont répertoriées. Les icônes et les polices se trouvent dans le système de fichiers interne et seront disponibles sous forme de fichiers normaux en lecture seule. Ajout de dépendances sur les bibliothèques pjsip et nuklear.
Après avoir exécuté l'application sur la carte, j'ai remarqué que la police par défaut de Nuclear avait l'air terrible sur l'écran STM32F769I. Certaines lettres ont tout simplement été perdues. Par exemple, "1" ressemblait à "|" et "m" ressemblait à "n". J'ai dû connecter des polices à partir de fichiers ttf - Roboto-Regular.ttf. Cette police occupe environ 150 Ko de mémoire flash, mais le texte est lisible.
Après avoir vérifié sous Linux, j'ai décidé qu'il s'agissait d'une somme modique. Et j'ai essayé d'utiliser différentes tailles de police 32 et 38. Mais j'ai eu un segfault. En fin de compte, j'ai abandonné l'idée de charger plusieurs tailles de police à partir d'un fichier, et je n'ai chargé que la 32e et je l'ai mise à l'échelle.
Caractéristiques du lancement sur matériel
Revenons au lancement du tableau. La chose importante à comprendre ici est que dans le cas de l'interface utilisateur, un framebuffer est nécessaire. Puisque nous voulons le mode plein écran et que l'écran est de 800x480, même avec une palette RVB de 1 octet, il nous fallait 800 * 480 * 1 = 384000 octets, soit 375 Ko. Étant donné que nous avons déjà occupé presque toute la mémoire interne de 512 Ko pour les besoins de PJSIP, il ne fonctionnera pas pour trouver une place pour le framebuffer. Pour cette raison, nous utiliserons la SDRAM. 16 Mo sont disponibles sur STM32F76I-Discovery. Comme nous utilisons déjà de la mémoire externe, nous n'économiserons pas beaucoup et mettrons RGBA 32 bits. Ainsi, le framebuffer sera de 800 * 480 * 4 = 1536000 octets ou 1,5 Mo.
Dans notre configuration, la SDRAM se trouve à l'adresse 0x60000000. Nous le spécifions comme l'adresse du framebuffer.
@Runlevel(1) include embox.driver.video.stm32f7_lcd( fb_base=0x60000000, width=800, height=480, ltdc_irq=88, bpp=32 ) include embox.driver.video.fb
J'ai déjà couvert les effets du scintillement lors de l'utilisation d'un tampon dans un autre article. Par conséquent, nous prendrons en compte le fait que le système utilise une double mémoire tampon et, par conséquent, nous avons besoin de mémoire supplémentaire pour un autre tampon de 1,5 Mo. De plus, les polices nécessiteront 256 Ko supplémentaires. Au total, vous devez augmenter le tas de 2 Mo. Nous le plaçons également en mémoire externe:
@Runlevel(2) include embox.driver.input.touchscreen.stm32f7cube_ts @Runlevel(2) include embox.driver.input.input_dev_devfs
Maintenant, l'écran tactile sera le périphérique / dev / stm32-ts dans le système de fichiers devfs dans Embox, et vous pouvez travailler avec lui via l'habituel open () / read ().
Ceci termine la configuration. Pourquoi presque? En fait, nous avons pris en compte toutes les nuances de mémoire, mais pas les performances. Si dans le cas de PJSIP le son a été bien transmis, alors lorsque vous essayez de le lancer avec des graphiques, il s'étouffe. Cet effet est bien sûr très difficile à déboguer sous Linux. Mais cela s'est avéré suffisant pour activer les caches de notre tableau.
@Runlevel(0) include embox.arch.arm.armmlib.armv7m_cpu_cache( log_level=4, sram_nocache_section_size=0x10000 )
Dans Embox, les descripteurs et les données de paquets réseau, ainsi que les tampons audio avec lesquels DMA fonctionne, sont situés dans une section spéciale de la mémoire marquée comme mémoire non cacheable dans le MPU. Ceci est nécessaire pour que l'état des objets dans cette mémoire soit toujours correct pour le CPU et le DMA.
En conséquence, nous obtenons un téléphone SIP fonctionnel très simple avec une interface utilisateur avec des boutons qui fonctionne très bien.
Ci-dessous, j'ai essayé de décrire l'allocation de mémoire finale:
Développement sur un système hôte
Mon processus de développement se résume à ce qui est montré dans la figure.
Et cela a pris très peu de temps. Un jour pour une application sur Linux et un jour de plus pour des améliorations sur la plateforme choisie. Oui, Embox avait déjà des pilotes d'affichage, de carte réseau et audio pour cette carte. Mais le développement de ces pièces prend également un peu de temps, pas plus d'une semaine pour chaque pilote. Il faut beaucoup plus de temps pour développer une telle fonctionnalité directement sur la carte. Dans notre cas, la plupart des fonctionnalités sont développées dans un environnement système hôte pratique. Cela nous a permis de réduire considérablement le temps de développement.
Il y a une vidéo des résultats au début de l'article. Si vous le souhaitez, vous pouvez tout reproduire vous-même selon les instructions du wiki .