1364 views|8 replies

20

Posts

0

Resources
The OP
 

[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

  1. The host computer sends Beijing time to the microcontroller.
  2. 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.
  3. The microcontroller returns data to the host computer.
  4. 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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

4. Compilation

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.

sudo apt-get install mingw-w64
x86_64-w64-mingw32-gcc -o serial.exe serial.c -lpthread
gcc -o serial serial.c -lpthread

sudo apt-get install socat
socat -d -d pty,raw,echo=0 pty,raw,echo=0

5. Code

#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef _WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#endif

#ifdef _WIN32
#define DEFAULT_BAUD_RATE CBR_115200
#else
#define DEFAULT_BAUD_RATE B115200
#endif

#ifdef _WIN32
HANDLE serial_port;
#else
int16_t serial_port;
#endif
// 定义帧头和帧尾
#define frame_header1 0x55
#define frame_header2 0xAA
#define frame_footer1 0x0A
#define frame_footer2 0x0D

bool open_serial_port(int8_t *port_name, int16_t baud_rate)
{
#ifdef _WIN32
    serial_port = CreateFileA(port_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    if (serial_port == INVALID_HANDLE_VALUE)
    {
        printf("无法打开串口设备\n");
        return false;
    }
    DCB dcbSerialParams = {0};
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(serial_port, &dcbSerialParams))
    {
        printf("无法获取串口属性\n");
        CloseHandle(serial_port);
        return false;
    }
    dcbSerialParams.BaudRate = baud_rate;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    if (!SetCommState(serial_port, &dcbSerialParams))
    {
        printf("无法设置串口属性\n");
        CloseHandle(serial_port);
        return false;
    }
    COMMTIMEOUTS timeouts = {0};
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier = 10;
    if (!SetCommTimeouts(serial_port, &timeouts))
    {
        printf("无法设置串口超时时间\n");
        CloseHandle(serial_port);
        return false;
    }
    return true;
#else
    serial_port = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_port < 0)
    {
        printf("无法打开串口设备\n");
        return false;
    }
    struct termios tty;
    memset(&tty, 0, sizeof(tty));
    if (tcgetattr(serial_port, &tty) != 0)
    {
        printf("无法获取串口属性\n");
        close(serial_port);
        return false;
    }
    cfsetospeed(&tty, baud_rate);
    cfsetispeed(&tty, baud_rate);
    tty.c_cflag |= CLOCAL;
    tty.c_cflag |= CREAD;
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;
    tty.c_cflag &= ~PARENB;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;
    tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_iflag &= ~(INLCR | IGNCR | ICRNL);
    tty.c_oflag &= ~(ONLCR | OCRNL);
    tty.c_cc[VTIME] = 0;
    tty.c_cc[VMIN] = 1;
    tcflush(serial_port, TCIFLUSH);
    if (tcsetattr(serial_port, TCSANOW, &tty) != 0)
    {
        printf("无法设置串口属性\n");
        close(serial_port);
        return false;
    }
    return true;
#endif
}

void close_serial_port()
{
#ifdef _WIN32
    CloseHandle(serial_port);
#else
    close(serial_port);
#endif
}

bool write_serial_port(uint8_t *data, int16_t length)
{
#ifdef _WIN32
    DWORD bytes_written;
    if (!WriteFile(serial_port, data, length, &bytes_written, NULL))
    {
        printf("写入串口数据失败\n");
        return false;
    }
#else
    int16_t bytes_written = write(serial_port, data, length);
    if (bytes_written < 0)
    {
        printf("写入串口数据失败\n");
        return false;
    }
#endif
    return true;
}

int16_t read_serial_port(uint8_t *buffer, int16_t length)
{
#ifdef _WIN32
    DWORD bytes_read;
    if (!ReadFile(serial_port, buffer, length, &bytes_read, NULL))
    {
        printf("读取串口数据失败\n");
        return false;
    }
#else
    int16_t bytes_read = read(serial_port, buffer, length);
    if (bytes_read < 0)
    {
        // printf("读取串口数据失败\n");
        return -1;
    }
#endif
    return bytes_read;
}

