• Non ci sono risultati.

• l’interfaccia esterna, cioè ricevere, analizzare ed attuare i comandi ricevuti dall’esterno, provvedendo poi ad inviare le opportune risposte perché vengano visualizzate all’utente

N/A
N/A
Protected

Academic year: 2021

Condividi "• l’interfaccia esterna, cioè ricevere, analizzare ed attuare i comandi ricevuti dall’esterno, provvedendo poi ad inviare le opportune risposte perché vengano visualizzate all’utente"

Copied!
13
0
0

Testo completo

(1)

5. La nuova realizzazione: Software

n questo capitolo saranno presentate le funzionalità supportate dal prototipo realizzato, e sarà descritto il codice che le implementa, ponendo l’attenzione sull’architettura del software stesso e su come questo si coniughi alle risorse hardware, al fine di consentire il funzionamento del sistema.

I

5.1 Funzionalità implementate

Prima di entrare nello specifico della descrizione della struttura del codice è opportuno riassumere quali funzioni debbano essere espletate dal codice stesso.

Il programma che risiede sul DSP deve gestire:

• i due chipset BlueCore, sia relativamente allo scambio dei campioni audio che per quanto riguarda l’inizializzazione ed il reset, la memorizzazione di dati nella memorie non volatili (ad esempio i codici PIN dei caschi) e l’attivazione dei canali di trasmissione;

• l’interfaccia esterna, cioè ricevere, analizzare ed attuare i comandi ricevuti dall’esterno, provvedendo poi ad inviare le opportune risposte perché vengano visualizzate all’utente;

• il codec e le sorgenti audio analogiche, riguardo al campionamento dei dati in ingresso, alla formattazione dei dati in uscita, al missaggio dei campioni stessi secondo gli ordini ricevuti dall’interfaccia e codificati nello stato del sistema.

(2)

• la conversione dei campioni da uno standard all’altro, per permettere la comunicazione tra entità che adottano standard di comunicazione diversi (si deve infatti realizzare un gateway)

Per implementare queste funzioni si è deciso di strutturare il codice su un main loop (uno scheduler ciclico), che va in esecuzione ogni 10 millisecondi sulla base di una interruzione generata dal core timer.

All’interno di questo ciclo, in base ad opportune condizioni, vengono eseguite quattro funzioni principali, che, seppur impropriamente, chiameremo threads. Ognuna di queste funzioni, che saranno descritte successivamente in dettaglio, si occupa di gestire una funzionalità tra quelle descritte.

Per consentire la sincronizzazione con gli eventi esterni vengono settate alcune sorgenti di interruzione: ad ognuna di queste è assegnata una Interrupt Service Routine (ISR) che, all’arrivo dell’evento “scatenante”

l'interruzione, va in esecuzione. Tipicamente una ISR esegue le seguenti funzioni:

1. segnala che l'interruzione è stata servita;

2. prende dei dati da una periferica e li aggiunge ad una coda o ad un buffer;

3. segnala quello che ha fatto modificando una variabile globale.

Fa eccezione la ISR che si occupa di raccogliere i dati ricevuti dalla UART emulata da SPORT1: in questo caso i dati ricevuti vengono, in pratica, sovracampionati, e la ISR si occupa di emulare il comportamento di un ricevitore asincrono, effettuando quelle operazioni necessarie alla

“ripulitura” di ciò che è arrivato[10] [11].

(3)

5.2 Il main loop ed i threads

5.2.1 Il main loop

Come detto il main loop si occupa di mandare ciclicamente in esecuzione i threads: oltre a questa, che è la funzionalità implementata quando il sistema è ormai a regime, il main loop si occupa, all'accensione, di procedere all'inizializzazione di tutte le periferiche e, più in generale, di tutte le risorse hardware utilizzate. Al reset del sistema vanno in esecuzione le seguenti funzioni:

• Init_EBIU( ): si occupa di inizializzare l'External Bus Interface Unit, tramite cui si può accedere al banco di memoria flash su cui risiede il resto del programma;

• Init_Flash( ): viene inizializzata la direzione dei piedini con cui si accede alla memoria flash;

