Hey,
I'm having a problem reading from a device attached to my serial port.

I have a big application which uses a thread to read from the serial device and writes the data to a log file. The device is set to transmit data at 500Hz, which means that my thread receives data of 14 bytes every 2 miliseconds.
The baud rate is set to 115200 and actually everything is working pretty fine accept it takes too long.

I'm using a polling method, meaning I check if there are enough bytes in the buffer and I read them. After the device stops transmitting I read from the buffer until it's empty and the thread ends.
The problem with this is that I'm constantly polling the buffer which results in 100% CPU usage. Since we're talking about a thread which is a part of a much bigger application this CPU business is out of the question.

Thus, I'm forced to use the Sleep() function. And here the problem begins:

without Sleep: After the device stops transmitting the thread empties the buffer almost immediately and the thread ends in a matter of seconds.

with Sleep: After the device stops transmitting the thread takes from 5 minutes to 35 minutes to empty the buffer (depends on the transmission duration).

Minutes are not the timing I had in mind when I wrote this thread. I tried to change the thread priority but it didn't work. I also tried to use event driven programming but it's too slow for my thread.

I could really use your help, any suggestion would be welcomed.

Thank You,
Gadi

I'm not very experienced with threads in C++, but from what you said, it shouldn't make a difference in the amount of time it takes to empty the buffer. Have you tried it on another computer? And how long are you setting it to sleep for? Also, some code might help.

Nick

Hi,
I didn't try on another computer yet.

The Sleep() function is set to 1 milisecond. It is the minimum, so I tested to see if a "longer" sleep would help me - it didn't, it took longer.

Here's the thread:
(I'm using a SerialCom object that somebody else wrote. The function ReadString() uses ReadFile() and returns FALSE if operation didn't succeed. Also, I used AfxBeginThread() to start this thread)

