Panoramica sulle Routines

Precedente Home Su

Struttura  di chiamata per Real  Time Workshop

Struttura di chiamata per modalità esterna

Trattamento dati nelle S-Function

Controllo ed elaborazione parametri

Cambiamento dei parametri

Definizione delle caratteristiche di un blocco

Configurare le proprietà delle porte di I/O

Impostazione dei tempi di campionamento

Configurazione di Work Vectors

Allocazione di memoria

Inizializzazione delle S-Function

Utilizzo delle S-Function con Real-Time Workshop

 

Introduzione

La sequenza in cui vengono chiamate le ruotine delle S_Function varia a seconda dell'ambiente (Target) per cui viene costruito l'eseguibile.

Struttura  di chiamata per Real  Time Workshop

Se si sta utilizzando Real Time Workshop per produrre il codice della S_Function non è necessario inserire nel programma tutta la struttura vista nel precedente paragrafo, ma basta rispettare il seguente schema: 

Figura 4 : Sequenza di esecuzione Simulink per Real Time Workshop

Per un approfondimento su come opera RTW si faccia riferimento ai relativi manuali.

Struttura di chiamata per modalità esterna

Anche quando si esegue Simulink in modalità esterna (external mode) la sequenza di chiamata di routine è differente e rispetta il seguente schema:

 

Figura 5 : Sequenza di esecuzione Simulink in modalità esterna

La chiamata alla routine mdlRTW viene effettuata una volta, quando si accede alla modalità esterna, ed ogni volta che si seleziona Update Diagram dal menu Edit del blocco S_Function.

Trattamento dati nelle S-Function

I blocchi S_Function sono provvisti di segnali di input e output, parametri, stati interni del blocco, più aree di memoria riservate. La lettura e la scrittura dei segnali di input e output avviene tramite gli altri blocchi del modello collegati alla S_Function, che generalmente hanno ingressi e/o uscite vettoriali. Gli ingressi possono anche essere del tipo:

Ingressi esterni attraverso i blocchi inport

Sconnessi, se non è presente alcun segnale

Anche le uscite possono essere collegate a blocchi di tipo outport. I segnali di ingresso e uscita possono avere anche le seguenti caratteristiche:

Stati continui

Stati discreti

Aree dati di tipo reale, intero, o vettori di puntatori.

Alcuni parametri possono essere forniti alla S_Function direttamente nella finestra di dialogo del blocco, vedremo in seguito come.

Le lunghezze dei vari segnali e vettori può essere configurata per mezzo della routine mdlInitializeSizes. L’accesso alla struttura contenente i parametri del blocco può essere effettuato mediante i seguenti comandi:

      nputRealPtrs  uPtrs = ssGet InputPortRealSignalPtrs(S,indiceporta)

Che fornisce in uPtrs un array di puntatori, dove indiceporta che è un valore intero compreso tra 0 e n-1 (con n numero di porte di ingresso). Per il generico elemento della porta si userà:

     *uPtrs[elem]

Questi azioni corrispondono al seguente schema:

Per accedere all’ingresso 1:

    InputRealPtrs  uPtrs0 = ssGetInputRealSignalPtrs(S;0)

Per accedere all’ingresso 2:

    InputRealPtrs  uPtrs1 = ssGetInputRealSignalPtrs(S;1)

Come si può notare l’array di puntatori non indirizza necessariamente aree di memoria contigue. Per quanto riguarda il segnale d’uscita vi si può accedere nel modo seguente:

    real_T  *y = ssGetOutputPortSignal(S,indiceportauscita);

dove indiceportauscita ha lo stesso significato di indiceporta visto in precedenza.

Di seguito viene fornito un esempio di come accedere ai dati della porta di ingresso, leggendoli e successivamente copiandoli, senza alterazioni, nella porta di uscita, si noti che in tal caso è presente un direct feedthrough che va quindi opportunamente specificato, come detto precedentemente.

 

int_T element;

int_T portWidth = ssGetInputPortWidth(S,inputPortIndex) ;

