Contents

树莓派5串口调试

Contents

在完成一个物联网项目时,尝试以stm32作为数据采集设备,通过树莓派与stm32通信

我们需要通过raspi_config禁止串口登陆以释放串口设备给程序使用,同时保留硬件串口

运行 sudo raspi_config,选择 Interface Options

menu

选择 Serial Port

menu

随后在提示符 Would you like a login shell to be accessible over serial?下选择 No

在提示符 Would you like the serial port hardware to be enabled?下选择Yes

保存退出并重启树莓派

打开并编辑 /boot/firmeare/config.txt,将文件末尾修改如下:

[all]
dtoverlay=uart0-pi5

若要使用其他次UART也可考虑禁用蓝牙控制器

根据树莓派官网介绍:树莓派5包含5个PL011 UART,不包含mini UART,除 UART0外其他默认情况下禁用。

ID 类型
UART0 PL011
UART1 PL011
UART2 PL011
UART3 PL011
UART4 PL011

在树莓派上,一个 UART 通过 GPIO 14(发送)和 15(接收),即主UART。默认情况下,这也是 Linux 控制台可能存在的 UART

文档这里将树莓派5的主UART标为UART10,而后在介绍Linux设备时说:

Linux设备 描述
/dev/ttyAMA0 UART0
/dev/serial0 主UART0
/dev/ttyAMA10 调试UART

同时又在下面指出 /dev/serial0会通过符号链接指向 /dev/ttyAMA0(因为树莓派5不包含mini UART)

实际上,查看 /dev/可以看到:

$ ll | grep ttyAMA
lrwxrwxrwx 1 root root           8 May  6 23:03 serial0 -> ttyAMA10
crw-rw---- 1 root dialout 204,  64 May  6 23:15 ttyAMA0
crw-rw---- 1 root dialout 204,  74 May  6 23:07 ttyAMA10

使用 /dev/serial0通常是一个安全的选择,但它似乎被错误指向了 /dev/ttyAMA10,这是板上SH1.0的3p端口。因此在调试UART时,若希望不禁用蓝牙连接而使用三针UART,应直接使用 /dev/ttyAMA0这个设备来访问UART0

串口配置:

int configure_serial(int fd) {
    struct termios options{};
    if (tcgetattr(fd, &options) < 0) {
        perror("获取串口属性失败");
        return -1;
    }

    cfmakeraw(&options);
    cfsetispeed(&options, B115200);
    cfsetospeed(&options, B115200);

    options.c_cflag |= (CLOCAL | CREAD); // 启用接收
    options.c_cflag &= ~CSTOPB;          // 1位停止位
    options.c_cc[VMIN] = 0;  // 接收最小字节数
    options.c_cc[VTIME] = 10;            // 超时 1 秒(单位0.1s)

    if (tcsetattr(fd, TCSANOW, &options) < 0) {
        perror("配置串口失败");
        return -1;
    }
    return 0;
}

接收线程,包含字节检测:

void* recv_thread(void* arg) {
    (void)arg;
    while (1) {
        fd_set read_fds;
        struct timeval timeout;
        FD_ZERO(&read_fds);
        FD_SET(serial_fd, &read_fds);
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;

        int retval = select(serial_fd + 1, &read_fds, NULL, NULL, &timeout);
        if (retval == -1) {
            perror("[recv] select error");
            continue;
        } else if (retval == 0) {
            continue;
        }

        ssize_t read_bytes = read(serial_fd, recv_struct, sizeof(Recv));
        if (read_bytes < 0) {
            if (errno == EINTR) continue;
            perror("[recv] read error");
            continue;
        } else if (read_bytes != sizeof(Recv)) {
            fprintf(stderr, "[recv] 不完整数据 (%zd/%zu 字节)\n",
                    read_bytes, sizeof(Recv));
            tcflush(serial_fd, TCIFLUSH);
            continue;
        }

        printf("[recv] 收到%zd字节反馈数据\n", read_bytes);
    }
    return NULL;
}

发送线程:

void* send_thread(void* arg) {
    (void)arg;
    while (1) {
        // 从服务器获取数据
        sync_from_remote();

        if (control_container->mode == 1) {
            code_light_groups_normal_mode(light_struct);
        } else if (control_container->mode == 2) {
            code_light_groups_smart_mode(car_mgr, light_struct);
        } else {
            printf("[send] Invalid mode\n");
            sleep(1);
            continue;
        }

        ssize_t written = write(serial_fd, light_struct, sizeof(Light));
        if (written < 0) {
            perror("[send] 写入失败");
        } else {
            printf("[send] 写入%zd字节\n", written);
        }

        tcflush(serial_fd, TCOFLUSH);
        sleep(1); // 控制发送间隔
    }
    return NULL;
}

主程序:

#include <pthread.h>
// ...
int serial_fd = -1;

int main()
{
    // ...
    // 打开串口0
    if ((serial_fd = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY)) < 0) {
        perror("打开串口失败");
        return -1;
    }
    if (configure_serial(serial_fd) < 0) {
        close(serial_fd);
        return -1;
    }
    pthread_t send_tid, recv_tid;
    pthread_create(&send_tid, NULL, send_thread, NULL);
    pthread_create(&recv_tid, NULL, recv_thread, NULL);

    pthread_join(send_tid, NULL);
    pthread_join(recv_tid, NULL);
    close(serial_fd);

    // ...
    return 0;
}

Related Content