• Init1836( ): questa funzione resetta il codec audio, dopodiché provvede ad settare la porta SPI ed il programmable flag 4 per accedere ai registri di configurazione del codec, che vengono inizializzati ai valori voluti impostando un trasferimento DMA;

• Init_Sport0( ): per il trasferimento dei campioni audio da/per il codec viene usata la SPORT0, che questa funzione setta per ottenere una comunicazione coerente allo standard I2S;

• Init_DMA( ): inizializza i canali DMA 1 e 2 per operare su SPORT0 in autobuffer mode, il primo in ricezione ed il secondo in trasmissione;

• UARTInit(921600): inizializza la porta UART per le comunicazioni con il BlueCore. Il baud rate (fissato, come si vede, a 921600 bit/s) viene passato come parametro della funzione.

• UART2_Init(115200): Inizializza la SPORT1 affinché emuli il comportamento di una porta seriale asincrona: il baud rate (fissato a

(4)

115200 bit/s) viene passato alla funzione come parametro. Nel prototipo questa porta è usata per le comunicazioni con l'interfaccia, ma, dato che su questa porta è possibile settare dei trasferimenti in DMA, sul sistema definitivo sarà utilizzata per connettere il secondo BlueCore per la connessione del telefono GSM.

• InitTimer(10): questa funzione inizializza il core timer che controlla l'esecuzione ciclica del main loop.

• Init_Flags( ): con questa funzione inizializza il programmable flag 8 per la generazione di interruzioni; tale piedino servirà per il riconoscimento della ricezione di una chiamata dal telefono cellulare.

• InterruptInit( ): vengono abilitate tutte le sorgenti di interruzione.

• Enable_DMA_Sport0( ): vengono abilitati i canali DMA che operano sulla UART in supporto al BlueCore.

• Enable_DMA_UART( ): vengono abilitati i canali DMA che operano sulla SPORT0 in supporto al codec.

• ResetBlueCore( ): viene resettato il BlueCore; tale reset si esegue forzando al livello logico 0 il piedino di ricezione del BlueCore per un tempo superiore a quello necessario alla trasmissione di qualche frame.

Quando tutte queste funzioni sono state eseguite parte il ciclo vero e proprio.

5.2.2 Control Unit

Il thread Control Unit si occupa di tenere aggiornate le variabili che contengono lo stato del sistema: gestisce dunque i comandi e gli eventi che provengono dall'interfaccia e dal BlueCore, ed in più tiene sotto controllo il programmable flag che segnala l'arrivo di una chiamata sul telefono cellulare.

(5)

Il flusso di programma prevede che, ad ogni esecuzione, si vada a vedere se sono arrivati dei comandi dall’interfaccia o degli eventi dal BlueCore: se questo si è verificato viene chiamata una funzione ausiliaria (BcParser o ButtonParser) che analizza quello che si è ricevuto; la funzione ausiliaria ripassa al thread il “senso” del messaggio, ed il thread provvede ad aggiornare lo stato del sistema ed a comunicare all’interfaccia ed al BlueCore i cambiamenti avvenuti. L’arrivo di una chiamata sul telefono causa l'invio di un segnale acustico al guidatore, con la contestuale attivazione automatica della sua sorgente GSM.

Per evitare che questo thread vada in esecuzione ad ogni ciclo dello scheduler anche se non si sono ricevuti comandi o eventi, la sua esecuzione è controllata da una maschera che effettua l’or logico fra tre flag: il primo segnala l’arrivo di un comando, il secondo l’arrivo di un evento, il terzo di una telefonata. Questi flag vengono settati, rispettivamente, dalla ISR che recepisce i comandi dall’interfaccia, dal thread UART Reader, dalla ISR che recepisce l’interruzione sul programmable flag deputato alla ricezione di chiamata.

5.2.3 Audio Manager

Il thread Audio Manager, sulla base delle variabili di stato che Control Unit mantiene aggiornate, effettua le conversioni di protocollo, il missaggio e l’instradamento dei segnali audio.

In particolare, si deve tenere presente che i campioni provenienti dal BlueCore sono su 8 bit, compressi secondo la legge a-law, mentre i campioni provenienti dal codec sono interi su 16 bit in complemento a due;

