943,541 Members | Top Members by Rank

Ad:
  • C++ Discussion Thread
  • Marked Solved
  • Views: 2271
  • C++ RSS
Dec 3rd, 2008
0

reading from serial port way too slow (C++)

Expand Post »
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
Last edited by GadiK; Dec 3rd, 2008 at 2:02 pm.
Similar Threads
Reputation Points: 18
Solved Threads: 0
Light Poster
GadiK is offline Offline
32 posts
since Dec 2008
Dec 3rd, 2008
0

Re: reading from serial port way too slow (C++)

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
Reputation Points: 69
Solved Threads: 48
Posting Whiz in Training
nmaillet is offline Offline
203 posts
since Aug 2008
Dec 4th, 2008
0

Re: reading from serial port way too slow (C++)

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)	{}	[
				
				Sleep(1);
				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---------------------------------------------*/
Last edited by GadiK; Dec 4th, 2008 at 4:55 am.
Reputation Points: 18
Solved Threads: 0
Light Poster
GadiK is offline Offline
32 posts
since Dec 2008
Dec 4th, 2008
0

Re: reading from serial port way too slow (C++)

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.
Reputation Points: 18
Solved Threads: 0
Light Poster
GadiK is offline Offline
32 posts
since Dec 2008
Dec 4th, 2008
0

Re: reading from serial port way too slow (C++)

Team Colleague
Reputation Points: 5862
Solved Threads: 950
Posting Sage
Salem is offline Offline
7,164 posts
since Dec 2005
Dec 4th, 2008
0

Re: reading from serial port way too slow (C++)

Thanks.
I'll try it on Monday, report to you then
Reputation Points: 18
Solved Threads: 0
Light Poster
GadiK is offline Offline
32 posts
since Dec 2008
Dec 17th, 2008
0

Re: reading from serial port way too slow (C++)

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.
Reputation Points: 18
Solved Threads: 0
Light Poster
GadiK is offline Offline
32 posts
since Dec 2008

This thread is solved

Either the thread starter or a moderator has marked this thread as solved. You can most likely trust the responses and answers given. There is most likely no reason for any further responses to be posted here. If you have a related question, please start a new thread in this forum instead.

This thread is more than three months old

No one has posted to this discussion for at least three months. Please let old threads die and do not reply to them unless you feel you have something new and valuable to contribute that absolutely must be added to make the discussion complete. Otherwise, please start a new thread in this forum instead.
Message:
Previous Thread in C++ Forum Timeline: Set the include path
Next Thread in C++ Forum Timeline: Help with saving/loading system.





About Us | Contact Us | Advertise | Acceptable Use Policy
Forum Index | Build Custom RSS Feed


Follow us on Twitter


© 2011 DaniWeb® LLC