MATLAB: [11] This article takes you to understand the principle and implementation of serialport serial port transceiver

Broken thoughts:

The main work this week is still focused on FOC, because I envy the beautiful Qt panel made by Mr. Zhihui, so in the process of using MATLAB to reproduce, I learned the use of serialport. The GUI part of FOC is in the process of writing overtime. At the same time, I plan to open a new pit recently. You can look forward to it hahaha.

Welcome everyone to like + bookmark + follow~ o(* ̄▽ ̄*)ブ

Table of contents

1 serial port receiving

2 serial port send


Considering that the use cases of the latest serialport in MATLAB on the Internet are somewhat confusing, and many of them are based on the serial library that has been eliminated, which seriously lacks ease of use, so this article gives a simple serial port transceiver template, especially the serial port callback A use case for the function.

1 serial port receiving

Serial port reception refers to the process in which the development board sends data to the computer, and the computer reads the data and performs data analysis and processing.

If you want to figure out how to receive data from the serial port, you first need to know how the data from the serial port is sent .

Imagine such an application scenario, a temperature sensor is installed on my development board, and the data length collected by the temperature sensor is 3 bytes (24 bits); I need to display the temperature information collected by the development board on the screen in real time, I need How to do it?

The following points need to be noted:

  1. The temperature sensor is 3 bytes, how to determine where a received byte is located in the three bytes?
  2. Real-time display requires that I need to respond to the data sent every time. How should this response be done?

For question 1:

In fact, this is also a common problem for beginners. Sometimes the data sent by the serial port is like a water pipe that cannot be blocked, and I don’t know how to deal with it.

Due to the limitations of the serial port protocol, it can only send one byte at a time. For multi-byte data [ABC], it can only pass three bytes [A], [B], [C] to send, as shown in the figure below (the leftmost byte is the first received byte):

This will obviously encounter problems. At any moment, I can't determine where the received data is in [ABC]; what's more fatal, due to the influence of the physical medium, it may even cause data loss. This further affects the acceptance of the data.

How to solve this problem? People began to think of the way of "packaging", which can also be understood as the concept of "frame" we often say. Just add some signs at the beginning of each set of data to indicate that this is the initial position of the data, as shown in the figure below (the left end is the first received byte):

Assume that the flag we set is [FF, FF]. When the host computer detects two consecutive [FF], it means that the next three bytes are [A], [B], and [C].

This actually solves this problem 1, realizing the determination of the position of each byte in a frame.

For question 2:

After solving problem 1, we can of course use the method of sequential execution to realize one-time reading and data processing of serial port data. But how to realize that every time a specific signal is detected, the data processing function is called once?

It is necessary to understand the use logic of serialportlist in MATLAB. Overall, serialportlist is an upgraded version of serial (it is also mentioned in the help page). It realizes the setting and reading and writing of serial port parameters by constructing SerialObject objects. .

For details, please refer to the MATLAB document serialport . Too comprehensive parameter settings are too redundant and are beyond the scope of this article. Here we mainly introduce two important concept buffers and callback functions.

buffer:

In serialport, the buffer automatically exists in the SerialObject object, but sometimes it is not necessary to set the size of the buffer when using it (such as this article). It can be understood as a FIFO queue with a fixed length. When a specific signal is detected, each byte of data transmitted by the serial port is stored in it in order. When the length is full, no new ones will be added to it. data.

The functions that may be used are

flush(SerialObj)

It can be used to clear the buffer, and is often used when the serial port object is initialized.

Callback:

This is the key to solving problem 2. The callback function can be understood as the operation that needs to be performed after a switch is triggered (or simply understood as the interrupt processing function of the single-chip microcomputer); we can set the detected signal through the object setting of SerialObject ( this signal as the end of a frame ) , the callback function is executed.

Take scheme A as an example, we can set to read three bytes of data when the [FF FF] signal is detected. (Although it is not used in this way, I will explain why later)

As shown in the figure above, when we set the trigger condition of the callback function according to the method of solution A above, what is the problem? Whenever [FF FF] is detected, the callback function will be triggered.

It seems to be no problem, but at this time the combination of a frame has changed from [FF FF ABC] to [ABC FF FF], because we mentioned that the callback function is sensitive to the end of a frame. When [FF FF] is detected, the next byte is obviously [A]. This is actually non-standard, and we cannot take it for granted that every frame is transmitted correctly.

for example:

【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】【 A B C FF FF】

The red ABC in the middle indicates a transmission error caused by poor contact of the data line. If there is a fixed frame header, there may be an error in the frame header, so this frame error signal [ABC FF FF] will be skipped directly .

Therefore, a fixed frame header must be used to determine whether the transmission is complete data at this time.

This requires us to further modify the structure of a frame so that it completely includes the "frame header" and "frame tail". The configureTerminator method is given in MATLAB, which can edit the end-of-frame signal that SerialObject needs to detect. For a detailed explanation, you can see the official configureTerminator document , which has this introduction:

configureTerminator(t,terminator) defines the terminator for both read and write communications with the remote host specified by the TCP/IP client t. Allowed terminator values are "LF" (default), "CR", "CR/LF", and integer values from 0 to 255. The syntax sets the Terminator property of t.

It is mentioned here that we can set the end-of-frame signal to be detected as "LF", "CR", "CR/LF" or an integer of 0-255 (just corresponding to an 8-bit unsigned number, that is, one byte).

According to the above instructions, we can modify the previous frame as shown in the figure below, plus the end of the frame (the leftmost byte is the first received byte):

In this way, we can call the callback function by detecting the end of the frame (orange part). But a new question was born again: I understand how to send the number of 0-255, but this is a single byte after all, will it cause confusion in data reading? What are the three "LF", "CR" and "CR/LF" mentioned above? (This is also a problem that has bothered me for a while)

Explanation of the concepts of "LF", "CR", and "CR/LF":

Quoted from: CR, LF detailed explanation_Berwyn's blog-CSDN blog_cr's hexadecimal

In terms of origin, before computers appeared, there was a thing called a teletypewriter (Teletype Model 33, from which the concept of tty under Linux/Unix also came from), which could type 10 characters per second. But it has a problem, that is, it takes , which is just enough to type two characters . If within this 0.2 seconds, a new character is transmitted, then this character will be lost.

Therefore, the developers thought of a way to solve this problem, which is to add two characters at the end of each line to indicate the end. One is called "carriage return" and tells the typewriter to position the print head at the left margin; the other is called "line feed" and tells the typewriter to move the paper down one line. This is the origin of "line feed" and "carriage return", as can be seen from their English names.

Later, when the computer was invented, these two concepts were generalized to the computer. At that time, memory was very expensive, and some scientists thought it was too wasteful to add two characters at the end of each line, and just add one. Thus, differences arose. In different systems, the following situation occurs:

system symbol name Hexadecimal (ASCII)
Linux ’\n’ LF 0x0A
Mac ’\r’ CR 0x0D
Windows ’\r\n’ CR/LF 【0x0D 0x0A】
Note: This does not mean that only CR/LF can be used as the end of the frame in the Windows system. What is mentioned in the table is the default line break in the text editor of the corresponding system.

Do you feel enlightened? Then we can take it for granted to change the previous picture to the following (the leftmost end is the first received byte):

After reading this, I think readers have gradually understood what I said at the beginning: if you want to figure out how to receive data from the serial port, you first need to know how the data from the serial port is sent out . Recall our thinking, because we want to achieve multi-byte reading, we need to give a fixed frame header to determine the position of each byte; in order to provide a signal that can activate the callback function without affecting the existence of the frame header , we need to add a frame trailer. Combined with the setting signal in configureTerminator, we found that "LF", "CR", "CR/LF" or 0-255 numbers can be used as the end of the frame to activate the callback function. By referring to the previous three "LF", " CR" and "CR/LF" refer to the ASCII code of the newline character, we can use the development board to let them send out the corresponding hexadecimal data to represent.

So far, we know the structure of the data sent from the development board. Comparing the four frame endings, only "CR/LF" is two bytes, which is the safest for unknown data signals such as temperature, and can better avoid similar situations and lead to reading errors.

for example:

When the data we send is: [FF FF ABC end of frame].

When the frame tail is 1 byte, it is very likely that [C] and [frame tail] are the same. If the [frame tail] is two bytes, the probability of [BC] being the same will be much reduced.

Therefore, we choose to send data to the upper computer in the development board as shown in the figure below (the leftmost end is the first received byte), which needs to be defined in the development board first. This article assumes that the reader has completed this part. If necessary, readers can also leave a message to me, and I will publish a separate article to explain:

So now you can start the exciting (bushi) MATLAB programming session. Based on the MATLAB document serialport , a simple template is given below:

Port_List = serialportlist("available");
SerialObj = serialport("COM7",115200);
configureTerminator(SerialObj,"CR/LF");
flush(SerialObj);
SerialObj.UserData = struct("Data",[]);
configureCallback(SerialObj,"terminator",@readSerialData);

% 回调函数
function readSerialData(src, ~)
    data = read(src,7,"uint8");
    src.UserData.Data = data;
    ShowTemp(src);
end

% 温度数据处理与展示
function ShowTemp(src)
    if(src.UserData.Data(1:2) == [0xFF 0xFF])
        Temperature = src.UserData.Data(3)*256*256 + src.UserData.Data(4)*256 + src.UserData.Data(5);
        disp(Temperature);
    end
end

Let's explain the code:

Port_List = serialportlist("available");

Displays the list of serial ports available in the current system, corresponding to the ports in the computer device manager.


SerialObj = serialport("COM7",115200);

Use the serialport function to construct a serial port object SerialObj, set the corresponding port to be COM7 port, and the baud rate to be 115200.


configureTerminator(SerialObj,"CR/LF");

Set the end of frame to be detected to be "CR/LF".


flush(SerialObj);

Clear the receive buffer of the serial port object.


SerialObj.UserData = struct("Data",[]);