per effettuare un missaggio tra campioni di diversa provenienza è necessario dunque procedere ad uniformare lo standard in cui sono codificati i campioni. Si è deciso di riportare sempre tutti i campioni su 16 bit in

(6)

complemento a due, anche se questo non sarebbe, a rigore sempre necessario: ad esempio, se è attiva la sola funzione interfono, i campioni potrebbero essere semplicemente instradati da un casco all’altro, senza dover subire alcuna elaborazione. La decisione di espandere comunque i dati è stata presa in considerazione della futura integrazione delle funzioni di soppressione del rumore e riconoscimento vocale, che avrebbero richiesto comunque l’espansione dei campioni; inoltre, seppur al prezzo di far eseguire al DSP alcune operazioni (talvolta) inutili, si è così semplificata la struttura del codice.

Nel missaggio dei campioni si deve verificare che le somme non causino overflow; se questo accade si satura il valore del campione al massimo consentito. Inoltre la somma tra i campioni deve conservare la coerenza temporale, dunque tale somma viene eseguita tra un numero di dati pari al minor numero di campioni non ancora processati presenti in una delle code attive: ad esempio, se sono attive sia la radio che l’interfono, e la radio ha trasferito 80 campioni, mentre l’interfono ne ha trasferiti 65, vengono processati tutti i campioni provenienti dall’interfono, ma solo i primi 65 provenienti dalla radio.

Una volta eseguito il missaggio si segnala che sono pronti dei dati nelle code di uscita dei caschi e si torna al main loop.

5.2.4 UART Reader

Il thread UART Reader si occupa di analizzare i dati provenienti dal BlueCore via UART; infatti il BlueCore invia (e riceve) i dati in pacchetti di più frames, secondo le specifiche dell'HCI UART transfer layer che descrive quale formattazione debbano avere questi pacchetti in funzione del tipo di dati scambiati.

(7)

In pratica i frames con i dati veri e propri vengono “impacchettati” da alcuni frames di intestazione (vedi appendice) per permettere al codice residente sul BlueCore di classificare i dati in arrivo; il thread UART Reader, dunque,

1. verifica se siano arrivati nuovi frames,

2. verifica se questi frames costituiscono un nuovo pacchetto o se appartengono ad un pacchetto la cui ricezione non era stata ancora completata,

3. appena è disponibile un nuovo pacchetto completo effettua l’analisi della sua intestazione per capire cosa è contenuto in quel pacchetto,

4. a seconda del risultato di questa analisi, sposta i pacchetti dal buffer circolare in cui vengono memorizzati dal DMA alla opportuna coda (audio o eventi),

5. segnala l’arrivo dei nuovi dati al thread cui quei dati competono (cioè Audio Manager in caso di campioni audio o Control Unit in caso di eventi HCI).

Contestualmente a queste operazioni il thread deve aggiornare gli indici di tutti i buffer su cui opera, garantendone la circolarità, per evitare che siano analizzati dati non validi (ad esempio pacchetti incompleti, o dati che erano già stati processati).

A prima vista, la decisione di procedere con il parsing dei pacchetti solo quando la loro ricezione è terminata, non sarebbe necessaria; considerando che il trasferimento via UART è asincrono, non sussistono vincoli temporali tra l’invio di un frame e l’invio del successivo: sarebbe comunque possibile esaminare, ad esempio, solo la prima parte di un pacchetto, spostare quello che è arrivato e rimandare al ciclo successivo lo spostamento della parte restante del pacchetto, mantenendo ovviamente memoria del tipo di dati

(8)

arrivati in precedenza. Si è però preferito adottare la soluzione esposta perché l’alternativa avrebbe comportato, da un lato, una complicazione del software, e, sotto altri punti di vista addirittura un rallentamento nell’analisi dei dati; infatti, in taluni casi, il BlueCore invia più pacchetti in rapida successione, e, se l’ultimo non fosse stato completamente ricevuto al momento del parsing, si sarebbe dovuto complicare ulteriormente la sincronizzazione tra i threads per consentire, ad esempio a Control Unit, di analizzare i primi pacchetti senza considerare gli ultimi dati inseriti in coda ed appartenenti al pacchetto non ancora completo.