inputRealPtrs uPtrs = ssGetInputPortRealSignalPtrs(S,inputPortIndex);

real_T  *y = ssGetOutputPortSignal(S,outputPortIdx);

 

for (element=0; element<portWidth; element++)

{

  y[element] = *uPtrs[element];

}

Errori comuni sono quelli compiuti sui puntatori, spesso, infatti, si pensa di potervi accedere ponendo :

real_T  *uPtrs;

nel campo delle dichiarazioni, e utilizzare il seguente codice per copiare i dati:

    *y++ = *u++

tutto ciò non è corretto.

 

Controllo ed elaborazione parametri

Abbiamo già parlato precedentemente della possibilità di introdurre parametri in modo interattivo, vediamo ora come ciò avviene utilizzando il campo S_Function Parameters nella finestra di dialogo del blocco:

Occorre determinare l’ordine in cui i parametri vengono forniti nella finestra.

Nella funzione mdlInitializeSizes si utilizzi la macro ssSetNumSFcnParams per comunicare a Simulink il numero di parametri. Si specifichi S (è un puntatore a SimStrusct) come primo argomento della macro ed il numero di parametri come secondo.

L’accesso a questi parametri,all’interno della S_Function, è consentito mediante la macro ssGetSFcnParam, specificando sempre S come primo argomento ed il numero d’ordine del parametro come secondo.

Concludiamo quindi con un esempio. Si inseriscano i seguenti comandi nella sezione delle definizioni:

#define BASE_ADDRESS_PRM(S)               ssGetSFcnParams(S,0)

#define GAIN_RANGE_PRM(S)                 ssGetSFcnParams(S,1)

#define PROG_GAIN(S)                      ssGetSFcnParams(S,2)

#define NUM_OF_CHANNELS_PRM(S)            ssGetSFcnParams(S,3)  

Questo codice definisce l’ordine in cui i valori o i nomi dei parametri vanno inseriti nel campo S_Function Parameters della finestra di dialogo del blocco. La presenza di parametri forniti interattivamente deve essere specificata inserendo:

ssSetSFcnParams(S,4);

all’interno della funzione mdlInitializeSizes.

Cambiamento dei parametri

Le routine che consentono all’utente di controllare ed elaborare i parametri aggiornati all’interno della S_Function sono: mdlCheckParameters, interna a mdlInitializeSizes, e mdlProcessParameters e possono essere usate solo all’interno di S_Function C-MEX. L’utilizzo di questa caratteristica assume una importanza rilevante quando si usa Real-Time Workshop, in tal caso deve essere attivata la funzionalità External mode. Quest’ultima pone i dati aggiornati direttamente nel codice eseguibile contenuto in memoria solo dopo che Simulink ha chiamato le precedenti routine ed effettuato le azioni che esse richiedono.

Mediante le routine mdlCheckParameters e mdlInitializeSizes è infatti possibile controllare, con la prima, ed elaborare, con la seconda, i parametri aggiornati della S-Function. I relativi codici sono i seguenti:

mdlCheckParameters

#define MDL_CHECK_PARAMETERS

#if defined  (MDL_CHECK_PARAMETERS)

static void mdlCheckParameters(SimStruct *S)

{

}

#endif

 

mdlProcessParameters

#define MDL_PROCESS_PARAMETERS

#if defined(MDL_PROCESS_PARAMETERS)

static void mdlProcessParameters(SimStruct *S)

{

}

#endif

All’interno delle routine andrà inserito il codice opportuno per la validazione o l’elaborazione dei parametri aggiornati.

Definizione delle caratteristiche di un blocco

Il campo sizes del record SimStruct contiene delle informazioni importanti sulla S_Function quali il numero di ingressi, uscite, stati ed altre caratteristiche del blocco.Tale struttura è inizializzata nella funzione mdlInitializeSizes ed i suoi valori possono essere letti e scritti mediante delle macro.

mdlInitializeSizes

