我注意到有些奇怪但可复制的东西。
我首先检查我的串行端口设置:
bash-3.1# stty -F /dev/ttyS0
speed 0 baud; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; start = <undef>;
stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>;
min = 1; time = 0;
-cread
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
然后将速度更改为1200bps:
bash-3.1# stty -F /dev/ttyS0 1200
然后我在程序中执行此片段以更改波特率:
fd=open(dev,O_NOCTTY | O_NONBLOCK | O_RDWR);
struct termios ser[1];
tcflush(fd,TCIFLUSH);
tcflush(fd,TCOFLUSH);
cfmakeraw(ser);
// I call tcsetattr after each terminal setting to make sure its applied.
if (tcsetattr(fd,TCSANOW,ser) < 0){
return -1;
}
cfsetspeed(ser,B9600);
if (tcsetattr(fd,TCSANOW,ser) < 0){
return -2; //returns this after manually setting port via STTY
}
问题是波特率没有正确更改。实际上,我从函数中返回-2,而strerror(errno)返回“输入/输出错误”。
程序执行后,我检查系统端口设置:
bash-3.1# stty -F /dev/ttyS0
speed 0 baud; line = 0;
intr = <undef>; quit = <undef>; erase = <undef>; kill = <undef>; eof = <undef>; start = <undef>;
stop = <undef>; susp = <undef>; rprnt = <undef>; werase = <undef>; lnext = <undef>; flush = <undef>;
min = 1; time = 0;
-cread
-brkint -icrnl -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe -echok -echoctl -echoke
即使我特别要求9600bps,它也会重置为零bps。
为什么要这么做?以及如何强制通过编程将速度提高到9600bps?
答案 0 :(得分:1)
您的代码中有很多错误。
O_NONBLOCK
打开tty设备,因此发出ioctl
调用时(tc*attr(3)
调用会导致ioctl(2)
syscall,具体取决于您使用的unix风格),您不知道该设备是否已经打开即可进行tc*attr(3)
通话。 O_NOCTTY
标志也是如此。您将这些标志放在打开系统调用中时不知道它们的功能是什么。 O_NOCTTY
在从会话内部运行的程序中是无用的,如果设备没有运行,O_NONBLOCK
会进行tc*attr(3)
的调用以返回错误(EAGAIN
)尝试进行参数调整时将其打开。open(2)
调用的结果。如果您尝试使用-1
作为文件描述符(ENODEV
,ENOTTY
或EBADF
,EINVAL
,ENXIO
,这可能会出错。等)struct termios
结构的数据,所以这可能就是您得到错误的原因。如您所示(示例代码段不完整,一个告诉您阅读How to create a Minimal, Complete, and Verifiable example的原因)您使用的struct termios
是在自动变量上声明的(因为其声明已嵌入到代码中)这肯定是未初始化的,并且带有垃圾数据。通常,您需要对其进行tcgetattr()
初始化为适当的值,并能够在程序结束后恢复设置。bash(1)
使ioctl(2)
可以在连接到tty设备的标准输入描述符上设置和获取termios参数。如果您正在使用stdin,则必须考虑bash(1)
的干扰。这使您获得的值与通过stty
设置的值有所不同。stty
终止(在tty的最后关闭时),这些参数将重置为默认值。在发出sleep 999999999 </dev/ttyBlaBla &
命令之前先进行stty(1)
,以便在设置sleep
后端口保持打开状态(通过stty(1)
的重定向)。termios(3)
页,因此您可以从程序本身设置参数。仅当您的程序通常不处理设置参数时,您才无需以编程方式进行设置。但是,更改终端参数毫无意义,因此最好学习如何对设备参数进行编程。正确的操作方式应该是这样(从您的代码段复制并编辑):
#include <string.h> /* for strerror */
#include <errno.h> /* for errno definition */
/* ... */
fd=open(dev,O_NOCTTY | O_NONBLOCK | O_RDWR);
if (fd < 0) {
fprintf(stderr, "OPEN: %s (errno = %d)\n",
strerror(errno), errno);
return -1;
}
struct termios ser; /* why an array? */
tcflush(fd,TCIFLUSH); /* unneeded, you have not used the tty yet */
tcflush(fd,TCOFLUSH); /* idem. */
/******* THIS IS THE MOST IMPORTANT THING YOU FORGOT ***********/
int res = tcgetattr(fd, &ser); /* *****this initializes the struct termios ser***** */
if (res < 0) {
fprintf(stderr, "TCGETATTR: %s (errno = %d)\n",
strerror(errno), errno);
return -2; /* cannot tcgetattr */
}
/***************************************************************/
cfmakeraw(&ser); /* now it is valid to set it */
cfsetspeed(&ser,B9600); /* better do all in only one system call */
// I call tcsetattr after each terminal setting to make sure its applied.
/* nope, tcsetattr and tcgetattr are the only calls that make something
* on the tty, the rest only manipulate bits on the struct termios
* structure, but don't do anything to the terminal, you begun with a
* trashed struct termios, so it's normal you end with an error. */
if ((res = tcsetattr(fd, TCSANOW, &ser)) < 0){
fprintf(stderr, "ERROR: %s (errno = %d)\n",
strerror(errno), errno); /* better to know what happened. */
return -3; /* couldn't tcsetattr */
}
最后,此代码(为您的第一个代码)未经测试,主要是因为您没有张贴完整,最少和可验证的示例。因此,在将其包含在代码中之前,您可能需要稍作修改。并且,请 RTFM (完整阅读termios(3)
的最后含义,最重要的是:How to create a Minimal, Complete, and Verifiable example):)。另外,如果您使用的是bash(1)
,请不要在stdin上检查tty设置,因为它通常会在命令退出后,在发出提示之前恢复tty
的设置。