unsigned int CRmi_demoDlg::GetADISData(void *Sender)
{
	CRmi_demoDlg* pSender = NULL;
	pSender = (CRmi_demoDlg *)Sender;
	char filename[60];
	int rd_iter, i;
	GetSystemTime(&st);	
	//------------------------Files--------------------
	
	//open the serial device com port 
	pSender->ADISDevice.Open("COM8");
	
	//setting buffer size for serial device to avoid buffer overrun
	if (!SetupComm(pSender->ADISDevice.hCom,0x00F00000,64))
		myprintf("\nCouldn't set device buffer size!");

	GetCommProperties(pSender->ADISDevice.hCom,&commProp);
	//myprintf prints to a console which logs the threads activities
        myprintf("\nADIS buffer size: %d bytes",(int) commProp.dwCurrentRxQueue);
	
	//setup the serial port
	pSender->ADISDevice.Setup(115200,8,0,1);
	
	
	//if port isn't open;
	if (pSender->ADISDevice.hCom == INVALID_HANDLE_VALUE) {
		pSender->ADISDevice.Close();
		AfxMessageBox((CString)"Couldn't get device handle");
		return 0;  
	}
	
	sprintf_s(filename,60,"test%d_ADIS_%d.%d.%d_%d.            %d.%d.txt",test_num,st.wDay,st.wMonth,st.wYear,st.wHour+3,st.wMinute,st.wSecond);
		
	// open the log file
	ofstream logfile(filename);
	
	
	if ( !logfile.is_open() ) {
		AfxMessageBox((CString) "Couldn't open LOG file!");
		return 0;
	}

	CreateADISFileHeader(logfile);
	//------------------------Sampling--------------------
	
         //clear com before working with it	
         pSender->ADISDevice.clearCom();  
	
        if(pSender && pSender->initialized )
	{
		 char finger_byte[14]="0";
		 short finger_word, index;
		 short short_lowbyte=0;
		 short short_highbyte=0;
		 short flags = 0;
		 short data_error;		//keeps tabs of bit errors
		 bool r_sent = false;
		
		pSender->ADISDevice.WriteString("r",1);  //send "reset" command
		pSender->ADISDevice.WriteString("s",1); //send "start" command
		while ( pSender->ADISDevice.getNumBytesInQue() < 1 ) {};	//wait for data to come into the buffer
		myprintf("\nReceiving Data");
		
		//stop running if no more data in buffer or GUI closed
                while( pSender->initialized && (!r_sent || pSender->ADISDevice.getNumBytesInQue() > 0) )	
		{
			if ( !pSender->is_sample )
			{
				//if stop button pressed tell the ADIS to stop sending data
                               if ( !pSender->ADISDevice.WriteString("r",1) ) myprintf("\nCouldn't write to ADIS"); 
				if (!r_sent) myprintf("\nStop button pressed");
				r_sent = true; //r_sent flags that the device was told to stop sending data
			}
			GetSystemTime(&st);
			//first two bytes of every iteration are 0x55
			if ( pSender->ADISDevice.getNumBytesInQue() >= 2 )
			{
				pSender->ADISDevice.ReadString(finger_byte, 2);
			}
			else if (!r_sent) continue;		//loop only if ADIS stil working
			
			
			if ( (finger_byte[0] != finger_byte[1]) || (finger_byte[0] != 0x55) )
			{
				AfxMessageBox((CString)"Out of sync!");
				logfile.close();
				logfile2.close();
				pSender->ADISDevice.Close();
				AfxEndThread(0);
				return 0;
			}
			//reading iteration = 15 times, each time 14 bytes
			for (rd_iter=0; rd_iter < 15 ; rd_iter++)
			{
				data_error = 0;
				logfile << setw(5) << setfill(' ') << st.wHour+3 << ", " 
						<< setw(7) << setfill(' ') << st.wMinute << ", " 
						<< setw(7) << setfill(' ') << st.wSecond << ", " 
						<< setw(7) << setfill(' ') << st.wMilliseconds << ", ";
				
                               // wait untill there is enough information in the buffer unless ADIS stopped 
                               while ( pSender->ADISDevice.getNumBytesInQue() < 14 && !r_sent)	{}	[
				
				[B]Sleep(1);[/B]
				if ( !pSender->ADISDevice.ReadString(finger_byte, 7*2) ) 
				{
					//if no more data in buffer end loop and make sound
                                        myprintf("\nCouldn't read from ADIS");
					MessageBeep(MB_ICONASTERISK);
					Sleep(500);
					MessageBeep(MB_ICONASTERISK);
					Sleep(500);
					MessageBeep(MB_ICONASTERISK);
					Sleep(500);
					break;
				}
				
                                //processing the received 14 bytes
                                //and writing them to the log file
                                for (i=0; i < 7*2 ; i++) 
				{
					short_highbyte=(( short)finger_byte[i]) & 0x00FF;
					short_lowbyte=(( short)finger_byte[++i]) & 0x00FF;
					finger_word=0;
					finger_word=short_highbyte*256+short_lowbyte;
					
					if (i == 1)  //first word is always index
					{
						finger_word = ( short) finger_word/64;  //shift right 6 times
						finger_word = finger_word & 0x000F;
						index = finger_word;
					}
					else  
					{
						flags = 0xC000 & finger_word;  
                                                //contracting 2 msb which are NewData 
                                                //and ErrorAlarm the other 14 bits are data
						finger_word <<= 2;
						finger_word >>= 2;	//with bit extraction because its signed short
											
						// put '1' at the bit corresponding to processed data 
                                                //for example: XAccl error is at bit number 5
						// ZGyro error is at bit number 0
						data_error = data_error << 2;
						flags = flags >>14;	//without bit extraction because its unsigned 
                                                                                   short
						data_error |= flags;
					}


					logfile << setw(10) << setfill(' ') << finger_word << ", ";
				}
				
				
				logfile << setw(10) << setfill(' ') << data_error << endl;
			}
		}
	}
	myprintf("\nNo more data in buffer");
	pSender->ADISDevice.WriteString("r",1);  //send "reset" command
	while (pSender->ADISDevice.getNumBytesInQue() > 0) pSender->ADISDevice.clearCom();
	pSender->ADISDevice.Close();
	
	if( logfile.is_open() )logfile.close();
	AfxEndThread(0);
	return 0;
	
}
/*----------------------------End of GetADISData---------------------------------------------*/

Another development:
If I set the following conditions I am able to run the entire application with 62% CPU usage (where without the above thread the main application uses 52%):
SetPriorityClass(HIGH_PRIORITY_CLASS);
SetThreadPriority(THREAD_PRIORITY_HIGH);
And I set the Sleep() argument to be 0.01 - meaning I call it like this: Sleep(0.01).

If I don't set the priorities and only use the Sleep(0.01) it doesn't work. If I only set the priorities but use Sleep(1) it doesn't work.

I don't know how it is possible that the Sleep() function receives a float argument since it should get DWORD arguments, but it does.
I get a warning that I am sending a float instead of DWORD so I gather that a casting from float to DWORD occurs, however I tried to use Sleep(0) and it gave me 90% CPU usage.

This is still not the solution I am seeking. Since the high priorities may affect the performance of the rest of the application, and since the purpose of the application is to control a small vehicle, I wouldn't want my small thread to cause an accident.

P.S. I've changed the thread creation to CreateThread() instead of AfxBeginThread() so that I could get a HANDLE on the thread.

okay, just wanted to let you know that I've worked things out.
Your tips were helpful so thank you very much.

I'll explain what I did so that others with a similar problem would try it.
Instead of trying to capture every data package that is sent, I waited 'till there were 5 packages and then read all of them into my working buffer (which was basically an array of chars with the appropriate size). After I rewrote my thread to work like this, the CPU usage dropped down to almost none.
I tried using my thread reading a single package at a time, and it worked great!

So, again, thanks everyone
And have a good and happy new year.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.