linux下串口读写问题 read 一次读不全

hdifilwy 2007-08-01 07:47:57
我要在本机(linux)串口上进行读写,一开始时发送接收都正常,但是我用windos下的“串口调试助手”发送给我的程序时必须要加上回车换行,否则read函数不返回,后来google了一下,又添加了2条语句设置串口(代码在下面说明),现在收数据可以不用加回车换行了,但是每次read返回的数据都不完整,比如我发了20个字符,read先是读取了8个字符到缓冲区,然后又读取剩下的字符。有没有什么办法可以一次读取全部的,或者可以判断数据还没有收完,接着read,直到结束.

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
B38400, B19200, B9600, B4800, B2400, B1200, B300,};
int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300, 38400,
19200, 9600, 4800, 2400, 1200, 300,};

/**
*@brief Set Serial Port BitRate
*@param fd Type : int The File Description of Serial Port
*@param speed Type : int Serial Speed
*@return void
*/
void set_speed(int fd, int speed)
{
int i;
int status;
struct termios Opt;
tcgetattr(fd, &Opt);
for( i=0; i < (sizeof(speed_arr) / sizeof(int)); i++ )
{
if (speed == name_arr[i])
{
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr[i]);
cfsetospeed(&Opt, speed_arr[i]);
status = tcsetattr(fd, TCSANOW, &Opt);
if (status != 0)
{
perror("tcsetattr fd");
return;
}
tcflush(fd,TCIOFLUSH);
}
}
}

/**
*@brief Set Serial Port Databits, Stopbits and Parity.
*@param fd Type: int The File Description of Serial Port
*@param databits Type: int Databits 7 or 8
*@param stopbits Type: int Stopbits 1 or 2
*@param parity Type: int Parity Type: n,N,e,E,o,O,s,S
*/
int set_Parity(int fd,int databits,int stopbits,int parity)
{
struct termios options;
if ( tcgetattr( fd,&options) != 0)
{
perror("SetupSerial 1");
return(-1);
}
options.c_cflag &= ~CSIZE;
switch (databits) /*Set Datebits*/
{
case 7:
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return(-1);
}

switch (parity) /*Set Parity*/
{
case 'n':
case 'N':
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
options.c_cflag |= (PARODD | PARENB); /* Odd Checking*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
options.c_cflag |= PARENB; /* Enable parity */
options.c_cflag &= ~PARODD; /* Even Checking*/
options.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'S':
case 's': /*as no parity*/
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
break;
default:
fprintf(stderr,"Unsupported parity\n");
return(-1);
}

switch (stopbits) /*Set Stobits*/
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return(-1);
}
/* Set input parity option */
if (parity != 'n')
options.c_iflag |= INPCK;

/*以下两句添加后发送方可以不加回车换行,但是read读取不完整*/
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/


tcflush(fd,TCIFLUSH);
options.c_cc[VTIME] = 150; /* Timeout in 15 seconds*/
options.c_cc[VMIN] = 0; /* Update the options and do it NOW */
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return(-1);
}
return(0);
}

int main (int argc, char *argv[])
{
char buffer[512]="";
int ser_fd = open("/dev/ttyS1",O_RDWR | O_NOCTTY);
if ( -1 == ser_fd)
{
perror("Cannot Open Serial Port");
return(-1);
}


set_speed(ser_fd,19200);
if ( -1 == set_Parity(ser_fd,7,1,'E') )
{
printf("Set Parity Error\n");
close(ser_fd);
exit (-1);
}
while( 1 )
{
nread = read( ser_fd, buffer, sizeof(buffer));
if ( nread < 0 )
perror("read from Serial Port Error:\n");
}

}

...全文
3240 11 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
kf701 2007-08-07
  • 打赏
  • 举报
回复
http://libsoftwarefactory.googlecode.com/svn/trunk/test/tty_test.c

read a total line one time !
ma100 2007-08-07
  • 打赏
  • 举报
回复
CSerial g_COM1;

int main()
{
g_COM1.Initial ( 0, B115200 );
....
g_COM1.Cleanup();
return 0;
}
ma100 2007-08-07
  • 打赏
  • 举报
