Ciao a tutti.
Tempo fa abbiamo di come siamo riusciti a far funzionare un telefono SIP su STM32F4-Discovery con 1 MB di ROM e 192 KB di RAM) basato su . È importante dire che quella versione era minimale e collegava due telefoni direttamente senza server e con trasmissione vocale solo in una direzione. Per questo abbiamo deciso di lanciare un telefono più completo con chiamate tramite server, trasmissione vocale in entrambe le direzioni, cercando di rimanere il più leggeri possibile in termini di memoria.

Per il telefono abbiamo scelto l'applicazione simple_pjsua facente parte della libreria PJSIP. Questa è un'applicazione minima in grado di registrarsi su un server, ricevere e rispondere alle chiamate. Di seguito troverete subito la descrizione di come avviarla su STM32F7-Discovery.
Come avviare
- Configuriamo Embox
make confload-platform/pjsip/stm32f7cube - Nel file conf/mods.config definiamo l'account SIP necessario.
include platform.pjsip.cmd.simple_pjsua_imported( sip_domain="server", sip_user="username", sip_passwd="password")dove server — è il server SIP (ad esempio, sip.linphone.org), username e password — nome utente e password per l'account.
- Compiliamo Embox con il comando make. Abbiamo informazioni sul firmware della scheda nella e nel .
- Avviamo nel terminale di Embox il comando “simple_pjsua_imported”
00:00:12.870 pjsua_acc.c ....Lo stato della registrazione SIP per l'acc 0 non è attivo 00:00:12.884 pjsua_acc.c ....sip:alexk2222@sip.linphone.org: registrazione avvenuta con successo, stato=200 (Registrazione riuscita 00:00:12.911 pjsua_acc.c ....Timer Keep-alive avviato per acc 0, destinazione: 91.121.209.194:5060, intervallo: 15s - Infine, rimane da collegare le casse o le cuffie all'uscita audio, e parlare nei due piccoli microfoni MEMS vicino al display. Stiamo chiamando da Linux attraverso l'applicazione simple_pjsua, pjsua. O si può usare anche qualsiasi altro tipo di linphone.
Tutto questo è descritto nel nostro .
Come ci siamo arrivati
Quindi, inizialmente ci siamo posti la questione della scelta della piattaforma hardware. Poiché era chiaro che STM32F4-Discovery non sarebbe stato adatto per via della memoria, abbiamo scelto STM32F7-Discovery. Questa ha 1 MB di flash e 256 KB di RAM (+ 64 KB di memoria veloce speciale, che utilizzeremo anch'essa). Non è molto per le chiamate attraverso il server, ma abbiamo deciso di provare a rimanere dentro.
Abbiamo sostanzialmente suddiviso il compito in diverse fasi:
- Avvio di PJSIP su QEMU. Questo è stato utile per il debugging, in più avevamo già il supporto per il codec AC97.
- Registrazione della voce e riproduzione su QEMU e su STM32.
- Portare l'applicazione simple_pjsua dal pacchetto PJSIP. Questo consente di registrarsi su un server SIP e di effettuare chiamate.
- Avvia il tuo server basato su Asterisk e testalo, per poi provare esterni come sip.linphone.org
L'audio in Embox funziona tramite Portaudio, che è utilizzato anche in PISIP. Su QEMU si sono manifestati i primi problemi: i WAV a 44100 Hz venivano riprodotti bene, mentre a 8000 qualcosa non andava. Si è scoperto che il problema era nella configurazione della frequenza: per impostazione predefinita nell'hardware era 44100 e noi non lo cambiavamo a livello software.
Qui vale la pena spiegare un po' come avviene la riproduzione audio. Alla scheda audio può essere assegnato un puntatore a un blocco di memoria da cui riprodurre o registrare a una frequenza prestabilita. Una volta esaurito il buffer, si genera un'interruzione e l'esecuzione continua con il buffer successivo. Il fatto è che questi buffer devono essere riempiti in anticipo, mentre si riproduce quello precedente. Ci imbattemo in questo problema più avanti su STM32F7.
Successivamente, abbiamo noleggiato un server e installato Asterisk. Poiché era necessario fare molte configurazioni e non volevamo parlare al microfono, era necessario implementare un riproduzione e registrazione automatica. A questo scopo, abbiamo modificato simple_pjsua in modo da poter utilizzare file al posto dei dispositivi audio. In PJSIP, questo è piuttosto semplice, poiché hanno il concetto di porta, che può essere sia un dispositivo che un file. Queste porte possono essere collegate in modo flessibile ad altre porte. È possibile vedere il codice nel nostro pjsip . Alla fine, lo schema era il seguente. Sul server Asterisk ho creato due account: uno per Linux e uno per Embox. Successivamente, su Embox viene eseguito il comando simple_pjsua_imported, Embox si registra sul server, dopodiché chiamiamo Embox da Linux. Nel momento della connessione, controlliamo sul server Asterisk che tutte le connessioni siano stabilite e, dopo un po', dovremmo sentire su Embox il suono da Linux, mentre su Linux salviamo il file che viene riprodotto da Embox.
Dopo che è stato implementato su QEMU, siamo passati al porting su STM32F7-Discovery. Il primo problema è stato che non riuscivamo a stare nei 1 MB di ROM senza l'ottimizzazione del compilatore "-Os" per la dimensione dell'immagine. Quindi abbiamo abilitato "-Os". Inoltre, abbiamo disabilitato il supporto per C++ tramite un patch, poiché è necessario solo per pjsua, mentre noi utilizziamo simple_pjsua.
Dopo aver ottimizzato simple_pjsua, abbiamo deciso che ora c'erano buone probabilità di farlo funzionare. Ma prima dovevamo capire come registrare e riprodurre la voce. La domanda era: dove registrare? Abbiamo scelto la memoria esterna — SDRAM (128 MB). Puoi provare anche tu:
Crea un WAV stereo con frequenza 16000 Hz e durata di 10 secondi:
record -r 16000 -c 2 -d 10000 -m C0000000
Riproduciamo:
play -m C0000000
Qui si sono presentati due problemi. Il primo riguarda il codec — si utilizza il WM8994, che ha un concetto di slot, e ce ne sono 4. Di default, se non si configura nulla, durante la riproduzione audio, il suono viene emesso in tutti e quattro gli slot. Pertanto, a una frequenza di 16000 Hz ottenevamo 8000 Hz, mentre per 8000 Hz la riproduzione semplicemente non funzionava. Quando abbiamo selezionato solo gli slot 0 e 2, ha funzionato come previsto. Un altro problema riguardava l'interfaccia audio in STM32Cube, in cui l'uscita audio funziona tramite SAI (Serial Audio Interface) in modo sincrono con l'ingresso audio (non ho approfondito, ma sembra che condividano lo stesso clock e che all'inizializzazione dell'uscita audio venga in qualche modo collegato l'ingresso audio). Questo significa che non possono essere avviati separatamente, quindi abbiamo fatto quanto segue: l'ingresso e l'uscita audio funzionano sempre (compresi gli interrupt generati). Ma quando nulla viene eseguito nel sistema, semplicemente forniamo all'uscita audio un buffer vuoto, e quando inizia la riproduzione, iniziamo a riempirlo normalmente.
In seguito ci siamo imbattuti nel fatto che il suono durante la registrazione della voce era molto debole. Questo avviene perché i microfoni MEMS sulla STM32F7-Discovery funzionano male a frequenze inferiori a 16000 Hz. Quindi impostiamo 16000 Hz, anche se in entrata ci sono solo 8000 Hz. Tuttavia, è stato necessario aggiungere una conversione software da una frequenza all'altra.
Successivamente, abbiamo dovuto aumentare le dimensioni dello heap, che si trova nella RAM. Secondo i nostri calcoli, pjsip richiedeva circa 190 KB, mentre a noi ne rimanevano solo circa 100 KB. Qui è stata necessaria un po' di memoria esterna — SDRAM (circa 128 KB).
Dopo tutte queste modifiche, ho visto i primi pacchetti tra Linux e Embox, e ho sentito il suono! Ma il suono era orribile, completamente diverso da quello su QEMU, non si riusciva a distinguere nulla. Allora abbiamo cominciato a chiederci quale potesse essere il problema. Il debug ha mostrato che Embox semplicemente non riusciva a riempire/sgomberare i buffer audio in tempo. Mentre pjsip gestiva un frame, si verificavano 2 interruzioni di completamento del trattamento dei buffer, il che era troppo. La prima idea per accelerare era l'ottimizzazione del compilatore, ma era già attivata in PJSIP. La seconda — il punto fluttuante hardware, di cui abbiamo parlato in . Ma come ha dimostrato la pratica, l'FPU non ha fornito un incremento significativo della velocità. Il passo successivo è stato impostare le priorità dei thread. In Embox ci sono diverse strategie di pianificazione, e ho attivato quella che supporta le priorità, assegnando la massima priorità ai flussi audio. Anche questo non ha aiutato.
L'idea successiva è stata che lavoriamo con la memoria esterna e sarebbe utile spostare le strutture a cui si accede molto frequentemente. Ho condotto un'analisi preliminare per capire quando e per cosa simple_pjsua viene allocata la memoria. Si è scoperto che su 190 Kb, i primi 90 Kb sono allocati per le esigenze interne di PJSIP e a cui si accede non molto frequentemente. Successivamente, durante una chiamata in arrivo, viene chiamata la funzione pjsua_call_answer, in cui vengono allocati i buffer per lavorare con i frame in entrata e in uscita. Ciò ha richiesto ulteriori 100 Kb. E a questo punto abbiamo adottato il seguente approccio. Fino al momento della chiamata, i dati sono posizionati nella memoria esterna. Non appena inizia la chiamata, sostituiamo immediatamente il heap con un altro - in RAM. In questo modo, tutti i dati "caldi" sono stati trasferiti in una memoria più veloce e prevedibile.
Alla fine, tutto questo insieme ha permesso di avviare simple_pjsua e chiamare attraverso il proprio server. Poi anche tramite altri server come sip.linphone.org.
Conclusioni
Alla fine siamo riusciti a far partire simple_pjsua la trasmissione vocale in entrambe le direzioni tramite il server. Il problema con i 128 Kb di SDRAM aggiuntivi può essere risolto utilizzando un Cortex-M7 leggermente più potente (ad esempio, STM32F769NI con 512 Kb di RAM), ma nel frattempo non abbiamo ancora perso la speranza di rimanere nei 256 Kb 🙂 Saremmo felici se qualcuno fosse interessato, e ancora meglio - se provasse. Tutti i sorgenti, come al solito, sono nel nostro .
Fonte: habr.com