By looking at the properties of the SerialObj object, you can see that there is a property called UserData, which can be used to store data. Here we define it as a structure, which defines only one data called Data.


configureCallback(SerialObj,"terminator",@readSerialData);

Specify the callback function, that is, the readSerialData function mentioned in the third attribute, which indicates the operation to be performed after detecting the end of the frame. The "terminator" parameter means to detect the terminator, and the reader only needs to modify the last parameter readSerialData.


function readSerialData(src, ~)

Define the callback function, src represents the object passed in from the user, so no modification is required.


    data = read(src,7,"uint8");

The read function means to read 7 bytes of data from the serial port object, because it starts after the end character is detected, which is the content of the 7 bytes [FF FF ABC 0D 0A]. "uint8" indicates that an 8-bit unsigned number is read. It is worth noting that there are other functions that can be used in this part, such as the readline function used to read a line of characters, which is also clearly introduced in the MATLAB document serialport .

In fact, here we can further understand the reason why CR/LF is a newline character. Isn’t it the meaning of reading a line (readline) when reading from one newline character to another?


    src.UserData.Data = data;

Store the read data data in Data under the structure in the object property UserData to realize data storage. The data storage method is an array with a length of 7, which can be called directly by index 1-7.

    ShowTemp(src);

Call the data processing function to preprocess and display the received data.


end


function ShowTemp(src)

Define data processing functions


    if(src.UserData.Data(1:2) == [0xFF 0xFF])

Use the if statement to judge whether the data header is [FF FF], and determine whether there is a transmission error.


        Temperature = src.UserData.Data(3)*256*256 + src.UserData.Data(4)*256 + src.UserData.Data(5);

The next three bytes are [ABC], each of which is 8 bits, so they must be multiplied by their weights for calculation to obtain the original data.


        disp(Temperature);

Display the current data to the console.


    end
end

2 serial port send

Serial port transmission refers to the process in which the computer integrates the data to be sent (generally instructions or parameter setting information) and sends them to the development board.

I believe that with the previous basis of serial port reception, this is very simple for everyone. Here, we still assume an application scenario to explain. Since readers will use the GUI to test serial port transmission, here we use Take the data format of the text input box in the GUI as an example.

In the GUI, I need to send a hexadecimal string "FF 01 02 03 04" to the development board, what do I need to do? The GUI, as shown in the figure below, is a module of type "Text Area":

Here you need to pay attention to the following issues:

  1. How to get data from the GUI (only when using the GUI, if it is a script file, it needs to be processed according to the string).
  2. The data obtained in the GUI is actually a cell type, not a simple string type (only when the GUI is used, if it is a script file, it needs to be processed according to the string).
  3. How to divide the data and send it.

Here, since the three problems are quite clear and easy to solve, I directly give the use case of the serial port sending function write:

Port_List = serialportlist("available");
SerialObj = serialport("COM7",115200);
send_data = get(app.TextAreaTabSend, "value");
HEX       = hex2dec(strsplit(cell2mat(send_data), " "));
write(app.SerialObject,HEX,"uint8");

Let's explain the code:

Port_List = serialportlist("available");

Displays the list of serial ports available in the current system, corresponding to the ports in the computer device manager.


SerialObj = serialport("COM7",115200);

Use the serialport function to construct a serial port object SerialObj, set the corresponding port to be COM7 port, and the baud rate to be 115200.


send_data = get(app.TextAreaTabSend, "value");

Get the value information in the current TextArea from the GUI, and return the data of the cell type.


HEX       = hex2dec(strsplit(cell2mat(send_data), " "));

From the inner layer to the outer layer, complete cell2mat() to convert the cell type to mat type; strsplit() to split the mat type according to spaces; hex2dec() to convert the string as hex type data into decimal for transmission.

If it is a simple string operation, you can replace it with the following function:

HEX       = hex2dec(strsplit(send_str, " "));

Split the string first, and then convert it into a decimal array.

Note that these two writing methods are the default, and the sent information must be separated by spaces between each byte, because the write function is used, and it is of type uint8.


write(SerialObj ,HEX,"uint8");

Send the data HEX to the SerialObj object to realize the sending. The write function is used here. In fact, there is another function writeline. Readers can refer to the MATLAB document serialport for reference.

So far, all the data sending and receiving tasks have been completed. When the serial port needs to be closed, just use the following function to delete the created object.

delete(SerialObj);                        %通过删除对象来断开串口

Finally, let me mention again, why do I always use the uint8 type of write and read? On the one hand, our application environment is still dominated by the transmission of numbers, and the transmission of strings is not very involved here. On the other hand, sending and receiving byte by byte is more convenient to understand the principle of the serial port protocol in my opinion, and ASCII itself is an 8-bit unsigned number.


This is the first time I try this way of writing. I hope this article can give readers some help. At the same time, due to my limited level, if you have some questions, please leave a message to point out. I will accept it with an open mind!

This is the whole content of this issue. If you like my article, don't forget to like + bookmark + follow, and share it with your friends~

Guess you like

Origin blog.csdn.net/Alex497259/article/details/125922427