[Evaluation of nucleo development board f413zh] Part 5 Serial port host computer[Copy link]
This post was last edited by Robot Enthusiast 1991 on 2023-10-12 18:01
In order to facilitate debugging, I wrote a C language version of the console-based host computer.
1. Function
The host computer sends Beijing time to the microcontroller.
The host computer captures the keyboard input and sends speed instructions to the microcontroller. The W button represents forward movement, the S button represents backward movement, the A button represents left turn, the D button represents right turn, and the others represent pause. The capture of the buttons is set to a 1s timeout.
The microcontroller returns data to the host computer.
Load the configuration file, which contains the serial port number and baud rate.
2. Data Protocol
In order to ensure the correctness of data transmission, the frame header, frame trailer and CRC16 check code are added.
Frame Header
Command (read and write)
Operation Code
Data length
low
high
low
high
crc16_low
crc16_low
Frame end
0x55
0 yes
0x06
0x02
0x04
0x64
0x00
0x64
0x00
0x35
0xd4
0x0a
0x0d
The frame header is used to mark the beginning of the data packet and help the receiver to correctly parse the data. The binary representation of 0x55 is 01010101, and the binary representation of 0xaa is 10101010. When troubleshooting, the alternation of 01 can facilitate troubleshooting.
The frame tail is usually set to 0x0a and 0x0d because they represent the line feed character (\n) and the carriage return character (\r), respectively. In communication protocols, the frame tail is used to mark the end of a data packet. By setting the frame tail to 0x0a and 0x0d, the receiver can detect the end of the data packet and process it accordingly. This ensures the integrity and correct parsing of the data packet. In addition, 0x0a and 0x0d are also widely used for line feed and carriage return operations in text files.
The command (read and write) uses 0x03 for read and 0x06 for write. The operation code 0x01 represents time and the operation code 0x02 represents speed. The data length bit indicates how many bits are counted from the current bit, which is the valid part of the data.
CRC16 (Cyclic Redundancy Check) is an algorithm that calculates a check value based on the data content and can be used to detect errors or damage during data transmission. First, the data to be checked needs to be prepared. Initialize the CRC register to a specific initial value, usually 0xFFFF. Then, process the data bit by bit, XOR the current data bit with the highest bit of the CRC register, and then shift the CRC register one bit to the left. If the highest bit of the CRC register is 1, perform an XOR operation and XOR with a predefined polynomial (such as 0x8005). Continue to process the next data bit and repeat the above steps until all data bits have been processed. Finally, the value stored in the CRC register is the CRC16 check code. By comparing the calculated CRC16 check code with the received check code, it can be determined whether there is an error in data transmission.
3. Code flow
Load the configuration file and configure the serial port number to be opened. void load_config(char *config_file, char *port_name, int *baud_rate) This function allows the port name and baud rate to be loaded from the configuration file. If the configuration file does not exist, the default values are used.
port_name=/dev/ttyUSB0
baud_rate=115200
Open the file and use the fopen function to try to open the specified configuration file. Read the file content line by line and use the fgets function to read the content of the configuration file line by line. Parse the configuration items and use the strncmp function to check whether each line contains the configuration item of "port_name=" or "baud_rate=". Extract the configuration value and use the strtok function to extract the configuration value and remove the newline character. Process "port_name=": If it is a configuration item of "port_name=", copy the extracted value to the passed port_name parameter. Process "baud_rate=": If it is a configuration item of "baud_rate=", convert the extracted value to an integer and store it in the passed baud_rate parameter. Close the file and use the fclose function to close the opened file. Handle file opening failure. If the file opening fails, use the default values of /dev/ttyUSB0 and 115200.
Open the serial port. The code is used to open the serial port device and configure its communication parameters. It is a cross-platform function that uses different APIs to operate the serial port device according to different operating systems. Because my computer is sometimes win, and when I am at work, it is ubuntu, so it has to be set to cross-platform.
First, the code distinguishes whether the operating system is Windows through conditional compilation (#ifdef _WIN32). If it is a Windows operating system, use the CreateFileA function to open the serial port device, use port_name as the name of the serial port device, and open it in read-write mode. If the serial port device cannot be opened, an error message will be output and false will be returned. Get the properties of the current serial port device and set communication parameters such as baud rate (BaudRate), data bits (ByteSize), stop bits (StopBits) and check bits (Parity). If the serial port properties cannot be set, an error message will be output and the serial port device will be closed, and false will be returned. Set the read and write timeout of the serial port. If the timeout cannot be set, an error message will be output and the serial port device will be closed, and false will be returned. If everything is normal, true is returned to indicate that the serial port is opened and configured successfully.
If it is not a Windows operating system, the code executes the following steps. Use the open function to open the serial port device, use port_name as the path of the serial port device, and open it in read-write mode. If the serial port device cannot be opened, an error message will be output and false will be returned. Get the properties of the current serial port device and set communication parameters such as baud rate (BaudRate), data bits (ByteSize), stop bits (StopBits), check bits (Parity), and flow control (CRTSCTS). If the serial port properties cannot be set, an error message will be output and the serial port device will be closed, and false will be returned. Set the input and output modes of the serial port, disable the canonical mode (ICANON) and local echo (ECHO), etc. Set the input control mode of the serial port, disable software flow control (IXON and IXOFF). Set the input character processing of the serial port, disable carriage return conversion (INLCR) and line feed conversion (IGNCR), etc. Set the output character processing of the serial port, disable carriage return conversion (ONLCR) and line feed conversion (OCRNL). Set the serial port read waiting time (VTIME) and the minimum number of characters (VMIN). Clear the serial port input buffer. If the serial port properties cannot be set, an error message will be output and the serial port device will be closed, and false will be returned. If everything is normal, true will be returned to indicate that the serial port is opened and configured successfully.
Serial port sending. The write_serial_port function is used to write data to the serial port. It is implemented differently depending on the operating system. On the Windows platform (under the _WIN32 macro definition), use the WriteFile function to write data to the serial port. The parameters of the WriteFile function include the serial port handle (serial_port), the data buffer pointer (data), the data length (length), and the variable used to store the actual number of bytes written (bytes_written). If the WriteFile function returns failure, it prints an error message and returns false to indicate that the write failed. On other operating system platforms, use the write function to write data to the serial port. The parameters of the write function include the serial port file descriptor (serial_port), the data buffer pointer (data), and the data length (length). If the number of bytes written returned by the write function is less than 0, it prints an error message and returns false to indicate that the write failed. Finally, if the write is successful, the function returns true to indicate that the write is successful.
Serial port reception. This code is used to read data from the serial port and store the read data in the specified buffer. The function is called read_serial_port and accepts two parameters: a pointer to the buffer and the length of the buffer. Conditional compilation is used in the code to select different read functions according to the operating system. For Windows systems, use the ReadFile function to read data from the serial port; for other systems, use the read function to read data from the serial port. If the data reading fails, it returns false or -1, depending on the operating system. If the data is read successfully, it returns the number of bytes read.
Keyboard input.
This code defines a function called get_user_input_with_timeout that gets characters from user input and sets a timeout.
First, a handle to standard input is obtained using different methods depending on the operating system (GetStdHandle for Windows and tcgetattr for other systems). Next, the input mode is set, depending on the operating system. In Windows, echo and line input are disabled using the SetConsoleMode function. In other systems, the current terminal settings are obtained using the tcgetattr function, and then the canonical mode and echo are disabled by modifying the c_lflag field, setting the VTIME field to one-tenth of the timeout, setting the VMIN field to 0 to enable nonblocking mode, and finally applying the new settings to the terminal using the tcsetattr function. A character variable command is declared to store the characters entered by the user. A character is read from standard input using different methods depending on the operating system. In Windows, the character is read using the ReadFile function and the number of bytes read is checked to see if it is greater than 0. In other systems, use the read function to read characters and check whether the return value is greater than 0. If the character is read successfully, restore the previous input mode and return the read character. If the read fails or times out, restore the input mode and return a null character (\0).
It should be noted that some platform-specific functions and macros are used in this code, such as GetStdHandle, SetConsoleMode, tcgetattr, tcsetattr, read, STD_INPUT_HANDLE, ENABLE_ECHO_INPUT, ENABLE_LINE_INPUT, ICANON, ECHO, VTIME, VMIN, etc. The definitions of these functions and macros need to be adapted and adjusted according to the specific operating system and compilation environment.
CRC-16 check. Used to detect whether errors or data corruption occur during data transmission. The function name is calculate_crc, which accepts four parameters: one is a pointer to the data, one is the length of the data, and the other two are pointers to the CRC check results (low byte and high byte). First, set the initial CRC value to 0xFFFF. Then, for each byte in the data, perform an XOR operation with the CRC. Next, for each bit of each byte, determine whether the current bit is 1. If it is 1, shift the CRC right by one bit and perform an XOR operation with 0xA001; if it is 0, only shift the CRC right by one bit. Finally, store the calculated CRC values in crc_low and crc_high respectively, where crc_low stores the low byte of the CRC and crc_high stores the high byte of the CRC.
The main function defines a character array port_name and an integer baud_rate to store the serial port name and baud rate. The load_config function is called to load the serial port name and baud rate from the configuration file. The printf function is used to print out the loaded serial port name and baud rate. The open_serial_port function is called to open the serial port, and -1 is returned if the opening fails. A thread is created to receive serial port data. Enter an infinite loop to continuously read the commands entered by the user. The get_user_input_with_timeout function is used to obtain the characters entered by the user and perform corresponding operations according to the input commands. In the switch statement, different operations are performed according to different input commands, such as forward, backward, left turn, right turn, etc. In each loop, the get_current_time function is called to obtain the current time and send data. Finally, the serial port is closed and 0 is returned.
I compiled the exe for win on ubuntu2004, using x86_64-w64-mingw32-gcc. socat is used to simulate the serial port. The following two pictures are screenshots of simple debugging on ubuntu2004 and win10.
Haha, thanks for your support. The leader was right behind me, so I opened vscode and used the virtual serial port to test the communication. Then I ported it.
Details
Published on 2023-10-10 21:59
lugl4313820 posted on 2023-10-10 21:25 The OP has his own unique insights into communication protocols, and also has superior writing skills, which is very strong!
Haha, thank you for your support. The leader was right behind me, so I opened vscode and used the virtual serial port to test the communication. Then I ported it.
I looked up information and wrote it. Time is tight and there are still some shortcomings. I will adjust the UI when I have time. I tested the win program today and it works. But it doesn't send data if you don't press the button. I will look at it when I have time. The other requirements are met.
Details
Published on 2023-10-12 18:06
Personal signaturehttp://shop34182318.taobao.com/
https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
ddllxxrr posted on 2023-10-12 06:07 It spans two major operating systems, which is a bit difficult.
I looked up information and wrote it. Time is tight and there are still some shortcomings. I will adjust the UI when I have time. I tested the win program today and it works. But it doesn't send data if you don't press the button. I will look at it when I have time. The other requirements are met.