UART 没有读取我想要的那么多数据
UART not reading as much data as I would like
我正在尝试编写一个函数来读取通过 UART 发送的一大块数据。我在 RaspberryPi 型号 B 上使用 Raspbian Jessie 运行,但我想在 openwrt 上使用此 C 代码(进行任何必要的修改)。到目前为止,这就是我写的。
Header:
#ifndef __UART_LIB__
#define __UART_LIB__
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h> //
#include <sys/ioctl.h>
#define BITS_PER_PACKAGE_ 11
#define WAIT_PROLONGATION_CONSTANT_ 1.1f
//Some values used by default, left for the user to change if needed
unsigned int BAUD_ ;
unsigned int NUM_BITS_ ;
char *UART_PATH_ ;
unsigned int MAX_SIZE_ ;
unsigned int OPEN_FLAG_ ;
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
struct timeval WAIT_CONSTANT_ ;
int open_conf_UART_() ;
int read_UART_(int uart_filestream, char** dest, int max_len) ;
#endif
.c 文件:
#include "uartlib.h"
unsigned int BAUD_ = B115200 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int MAX_SIZE_ = 128 ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 5 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
int open_conf_UART_()
{
int indicator, old_fl;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY );
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// I found a question on stackoverlow where the answer said that VTIME and VMIN will be ignored unless I
// switch the FNDELAY flag off
old_fl = fcntl(uart_filestream, F_GETFL);
if(old_fl < 0)
{
return -1;
}
old_fl &= ~FNDELAY;
fcntl(uart_filestream, old_fl);
//Setting the options
options.c_cflag = CRTSCTS | BAUD_ | NUM_BITS_ | CLOCAL | CREAD ;
options.c_iflag = 0;
options.c_oflag = 0;
options.c_lflag = 0;
//I want the uart to wait 1/10 of a second between bytes at most
options.c_cc[VTIME] = 1;
options.c_cc[VMIN] = 0;
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char** dest, int max_len)
{
int indicator;
int buffer_length;
indicator = tcflush(uart_filestream, TCIFLUSH);
if(indicator < 0)
{
// Unable to flush
return -1;
}
//Do the actual reading
buffer_length = read(uart_filestream, (void*)(*dest), max_len);
if(indicator < 0)
{
return -1;
}
else
{
// Returning number of read bytes
return buffer_length;
}
// Both branches of the if statement above have return, so this will not be reached
}
因此,当我尝试读取超过 8 个字节时,消息被截断为 8 个字节。
正如我所读,将 VTIME 设置为某个值允许两个字节之间的时间间隔最多那么长。
我不确定发生了什么,但我怀疑 read() 调用在数据接收完成之前读取了缓冲区。
我的愿望是读取一大块未定义大小的数据。我还在读取之前使用了 select() 和超时,以确保程序不会完全阻塞。
我阅读了很多关于该主题的论坛主题、Whosebug 问题、指南等,none 似乎对我的解决方案有所帮助。
那么,任何人都可以解释这里发生了什么吗?是否可以如我所愿?
请注意,我删除了一些代码(我还编写了一个写入 UART 端口的函数),所以这里可能有一些冗余的包含、全局变量等。
所以,我解决了我的问题。我仍然无法刷新(当系统刚刚启动并且在我打开程序之前有一个信号时,缓冲区中有一些旧内容,在我看来)。
我使用了这个假设:包裹将以较小的突发速度到达,并且它们将被 TIMEOUT_BYTE_ 时间间隔分开。如果到期,我认为包裹已经结束。
另外,我有一个初始等待数据的超时,但我认为这是视情况而定。
头文件:
#ifndef UART_LIB_
#define UART_LIB_
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/select.h> //Used for select(), etc
//Some values used by default, left for the user to change if needed
//Used to set up the baud rate
unsigned int BAUD_ ;
//Used to indicate number of bits in one backage
unsigned int NUM_BITS_ ;
//Path to the UART device
char *UART_PATH_ ;
//Flag for opening the device
unsigned int OPEN_FLAG_ ;
//Timeout for answer from the other side
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
//Time interval between two bursts of data inside the package
suseconds_t TIMEOUT_BYTE_ ;
int open_conf_UART_(void) ;
int read_UART_(int uart_filestream, char* dest, int max_len) ;
#endif
源文件:
#include <errno.h>
#include "uartlib.h"
unsigned int BAUD_ = B38400 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 2 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
// This needs to be finely tuned
suseconds_t TIMEOUT_BYTE_ = 5000;
int open_conf_UART_()
{
// Variable section
int indicator;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY | O_NONBLOCK);
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// Setting the options
cfmakeraw(&options);
options.c_cflag |= BAUD_ | NUM_BITS_ | CREAD;
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char* dest, int max_len)
{
// Variable section
int indicator;
int buffer_length;
char *tmp_dest;
fd_set set;
struct timeval timeout, init_timeout;
while(1)
{
// Reseting the set and inserting the uart_filestream in it
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Setting the time for initial contact
init_timeout.tv_sec = TIMEOUT_SEC_ ;
init_timeout.tv_usec = TIMEOUT_USEC_ ;
// Waiting for the first contact. If this times out, we assume no contact.
indicator = select(uart_filestream + 1, &set, NULL, NULL, &init_timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
return -1;
}
else if(indicator == 0)
{ // Timeout has occurred
return -2;
}
else
{
break;
}
}
// This section means that there is something to be read in the file descriptor
buffer_length = 0 ;
tmp_dest = dest ;
// The first select is redundant but it is easier to loop this way.
while(buffer_length < max_len)
{
// select changes the timeval structure so it is reset here
timeout.tv_sec = 0;
timeout.tv_usec = TIMEOUT_BYTE_;
// Reinitialize the sets for reading
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Wait for the file descriptor to be available or for timeout
indicator = select(uart_filestream+1, &set, NULL, NULL, &timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
// This indicates an error
return -1;
}
else if(indicator == 0)
{
// This indicates a timeout; We assume that the transmission is over once first timeout is reached
return buffer_length;
}
// There's been a select that didn't time out before this read
indicator = read(uart_filestream, (void*)tmp_dest, max_len - buffer_length);
if(indicator < 0)
{
if(errno == EINTR)
{
// If the call was interrupted, try again
continue;
}
// If it was any other condition, the read is corrupt.
return -1;
}
else if(indicator == 0)
{
// If, somehow, EOF was reached
break;
}
// Change the necessary values
buffer_length += indicator ;
tmp_dest += indicator;
}
// Both branches of the if statement above have return, so this will not be reached
// but a warning is generated
return buffer_length;
}
void flush_buffer_UART_(int uart_filestream)
{
char c;
while(read(uart_filestream, &c, 1) > 0);
}
我知道这不是这里的主题,但是如果有人知道如何解决刷新问题,请回复。
另外,非常欢迎任何建设性的批评。
P.S。我也有一个 write_UART() 函数,但我认为没有必要 post 它,因为它没有问题(用示波器测量,后来用 echo 试过。Echo 无法给出我也是同样的信息)。
编辑:已引入 Flush,然后与源文件合并。还没搞清楚有没有用
我正在尝试编写一个函数来读取通过 UART 发送的一大块数据。我在 RaspberryPi 型号 B 上使用 Raspbian Jessie 运行,但我想在 openwrt 上使用此 C 代码(进行任何必要的修改)。到目前为止,这就是我写的。
Header:
#ifndef __UART_LIB__
#define __UART_LIB__
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h> //
#include <sys/ioctl.h>
#define BITS_PER_PACKAGE_ 11
#define WAIT_PROLONGATION_CONSTANT_ 1.1f
//Some values used by default, left for the user to change if needed
unsigned int BAUD_ ;
unsigned int NUM_BITS_ ;
char *UART_PATH_ ;
unsigned int MAX_SIZE_ ;
unsigned int OPEN_FLAG_ ;
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
struct timeval WAIT_CONSTANT_ ;
int open_conf_UART_() ;
int read_UART_(int uart_filestream, char** dest, int max_len) ;
#endif
.c 文件:
#include "uartlib.h"
unsigned int BAUD_ = B115200 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int MAX_SIZE_ = 128 ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 5 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
int open_conf_UART_()
{
int indicator, old_fl;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY );
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// I found a question on stackoverlow where the answer said that VTIME and VMIN will be ignored unless I
// switch the FNDELAY flag off
old_fl = fcntl(uart_filestream, F_GETFL);
if(old_fl < 0)
{
return -1;
}
old_fl &= ~FNDELAY;
fcntl(uart_filestream, old_fl);
//Setting the options
options.c_cflag = CRTSCTS | BAUD_ | NUM_BITS_ | CLOCAL | CREAD ;
options.c_iflag = 0;
options.c_oflag = 0;
options.c_lflag = 0;
//I want the uart to wait 1/10 of a second between bytes at most
options.c_cc[VTIME] = 1;
options.c_cc[VMIN] = 0;
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char** dest, int max_len)
{
int indicator;
int buffer_length;
indicator = tcflush(uart_filestream, TCIFLUSH);
if(indicator < 0)
{
// Unable to flush
return -1;
}
//Do the actual reading
buffer_length = read(uart_filestream, (void*)(*dest), max_len);
if(indicator < 0)
{
return -1;
}
else
{
// Returning number of read bytes
return buffer_length;
}
// Both branches of the if statement above have return, so this will not be reached
}
因此,当我尝试读取超过 8 个字节时,消息被截断为 8 个字节。 正如我所读,将 VTIME 设置为某个值允许两个字节之间的时间间隔最多那么长。 我不确定发生了什么,但我怀疑 read() 调用在数据接收完成之前读取了缓冲区。 我的愿望是读取一大块未定义大小的数据。我还在读取之前使用了 select() 和超时,以确保程序不会完全阻塞。 我阅读了很多关于该主题的论坛主题、Whosebug 问题、指南等,none 似乎对我的解决方案有所帮助。 那么,任何人都可以解释这里发生了什么吗?是否可以如我所愿?
请注意,我删除了一些代码(我还编写了一个写入 UART 端口的函数),所以这里可能有一些冗余的包含、全局变量等。
所以,我解决了我的问题。我仍然无法刷新(当系统刚刚启动并且在我打开程序之前有一个信号时,缓冲区中有一些旧内容,在我看来)。 我使用了这个假设:包裹将以较小的突发速度到达,并且它们将被 TIMEOUT_BYTE_ 时间间隔分开。如果到期,我认为包裹已经结束。 另外,我有一个初始等待数据的超时,但我认为这是视情况而定。
头文件:
#ifndef UART_LIB_
#define UART_LIB_
#include <stdlib.h> //Errors, etc
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART
#include <termios.h> //Used for UART
#include <sys/types.h> //These includes are for timeout
#include <sys/select.h> //Used for select(), etc
//Some values used by default, left for the user to change if needed
//Used to set up the baud rate
unsigned int BAUD_ ;
//Used to indicate number of bits in one backage
unsigned int NUM_BITS_ ;
//Path to the UART device
char *UART_PATH_ ;
//Flag for opening the device
unsigned int OPEN_FLAG_ ;
//Timeout for answer from the other side
time_t TIMEOUT_SEC_ ;
suseconds_t TIMEOUT_USEC_ ;
//Time interval between two bursts of data inside the package
suseconds_t TIMEOUT_BYTE_ ;
int open_conf_UART_(void) ;
int read_UART_(int uart_filestream, char* dest, int max_len) ;
#endif
源文件:
#include <errno.h>
#include "uartlib.h"
unsigned int BAUD_ = B38400 ;
unsigned int NUM_BITS_ = CS8 ;
char *UART_PATH_ = "/dev/ttyAMA0" ;
unsigned int OPEN_FLAG_ = O_RDWR ;
time_t TIMEOUT_SEC_ = 2 ;
suseconds_t TIMEOUT_USEC_ = 0 ;
// This needs to be finely tuned
suseconds_t TIMEOUT_BYTE_ = 5000;
int open_conf_UART_()
{
// Variable section
int indicator;
int uart_filestream ;
struct termios options ;
// Opening the port in a read/write mode
uart_filestream = open(UART_PATH_, OPEN_FLAG_ | O_NOCTTY | O_NONBLOCK);
if (uart_filestream < 0)
{
// Unable to open the serial port, so produce an error and halt
return -1;
}
// Configuring the options for UART
// Flushing the file stream (the input and the output area)
indicator = tcflush(uart_filestream, TCIOFLUSH);
if(indicator < 0)
{
// Unable to flush
close(uart_filestream);
return -1;
}
// Retrieve the options and modify them.
indicator = tcgetattr(uart_filestream, &options);
if(indicator < 0)
{
// Unable to get the attributes
close(uart_filestream);
return -1;
}
// Setting the options
cfmakeraw(&options);
options.c_cflag |= BAUD_ | NUM_BITS_ | CREAD;
// Setting the options for the file stream.
indicator = tcsetattr(uart_filestream, TCSANOW, &options);
if(indicator < 0)
{
// Unable to set the attributes
close(uart_filestream);
return -1;
}
return uart_filestream;
}
int read_UART_(int uart_filestream, char* dest, int max_len)
{
// Variable section
int indicator;
int buffer_length;
char *tmp_dest;
fd_set set;
struct timeval timeout, init_timeout;
while(1)
{
// Reseting the set and inserting the uart_filestream in it
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Setting the time for initial contact
init_timeout.tv_sec = TIMEOUT_SEC_ ;
init_timeout.tv_usec = TIMEOUT_USEC_ ;
// Waiting for the first contact. If this times out, we assume no contact.
indicator = select(uart_filestream + 1, &set, NULL, NULL, &init_timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
return -1;
}
else if(indicator == 0)
{ // Timeout has occurred
return -2;
}
else
{
break;
}
}
// This section means that there is something to be read in the file descriptor
buffer_length = 0 ;
tmp_dest = dest ;
// The first select is redundant but it is easier to loop this way.
while(buffer_length < max_len)
{
// select changes the timeval structure so it is reset here
timeout.tv_sec = 0;
timeout.tv_usec = TIMEOUT_BYTE_;
// Reinitialize the sets for reading
FD_ZERO(&set);
FD_SET(uart_filestream, &set);
// Wait for the file descriptor to be available or for timeout
indicator = select(uart_filestream+1, &set, NULL, NULL, &timeout);
if(indicator < 0)
{
if(errno == EINTR)
{
// Try again
continue;
}
// This indicates an error
return -1;
}
else if(indicator == 0)
{
// This indicates a timeout; We assume that the transmission is over once first timeout is reached
return buffer_length;
}
// There's been a select that didn't time out before this read
indicator = read(uart_filestream, (void*)tmp_dest, max_len - buffer_length);
if(indicator < 0)
{
if(errno == EINTR)
{
// If the call was interrupted, try again
continue;
}
// If it was any other condition, the read is corrupt.
return -1;
}
else if(indicator == 0)
{
// If, somehow, EOF was reached
break;
}
// Change the necessary values
buffer_length += indicator ;
tmp_dest += indicator;
}
// Both branches of the if statement above have return, so this will not be reached
// but a warning is generated
return buffer_length;
}
void flush_buffer_UART_(int uart_filestream)
{
char c;
while(read(uart_filestream, &c, 1) > 0);
}
我知道这不是这里的主题,但是如果有人知道如何解决刷新问题,请回复。 另外,非常欢迎任何建设性的批评。
P.S。我也有一个 write_UART() 函数,但我认为没有必要 post 它,因为它没有问题(用示波器测量,后来用 echo 试过。Echo 无法给出我也是同样的信息)。
编辑:已引入 Flush,然后与源文件合并。还没搞清楚有没有用