linux下串口无法输入的问题

qyangxjtu 2009-02-19 02:02:21
小弟想在linux下自己写一个串口程序,通过它与嵌入式设备进行交互;
目前,读数据基本是正常的,因为在启动时,若打开串口程序,则会输出启动信息,和在windows下输出一样;
主要问题在于输入,例如我在设备启动后,我想让设备重启,在windows下在串口中输入reboot就可以实现重启,我在我的程序中,想通过write(fd, "reboot\r\n", 8);实现设备重启,但是设备没有反应;
在console下,有时候会回显几个字符,类似reboot,而且有提示符#出现,证明命令是输进去了,可能经过串口处理后边成了r9、lt一类的乱码

可能是输出设置哪里有问题,我现在设的是波特率38400,8位数据位,1位停止位,没有校验,没有流控制,这也是windows下的设置
newtio.c_lflag |= (ECHO|ICANON|ECHOE);是否设回显一类的尝试了都没用。

我现在想得到最简单的功能就是,在主程序中write(fd, "reboot\r\n", 8);时能够重启,加入read只是为了看输入是否正确;

请教要怎样才能实现从串口输入命令控制嵌入式设备?


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

int read_port(int fd);
int init_port(int fd);

int main(int argc, char *argv[])
{
int fd;
int nread;
char buff[512];
unsigned char wbuf[512]="reboot\r\n";
//unsigned char wbuf[512]="abcdefghijklmnopqrstuvwxyz\r\n";
int ret;
char *dev = "/dev/ttyS0";

fd = open("/dev/ttyS0", O_RDWR|O_NONBLOCK);
if( fd == -1)
perror("can't open serial 0\n");
printf("open OK\n");

init_port(fd);

printf("finished init the serial port\n");
ret = write(fd, wbuf, strlen(wbuf));

read_port(fd);

close(fd);
return 0;
}

int init_port(int fd)
{
struct termios newtio;

tcgetattr(fd, &newtio);
bzero(&newtio, sizeof(newtio));

newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8; //8bits data
newtio.c_cflag &= ~PARENB; //no parity
newtio.c_cflag &= ~INPCK; //no parity
newtio.c_cflag &= ~CSTOPB; //1 bit stop
newtio.c_cflag &= ~CRTSCTS; //no flow control
newtio.c_cflag |= (CLOCAL|CREAD);//open read

newtio.c_cc[VTIME] = 250; //set out time;
newtio.c_cc[VMIN] = 0; //update the options and do it NOW


newtio.c_oflag &= ~OPOST;

newtio.c_iflag &= ~(IXON|IXOFF|IXANY);//when | is software flow control
//newtio.c_iflag &= ~(ICANON|ECHO|ECHOE);
newtio.c_lflag |= (ECHO|ICANON|ECHOE);

cfsetispeed(&newtio, B38400);
cfsetospeed(&newtio, B38400);

tcflush(fd, TCIFLUSH);

if(tcsetattr(fd, TCSANOW, &newtio) != 0 )
{
perror("Setup serial 3");
return -1;
}
}


int read_port(int fd)
{
int n, max_fd, len, count=0;
fd_set input;
struct timeval timeout;
char buf[8196];
char *outbuf;
char *ptr = buf;

FD_ZERO(&input);
FD_SET(fd, &input);
max_fd = fd + 1;

while(1)
{
timeout.tv_sec = 5;
timeout.tv_usec = 0;

if( write(fd, "hello world\r\n", 13) < 0 )
printf("write error\n");
n = select(max_fd, &input, NULL, NULL, &timeout);

if(n<0)
perror("select failed");
else if(n == 0)
{
puts("TIMEOUT");
}
else
{
len = read(fd, ptr, 512);
ptr[len] = '\0';
printf("%s", ptr);

}
}
return 0;
}
...全文
1740 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
qyangxjtu 2009-02-25
  • 打赏
  • 举报
