MATLAB Language
Usando le porte seriali
Ricerca…
introduzione
Le porte seriali sono un'interfaccia comune per la comunicazione con sensori esterni o sistemi embedded come Arduinos. Le moderne comunicazioni seriali sono spesso implementate tramite connessioni USB usando adattatori USB-seriali. MATLAB fornisce funzioni integrate per le comunicazioni seriali, inclusi i protocolli RS-232 e RS-485. Queste funzioni possono essere utilizzate per porte seriali hardware o connessioni USB-seriale "virtuali". Gli esempi qui illustrano le comunicazioni seriali in MATLAB.
Parametri
Parametro porta seriale | cosa fa |
---|---|
BaudRate | Imposta il baud rate. Il più comune oggi è 57600, ma sono spesso visti anche 4800, 9600 e 115200 |
InputBufferSize | Il numero di byte conservati in memoria. Matlab ha un FIFO, il che significa che i nuovi byte saranno scartati. Il valore predefinito è 512 byte, ma può essere facilmente impostato su 20 MB senza problemi. Ci sono solo alcuni casi limite in cui l'utente vorrebbe che fosse piccolo |
BytesAvailable | Il numero di byte in attesa di essere letti |
ValuesSent | Il numero di byte inviati da quando la porta è stata aperta |
ValuesReceived | Il numero di byte letti da quando la porta è stata aperta |
BytesAvailableFcn | Specificare la funzione di callback da eseguire quando un numero specificato di byte è disponibile nel buffer di input o viene letto un terminatore |
BytesAvailableFcnCount | Specificare il numero di byte che devono essere disponibili nel buffer di input per generare un evento bytes-available |
BytesAvailableFcnMode | Specificare se l'evento bytes-available viene generato dopo che un numero specificato di byte è disponibile nel buffer di input o dopo la lettura di un terminatore |
Creazione di una porta seriale su Mac / Linux / Windows
% Define serial port with a baud rate of 115200
rate = 115200;
if ispc
s = serial('COM1', 'BaudRate',rate);
elseif ismac
% Note that on OSX the serial device is uniquely enumerated. You will
% have to look at /dev/tty.* to discover the exact signature of your
% serial device
s = serial('/dev/tty.usbserial-A104VFT7', 'BaudRate',rate);
elseif isunix
s = serial('/dev/ttyusb0', 'BaudRate',rate);
end
% Set the input buffer size to 1,000,000 bytes (default: 512 bytes).
s.InputBufferSize = 1000000;
% Open serial port
fopen(s);
Lettura dalla porta seriale
Supponendo che si è creato l'oggetto porta seriale s
come in questo esempio, allora
% Read one byte
data = fread(s, 1);
% Read all the bytes, version 1
data = fread(s);
% Read all the bytes, version 2
data = fread(s, s.BytesAvailable);
% Close the serial port
fclose(s);
Chiusura di una porta seriale anche se persa, cancellata o sovrascritta
Supponendo che si è creato l'oggetto porta seriale s
come in questo esempio, allora per chiuderlo
fclose(s)
Tuttavia, a volte è possibile perdere accidentalmente la porta (ad es. Cancellare, sovrascrivere, cambiare ambito, ecc.) E fclose(s)
non funzionerà più. La soluzione è facile
fclose(instrfindall)
Maggiori informazioni su instrfindall()
.
Scrivere sulla porta seriale
Supponendo che si è creato l'oggetto porta seriale s
come in questo esempio, allora
% Write one byte
fwrite(s, 255);
% Write one 16-bit signed integer
fwrite(s, 32767, 'int16');
% Write an array of unsigned 8-bit integers
fwrite(s,[48 49 50],'uchar');
% Close the serial port
fclose(s);
Scegli la tua modalità di comunicazione
Matlab supporta la comunicazione sincrona e asincrona con una porta seriale. È importante scegliere la giusta modalità di comunicazione. La scelta dipenderà da:
- come si comportano gli strumenti con cui stai comunicando.
- quali altre funzioni il tuo programma principale (o GUI) dovrà fare a parte la gestione della porta seriale.
Definirò 3 diversi casi da illustrare, dai più semplici ai più esigenti. Per i 3 esempi, lo strumento a cui mi sto collegando è un circuito stampato con un inclinometro, che può funzionare nelle 3 modalità che descriverò di seguito.
Modalità 1: sincrono (master / slave)
Questa modalità è la più semplice. Corrisponde al caso in cui il PC è il Master e lo strumento è lo slave . Lo strumento non invia nulla alla porta seriale, ma risponde solo dopo aver ricevuto una domanda / comando dal Master (il PC, il programma). Per esempio:
- Il PC invia un comando: "Datemi una misura ora"
- Lo strumento riceve il comando, prende la misura e restituisce il valore di misurazione alla linea seriale: "Il valore dell'inclinometro è XXX".
O
- Il PC invia un comando: "Cambia dalla modalità X alla modalità Y"
- Lo strumento riceve il comando, lo esegue, quindi invia un messaggio di conferma alla linea seriale: " Comando eseguito " (o " Comando NON eseguito "). Questo è comunemente chiamato risposta ACK / NACK (per "Acknowledge (d)" / "NOT Acknowledged").
Riepilogo: in questa modalità, lo strumento (lo Slave ) invia i dati alla linea seriale immediatamente dopo essere stato richiesto dal PC (il Master )
Modalità 2: asincrona
Ora supponiamo di aver avviato il mio strumento, ma è più di un semplice sensore stupido. Controlla costantemente la propria inclinazione e finché è verticale (entro una tolleranza, diciamo +/- 15 gradi), rimane in silenzio. Se il dispositivo è inclinato di oltre 15 gradi e si avvicina all'orizzontale, invia un messaggio di allarme alla linea seriale, immediatamente seguito da una lettura dell'inclinazione. Finché l'inclinazione è sopra la soglia, continua a inviare una lettura di inclinazione ogni 5 secondi.
Se il tuo programma principale (o GUI) è costantemente "in attesa" del messaggio che arriva sulla linea seriale, può farlo bene ... ma nel frattempo non può fare altro. Se il programma principale è una GUI, è altamente frustrante avere una GUI apparentemente "bloccata" perché non accetterà alcun input da parte dell'utente. Essenzialmente, è diventato lo schiavo e lo strumento è il maestro . A meno che non abbiate un modo elegante per controllare la vostra GUI dallo strumento, questo è qualcosa da evitare. Fortunatamente, la modalità di comunicazione asincrona ti consente di:
- definire una funzione separata che indica al programma cosa fare quando viene ricevuto un messaggio
- mantenere questa funzione in un angolo, verrà solo richiamata ed eseguita quando un messaggio arriva sulla linea seriale. Il resto del tempo la GUI può eseguire qualsiasi altro codice che deve essere eseguito.
Riepilogo: in questa modalità, lo strumento può inviare messaggi alla linea seriale in qualsiasi momento (ma non necessariamente per tutto il tempo). Il PC non attende in modo permanente l'elaborazione di un messaggio. È consentito eseguire qualsiasi altro codice. Solo quando arriva un messaggio, attiva una funzione che leggerà ed elaborerà questo messaggio.
Modalità 3: streaming ( tempo reale )
Ora scateniamo tutta la potenza del mio strumento. Lo metto in una modalità in cui invierà costantemente misurazioni alla linea seriale. Il mio programma desidera ricevere questi pacchetti e visualizzarli su una curva o su un display digitale. Se invia solo un valore ogni 5 secondi come sopra, nessun problema, mantieni la modalità precedente. Ma il mio strumento a pieno ritmo invia un punto dati alla linea seriale a 1000 Hz, cioè invia un nuovo valore ogni millisecondo. Se rimango nella modalità asincrona sopra descritta, c'è un alto rischio (in realtà una certezza garantita) che la funzione speciale che abbiamo definito per elaborare ogni nuovo pacchetto richiederà più di 1 ms per eseguirla (se si desidera tracciare o visualizzare il valore, le funzioni grafiche sono piuttosto lente, non considerando nemmeno il filtraggio o la trasmissione FFT del segnale). Significa che la funzione inizierà ad essere eseguita, ma prima che finisca, un nuovo pacchetto arriverà e attiverà di nuovo la funzione. La seconda funzione viene inserita in una coda per l'esecuzione e inizia solo quando viene eseguita la prima ... ma a questo punto sono arrivati alcuni nuovi pacchetti e ognuno ha inserito una funzione da eseguire nella coda. Puoi prevedere rapidamente il risultato: quando sto tracciando il quinto punto, ne ho già centinaia in attesa di essere tracciato anche ... il gui rallenta, alla fine si congela, lo stack cresce, i buffer si riempiono, finché qualcosa non dà. Alla fine ti rimane un programma completamente congelato o semplicemente uno in crash.
Per superare questo, disconnetteremo ulteriormente il collegamento di sincronizzazione tra il PC e lo strumento. Lasceremo che lo strumento invii i dati al proprio ritmo, senza attivare immediatamente una funzione ad ogni arrivo di pacchetto. Il buffer della porta seriale accumula solo i pacchetti ricevuti. Il PC raccoglierà i dati nel buffer solo a un ritmo che può gestire (un intervallo regolare, impostato sul lato PC), farà qualcosa con esso (mentre il buffer verrà ricaricato dallo strumento), quindi raccoglierà un nuovo batch di dati dal buffer ... e così via.
Riepilogo: in questa modalità, lo strumento invia continuamente dati, che vengono raccolti dal buffer della porta seriale. A intervalli regolari, il PC raccoglie i dati dal buffer e fa qualcosa con esso. Non esiste un collegamento di sincronizzazione tra il PC e lo strumento. Entrambi eseguono i loro compiti secondo i loro tempi.
Elaborazione automatica dei dati ricevuti da una porta seriale
Alcuni dispositivi collegati tramite una porta seriale inviano dati al programma a una velocità costante (dati di streaming) o inviano dati a intervalli imprevedibili. È possibile configurare la porta seriale per eseguire automaticamente una funzione per gestire i dati ogni volta che arriva. Questa è chiamata la "funzione di callback" per l'oggetto della porta seriale.
Esistono due proprietà della porta seriale che devono essere impostate per utilizzare questa funzione: il nome della funzione desiderata per il callback ( BytesAvailableFcn
) e la condizione che deve attivare l'esecuzione della funzione di callback ( BytesAvailableFcnMode
).
Esistono due modi per attivare una funzione di callback:
- Quando un certo numero di byte è stato ricevuto sulla porta seriale (tipicamente usato per dati binari)
- Quando un certo carattere viene ricevuto sulla porta seriale (tipicamente usato per testo o dati ASCII)
Le funzioni di callback hanno due argomenti di input obbligatori, chiamati obj
e event
. obj
è la porta seriale. Ad esempio, se si desidera stampare i dati ricevuti dalla porta seriale, definire una funzione per stampare i dati chiamati newdata
:
function newdata(obj,event)
[d,c] = fread(obj); % get the data from the serial port
% Note: for ASCII data, use fscanf(obj) to return characters instead of binary values
fprintf(1,'Received %d bytes\n',c);
disp(d)
end
Ad esempio, per eseguire la funzione newdata
ogni volta che vengono ricevuti 64 byte di dati, configurare la porta seriale in questo modo:
s = serial(port_name);
s.BytesAvailableFcnMode = 'byte';
s.BytesAvailableFcnCount = 64;
s.BytesAvailableFcn = @newdata;
Con dati di testo o ASCII, i dati sono generalmente divisi in righe con un "carattere di terminazione", proprio come il testo su una pagina. Per eseguire la funzione newdata
ogni volta che viene ricevuto il carattere di ritorno a newdata
, configurare la porta seriale in questo modo:
s = serial(port_name);
s.BytesAvailableFcnMode = 'terminator';
s.Terminator = 'CR'; % the carriage return, ASCII code 13
s.BytesAvailableFcn = @newdata;