MATLAB Language
Usando puertos seriales
Buscar..
Introducción
Los puertos serie son una interfaz común para comunicarse con sensores externos o sistemas integrados como Arduinos. Las comunicaciones seriales modernas a menudo se implementan a través de conexiones USB utilizando adaptadores serie USB. MATLAB proporciona funciones integradas para comunicaciones en serie, incluidos los protocolos RS-232 y RS-485. Estas funciones se pueden utilizar para puertos serie de hardware o conexiones serie-USB "virtuales". Los ejemplos aquí ilustran las comunicaciones seriales en MATLAB.
Parámetros
Parámetro puerto serie | Que hace |
---|---|
BaudRate | Establece la velocidad en baudios. Hoy en día, el más común es 57600, pero también se ven con frecuencia 4800, 9600 y 115200 |
InputBufferSize | El número de bytes guardados en la memoria. Matlab tiene un FIFO, lo que significa que los nuevos bytes serán descartados. El valor predeterminado es 512 bytes, pero se puede establecer fácilmente en 20 MB sin problemas. Solo hay unos pocos casos de borde en los que el usuario desearía que esto fuera pequeño. |
BytesAvailable | El número de bytes que esperan ser leídos. |
ValuesSent | El número de bytes enviados desde que se abrió el puerto. |
ValuesReceived | El número de bytes leídos desde que se abrió el puerto |
BytesAvailableFcn | Especifique la función de devolución de llamada que se ejecutará cuando un número especificado de bytes esté disponible en el búfer de entrada o se lea un terminador |
BytesAvailableFcnCount | Especifique el número de bytes que deben estar disponibles en el búfer de entrada para generar un evento de bytes-available |
BytesAvailableFcnMode | Especifique si el evento de bytes-available se genera después de que un número específico de bytes esté disponible en el búfer de entrada, o después de que se lea un terminador |
Creando un puerto serial en 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);
Leyendo desde el puerto serie
Suponiendo que ha creado la serie de objetos puerto s
como en este ejemplo, entonces
% 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);
Cerrar un puerto serie incluso si se pierde, se elimina o se sobrescribe
Suponiendo que ha creado la serie de objetos puerto s
como en este ejemplo, entonces para cerrarlo
fclose(s)
Sin embargo, a veces puede perder el puerto accidentalmente (por ejemplo, borrar, sobrescribir, cambiar el alcance, etc.), y fclose(s)
ya no funcionará. La solucion es facil
fclose(instrfindall)
Más información en instrfindall()
.
Escribiendo al puerto serie
Suponiendo que ha creado la serie de objetos puerto s
como en este ejemplo, entonces
% 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);
Elegir su modo de comunicación
Matlab admite la comunicación síncrona y asíncrona con un puerto serie. Es importante elegir el modo de comunicación correcto. La elección dependerá de:
- Cómo se comporta el instrumento con el que te estás comunicando.
- qué otras funciones tendrá que hacer su programa principal (o GUI) además de administrar el puerto serie.
Definiré 3 casos diferentes para ilustrar, desde el más simple hasta el más exigente. Para los 3 ejemplos, el instrumento al que me estoy conectando es una placa de circuito con un inclinómetro, que puede funcionar en los 3 modos que describiré a continuación.
Modo 1: Sincrónico (Maestro / Esclavo)
Este modo es el más sencillo. Corresponde al caso en el que la PC es el maestro y el instrumento es el esclavo . El instrumento no envía nada al puerto serie por sí mismo, solo responde una respuesta después de que el maestro (la PC, su programa) le haga una pregunta o una orden. Por ejemplo:
- La PC envía un comando: "Dame una medida ahora"
- El instrumento recibe el comando, toma la medida y luego devuelve el valor de la medida a la línea serie: "El valor del inclinómetro es XXX".
O
- La PC envía un comando: "Cambiar del modo X al modo Y"
- El instrumento recibe el comando, lo ejecuta y luego envía un mensaje de confirmación a la línea serie: " Comando ejecutado " (o " Comando NO ejecutado "). Esto se denomina comúnmente respuesta ACK / NACK (para "Confirmar (d)" / "NO reconocido").
Resumen: en este modo, el instrumento (el Esclavo ) solo envía datos a la línea serie inmediatamente después de que el PC (el Maestro ) le haya pedido
Modo 2: asíncrono
Ahora supongamos que inicié mi instrumento, pero es más que un simple sensor. Monitorea constantemente su propia inclinación y mientras sea vertical (dentro de una tolerancia, digamos +/- 15 grados), permanece en silencio. Si el dispositivo está inclinado más de 15 grados y se acerca a la horizontal, envía un mensaje de alarma a la línea serie, seguido inmediatamente por una lectura de la inclinación. Mientras la inclinación esté por encima del umbral, continúa enviando una lectura de inclinación cada 5 s.
Si su programa principal (o GUI) está constantemente "esperando" el mensaje que llega a la línea serie, puede hacerlo bien ... pero no puede hacer nada más mientras tanto. Si el programa principal es una GUI, es muy frustrante tener una GUI aparentemente "congelada" porque no aceptará ninguna entrada del usuario. Esencialmente, se convirtió en el Esclavo y el instrumento es el Maestro . A menos que tenga una forma elegante de controlar su GUI desde el instrumento, esto es algo que debe evitar. Afortunadamente, el modo de comunicación asíncrono le permitirá:
- define una función separada que le dice a tu programa qué hacer cuando se recibe un mensaje
- mantenga esta función en una esquina, solo se llamará y ejecutará cuando llegue un mensaje a la línea serie. El resto del tiempo la GUI puede ejecutar cualquier otro código que tenga que ejecutar.
Resumen: en este modo, el instrumento puede enviar un mensaje a la línea serie en cualquier momento (pero no necesariamente todo el tiempo). La PC no espera permanentemente a que se procese un mensaje. Se permite ejecutar cualquier otro código. Solo cuando llega un mensaje, activa una función que luego leerá y procesará este mensaje.
Modo 3: Streaming ( tiempo real )
Ahora desatemos todo el poder de mi instrumento. Lo puse en un modo en el que enviará constantemente mediciones a la línea serie. Mi programa desea recibir estos paquetes y mostrarlos en una curva o en una pantalla digital. Si solo envía un valor cada 5 segundos como antes, no hay problema, mantenga el modo anterior. Pero mi instrumento a toda potencia envía un punto de datos a la línea serie a 1000Hz, es decir, envía un nuevo valor cada milisegundo. Si me quedo en el modo asíncrono descrito anteriormente, existe un alto riesgo (en realidad, una certeza garantizada) de que la función especial que definimos para procesar cada nuevo paquete tomará más de 1 ms en ejecutarse (si desea trazar o mostrar el valor, las funciones gráficas son bastante lentas, ni siquiera considerando el filtrado o FFT de la señal). Significa que la función comenzará a ejecutarse, pero antes de que finalice, un nuevo paquete llegará y activará la función nuevamente. La segunda función se coloca en una cola para su ejecución, y solo se iniciará cuando se complete la primera ... pero para este momento llegaron algunos paquetes nuevos y cada uno colocó una función para ejecutar en la cola. Puede prever rápidamente el resultado: Cuando estoy trazando los puntos 5, ya tengo cientos esperando para ser trazados también ... el gui se ralentiza, eventualmente se congela, la pila crece, los búferes se llenan, hasta que algo cede. Finalmente, te quedas con un programa completamente congelado o simplemente uno bloqueado.
Para superar esto, desconectaremos aún más el enlace de sincronización entre la PC y el instrumento. Dejaremos que el instrumento envíe datos a su propio ritmo, sin activar inmediatamente una función a cada llegada de paquete. El búfer del puerto serie solo acumulará los paquetes recibidos. La PC solo recopilará los datos en el búfer a un ritmo que pueda administrar (un intervalo regular, configurado en el lado de la PC), hará algo con ella (mientras el búfer se llena por el instrumento) y luego recopila un nuevo lote de Datos del búfer ... y así sucesivamente.
Resumen: en este modo, el instrumento envía datos continuamente, que son recopilados por el búfer del puerto serie. A intervalos regulares, la PC recopila datos del búfer y hace algo con él. No hay un enlace de sincronización duro entre la PC y el instrumento. Ambos ejecutan sus tareas en su propio tiempo.
Procesando automáticamente los datos recibidos de un puerto serie
Algunos dispositivos conectados a través de un puerto serie envían datos a su programa a una velocidad constante (transmisión de datos) o envían datos a intervalos impredecibles. Puede configurar el puerto serie para ejecutar una función automáticamente para manejar datos cada vez que llega. Esto se denomina "función de devolución de llamada" para el objeto de puerto serie.
Hay dos propiedades del puerto serie que deben configurarse para usar esta función: el nombre de la función que desea para la devolución de llamada ( BytesAvailableFcn
) y la condición que debe activar la ejecución de la función de devolución de llamada ( BytesAvailableFcnMode
).
Hay dos formas de activar una función de devolución de llamada:
- Cuando se ha recibido un cierto número de bytes en el puerto serie (normalmente se utiliza para datos binarios)
- Cuando se recibe un determinado carácter en el puerto serie (normalmente se utiliza para texto o datos ASCII)
Las funciones de devolución de llamada tienen dos argumentos de entrada requeridos, llamados obj
y event
. obj
es el puerto serie. Por ejemplo, si desea imprimir los datos recibidos del puerto serie, defina una función para imprimir los datos llamados 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
Por ejemplo, para ejecutar la función newdata
cada vez que se reciben 64 bytes de datos, configure el puerto serie de esta manera:
s = serial(port_name);
s.BytesAvailableFcnMode = 'byte';
s.BytesAvailableFcnCount = 64;
s.BytesAvailableFcn = @newdata;
Con el texto o los datos ASCII, los datos se suelen dividir en líneas con un "carácter de terminación", al igual que el texto en una página. Para ejecutar la función newdata
cada vez que se recibe el carácter de retorno de carro, configure el puerto serie de esta manera:
s = serial(port_name);
s.BytesAvailableFcnMode = 'terminator';
s.Terminator = 'CR'; % the carriage return, ASCII code 13
s.BytesAvailableFcn = @newdata;