[SOLVED] Blocking USB read + select and VMIN doesn't seem to work
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Blocking USB read + select and VMIN doesn't seem to work
Hello,
I have a device attached using a COM-to-USB connection to the PC. I'm running Ubuntu Linux, though on Fedora I've had similar results. A simple program that prints out all data received from the serial port proves that the data is being received correctly.
The function that I'm writing will be part of a bigger program with many threads. I want to save CPU time by waiting until a certain BLOCK_SIZE of data is received on the serial port before processing it.
I tried setting VMIN to 17 (minimal block size), 51 or 102 or 255 (all multiples of 17) but with no noticeable difference.
Here's a snippet of the code and below is the current output.
Code:
#define BAUDRATE B230400
#define RS485_DEV "/dev/ttyUSB0"
#define _POSIX_SOURCE 1
#define SET_UART 1
#define INPUTBUFFERSIZE 0x40000
typedef struct tagCOMMSTRUCT
{
int ipid //if(ipid > -1)Comm read ==> IP write
, fd //handle to comport
, signal_close
, thread_started;
pthread_t hReadThread;
DWORD rx_queue_size //size of rx circular buffer
, rx_head_ptr //ptr to start position to get more rx data
, rx_queue_count //number of bytes as yet unread
, rx_write_ptr //ptr to position to put more rx data
, rx_tail_ptr //ptr to end of rx data
, cbTXQueued;
pthread_mutex_t cs_mutex;
pthread_cond_t cs_cond_fill, cs_cond_empty;
} COMMSTRUCT;
COMMSTRUCT cs[1];
/*-------------------------------------*\
Uart_Open
\*-------------------------------------*/
int Uart_Open(char *dev_name)
{
int fd;
//int c=0, res;
struct termios oldtio, newtio;
printf("Start...\n");
fd = open(dev_name, O_RDWR|O_NOCTTY);
if (fd < 0) {
perror(dev_name);
return -1;
}
printf("Open...\n");
tcgetattr(fd, &oldtio);
bzero(&newtio, sizeof(newtio));
newtio = oldtio;
/*
BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
CRTSCTS : output hardware flow control (only used if the cable has
all necessary lines. See sect. 7 of Serial-HOWTO)
CS8 : 8n1 (8bit,no parity,1 stopbit)
CLOCAL : local connection, no modem contol
CREAD : enable receiving characters
*/
newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
/*
IGNPAR : ignore bytes with parity errors
ICRNL : map CR to NL (otherwise a CR input on the other computer
will not terminate input)
otherwise make device raw (no other input processing)
*/
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0; // Raw output
/* set input mode (non-canonical, no echo,...) */
newtio.c_lflag = 0;
newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
newtio.c_cc[VMIN] = 51; /* blocking read until 17*3 chars received */
cfsetispeed(&newtio, BAUDRATE); // 230400
cfsetospeed(&newtio, BAUDRATE); // 230400
cfmakeraw(&newtio);
/*
now clean the modem line and activate the settings for the port
*/
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
return fd;
}
/*-------------------------------------*\
mOpenComm
\*-------------------------------------*/
int mOpenComm(char *szCom, int cb_read)
{
int cid, status;
static int do_once = 1;
g_cid = cid = 0;
//open comport
cs[cid].fd = Uart_Open(szCom);
// Create the Read thread.
cs[cid].thread_started = 0;
status = pthread_create(&cs[cid].hReadThread, NULL, StartReadThreadProc, NULL);
if(status != 0) //OSA_SOK)
{
printf("Unable to create Read thread");
close(cs[cid].fd);
return -1;
}
// Everything was created ok. Ready to go!
cs[cid].rx_queue_size = INPUTBUFFERSIZE; // 0x40000
cs[cid].rx_head_ptr = 0;
cs[cid].rx_queue_count = 0;
cs[cid].rx_write_ptr = 0;
cs[cid].rx_tail_ptr = 0;
pthread_mutex_init(&cs[cid].cs_mutex, NULL);
pthread_cond_init(&cs[cid].cs_cond_fill, NULL);
pthread_cond_init(&cs[cid].cs_cond_empty, NULL);
while(!cs[cid].thread_started)
{
//wait for thread to start...
usleep(10);
}
return cid;
}
/*-------------------------------------*\
StartReadThreadProc
\*-------------------------------------*/
static void *StartReadThreadProc(void *lpvParam)
{
DWORD nNumberOfBytesRead;
fd_set fds;
struct timeval timeout = {25, 0}; //25 sec
int rc, cid = (int)lpvParam;
printf("StartReadThreadProc: cid = %d", cid);
FD_ZERO(&fds);
FD_SET(cs[cid].fd, &fds);
cs[cid].thread_started = 1;
while(!cs[cid].signal_close)
{
// set each time in case timeout corrupted
timeout.tv_sec = 25;
timeout.tv_usec = 0;
FD_SET(cs[cid].fd, &fds);
rc = select(cs[cid].fd + 1, &fds, NULL, NULL, &timeout);
// ioctl(cs[cid].fd, FIONREAD, &rc);
if (rc == -1)
perror("select()");
else if (!rc)
{
printf("^"); //No data within 25 seconds.\n");
}
else // if (rc >= 51)
{
pthread_mutex_lock(&cs[cid].cs_mutex);
//while (cs[cid].rx_queue_count == INPUTBUFFERSIZE) // no more room in buffer
// pthread_cond_wait(&cs[cid].cs_cond_empty, &cs[cid].cs_mutex);
nNumberOfBytesRead = read(cs[cid].fd, szInputBuffer, INPUTBUFFERSIZE);
if (nNumberOfBytesRead)
{
cs[cid].rx_queue_count += nNumberOfBytesRead;
cs[cid].rx_head_ptr = 0;
cs[cid].rx_tail_ptr = nNumberOfBytesRead;
printf("\nSerialNumRead %u - 0x%X\n", nNumberOfBytesRead, szInputBuffer[0]);
}
pthread_cond_signal(&cs[cid].cs_cond_fill);
pthread_mutex_unlock(&cs[cid].cs_mutex);
}
}
cs[cid].signal_close = 0;
return NULL;
}
main()
{
int cid;
unsigned char buf[50];
// starts thread "producer"
cid = mOpenComm(RS485_DEV, 0x40000);
printf("\nRS485 Open COM+thread:\n");
// now the "consumer"
for(;;)
{
pthread_mutex_lock(&cs[cid].cs_mutex);
while (mBytesAvail(cid) < 17)
pthread_cond_wait(&cs[cid].cs_cond_fill, &cs[cid].cs_mutex);
// printf("\navail %d\n", mBytesAvail(cid));
while (mBytesAvail(cid) >= 17)
{
mReadComm(g_cid, buf, 17);
// printf("\nbufRead 17 0x%X\n", buf[0]);
}
//GetRecord(buf);
//pthread_cond_signal(&cs[cid].cs_cond_empty);
pthread_mutex_unlock(&cs[cid].cs_mutex);
}
//mCloseComm(cid);
}
Here's the output:
Quote:
SerialNumRead 17 - 0x7F
SerialNumRead 27 - 0x7F
SerialNumRead 8 - 0x55
SerialNumRead 17 - 0x7E
SerialNumRead 43 - 0x7E
SerialNumRead 51 - 0x55
SerialNumRead 68 - 0x55
SerialNumRead 9 - 0x55
SerialNumRead 42 - 0x7E
SerialNumRead 68 - 0x55
I also tried polling using
Code:
ioctl(cs[cid].fd, FIONREAD, &rc);
and checking for the returned value to be more that 51 - but similar results - never consistent - and the CPU was not freed enough.
I'd appreciate any help/ideas in this issue.
Thanks,
Mechi
Last edited by Mechi; 09-23-2014 at 05:41 AM.
Reason: to make more understandable
Well, since no one answered, I figured out the hard way that even though I treat the ttyUSB as a UART port, it's different and not every thing works like a standard COM.
I finally used mutexes, and only released the "producer" mutex when enough data was accumulated so that the "consumer" could do its calculations.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.