Proyek saya dapat dibagi menjadi dua bagian. Yang pertama adalah telepon, dan saya ingin memasukkannya ke dalam sumber daya sesedikit mungkin. Yang kedua adalah pengembangan antarmuka pengguna minimal yang memungkinkan Anda menerima panggilan dan berkomunikasi.
Telepon di papan STM32F769I-Discovery
Embox adalah OS yang dapat dikonfigurasi untuk sistem tertanam. Fitur khasnya adalah memungkinkan Anda untuk menggunakan perangkat lunak Linux tanpa mengubah kode sumber pada sistem dengan sumber daya terbatas.
Salah satu proyek telepon VOIP yang paling populer adalah PJSIP . Kami akan menggunakannya untuk tujuan kami.
Membangun PJSIP di Linux
Pertama, Anda perlu mengunduh, membangun, dan menjalankan bagian utama - PJSIP, tumpukan SIP sumber terbuka. Unduh versi terbaru . Saat ini ini adalah versi 2.10.
Selanjutnya, Anda perlu membangun proyek. Ini mudah dilakukan untuk OS host Anda. Dalam kasus saya, ini Linux.
$ ./configure --prefix=~/pj_build
Di sini saya tidak menentukan opsi apa pun kecuali awalan, jalur tempat pustaka yang dikompilasi dan file header akan diinstal. Ini diperlukan untuk menganalisis hal-hal yang berpotensi berakhir di mikrokontroler kita.
Lalu kami mengeksekusi
$ make dep $ make
Menjalankan PJSIP di Linux
Jika semuanya berhasil diselesaikan, maka kami telah menyusun PJSIP, serta aplikasi demo.
Mari kita mulai sesuatu yang sederhana namun fungsional. Kami membutuhkan panggilan di kedua arah, ambil pjsip-apps / src / samples / simple_pjsua.c. Ini adalah aplikasi sederhana dengan menjawab panggilan otomatis. Mari kita edit contoh simple_pjsua.c yang dipilih untuk menentukan akun SIP. Baris berikut bertanggung jawab untuk ini:
#define SIP_DOMAIN "example.com"
#define SIP_USER "alice"
#define SIP_PASSWD "secret"
Kami membangun kembali dan menjalankan:
$ ./pjsip-apps/bin/samples/x86_64-unknown-linux-gnu/simple_pjsua
Sesuatu yang serupa akan muncul:
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
Sekarang Anda dapat menerima panggilan masuk.
Membangun PJSIP di Embox
Mari kita gabungkan hal yang sama di Embox. Pertama, agar tidak mengkhawatirkan jumlah memori, kami akan membuat assembly untuk emulator Qemu.
Embox memiliki mekanisme untuk menghubungkan proyek eksternal. Ini memungkinkan Anda menyetel tautan untuk mengunduh proyek, menerapkan tambalan jika diperlukan, dan menetapkan aturan untuk tiga tahap: konfigurasi, buat, instal.
Untuk menggunakan mekanisme ini, cukup dengan menunjukkan bahwa dalam anotasi '@Build' Anda perlu menggunakan '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 }
Ini adalah Makefile yang digunakan untuk port perakitan ke 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) :
...
Seperti yang Anda lihat, ini adalah konfigurasi yang sama, make dep, make seperti untuk Linux. Tentu saja, saat mengonfigurasi, kami menunjukkan bahwa Anda perlu menggunakan kompilasi silang (--host, --target, CC, CXX) untuk platform target.
Selain itu, Anda bisa melihat perbedaan lainnya. Kami menetapkan --with-external-pa, artinya, untuk audio Anda perlu menggunakan driver dari Embox. Driver audio di Embox menyediakan antarmuka portaudio, yang juga tersedia di Linux.
Seperti yang Anda lihat, kami telah menonaktifkan pembangunan perpustakaan libyuv dan libwebrtc. Kami juga akan menonaktifkan semua codec audio yang tidak perlu terlebih dahulu kecuali PCMA / PCMU. Kami memeriksa kebenaran konfigurasi di 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
Untuk lebih mudah bekerja dengan aplikasi simple_pjsua, mari pindahkan kode ke Embox. Dari modifikasi, kami hanya akan mentransfer pengaturan parameter akun SIP dari kode C-shny ke file 'simple_pjsua_sip_account.inc', yang akan kami masukkan ke file konfigurasi. Artinya, untuk membangun aplikasi dengan akun berbeda, Anda hanya perlu mengubah file ini. Isinya tetap sama:
#define SIP_DOMAIN <sip_domain>
#define SIP_USER <sip_user>
#define SIP_PASSWD <sip_passwd>
Jalankan aplikasi simple_pjsua seperti sebelumnya di Linux. Jika berhasil, maka PJSIP dikonfigurasi dengan benar. Opsi konfigurasi ini kemudian dengan mudah dipindahkan kembali ke Makefile di Embox.
Makefile terakhir di bawah 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 $@
Mybuild terakhir di bawah 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 }
Sekarang Anda dapat menerima panggilan masuk, tetapi di Embox.
Meluncurkan PJSIP di STM32F769I-Discovery
Tetap mengubah konfigurasi Embox dari PJSIP untuk QEMU ke konfigurasi untuk papan tertentu - STM32F769I-Discovery. Untuk mengkonfigurasi Embox, Anda memerlukan beberapa komponen:
- File tanda kompilasi (build.conf).
- File linker menjelaskan memori apa yang tersedia dan bagaimana gambar akhir (lds.conf) akan ditempatkan di dalamnya.
- File konfigurasi modul embox (mods.conf).
- Konfigurasi PJSIP.
Dua poin pertama biasanya mudah dipahami. Ini adalah opsi compiler dan linker, dan mereka jarang berubah dari satu proyek ke proyek lain untuk papan yang sama. Kecuali mungkin untuk bendera kompilasi. Pekerjaan utama untuk menetapkan karakteristik sistem akhir akan dilakukan di paragraf ketiga dan keempat.
Pertama, mari kita lihat konfigurasi Embox. Apa perbedaan di sini dari menjalankan di Linux? Di Linux kami memiliki jumlah memori yang hampir tak terbatas, kami tidak peduli dengan jumlah tugas, jumlah memori yang dialokasikan, dll. Sekarang kami hanya memiliki ROM 2MB dan RAM 512MB, tidak termasuk memori eksternal. Oleh karena itu perlu ditetapkan berapa sumber daya yang kita butuhkan untuk kebutuhan khusus.
Misalnya, PJSIP berjalan di utasnya sendiri. Untuk setiap koneksi baru ada aliran lain. Dan satu utas lagi untuk bekerja dengan audio. Jadi, bahkan dengan satu koneksi, kami membutuhkan setidaknya 3 utas. Selanjutnya, kami ingin menambahkan DHCP - kami memilih satu aliran lagi. Secara total, sudah 4. Semua ini secara alami ditransfer ke konfigurasi:
include embox.kernel.thread.core(thread_pool_size=5,thread_stack_size=12000)
Kami telah mengatur tumpukan ke ukuran tetap. Anda bisa menanyakan hal yang berbeda. Itu semua tergantung tugasnya.
Selanjutnya, pilih jumlah paket yang dibutuhkan:
include embox.net.skbuff(amount_skb=28) include embox.net.skbuff_data(amount_skb_data=28)
Setel ukuran heap (tempat malloc () berfungsi):
include embox.mem.heap_bm include embox.mem.static_heap(heap_size=0x3C000)
Jika tidak, konfigurasinya tetap sama seperti di QEMU.
Mencari tahu ukuran heap
Pertanyaan utama yang muncul saat menyusun konfigurasi adalah bagaimana memilih parameter yang diperlukan? Misalnya, mengapa heapnya 0x3C000, jumlah paket jaringannya 28, dan stacknya 12Kb? Saya sering mengambil pendekatan berikut. Langkah pertama adalah menangani tumpukan dan heap. Banyak hal yang dapat dieksplorasi untuk memulai di Linux menggunakan Valgrind. Anda dapat menggunakan profiler Valgrind-Massif untuk ini. Ia bekerja pada "snapshot" pada titik waktu tertentu dan menunjukkan fungsi mana yang meminta berapa banyak memori.
Luncurkan valgrind dengan aplikasi kami:
$ valgrind --tool=massif --time-unit=B --massif-out-file=pjsip.massif ./pjsip-apps/bin/samples/x86_64-unknown-linux-gnu/simple_pjsua
Setelah menjalankan aplikasi, kami memvisualisasikan data menggunakan massif-visualizer:
$ massif-visualizer pjsip.massif
Di sini Anda dapat melihat bahwa memori dihabiskan tidak hanya untuk PJSIP, tetapi juga pada pustaka standar, serta libasound (ini adalah suara host - ALSA). PJSIP sendiri meminta dimensi dari subplot merah bawah. Ini berada di puncak 600 Kb, dan selama sambungan sekitar 320 Kb. Papan kami memiliki RAM 512kB. Oleh karena itu, kami mencoba mengkonfigurasi PJSIP, mengurangi konsumsi memori ...
Saya membuat konfigurasi berikut:
#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
Salin ke PJSIP ke file pjlib / include / pj / config_site.h. Kami membangun kembali dan menjalankan. Menganalisis hasil:
Pada puncaknya sudah sekitar 300 KB, yang bisa muat di papan tulis.
Selanjutnya, saya meletakkan tumpukan sekitar 300 KB di Embox dan mengatur kumpulan debug untuk melihat apakah ada yang meluap (perhatikan bahwa sebagai hasilnya, ukuran tumpukan dikurangi menjadi 240 KB). Debugging kumpulan diaktifkan dengan opsi:
#define PJ_POOL_DEBUG 1
di pjlib / include / pj / config_site.h yang sama.
Oke, yang tersisa hanyalah mengkonfigurasi tumpukan benang dan jumlah paket jaringan. Di sini Anda perlu mendistribusikan sumber daya yang tersisa dengan benar. Misalnya, jika ada terlalu sedikit paket jaringan, suaranya hanya akan "tersedak". Jika Anda mengalokasikan terlalu banyak paket, maka tidak akan ada yang tersisa untuk tumpukan. Prioritasnya tentu saja tumpukan. Jika tumpukan menjadi buruk, semuanya hilang.
Jadi, kami mulai dengan ukuran tumpukan semaksimal mungkin dan kemudian menguranginya hingga perangkat lunak berjalan di bawah beban. Jika kami menemukan kerusakan pada tumpukan, kami berhenti. Kami meletakkan interupsi pada tumpukan terpisah untuk meminimalkan ketidakpastian. Artinya, tumpukan Anda hanya untuk program Anda.
@Runlevel(0) include embox.arch.arm.armmlib.exception_entry(irq_stack_size=1024)
Setelah itu, kami memberikan sumber daya yang tersisa ke paket jaringan. Seperti yang saya sebutkan di atas, kami mendapat 28 di antaranya.
Semuanya, bagian pertama telah berhasil diselesaikan. Aplikasi Simple_pjsia berhasil berjalan di STM32F769I-Discovery dalam memori internal 512KB.
Kami sedang menyelesaikan telepon SIP. Kami menambahkan antarmuka pengguna.
Setelah berhasil meluncurkan versi konsol, Anda perlu menambahkan antarmuka pengguna. Untuk kesederhanaan, kami akan menganggap itu termasuk yang berikut ini. Saat aplikasi dimulai, harus ada semacam tulisan penjelasan di layar. Misalnya, “PJSIP DEMO”. Jika ada panggilan masuk, layar menampilkan dari mana panggilan itu berasal, dan dua tombol dengan ikon muncul - "Terima", "Tolak". Panggilan dapat diterima atau ditolak. Jika panggilan diterima, percakapan dimulai, informasi kontak tentang pelanggan ditampilkan, dan satu tombol tetap ada di layar - "Hang". Jika panggilan awalnya ditolak - semuanya sepele di sini - kita kembali ke gambar awal dengan “PJSIP DEMO”.
Berikut adalah contoh tampilan ini.
Pengembangan prototipe di Linux
Karena Embox sudah memiliki dukungan untuk Nuklear, saya memutuskan untuk menggunakan proyek ini. Meskipun kami sudah memiliki versi konsol telepon yang berfungsi di mikrokontroler, penting di sini bahwa jauh lebih mudah untuk memodifikasi UI di Linux, seperti yang sudah dilakukan dengan pengaturan PJSIP di atas.
Untuk melakukan ini, mari kita ambil dua contoh. Contoh pertama adalah simple_pjsua dari PJSIP. Contoh kedua adalah demo / x11_rawfb / dari Nuklear. Sekarang tugas kita adalah membuatnya bekerja bersama di Linux.
Hal pertama yang saya lakukan adalah mengganti jawaban panggilan PJSIP otomatis dengan kejadian eksternal (seperti menekan tombol). Selanjutnya, saya menulis logika di Nuklear.
Dalam prosesnya, ternyata untuk beberapa alasan ikon tidak digambar di dalam tombol. Pada gambar di bawah, Anda dapat melihat ikon telepon di dalam tombol hijau dan merah. Ini adalah gambar biasa, di mana semuanya 100% transparan kecuali untuk penerima telepon. Pada saat yang sama, awalnya hanya digambar kotak putih. Itu tentang implementasi plugin rawfb. Rupanya, ini tidak terlalu populer, jadi hanya kursor yang digambar di dalamnya. Saya telah menambahkan beberapa kode yang hanya menyalin konten gambar ke wilayah memori Nuklear yang benar.
Akibatnya, setelah seharian mengerjakan proyek, saya mendapatkan yang berikut:
Mengetahui bahwa STM32F76I-Discovery memiliki ukuran layar 800x480, dan di QEMU 800x600, saya segera mengatur dimensi yang diperlukan di Nuklear, agar lebih mudah dinavigasi dalam membuat label dan tombol dinamis. Kode yang dihasilkan adalah sebagai berikut:
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);
Luncurkan di papan
Tetap mentransfer proyek terlebih dahulu ke QEMU, dan kemudian ke dewan. Kami sudah menyiapkan semuanya untuk versi konsol, jadi kami hanya akan mentransfer aplikasi baru dari Linux. Untuk melakukan ini, cukup buat file Mybuild di sistem build 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 }
Seperti yang Anda lihat, sumbernya terdaftar. Ikon dan font terletak di sistem file internal dan akan tersedia sebagai file hanya-baca biasa. Juga menambahkan dependensi pada pjsip dan nuklear libraries.
Setelah menjalankan aplikasi di papan, saya perhatikan bahwa font default dari Nuclear terlihat mengerikan di layar STM32F769I. Beberapa surat hilang begitu saja. Misalnya "1" tampak seperti "|" dan "m" tampak seperti "n". Saya harus menghubungkan font dari file ttf - Roboto-Regular.ttf. Font ini membutuhkan sekitar 150 KB memori flash, tetapi teksnya dapat terbaca.
Setelah memeriksa di Linux, saya memutuskan bahwa ini adalah biaya yang kecil. Dan saya mencoba menggunakan ukuran font yang berbeda 32 dan 38. Tapi saya mendapat segfault. Pada akhirnya, saya melepaskan ide untuk memuat beberapa ukuran font dari sebuah file, dan hanya memuat yang ke-32 dan menskalakannya.
Fitur peluncuran di perangkat keras
Mari kembali ke peluncuran papan. Hal penting yang harus dipahami di sini adalah dalam kasus UI, framebuffer diperlukan. Karena kami menginginkan mode layar penuh, dan layarnya 800x480, bahkan dengan palet RGB 1-byte, kami membutuhkan 800 * 480 * 1 = 384000 byte, yaitu 375 KB. Mengingat kita telah menempati hampir seluruh memori internal 512 KB untuk kebutuhan PJSIP, maka tidak akan berhasil mencari tempat untuk framebuffer. Untuk alasan ini, kami akan menggunakan SDRAM. 16 MB tersedia di STM32F76I-Discovery. Karena kami sudah menggunakan memori eksternal, kami tidak akan banyak menghemat dan meletakkan RGBA 32 bit. Jadi, framebuffer akan menjadi 800 * 480 * 4 = 1536000 byte atau 1,5 MB.
Dalam konfigurasi kami, SDRAM terletak di alamat 0x60000000. Kami menetapkannya sebagai alamat 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
Saya telah membahas efek kedipan saat menggunakan satu buffer di artikel lain. Oleh karena itu, kami akan memperhitungkan bahwa sistem menggunakan buffering ganda, dan oleh karena itu, kami memerlukan memori tambahan untuk buffer 1,5 MB lainnya. Selain itu, font akan membutuhkan 256 KB lagi. Secara total, Anda perlu meningkatkan heap sebesar 2 MB. Kami juga menempatkannya di memori eksternal:
@Runlevel(2) include embox.driver.input.touchscreen.stm32f7cube_ts @Runlevel(2) include embox.driver.input.input_dev_devfs
Sekarang layar sentuh akan menjadi perangkat / dev / stm32-ts dalam sistem file devfs di Embox, dan Anda dapat mengerjakannya melalui open () / read () yang biasa.
Ini menyelesaikan konfigurasi. Mengapa hampir? Faktanya, kami memperhitungkan semua nuansa dari memori, tetapi tidak memperhitungkan kinerja. Jika dalam kasus PJSIP suaranya ditransmisikan dengan baik, maka ketika Anda mencoba meluncurkannya dengan grafik, itu akan tersedak. Efek ini, tentu saja, sangat sulit untuk di-debug di Linux. Tapi ternyata cukup hanya dengan mengaktifkan cache di papan kami.
@Runlevel(0) include embox.arch.arm.armmlib.armv7m_cpu_cache( log_level=4, sram_nocache_section_size=0x10000 )
Di Embox, deskriptor paket jaringan dan data, serta buffer audio yang bekerja dengan DMA, ditempatkan di bagian khusus memori yang ditandai sebagai memori yang tidak dapat di-cache di MPU. Ini diperlukan agar status objek dalam memori ini selalu benar untuk CPU dan DMA.
Hasilnya, kami mendapatkan telepon SIP yang berfungsi sangat sederhana dengan UI dengan tombol yang berfungsi cukup baik.
Di bawah ini saya mencoba menggambarkan alokasi memori akhir:
Pengembangan pada sistem host
Proses pengembangan saya bermuara pada apa yang ditunjukkan pada gambar.
Dan hanya butuh sedikit waktu. Satu hari untuk aplikasi di Linux dan satu hari lagi untuk perbaikan pada platform yang dipilih. Ya, Embox sudah memiliki display, kartu jaringan dan driver audio untuk kartu ini. Namun pengembangan suku cadang tersebut juga membutuhkan waktu yang tidak sedikit, tidak lebih dari seminggu untuk setiap pengemudi. Perlu lebih banyak waktu untuk mengembangkan fungsionalitas tersebut secara langsung di papan. Dalam kasus kami, sebagian besar fungsionalitas dikembangkan di bawah lingkungan sistem host yang nyaman. Ini memungkinkan kami mengurangi waktu pengembangan secara signifikan.
Ada video hasil di awal artikel. Jika mau, Anda dapat mereproduksi sendiri semuanya sesuai dengan instruksi di wiki .