5.2.5 UART Writer

UART Writer ha la funzione duale rispetto a quella di UART Reader, cioè si occupa di inviare i dati processati al BlueCore. Quando i threads Audio Manager o Control Unit hanno dei dati pronti per essere inviati al BlueCore, opportunamente collocati nelle code di loro pertinenza, segnalano questo evento settando una variabile globale; questa variabile

“sblocca” l’esecuzione di UART Writer all'interno del main loop, cosicché il thread può procedere all’inserimento nel buffer di uscita dei dati da inviare e poi al settaggio dei trasferimenti in DMA che porranno fisicamente in uscita i dati.

Si è deciso di dare la priorità, nel trasferimento dei dati, ai comandi rispetto ai campioni audio; si è preferito però non creare gerarchie tra i caschi, quindi ad ogni trasferimento si trasferisce un uguale numero di campioni audio ai due caschi (ovviamente solo se entrambi sono attivi):

quindi al momento di impostare un trasferimento si calcola lo spazio disponibile nel buffer al netto di quello che verrà occupato dai comandi, e lo si suddivide i parti uguali per entrambi gli headsets. Se uno dei due ha in

(9)

coda più campioni dell'altro, quelli di avanzo verranno trasferiti al ciclo successivo.

Per quanto riguarda i trasferimenti in DMA, tra le varie modalità messe a disposizione dal DSP è stata scelta la Descriptor Mode (Large List): questo è stato necessario per evitare il trasferimento di dati non significativi che si sarebbe avuto con l’Autobuffer Mode, ma ha reso necessaria l’implementazione a controllo di programma della circolarità del buffer.

Infatti i descrittori sono strutture che settano il controllore DMA per trasferire un blocco di memoria lineare, lungo x locazioni a partire da un dato indirizzo, senza possibilità di effettuare “salti” circolari. Allora, per garantire la circolarità del buffer di uscita quando un trasferimento riguarda dati memorizzati a cavallo della fine e dell’inizio del buffer, è stato necessario impostare due trasferimenti successivi, il primo dei quali copre i dati dall’inizio del trasferimento fino alla fine del buffer, mentre l’altro comincia a trasferire i dati dall'inizio del buffer fino alla fine del blocco da porre in uscita.

Nella pagina seguente si vede uno schema dell’architettura software del main loop, all’interno del quale sono evidenziati i threads, i flussi di dati audio, l’azione delle variabili di stato e sincronizzazione tra i threads. In particolare:

• le frecce (a) indicano il flusso di programma;

• le frecce (b) indicano il flusso dei dati audio, dei comandi per il BlueCore e degli eventi dal BlueCore;

• le frecce (c) indicano i flussi dati da e per l’interfaccia esterna;

• le frecce (d) indicano l'azione delle variabili di stato e di sincronizzazione tra i threads.

(10)

Control Unit

Audio Manager UART Reader

UART Writer

BlueCore Interfaccia

UART DMA

Code Audio Coda eventi

Coda Comandi

Buffer di ingresso ISR

Interfaccia esterna

ISR

DMA Buffer campioni

Codec DMA

Buffer circolare di ingresso Funzioni di inizializzazione

Legenda :

a b c d

I flussi dati che avvengono tramite DMA sono segnalati dall'etichetta DMA sopra la freccia corrispondente al trasferimento stesso. Si fanno notare anche le due ISR che contribuiscono al trasferimento dei dati, in

(11)

particolare quella che preleva i comandi ricevuti dall'interfaccia, che sblocca anche il thread Control Unit. Per una comprensione piú chiara delle strutture dati si faccia riferimento al prossimo paragrafo.

5.3 Le strutture dati

Nella descrizione dei vari threads si è accennato all'esistenza di strutture dati in cui vengono memorizzati i campioni audio, i comandi ricevuti dall'esterno o da inviare al BlueCore, le variabili di stato del sistema; in questo paragrafo vengono descritte le più importanti.

5.3.1 Variabili di stato

• SystemStatus: è una variabile di tipo enumerato che contiene lo stato del sistema nel suo complesso, indicando se il sistema è acceso, spento, o in programmazione;