Come ormai sappiamo è la prima routine chiamata da Simulink e serve per specificare le caratteristiche di un blocco (numero di ingressi, uscite, stati ecc.). Una di queste è la influenza diretta degli ingressi sulle uscite (direct feedthrough) che va specificata settando un opportuno flag in mdlOutput mdlGrtTimeOfNextVarHit.

Possono essere specificati in questa funzione anche parametri come: NumContStates, NumDiscStates, NumInputs, NumOutputs, NumRWork, NumIWork, NumPWork, NumModes e NumNonSampledZCs, dando loro valori interi non negativi che ne specificano l’ampiezza oppure specificando l’opzione DYNAMICALLY_SIZED che fa in modo che le ampiezze siano stabilite dai blocchi visti come ingressi dalla nostra S_Function.

Ci sono macro che permettono di controllare delle informazioni, come ssGetInputPortConnected(S), ssGetOutputPortConnected(S), che determinano il numero di porte connesse, oppure ssSetSFcnParamNotTunable(S,idparam) che stabilisce quali parametri non possono cambiare in corso di simulazione.

Se le uscite sono discrete è opportuno specificare il valore SS_OPTION_DISCRETE_VALUED_OUTPUT (ad es. se le uscite assumono solo i valori 1 e 2).

Nella funzione mdlInitializeSizes è inoltre possibile specificare la frequenza di campionamento, vedremo meglio più avanti come.

 

Configurare le proprietà delle porte di I/O

Nella funzione mdlInitializeSizes abbiamo specificato le ampiezze delle porte come un intero non negativo. Se si specifica SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION ed un intero positivo non nullo N, per una porta d’ingresso, allora l’ampiezza della rispettiva porta sarà o 1 o N. La routine ssGetInputPortWidth determina l’ampiezza attuale della porta specificata.

Se specifichiamo l’opzione DYNAMICALLY_SIZED l’ampiezza della porta verrà determinata da Simulink all’atto della propagazione dei segnali durante l’esecuzione del modello. L’assegnazione delle ampiezze avviene mediante le routine mdlSetInputPortWidth e mdlSetOutputPortWidth, se non specificate si avrà una configurazione di default (scalare). Studiamo ora alcune routine più attentamente.

 

mdlSetInputPortSampleTime

Chiamata una volta pervenuto il tempo di campionamento, nel caso in cui sia ereditato da blocchi precedenti, deve stabilire se questo è accettabile, in tal caso si può andare avanti ed acquisire tale tempo utilizzando ssSetInputPortSampleTime, oppure no, in tal caso occorre inviare un opportuno messaggio di errore mediante ssSetErrorStatus.

 

mdlSetOutputPortSampleTime

E’ l’equivalente della precedente per le porte d’uscita. Ovviamente in questo caso utilizzeremo ssSetOutpuPortSampleTime.

 

mdlSetInputPortWidth

Chiamata durante la propagazione delle ampiezze dei vettori per controllare la validità dell’ampiezza proposta. Anch’essa deve prevedere un messaggio di errore opportuno qualora l’ampiezza fornita non sia valida.

 

mdlSetOutputPortWidth

E’ l’equivalente della routine precedente per le porte d’uscita.

 

Impostazione dei tempi di campionamento

Simulink prevede blocchi che eseguono a frequenze di campionamento differenti. I modi previsti per determinare tali frequenze sono due:

    Tempi di campionamento basati sul blocco (block based)

    Tempi di campionamento basati sulla porta (port based)

Nel primo caso la S_Function specifica tutte le frequenze di campionamento del blocco ed elabora ingressi ed uscite alla frequenza più elevata fra quelle specificate se ciascun tempo di campionamento è multiplo intero di quello più veloce. Nel secondo caso, invece, la S_Function specifica il tempo di campionamento per la porta di ingresso e di uscita. Per capire meglio i due metodi si consideri il caso di due tempi, rispettivamente 0.5 (input) e 0.25 (output) secondi:

·        Nel primo caso Simulink effettuerà i calcoli con un periodo di 0.25 secondi.