void calculate_crc(const uint8_t *data, int16_t length, uint8_t *crc_low, uint8_t *crc_high)
{
    uint16_t crc = 0xFFFF;
    for (int16_t i = 0; i < length; i++)
    {
        crc ^= data[i];
        for (int16_t j = 0; j < 8; j++)
        {
            if (crc & 0x0001)
            {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else
            {
                crc >>= 1;
            }
        }
    }
    *crc_low = crc & 0xFF;
    *crc_high = crc >> 8;
}

void get_current_time()
{
    uint8_t write_data[21];
    uint8_t crc_low, crc_high;
    int16_t send_index = 0;

    write_data[send_index++] = frame_header1;
    write_data[send_index++] = frame_header2;
    write_data[send_index++] = 0x06; // 写入
    write_data[send_index++] = 0x01; // 时间
    write_data[send_index++] = 12;   // 数据长度

    time_t now = time(NULL);
    struct tm *timeinfo = localtime(&now);

    // 将年、月、日、时、分、秒按小端格式存储到数组中
    write_data[send_index++] = (timeinfo->tm_year + 1900) & 0xFF;
    write_data[send_index++] = (timeinfo->tm_year + 1900) >> 8;
    write_data[send_index++] = (timeinfo->tm_mon + 1) & 0xFF;
    write_data[send_index++] = (timeinfo->tm_mon + 1) >> 8;
    write_data[send_index++] = timeinfo->tm_mday & 0xFF;
    write_data[send_index++] = timeinfo->tm_mday >> 8;
    write_data[send_index++] = timeinfo->tm_hour & 0xFF;
    write_data[send_index++] = timeinfo->tm_hour >> 8;
    write_data[send_index++] = timeinfo->tm_min & 0xFF;
    write_data[send_index++] = timeinfo->tm_min >> 8;
    write_data[send_index++] = timeinfo->tm_sec & 0xFF;
    write_data[send_index++] = timeinfo->tm_sec >> 8;

    // 计算CRC16校验
    calculate_crc(&write_data[5], write_data[4], &crc_low, &crc_high);
    // 将CRC校验值赋值给write_data[16]和write_data[17]
    write_data[send_index++] = crc_low;
    write_data[send_index++] = crc_high;
    write_data[send_index++] = frame_footer1;
    write_data[send_index++] = frame_footer2;
    // 发送数据
    if (!write_serial_port(write_data, send_index))
    {
        printf("发送数据失败\n");
    }
    for (int16_t i = 0; i < send_index; i++)
    {
        printf(" %02X ", write_data[i]);
    }
    printf("\n");
}

void write_velocity_cmd(const int16_t left_velocity, const int16_t right_velocity)
{
    uint8_t write_data[21];
    uint8_t crc_low, crc_high;
    int16_t send_index = 0;

    write_data[send_index++] = frame_header1;
    write_data[send_index++] = frame_header2;
    write_data[send_index++] = 0x06; // 写入
    write_data[send_index++] = 0x02; // 速度
    write_data[send_index++] = 4;    // 数据长度

    write_data[send_index++] = left_velocity & 0xFF;
    write_data[send_index++] = left_velocity >> 8;
    write_data[send_index++] = right_velocity & 0xFF;
    write_data[send_index++] = right_velocity >> 8;

    // 计算CRC16校验
    calculate_crc(&write_data[5], write_data[4], &crc_low, &crc_high);
    // 将CRC校验值赋值给write_data[16]和write_data[17]
    write_data[send_index++] = crc_low;
    write_data[send_index++] = crc_high;
    write_data[send_index++] = frame_footer1;
    write_data[send_index++] = frame_footer2;
    // 发送数据
    if (!write_serial_port(write_data, send_index))
    {
        printf("发送数据失败\n");
    }
    for (int16_t i = 0; i < send_index; i++)
    {
        printf(" %02X ", write_data[i]);
    }
    printf("\n");
}
/*解析返回的速度*/
void read_velocity_cmd(const uint8_t *buffer, const int16_t length, int16_t *left_velocity, int16_t *right_velocity)
{
    if (0x06 != buffer[2] || 0x02 != buffer[3])
    {
        return;
    }
    *left_velocity = (int16_t *)(buffer[5] + ((int16_t)(buffer[6]) << 8));
    *right_velocity = (int16_t *)(buffer[7] + ((int16_t)(buffer[8]) << 8));
    printf("left_velocity=%d\n,right_velocity=%d\n", *left_velocity, *right_velocity);
}
/*解析返回的时间*/
void read_time_cmd(const uint8_t *buffer, const int16_t length, int16_t *year, int16_t *month, int16_t *day,
                   int16_t *hour, int16_t *minute, int16_t *second)
{
    if (0x06 != buffer[2] || 0x01 != buffer[3])
    {
        return;
    }
    *year = (int16_t *)(buffer[5] + ((int16_t)(buffer[6]) << 8));
    *month = (int16_t *)(buffer[7] + ((int16_t)(buffer[8]) << 8));
    *day = (int16_t *)(buffer[9] + ((int16_t)(buffer[10]) << 8));
    *hour = (int16_t *)(buffer[11] + ((int16_t)(buffer[12]) << 8));
    *minute = (int16_t *)(buffer[13] + ((int16_t)(buffer[14]) << 8));
    *second = (int16_t *)(buffer[15] + ((int16_t)(buffer[16]) << 8));
    printf("year=%d,month=%d,day=%d,hour=%d,minute=%d,second=%d\n", *year, *month, *day, *hour, *minute, *second);
}

void load_config(int8_t *config_file, int8_t *port_name, int32_t *baud_rate)
{
    FILE *file = fopen(config_file, "r");
    if (file != NULL)
    {
        int8_t line[256];
        while (fgets(line, sizeof(line), file))
        {
            if (strncmp(line, "port_name=", 10) == 0)
            {
                int8_t *value = strtok(line + 10, "\n");
                strncpy(port_name, value, strlen(value));
            }
            else if (strncmp(line, "baud_rate=", 10) == 0)
            {
                int8_t *value = strtok(line + 10, "\n");
                *baud_rate = atoi(value);
            }
        }
        fclose(file);
    }
    else
    {
        // 配置文件不存在,默认使用默认值
        strncpy(port_name, "/dev/ttyUSB0", sizeof(port_name));
        *baud_rate = 115200;
    }
}
int8_t get_user_input_with_timeout(int16_t timeout)
{
#ifdef _WIN32
    HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    DWORD fdwMode, fdwOldMode;
    GetConsoleMode(hStdin, &fdwOldMode);
    fdwMode = fdwOldMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
    SetConsoleMode(hStdin, fdwMode);
#else
    struct termios old_settings, new_settings;
    tcgetattr(STDIN_FILENO, &old_settings);
    new_settings = old_settings;
    new_settings.c_lflag &= ~(ICANON | ECHO); // 禁用回显和缓冲
    new_settings.c_cc[VTIME] = timeout * 10;  // 设置输入超时时间(以0.1秒为单位)
    new_settings.c_cc[VMIN] = 0;              // 设置非阻塞模式
    tcsetattr(STDIN_FILENO, TCSANOW, &new_settings);
#endif
    int8_t command;
#ifdef _WIN32
    DWORD dwRead;
    if (ReadFile(hStdin, &command, sizeof(command), &dwRead, NULL) && dwRead > 0)
#else
    if (read(STDIN_FILENO, &command, sizeof(command)) > 0)
#endif
    {
#ifdef _WIN32
        SetConsoleMode(hStdin, fdwOldMode);
#else
        tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
#endif
        return command;
    }
#ifdef _WIN32
    SetConsoleMode(hStdin, fdwOldMode);
#else
    tcsetattr(STDIN_FILENO, TCSANOW, &old_settings);
#endif
    return '\0';
}

void parse_serial_data(const uint8_t *buffer, const int16_t length)
{
    uint8_t crc_low = 0, crc_high = 0;
    int16_t left_velocity = 0, right_velocity = 0;
    int16_t year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
    // 帧头帧尾的校验
    if (frame_header1 != buffer[0] || frame_header2 != buffer[1] || frame_footer1 != buffer[length - 2] ||
        frame_footer2 != buffer[length - 1])
    {
        return;
    }

    calculate_crc(&buffer[5], buffer[4], &crc_low, &crc_high);

    if (crc_low != buffer[length - 4] || crc_high != buffer[length - 3])
    {
        printf("CRC校验不通过!\n");
        return;
    }
    printf("接收到的数据(长度:%d):", length);
    for (int16_t i = 0; i < length; i++)
    {
        printf("%02X ", buffer[i]);
    }
    printf("\n");
    read_velocity_cmd(buffer, length, &left_velocity, &right_velocity);
    read_time_cmd(buffer, length, &year, &month, &day, &hour, &minute, &second);
}

// 新增的线程函数,用于接收串口数据
void *receive_serial_data(void *arg)
{
    uint8_t buffer[256];
    while (1)
    {
        int16_t read_len = read_serial_port(buffer, sizeof(buffer));
        if (read_len > 0)
        {
            parse_serial_data(buffer, read_len);
        }
    }
    return NULL;
}

int16_t main()
{
    int8_t port_name[256];
    int32_t baud_rate;
    load_config("config.txt", port_name, &baud_rate);
    printf("Port name: %s\n", port_name);
    printf("Baud rate: %d\n", baud_rate);
    if (!open_serial_port(port_name, baud_rate))
    {
        return -1;
    }
    // 创建线程来接收串口数据
    pthread_t thread;
    pthread_create(&thread, NULL, receive_serial_data, NULL);

    while (1)
    {
        int8_t command = get_user_input_with_timeout(1);
        switch (command)
        {
        case 'w':
            // 执行前进命令
            printf("go ahead\n");
            write_velocity_cmd(100, 100);
            break;
        case 's':
            // 执行后退命令
            printf("go back\n");
            write_velocity_cmd(-100, -100);
            break;
        case 'a':
            // 执行左转命令
            printf("turn left\n");
            write_velocity_cmd(-100, 100);
            break;
        case 'd':
            // 执行右转命令
            printf("turn right\n");
            write_velocity_cmd(100, -100);
            break;
        case 'q':
            // 退出程序
            printf("exit\n");
            close_serial_port();
            return 0;
        default:
            printf("stop\n");
            write_velocity_cmd(0, 0);
            break;
        }
        // 获取当前时间并发送数据
        get_current_time();
        // 延时一段时间
    }

    close_serial_port();
    return 0;
}

This post is from stm32/stm8

Latest reply

It spans two major operating systems, which is a bit difficult.   Details Published on 2023-10-12 06:07
 

20

Posts

0

Resources
2
 

importserial



# Serial port configuration


port="/dev/pts/7"


baud_rate=115200



# Open the serial port


ser=serial.Serial(port,baud_rate)



# Prepare data to be sent (20 bytes)


data_hex="55AA0602046400640035D40A0D"


data_bytes=bytes.fromhex(data_hex)


print(data_hex)


# Send data to the serial port


ser.write(data_bytes)


# Close the serial port


ser.close()


This post is from stm32/stm8
 
 

6841

Posts

11

Resources
3
 

The author has his own unique insights into communication protocols, and he also has excellent writing skills, which is very strong!

This post is from stm32/stm8

Comments

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
 
 

20

Posts

0

Resources
4
 
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.


This post is from stm32/stm8
 
 
 

20

Posts

0

Resources
5
 
scripts.zip (102.98 KB, downloads: 2)
This post is from stm32/stm8
 
 
 

623

Posts

0

Resources
6
 

You are awesome.

All-rounder

This post is from stm32/stm8

Comments

Thank you for your praise. I wrote this program and article while looking up information.  Details Published on 2023-10-11 13:27
 
 
 

20

Posts

0

Resources
7
 
jobszheng5 posted on 2023-10-11 09:21 You are amazing and a versatile person

Thank you for your praise. I wrote this program and article while looking up information.

This post is from stm32/stm8
 
 
 

1w

Posts

16

Resources
8
 

It spans two major operating systems, which is a bit difficult.

This post is from stm32/stm8

Comments

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
 
 

20

Posts

0

Resources
9
 
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.


This post is from stm32/stm8
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list