回复
class CSerial
{
public:
CSerial ( void );
~CSerial ( void );

bool Initial ( int tty, int baut );
void Cleanup ( void );
int Send (char *data, int datalen );
int Receive (char *data, int datalen, int timeout = -1 );
private:
char m_sTTY[20]; //tty: 0, 1, 2, 3, 4, 5, 6, 7
int m_hCom; //handle
int m_nBaut; //baudrate
char m_cDataBit; //data bits, 5, 6, 7, 8
char m_cParityBit; //parity 0: none, 1: odd, 2: even
char m_cStopBit; //stop bits, 1, 2
char m_cFlow; //flow control, 0: none, 1: hardware, 2: software
};
ma100 2007-08-07
  • 打赏
  • 举报
回复
/*//////////////////////////////////////////////////////////////////////////////
Function: CSerial
Description: Constructor of class CSerial
Calls: ---
Called By: ---
Input: ---
Output: ---
Return: ---
Others: public
//////////////////////////////////////////////////////////////////////////////*/
CSerial::CSerial ( void )
{
m_cDataBit = 8;
m_cParityBit = 0;
m_cStopBit = 1;
m_cFlow = 0;
m_hCom = -1;
}

/*//////////////////////////////////////////////////////////////////////////////
Function: ~CSerial
Description: Destructor of class CSerial
Calls: ---
Called By: ---
Input: ---
Output: ---
Return: ---
Others: public
//////////////////////////////////////////////////////////////////////////////*/
CSerial::~CSerial ( void )
{
if ( m_hCom != -1 )
close(m_hCom);
}

/*//////////////////////////////////////////////////////////////////////////////
Function: Initial
Description: 串口初始化
Calls: ---
Called By: ---
Input: tty: 串口号 0~n 代表串口1~n+1
baut: 波特率 如B9600, 参数为系统定义的常量
Output: ---
Return: true: 初始化成功 false:失败
Others: public
//////////////////////////////////////////////////////////////////////////////*/
bool CSerial::Initial ( int tty, int baut )
{
termios termios_old, termios_new;

if ( m_hCom != -1 )
close(m_hCom);


sprintf ( m_sTTY, "/dev/ttyS%d", tty );
//打开串口
//O_NOCTTY: 通知系统,该程序不想成为此端口的“控制终端”。
// 如果不设,则任何输入(如键盘的中断信号)都会影响程序的运行
//O_NODELAY: 该程序不关注DCD信号线所处的状态,即不管对端设备是在运行还是挂起。
// 若不设置,则程序会被置为睡眠状态,直到DCD信号低为止(可能无流控制时无效)
m_hCom = open ( m_sTTY, O_RDWR | O_NOCTTY | O_NDELAY );

if ( m_hCom == -1 )
return false;

m_nBaut = baut;

bzero(&termios_old, sizeof(termios_old));
bzero(&termios_new, sizeof(termios_new));
cfmakeraw(&termios_new);
tcgetattr(m_hCom, &termios_old); //get the serial port attributions

//------------设置端口属性----------------
//baudrates
cfsetispeed(&termios_new, m_nBaut); //填入串口输入端的波特率
cfsetospeed(&termios_new, m_nBaut); //填入串口输出端的波特率
termios_new.c_cflag |= CLOCAL; //控制模式,保证程序不会成为端口的占有者
termios_new.c_cflag |= CREAD; //控制模式,使能端口读取输入的数据

// 控制模式,flow control
switch(m_cFlow)
{
case 1:
termios_new.c_cflag |= CRTSCTS; //hardware flow control
break;
case 2:
termios_new.c_iflag |= IXON | IXOFF |IXANY; //software flow control
break;
default:
termios_new.c_cflag &= ~CRTSCTS; //no flow control
break;
}

//控制模式,data bits
termios_new.c_cflag &= ~CSIZE; //控制模式,屏蔽字符大小位
switch(m_cDataBit)
{
case 5:
termios_new.c_cflag |= CS5;
case 6:
termios_new.c_cflag |= CS6;
case 7:
termios_new.c_cflag |= CS7;
default:
termios_new.c_cflag |= CS8;
}

//控制模式 parity check
switch(m_cParityBit)
{
case 1:
termios_new.c_cflag |= PARENB; //odd check
termios_new.c_cflag &= ~PARODD;
break;
case 2:
termios_new.c_cflag |= PARENB; //even check
termios_new.c_cflag |= PARODD;
break;
default:
termios_new.c_cflag &= ~PARENB; //no parity check
break;
}

//控制模式,stop bits
switch ( m_cStopBit )
{
case 2:
termios_new.c_cflag |= CSTOPB; //2 stop bits
break;
default:
termios_new.c_cflag &= ~CSTOPB; //1 stop bits
break;
}

//other attributions default
termios_new.c_oflag &= ~OPOST; //输出模式,原始数据输出
termios_new.c_cc[VMIN] = 1; //控制字符, 所要读取字符的最小数量
termios_new.c_cc[VTIME] = 1; //控制字符, 读取第一个字符的等待时间 unit: (1/10)second

tcflush(m_hCom, TCIFLUSH); //溢出的数据可以接收,但不读
tcsetattr(m_hCom, TCSANOW, &termios_new); //设置新属性,TCSANOW:所有改变立即生效 tcgetattr(fdcom, &termios_old);

return true;
}