回复
非常感谢楼上两位,我是因为某个参数没配好,但是不知道哪个参数没配好
后来采用了7楼大哥的方式,直接cfmakeraw(&tio); 结果就好了
多谢多谢
shaovey 2009-02-24
  • 打赏
  • 举报
回复
在终端通过ctrl+C能否退出你的程序呢,退出后再启动你的程序。这个办法应该是最简单最直接的办法了吧
cceczjxy 2009-02-24
  • 打赏
  • 举报
回复
我这有一个,和你这个类似的程序;当时操作gprs用的(华为gtm900b).本来可以使用minicom,不过为了能用自动的
测试脚本,就这么简单写了个东西。你可以看一下。

#include <stdlib.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <string.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
int fd;
int catchsig(int sig)
{
char a=3;
write(fd,&a,1);
}

int main(int argc,char**argv)
{
int len=0,maxfd=0,iofd=0;
struct termios tio;
fd_set write_fd,read_fd;
char buf[1500];
struct timeval over_timer;
fd=open("/dev/ttyS0",O_RDWR|O_NOCTTY);
tcgetattr(fd,&tio);
cfmakeraw(&tio);
cfsetispeed(&tio,B115200);
cfsetospeed(&tio,B115200);
tcsetattr(fd,TCSANOW,&tio);
memset(buf,0,sizeof(buf));
strcpy(buf,"at+cgmi\r\n");
iofd=fileno(stdin);
signal(SIGINT,catchsig);
while(1)
{
FD_ZERO(&write_fd);
FD_ZERO(&read_fd);
FD_SET(fd,&read_fd);
FD_SET(iofd,&read_fd);
if(fd>iofd)maxfd=fd;
else maxfd=iofd;
over_timer.tv_sec=5;
over_timer.tv_usec=0;
//FD_SET(fd,&write_fd);
if(len=select(maxfd+1,&read_fd,NULL, NULL, &over_timer) <= 0)
continue;
if(FD_ISSET(fd,&read_fd))
{
memset(buf,0,sizeof(buf));
read(fd,buf,sizeof(buf));
// printf(">>>>>>>>>>>>>>>>>>\n");
printf("%s\n",buf);
}
if(FD_ISSET(iofd,&read_fd))
{
printf("-------------------------------------\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
if(strlen(buf)==0)continue;
strcat(buf,"\r\n");
len=write(fd,buf,strlen(buf));
printf(">%d >>%s\n",len,buf);
//break;
}


/*write(fd,buf,strlen(buf));
memset(buf,0,sizeof(buf));
len=read(fd,buf,128);
printf("len=%d\n%s\n",len,buf);*/
}
sleep(2);
close(fd);
}
qyangxjtu 2009-02-20
  • 打赏
  • 举报
回复
谢谢了

后来改了程序的结构,while(1)在超时后就退出;

我想通过串口配置嵌入式设备,如write(fd, "reboot\r\n", 8);后希望设备重启

请问在初始化串口时该如何配置c_oflag,c_lflag,
在打开的时候是不是只用open("/dev/ttyS0", O_RDWR|ONONBLOCK)就可以了

现在从串口读数据是对的,即可以读到系统的启动信息,但是无法输入命令,如reboot等
在c_oflag为0,newtio.c_lflag &= ~(ICANON|ECHO|ECHOE);(原始方式)时,输入reboot的输出为r9\wt#q, 把输入长度8改为9(包括/0)后输出为r9\wt#x
配成标准模式newtio.c_lflag |= (ECHO|ICANON|ECHOE); 时,偶尔会出现以下输出(刚从非标准模式转为标准模式时)
{r
{r : not found
D"(# D"(P""P""P""D("
这是有not found这个输出,表明接收了{r命令,但是没找到该命令。所以我推断应该是采用标准模式

但不知为何,任何模式下回显都不对,也就是说输出的命令都不对

尝试将c_oflag配为其它的模式,因为不知道到底怎么配,也没得到预期的结果
qyangxjtu 2009-02-20
  • 打赏
  • 举报
回复
似乎和oflag没什么关系,因为我不读入输入的命令,不通过console读取命令再发给嵌入式设备,我只是将原来就写好的命令,如reboot通过write发给嵌入式设备,不过write返回的长度是我写的命令的长度,就是没有控制到嵌入式设备,而且再读取时显示也不对

似乎应该是用非标准模式,因为我看unix高级编程第18章最后一段,
就像t a k e命令一样,我们发送一个命令字符串给远程系统。这次命令是:
stty -echo; cat d> e s t f i l e; stty echo
我们必须关闭回送( e c h o),否则整个文件将回送给我们。为了终止c a t命令,发送文件终止符
(一般采用C t r l - D)。这要求本机和远程主机使用相同的文件终止符。另外,文件中不能含有远
程系统中的E R A S E和K I L L特殊字符。
而且第11章几个代码也用的非标准模式。

我尝试在运行时输入dafa,结果会在程序运行完了之后,console会输出-bash:dafa:command not found,也许我开始在规范模式下开启回显功能,输出的not found是console输出的,而不是嵌入式设备输出的。

在非标准模式下,write(fd, "r", 1)
通过read得到r;任何时候,如果只输入任意一个字符,得到的回馈都是对的
当连续多次调用用write(fd, "r", 1),输出的结果就不正确了

write(fd, "reboot\r", 7); 输出:r9\wtc
write(fd, "reboot\n", 7); 输出:r9\wtE
write(fd, "reboot\r\n", 8); 输出:r9\wt#q //有#说明有回车?
write(fd, "reboot\r\n", 9); 输出:r9\wt#x //包含了\0

没思路,随便试的,头痛中!

当设置为规范模式时,读入单个字符没有输出,可能是还在等待输入\r或\n吧

目前手中没有双母头的串口线,所以暂时没法测两台PC通信是否正常
  • 打赏
  • 举报
回复
出现乱码可能是在字符串后面没有加\0结束符;

以后每次都超时,是因为你没有刷新input,应该把FD_ZERO(&input); FD_SET(fd, &input); 这两句放在while(1)里面,每一次循环都要执行。
qyangxjtu 2009-02-19
  • 打赏
  • 举报
回复
open OK
finished init the serial port
aq.g;h:]Gmwqy/vy}#qTIMEOUT
以上是unsigned char wbuf[512]="abcdefghijklmnopqrstuvwxyz\r\n";的输出

open OK
finished init the serial port
r9\wt#qTIMEOUT
TIMEOUT
TIMEOUT
以上是输入"reboot\r\n"的输出
qyangxjtu 2009-02-19
  • 打赏
  • 举报
回复
用minicom尝试了一下,刚打开minicom时,没有输入提示符#,也无法输入
当重启设备后,minicom显示启动信息,然后才有输入提示符#,之后可以输入
不知道这和我一开始无法输入是不是一样

但是,我写的程序在启动后虽然出现#提示符,但是之后每次读都超时,我的意思是在读的时候有write,应该能把写的内容读到,那样就不会超时了
qyangxjtu 2009-02-19
  • 打赏
  • 举报
回复
某次的输入结果如下:(此时main的write中输入的是unsigned char wbuf[512]="abcdefghijklmnopqrstuvwxyz\r\n"; )
open OK
finished init the serial port
.g;h:]Gmw^Gqy^^/v^Oy}#Wlv^G]oy;9$~.n!:7,w^|q>s/{7}T}l{7,{yW~W{!]
u^~<M/_{?-l_
5k53-7eUW}UU"(P""^T^BP""^T^BP""^TP""^T^BP""D("
^BP"b D"(P""D("
TIMEOUT
TIMEOUT

再次运行则可能是其他输出,难道是因为没有每次输出缓冲区中的内容不同?
为何每次TIMEOUT后就没有输出了?在read中也有write

23,120

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