·        Nel secondo caso l’esecuzione dell’ingresso avviene ad una frequenza di 2Hz, mentre l’aggiornamento dell’uscita a 4Hz.

 

Se quindi una specifica applicazione prevede tempi di campionamento disuguali, e si vuole evitare di dover eseguire tutti i blocchi alla frequenza più alta, è preferibile utilizzare l’impostazione port based. Per le applicazioni di tipo più comune è comunque sufficiente l’impostazione block based.

 

Tempi di campionamento Block based

Quando si opta per una soluzione block based è necessario specificare delle informazioni. Si deve, infatti, configurare la nostra S_Function per lavorare con tempi di campionamento block based inserendo nella funzione mdlInitializeSizes l’istruzione:

    ssSetNumSampleTimes(S,numsampletimes);  

dove numsampletimes è un numero positivo, onde specificare il numero di istanti di campionamento.

Poiché all’interno di questa funzione Simulink chiama mdlInitializeSampleTimes occorre, in essa, specificare le seguenti informazioni:

Tempo di campionamento ed offset

Chiamate a funzioni

 

Nel primo caso si ricorrerà alle seguenti istruzioni:

    ssSetSampleTime(S, sampletimePairIndex, sample_time)

    ssSetOffsetTime(S, offsetTimePairIndex, offset_time)

 

Si consulti il manuale per maggiori informazioni sulle coppie [ sample_time , offset_time ].

Nel secondo si specificheranno quali elementi di uscita eseguono chiamate a funzioni mediante la macro ssSetCallSystemOutput.

 

Tempi di campionamento Port based

Se si opta per questa soluzione occorre, invece, definire le proprietà della S_Function nelle seguenti routine:

mdlInitializeSizes

mdlSetInputPortSampleTime

mdlSetOutputPortSampleTime

 

Nella prima routine specificheremo:

    ssSetNumSampleTime(S,PORT_BASED_SAMPLE_TIMES)

 

insieme a:

    ssSetInputPortSampleTime(S,idx,period)

    ssSetInputPortOffsetTime(S,idx,offset)

    ssSetputputPortSampleTime(S,idx,period)

    ssSetOutputPortOffsetTime(S,idx,offset)  

La seconda e la terza routine vengono chiamate solo nel caso di tempi di campionamento di tipo inherited, cioè acquisiti da altri blocchi connessi al nostro, e dovrà, quindi, prevedere controlli sull’accettabilità o meno del tempo acquisito, e, se questo è adeguato, provvedere all’impostazione utilizzando le stesse macro della prima routine.

E’ possibile anche realizzare blocchi S_Function che prevedano un funzionamento multirate, per il quale si rimanda ai manuali.

Configurazione di Work Vectors

Se la S_Function prevede l’utilizzo di memoria persistente (cioè un area di memoria statica accessibile solo dall’istanza attuale della S_Function) è possibile utilizzare i work vectors al posto di variabili di tipo static o global, altrimenti accessibili da più istanze della S_Function, qualora fossero presenti più blocchi facenti riferimento allo stesso file C-MEX.

Questa caratteristica di possedere un area di memoria privata viene detta re-entrancy. Queste aree di memoria definibili possono contenere tipi di dati interi, reali, puntatori e dati di tipo generale (es. numero di stati continui, discreti, di modi ecc.).

Le lunghezze di tali vettori vanno specificate, come sempre, all’interno di mdlInitializeSizes con l’ausilio delle macro di sistema, tra cui:

ssSetNumContStates

ssSetNumDiscStates

ssSetNumRWork

ssSettNumIWork

ssSetNumPWork

ssSetNumModes

ssSetNumnonSampledZCs

 

Nell’utilizzare tali macro occorre specificare:

 

 Il valore 0 se il vettore non è utilizzato.

Un intero (>0) che ne indica la dimensione.

DYNAMICALLY_SIZED, qualora la sua dimensione dipenda da altri blocchi, in tal caso per determinare le dimensioni dei vettori si utilizza la routine mdlSetWorkWidths.