/*//////////////////////////////////////////////////////////////////////////////
Function: Cleanup
Description: 串口清理
Calls: ---
Called By: ---
Input: ---
Output: ---
Return: ---
Others: public
//////////////////////////////////////////////////////////////////////////////*/
void CSerial::Cleanup ( void )
{
if ( m_hCom != -1 )
close(m_hCom);
}

/*//////////////////////////////////////////////////////////////////////////////
Function: Send
Description: 通过串口向外发N个字节
Calls: ---
Called By: ---
Input: data: 发送字节首地址
datalen: 发送字节长度
Output: ---
Return: -1: 发送失败 >0:已经发送的字节
Others: public
//////////////////////////////////////////////////////////////////////////////*/
int CSerial::Send ( char *data, int datalen )
{
if ( m_hCom == -1 ) //串口未被打开
return -1;

int len = 0;

len = write( m_hCom, data, datalen); //实际写入的长度
if(len == datalen)
{
return len;
}
else
{
tcflush(m_hCom, TCOFLUSH);
return -1;
}
}

/*//////////////////////////////////////////////////////////////////////////////
Function: Receive
Description: 从串口读入N个字节
Calls: ---
Called By: ---
Input: data: 读入字节首地址
datalen: 发送字节长度
timeout: 超时时间,单位秒 <0 时代表无限期等待
Output: ---
Return: -1: 读入失败 >0:已经读入的字节
Others: public, timeout默认-1
//////////////////////////////////////////////////////////////////////////////*/
int CSerial::Receive ( char *data, int datalen, int timeout )
{
int readlen, fs_sel;
fd_set fs_read;
struct timeval tv_timeout;

if ( m_hCom == -1 ) //串口未被打开
return -1;

FD_ZERO(&fs_read);
FD_SET(m_hCom, &fs_read);

if ( timeout > 0 )
{
tv_timeout.tv_sec = timeout; //time out : unit sec
tv_timeout.tv_usec = 0;

fs_sel = select( m_hCom+1, &fs_read, NULL, NULL, &tv_timeout);
}
else//死等
{
fs_sel = select( m_hCom+1, &fs_read, NULL, NULL, NULL );
}
if(fs_sel)
{
readlen = read(m_hCom, data, datalen);
return readlen;
}
else
{
return(-1);
}

return readlen;
}

manespz 2007-08-06
  • 打赏
  • 举报
回复
那就一直读,如果返回值不为你想要的值,就一直读下去,然后把你想要的值减去已经读出来的值,知道为0
lovejklife 2007-08-03
  • 打赏
  • 举报
回复
循环读到可以处理为止
lovejklife 2007-08-03
  • 打赏
  • 举报
回复
总是这样查询占用CPU时间呀
wangjiecdma2 2007-08-03
  • 打赏
  • 举报
回复
cceczjxy的方法是很好的 ,推荐.
dai_weitao 2007-08-02
  • 打赏
  • 举报
回复
你的读取方法有问题, 应该while循环内加if判断read返回值.
cceczjxy 2007-08-02
  • 打赏
  • 举报
回复
int qstatus;
ioctl(ser_fd, FIONREAD, &qstatus);
看qstatus的值,其值为串口缓冲内有多少数据可多.

你可用先判断数据够不够你要的在去读.
awjx 2007-08-02
  • 打赏
  • 举报
回复
read之前延时一下可以不?

23,217

社区成员

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

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