• NetworkStatus: è una variabile di tipo enumerato che contiene lo stato dell’host, indicando all’host controller se l’host è spento o acceso, se sta compiendo qualche operazione e, se sì, di che tipo, o se è acceso ma in attesa di comandi;

• ConnectionStatus: è un vettore di strutture che indica lo stato di ognuna delle connessioni radio;

• FunctionStatus: è una struttura che tiene memoria delle funzioni che sono state attivate per ogni casco;

• ToMix: è un vettore di strutture che indica al thread Audio Manager quali sorgenti debbano essere miscelate per ogni uscita.

(12)

5.3.2 Variabili per la comunicazione con l’interfaccia

• LedStatus: è una variabile di tipo enumerato che consente l’invio dello stato del sistema all’interfaccia;

• NewCommand: è una variabile di tipo enumerato che consente al thread Control Unit di interpretare i comandi ricevuti dall’interfaccia.

5.3.3 Variabili per l’immagazzinamento dei dati audio

I campioni audio possono essere scambiati con il BlueCore o con il codec; poiché nei due casi è diverso il formato dei campioni, deve essere diverso il tipo di strutture dati utilizzate.

I campioni provenienti dal BlueCore sotto forma di pacchetti HCI SCO vengono innanzitutto memorizzati dal DMA in un buffer circolare, chiamato data_UARTin: vengono poi presi in carco dal thread UART Reader, che in base all’analisi delle intestazioni dei pacchetti, trasferisce i dati audio in code dedicate, di tipo AudioQueue: queste sono implementate secondo il classico schema di classe coda, a cui sono state aggiunte delle funzioni membro per l’inserimento e l’estrazione di blocchi di elementi. Sarà il thread Audio Manager che provvederà ad effettuare l’espansione prima di procedere al missaggio con gli altri campioni provenienti dal codec; i campioni, poi, che dovranno essere inviati al BlueCore saranno compressi ancora da Audio Manager e copiati in un’altra coda di tipo AudioQueue. Il thread UART Writer preleverà i campioni dalla coda e li copierà in un altro buffer circolare, data_UARTout, settando poi il trasferimento DMA che li preleverà dal buffer e li invierà in uscita.

I campioni provenienti dal codec vengono scritti dal DMA su un piccolo buffer, da cui la ISR relativa al codec li preleva per copiarli nelle code audio relative alla sorgente da cui provengono; anche queste code sono di tipo AudioQueue. Dopo che il thread Audio Manager ha compiuto le opportune

(13)

operazioni di missaggio, i dati che devono essere inviati al codec vengono posti in una coda per l’uscita (di tipo AudioQueue) e la stessa ISR che preleva i campioni in ingresso provvederà a porre quelli in uscita nel buffer di uscita, dandoli “in pasto” al DMA.

Riferimenti

Documenti correlati

Questa tesi presenta una architettura per la realizzazione di un sistema di Intrusion Detection (rilevamento di intrusione) basato sulla rilevazione di anomalie nel traffico di

Il quadro delle provenienze, a livello regionale, si discosta un po’ rispetto a quello nazionale: se le nazioni come la Romania, l’Albania e il Marocco rappresentano comunque

La capacità di compiere validamente atti giuridici è attribuita dal legislatore al soggetto che abbia raggiunto un’adeguata maturità psichica la ratio è quella di far si che in

Il risultato è una tabella che comprende tutte le righe di IMPIEGATO per cui il valore Dipart è uguale ad almeno uno dei valori di Nome in DIPARTIMENTO, limitatamente alle tuple

Oltre ai vostri famigliari e alle altre persone a voi vicine, potete contare sulla consulenza specializzata degli orientatori e delle orientatrici dell’Ufficio dell’orientamento

Forse provengono da strati più antichi delle stesse località, oppure sono stati raccolti in località differenti ed in seguito confusi con i resti delle

Nel quarto capitolo viene presentata l’esperienza nell’ambito della qualità di C.re.a., una Cooperativa sociale di Viareggio, il tutto è preceduto dalla descrizione della storia

FULGENZIA - (dando una gomitata ad Andreina) La dottoressa Pazienza ha voluto rendersi conto della situazione… domani scenderà di nuovo anche monsignor Ciborio