L’accesso alle strutture così definite avviene per mezzo delle macro del tipo ssGet* (es. ssGetRWork), e tramite esse si possono inizializzare i valori dei vettori, in mdlStart o in mdlInitializeConditions.

Un caso a parte è rappresentato dai vettori mode-work e da quello degli attraversamenti (nonsampled zero crossing) utilizzato per determinare i passaggi per lo zero.

Allocazione di memoria

In alcuni casi la memoria messa a disposizione dai work vector definiti nel precedente paragrafo può non essere sufficiente. In tal caso l’allocazione dinamica di memoria non può essere effettuata mediante le macro standard del MATLAB API (Application Program Interface) perché realizzate per MEX-file utilizzati da Matlab, non da Simulink.

Il modo corretto per allocare memoria è, quindi, quello di utilizzare le macro calloc e free presenti nella libreria stdlib.h.

Si può dunque allocare, usando calloc, ed inizializzare la memoria in mdlStart ponendo il puntatore (ptr) a quest’area in un vettore pointer-work, come quelli visti prima, facendo:

     ssGetPWork(S)[i] = ptr;

o collegarlo come dato utente:

     ssSetUserData(S, ptr);

L’area di memoria non va liberata in mdlTerminate madiante la macro free.

Inizializzazione delle S-Function

Questa operazione può essere effettuata per mezzo delle routine mdlStart e mdlInitializeConditions, per definire i work vector o i segnali d’uscita della S_Function. La prima routine viene chiamata da Simulink una sola volta all’inizio dell’esecuzione del modello. La seconda all’inizio della simulazione e ogni volta che viene richiesta, se permessa, la revisione degli stati.

Utilizzo delle S-Function con Real-Time Workshop

Questo tipo di utilizzo, anche se in generale è permesso, necessita in alcuni casi di modifiche al codice della S_Function nei seguenti casi:

Introduzione di moduli extra nella  S_Function.

Impostazione dati per RTW (RTWdata).

Aggiunta della funzione mdlRTW.

 

Per quanto riguarda il primo caso, se per esempio la S_Function è costituita da più moduli, come ad esempio:

     mex sfun_mai.c  sfun_module1.c  sfun_module2

 

occorre allora specificare:

     set_param(sfun_blok,’SfuctionModules’,’sfun_module1’,’sfun_module2’)

 

dove il parametro fornito può anche essere una variabile:

     modules = ’sfun_module1 sfun_module2’

    set_param(sfun_blok,’SfuctionModules’,’modules’)

 

o, qualora i moduli siano identificatori validi, una stringa:

     set_param(sfun_blok,’SfuctionModules’,’’’sfun_module1 sfun_module2’’’)

La seconda proprietà è utile per l’inserimento della S_Function nel codice generato da RTW. RTWdata è una struttura di stringhe, da unire al blocco, che viene posta in model.rtw all’atto della generazione del codice. I seguenti comandi, ad esempio:

mydata.field1 =  ‘information for field1’;

mydata.field2 = ‘information for field2’;

set_param(gcb,’RTWdata’,mydata)

get_param(gcb,’RTWdata’)

producono i seguenti risultati:

ans =

field1:  ‘information for field1’

field2:  ‘information for field2’

All’interno di mdl.rtw sarà presente il codice:

Block {

     Type                        “S-Function”

      RTWdata  {

                 field1                                “information for field1”

              field2               “information for field2”

                    }

Per quanto riguarda, invece, la routine mdlRTW, questa permette il corretto inserimento della S_Function nel codice generato da RTW, permettendo un miglioramento delle prestazioni. Per maggiori chiarimenti è necessario consultare il manuale del compilatore usato (Target Language Compiler) e la guida di RTW.

Questa pagina è stata aggiornata il 03/01/04.

Testo originale: Massimo Demofonte

Aggiornamento e adattamento: Leonardo Daga

Leonardo Daga's Warehouseâ, http://leonardodaga.insyde.it
Send any Comments to: leonardo.daga@gmail.com