为什么我的Raspberry Pi的串口不可写? write() 写入零字节。 select() 超时

Why is my Raspberry Pi's serial port not writeable? write() writes zero bytes. select() times out

背景故事:我最近得到了一个 Raspberry Pi 4 Model B and a Protoneer RPI CNC Hat, which it controls via the serial port (/dev/ttyAMA0). After I put it all together and it didn't work at all (either in Minicom or bCNC),我一直在逐渐尝试将问题的根源归零。我的示波器显示,当将它们用作 GPIO 时,我可以控制相关的引脚,所以我不怀疑基本的硬件问题。然而,当我将它们用作串行端口时,我完全无法从这些引脚中激起响应。我已经编写了以下 C 程序来尽可能准确地描述问题。

问题: 我有一个串口,/dev/ttyAMA0,但它不工作。

$ sudo cat /proc/tty/driver/ttyAMA
serinfo:1.0 driver revision:
0: uart:PL011 rev2 mmio:0xFE201000 irq:34 tx:59596 rx:3105 RTS|CTS|DTR

$ ls -l /dev/ttyAMA0
crw-rw---- 1 root dialout 204, 64 Jul 11 08:24 /dev/ttyAMA0

$ groups
pi adm dialout cdrom sudo audio video plugdev games users input netdev gpio i2c spi

$ dmesg | grep tty
[    0.000258] console [tty0] enabled
[    0.420424] fe201000.serial: ttyAMA0 at MMIO 0xfe201000 (irq = 34, base_baud = 0) is a PL011 rev2
[    0.425685] fe215040.serial: ttyS0 at MMIO 0x0 (irq = 36, base_baud = 62500000) is a 16550
[    1.857049] systemd[1]: Created slice system-getty.slice.

$ echo hello | cat - > /dev/ttyAMA0
cat: write error: No space left on device

我可以成功open()它,但是当我使用select()等待它变得可写时,它超时了。当我尝试写入时,write() 成功写入 0 个字节。

/* Shamelessly stolen from Stack Overflow, and then modified (in the finest tradition).
 *  */

#include <errno.h>
#include <fcntl.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

int set_interface_attribs(int fd, int speed)
{
    struct termios tty;

    if (tcgetattr(fd, &tty) < 0) {
        printf("Error from tcgetattr: %s\n", strerror(errno));
        return -1;
    }

    cfsetospeed(&tty, (speed_t)speed);
    cfsetispeed(&tty, (speed_t)speed);

    tty.c_cflag |= (CLOCAL | CREAD);    /* ignore modem controls */
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;         /* 8-bit characters */
    tty.c_cflag &= ~PARENB;     /* no parity bit */
    tty.c_cflag &= ~CSTOPB;     /* only need 1 stop bit */
    tty.c_cflag &= ~CRTSCTS;    /* no hardware flowcontrol */

    /* setup for non-canonical mode */
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    /* fetch bytes as they become available */
    tty.c_cc[VMIN] = 1;
    tty.c_cc[VTIME] = 1;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        printf("Error from tcsetattr: %s\n", strerror(errno));
        return -1;
    }
    return 0;
}

int main()
{
    char *portname = "/dev/ttyAMA0";
    int fd;
    int wlen;

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd == -1) {
        perror("open: error");
        return -1;
    }
    /* baudrate 115200, 8 bits, no parity, 1 stop bit */
    set_interface_attribs(fd, B115200);

    /* select() on the serial port file descriptor to wait for it to be writeable.
     * It never does become writeable. Removing this section does not change
     * the behavior of the following call to write() */
    fd_set wfds;
    struct timeval tv;
    int select_retval;
    FD_ZERO(&wfds);
    FD_SET(fd, &wfds);
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    select_retval = select(fd + 1, NULL, &wfds, NULL, &tv);
    if (select_retval == -1)
    {
        perror("select: error");
        return -1;
    }
    else if (select_retval)
    {
        printf("select: fd can be written now\n");
    }
    else
    {
        /* This is what happens */
        printf("select: fd did not become writeable within 5 seconds\n");
    }

    /* Write some output. This returns 0 bytes written and no errors. */
    wlen = write(fd, "Hello!\n", 7);
    if (wlen == -1)
    {
        perror("write: error");
        return -1;
    }
    printf("write: wrote %d bytes\n", wlen);

    tcdrain(fd);
}

输出:

$ gcc -Wall -Werror -std=gnu17 -o serial-test serial-test.c
$ ./serial-test 
select: fd did not become writeable within 5 seconds
write: wrote 0 bytes

我不是串口高手。我很乐意执行您建议的任何实验。请帮助我。

指的是如何在树莓派 3(或更高版本)上进行串行工作 :

The miniUART is now available on /dev/ttyS0

所以你必须写在 /dev/ttyS0 而不是 /dev/ttyAMA0