为什么我的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
我不是串口高手。我很乐意执行您建议的任何实验。请帮助我。
The miniUART is now available on /dev/ttyS0
所以你必须写在 /dev/ttyS0
而不是 /dev/ttyAMA0
背景故事:我最近得到了一个 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
我不是串口高手。我很乐意执行您建议的任何实验。请帮助我。
The miniUART is now available on /dev/ttyS0
所以你必须写在 /dev/ttyS0
而不是 /dev/ttyAMA0