Link to home
Start Free TrialLog in
Avatar of qqqqqqqqq
qqqqqqqqq

asked on

Serial Port: Terminate Writes in Progress

Hi,
I'm using PJ Naughter's (www.naughter.com) CSerialPort v1.11 class for serial port communication on the Windows operating system. I'm wondering if this class or something else (like WIN32 API or MFC) comes with a function that lets me terminate all writes in progress and clear the write buffer. My problem is that I'm doing a write in a loop and the write is started the second time before it finishes the first time. This makes my application preform poorly, and causes many fatal errors. I looked through the documentation and found a few functions that looked like they might do the job but they didn't work (i might have used them wrong). Can someone point me in the right direction?
Avatar of Axter
Axter
Flag of United States of America image

I recommend that you use a multiple threads for your application.

Have one thread do the job of handling the serial port.
Avatar of qqqqqqqqq
qqqqqqqqq

ASKER

I'm using threads but the data being sent is real time. So if data is not used by the time new data is ready the old data is discarded. Is there a function to do this?
You can put you data in some kind of stack and write it to port from there in small peieces.

IF something else come up you can clear stack up....


Hope it helps...
well i don't want to store the data. I just want to send the newest data to the serial port. is there a way to fo what i said. I've tried the approaches you've said.
Have a look at WinAPI function PurgeComm. It will do what you whant (terminate pending writes and clear buffers) but I´m not sure if it's the right way. Using this approach to make your application preform better will cause, I think, bad data on the other end of your serial cable.
well, the device i'm programming just takes data in a binary sense (if it has data coming to it it's on, and if there's no data it's off). I need to update this quickly, but i don't have to worry about cutting off a transmition in the middle.
The PurgeComm lokks like a good bet.  I know that I use CanelIO to quickly abort a read/write operation.

However, since you seem to need this often, perhaps you could simply write in smaller chunks.   Then you won't ever get stuck waiting for a big block to get finished.

But assermark has an important point upon which I will expand:  

Nearly all devices and programs expect data to come in recognizable packets of some sort.  For instance, the fist byte is a 'Z' and the next is a binary number of bytes to expect (say 15), and then the packet ends after sending 15 bytes.  The device on the other end of the connection will be hoplessly confused if you cut off after, say 12 bytes.  It will not know that the next byte will be the start of a new packet.

What kind of device are you sending to?  What kind of data are you sending to the device?  THese are important questions.

-- Dan
DanRollins is right of cause, in general.
But this type of the problem usually easily fixed, if you have control over software on both ends of the communications.
You just make all you messages start with certain character.  Let's say STX ( 0x02).  The every time device get this it knows to abandon old message and start processing new one.
Of cause if you don't have control over software, it's totally different issue.  Then you really have to pay attention to DanRollins and assermark comments.

I still say that solution here may be not to cancel existing write, but organize writes in really small pieces.
This hardware isn't really a computer device, it's been modified to connect to a serial port. It doesn't expect packets. It just expects on or off. I will be cutting down on the size of the transmissions but I will also need this.

Dan: Which function would you reccomend?
PurgeComm or CanelIO
So what your are telling is that it doesn't expect any intellegently formatted data?

Then what is the problem then?  You can be writing one byte at the time with nothing to terminate?

well there is a minimum lenght (it needs to warm up). I can't send it byte by byte. but it doesn't send formatted data.
Yes, the ultimate version of 'smaller packets' -- just send one byte at a time.

I have only used CancelIO, and I have only used it in rare cases where things go awry ... not as a standard part of my program.  I didn't even know about PurgeComm.  That looks like your best option (aside from setting up your program so that you don't need either...)

Incidentally, to avoid having the O/S buffer up data for you, you could call FlushFileBuffers() after each one-byte WriteFile call.  Or it might work to call SetupComm(...) indicating a one-byte buffer (default is 1024).

-- Dan
So what is minimul length?
going into the details will just confuse you, however on average very few bytes are sent total but the baud rate can be very slow (it changes dynamically). It's set sot that it always takes 1 second. I can speed it up but it needs to be at least .25 seconds. I tried both CancelIO and PurgeComm, and niether works.

The main problem is that the write is not finishing before my program attempts another write. If canceling the write doesn't work, is there a way to wait and not do a second write until the first has finished?

My code:
while(1)
{
Write("my info");
}

I need somthing that either successfully stops the write immediatley before it's done again, or somthing that makes sure that a second write doesn't occur until it's finished the first time (but starts the second write immediatley).
Well second task seems simplier....  

The following is quick and dirty way - your resulting code may look quite different:

if(!SetCommMask(m_hPort,EV_RXCHAR | EV_TXEMPTY))
{
  ATLTRACE("Failed to set comm. mask");
  return 0;
}

DWORD dwEvtMask;
while(m_hPort != INVALID_HANDLE_VALUE && m_bContinue)
{
  WaitCommEvent(pDlg->m_hPort,&dwEvtMask,NULL);

  if((dwEvtMask & EV_RXCHAR) == EV_RXCHAR)
  {
   // do here what you need to do to read, if anything,
   // if not get rid of this code completely....
  }
  else if((dwEvtMask & EV_TXEMPTY) == EV_TXEMPTY)
  {
    // you buffer is empty now!!!!
    SetEvent(_hMyBufferIsEmpty);
  }
}

Now
HANDLE _hMyBufferIsEmpty;

_myBufferIsEmpty = CreateEvent(NULL,TRUE,TRUE,NULL);

now you can run a thread somewhere that sits and wait for _hMyBufferIsEmpty.  After it receives it it does immideate Write for you file ( serial port )

Of cause with this approach it seems that you either have to stack you data in some kind of buffer and hope that EVENTUALLY you will have enough time to send it all out....

Or each time when you need to send something you can start new thread and manage them and their "trigger" events from the list somewhere.....

I don't know, this doesn;t seem that attractive either....  you still may run into situation when you never have enough time to send all you data....
Of cause may be "more newer data" can cancel out neccesety to send "older new data"?  So if you got new data to send and there is still some data that wasn;t send, don't send it????

Hope it helps...
If you call ClearCommError() you can learn how many bytes are queued up to send; that is how many bytes have not been sent yet.  It is in COMSTAT.cbOutQue

So, if you call there repeatedly, you will eventually find that there are 0 bytyes to send.  At that particular point in time, you will be able to surmise that the output queue, being empty, has no data in it.  Your program logic can then (after comparing the count to zero and acsertaining equality thereof) joyously send some new bytes.

I how that helps.

-- Dan
ok, i just got your post and i don't have time to read it over in detail right now, but i'll get to it later today. I don't need to worry about a buffer because i only want the freshest data, so if data is generated and then new data is generated all before it's ready to write the data, i can just discard the first data. thanks
just got your comment Dan: do you think this approach is more logical/easier to implement, than the one proposed by mblat?
It should be easier.  I never trusted those Comm events much.  And what could be easier than making a single API call?

-- Dan
Thanks, that sounds like it will work. I used this code:

LPCOMSTAT lpStat=0;
while(1)
{
  ClearCommError(m_hComm, NULL, lpStat); // m_hComm is the handle of my serial port "file"
  if(lpStat->cbOutQue) Write("myoutput");
}

However the line with the if statment on it causes an access violation. The write statment does not so it must be lpStat->cbOutQue. Do you have any clue why this would happen?
Correction: the if line should have read
if(lpStat->cbOutQue==0) Write("myoutput");
Yes of cause that seems simplier, but that will work only for non-overlapped io.

If this something you can leave with - it is probably way to go.

2 DonRollins:  You NEVER TRUSTED THOSE COMM. EVENTS????????




just kiding - actually I think I know exactly what you mean. :-)
gggggggg,
don't pass in a pointer... unless it points to something!  That fn fills the structure that you provide to it...

COMSTAT rStat;
DWORD nErr;
ClearCommError( m_hComm, &nErr, &rStat );

if ( rStat.cbOutQue == 0 )...

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
mblat,
The whole COMM event scheme seems to be about overlapped I/O and getting other tasks done while waiting for something to happen with the serial posrt.  But I've alwyas found it easier to just run a thread that waits 'stupidly' for characters.  The U/I, file access,  and other stuff is taking place on a different thread.

-- Dan
that makes sense (however the docs said to use a LPCOMSTAT). The problem now is that data is never written (not once). here's my code:

COMSTAT rStat;
while(1)
{
ClearCommError(m_hComm, NULL, &rStat);
if(rStat.cbOutQue==0) Write("mydatatowrite");
}
Why not use the example I gave,?  Check the return value to see if there was an error in the ClearCommError call.

Better to light a candle than to curse the dark... use the debugger and examine the variables.

-- Dan
it seems that the function won't accept a NULL there so I used your example. After calling the function the DWORD nErr contains 0. There's a problem though. It seems that cbOutQue always equals 0. The write is conducted every time, regardless of if it has finished.
Are you using overlapped or non-overlapped io?
non-overlapped
Post the code of your Write() fn.  You must be doing something peculiar there.

-- Dan
from MSDN:

cbOutQue
Specifies the number of bytes of user data remaining to be transmitted for all write operations. This value will be zero for a nonoverlapped write.

Sorry..... It seems that im prev. post I made a mistake when said that this method will only work for nonoverlapped io.  It seems that it is other way around :-(

Oh, well... I haven't worked with Com ports for a while...
One other thing:

There is still possibility that you can write one byte at the time....
Look:
let's say you need to write 10 bytes in a first write.  You starting to write one byte at the time.  Every time Byte is written you are getting EV_TXEMPTY.
So every time you get EV_TXEMPTY you need to check if "newer" data have to be send.  If no - continue to write one byte at the time.  If yes - start to write "newer" data one byte at the time....
Yes, it seems like a lot of extra work, but code wise it actually very easy to do.

What do you think?
mblat: due to the nature of the device i can't write one byte at a time. The speed at which the data arrives is critical. I've tried one byte at a time, and it doesn't work (you'lll have to trust me on this)

dan: i just call Write(m_data, strlen(m_data);

all: i believe the problem is as mblat said: cbOutQue
Specifies the number of bytes of user data remaining to be transmitted for all write operations. This
value will be zero for a nonoverlapped write.

Is there a similar value or method that will work with nonoverlapped IO?
Hello?
   Write( buf, buflen)
is not a Windows API call.  So what is it?

Oh... it is part of the CSerialPort thingy.

Why don't you just use
       CSerialPort::TerminateOutstandingWrites()
?  And what is your evidence that the PurgeComm is failing?

-- Dan
The 16550 compatible UART buffers up 16 bytes in its internal FIFO buffer.  As far as the system is concerned, once it has sent those 16 to the UART, they are gone.

In your example, you used:
   Write("my info");
which would all go into the UART immediately.  Are you trying to abort a send of less than 16 bytes?

You can try going to the Windows Device Manager and telling the UART to use a one-byte FIFO buffer.  I don't know how to do that programmatically.

You might find some low-level fuctionality here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/serial/hh/serial/serref_61bm.asp

You can use the DeviceIoControl API to access these low-level device driver commands.

-- Dan
my evidence that PurgeComm is failing is that I used it and still got the same error. The biggest problem is that this error forces me to reboot my computer everytime it occurs, so this is really irritating that i've tried so many methods. Are you sure there's no way to do the cbOutQue thing without overlapped IO?
>>Are you sure there's no way to do the cbOutQue thing
>> without overlapped IO?
In truth, I have only used the cbInQueue value to keep an eye on how much data is available to be pulled from the device (that way I can ask for exactly that many bytes and never get stuck waiting for data).
>>>>>>>>>>>>>>>>>>>>>>>
>>that I used it and still got the same error.
Which error is that?

What I really mean... is do you have a method to monitor what is actually going out the port?
>>>>>>>>>>>>>>>>>>>>>
>> but the baud rate can be very slow (it changes dynamically).

Reading back, I saw that comment and it makes me wonder just what you are doing.  It is clear that you are not doing anything akin to normal serial output.  If you are making calls mid-byte to change the data rate, you will certainly have many oddball problems.  The device driver will get all tied up in a knot.

=-=-=-=-=-=-
If it is a big secret as to what you are doing, then fine.  But without a context, I have nowhere to go.

-- Dan
i'll give you more info on my project in a little (i have to go right now). But i'm not changing the baud rate while sending data (the baudrate is changed before each write)

code:

while(1)
{
SetState(dcb); //updates the baud rate to a value set in another thread
Write(m_data, strlen(m_data)); // sends data set in the same thread as the baud rate
}
hmm, wait maybe it is changing the baud during a write because i've assumed the problem is that the write is starting over again before it finishes. This means that if it starts a 2nd write before the 1st one is finished, it's also changing the baud rate. Should I put that all in an if block, and only have it execute if there is no write currently in progress? This brings me back to finding a way to see if a write is currently in progress.
here's some detail on what i'm doing:
I'm programming a device designed for the purpose of displaying a value between 0 and 8000. It determines which number to display by how much data it's recieving during a given time period. The way i thought to manipulate the number it displays (control how much data it's recieving in a given time), was to change the baud rate and the amount of data being sent. I set it so that the baudrate is 1/9 of the value i want to display, and the amout of characters sent to the serial port was 1/90 of the value i wanted to display. This resulted in displaying the value i wanted on the device for exactly 1 second (when the device stops recieving data it displays nothing).
I hope that has made what i'm trying to do more clear to you.
when new data was avaliable i wanted to stop the data going to the device and initiate a new write with the new baud and data amount, changing the value displayed on the device. This would result in updates more often than 1 second because the calculation of the new value takes less than 1 second to do.
Rather than manipulate the baud rate, why not keep it high, but just send short bursts of data?  If you carefully control the interval between bursts, you can be very specific about how much data gets sent within any given period.

-- Dan
That's true, but it sounds a lot more complicated (but a lot cleaner too). I tried my program with a fixed baud rate and I still get the same error (that repeats itself for eternity, until I reboot)

THENAMEOFMYSOFTWARE caused an exception e06d7363H in module MSVCRTD.DLL at 0167:10209869.
Registers:
EAX=00000003 CS=0167 EIP=10209869 EFLGS=00000206
EBX=81b0d770 SS=016f ESP=00adfe28 EBP=00adfe50
ECX=00000001 DS=016f ESI=1024fb48 FS=1aaf
EDX=e06d7363 ES=016f EDI=00adfe50 GS=0000
Bytes at CS:EIP:
5f 5e 8b e5 5d c2 08 00 cc cc cc cc cc cc cc cc
Stack dump:
00adfec0 00adfe60 e06d7363 00000001 00000000 00000000 00000003 19930520 00adfeb0 10019ec0 00adfecc 1000198c 00adfeb0 10019ec0 00adff2c 00adfed8

So i don't really think that the baud rate is the problem. Also, i'd still have the problem of the write starting a second time before it finished the first time. If it was easier to change than i think, and it might help resolve the problem, it would be nice to do.

Note: This is becoming a lot more complicated than i thought, so i'm willing to pump a few hundred more points in this question when a successful conclusion is reached.
When you get that crash, check the Call Stack to see what part of your program was executing when it happned.

I don't understand why you think my short-burst suggestion would be more complicated that varying the baud rate.  It seems very simple.  

1) Figure out the desired rate: bits-per-second
2) divide that by 9 or maybe 8 (call that n)  You want to send n BYTES per second.
3) Send n bytes
4) do nothing until one second has expired.
5) go to step 3
OR
3) send n/2 bytes
4) do nothing until 1/2 second has expired
5) go to step 3
OR
3) send n/4 bytes
4) do nothing until 250ms have expired
5) go to step 3

I can think of hundreds of examples like that!

-- Dan
you can think of hundreds of examples like that, but how many can you think of that will let me set which number is displayed by the device?
hey, i've got an idea. I think i know what the problem is. If it is what i think it is, and you can answer this simple question, i'll give you 300 points for your hard work on this question.

When my main program initializes it loads the plug-ins using LoadLibrary. When that happens the CDLLApp::CDLLApp() constructor is called, initializing the plug-in. Is there a function inside the plug-in that is called when the main program quits? (somthing like InitInstance although for when the plug-in closes, or do i need to handle this with some call from the main application like UnLoadLibrary?)
>>but how many can you think of that will let me set
which number is displayed by the device?

I can think of thousands of ways to do it for each value that you want displayed.

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>> Is there a function inside the plug-in that is called
>> when the main program quits?

DLLs?  Plugins???? what does this have to do with this question?  Your testing code should be in a very simple wrapper, all contained in a console-style or dialog-based app.  When the code works, then you can begin splicing the logic into some oddball 'plug-in' architecture.  

That is a programming technique that will save you (and me) thousands of hours of work:  Isolate the functionality.  Work on individual parts, developing and testing each functional module separately.  When something goes wrong, you know where to look.

Now the 300-point answer to that last question:  

Your DLLAPP::ExitInstance() fn will be called when the EXE calls FreeLibrary.  It gets there from the DllMain that is provides by CWinApp.  When the EXE calls FreeLibrary, the DLL's DllMain(...) is called with the second parameter being DLL_PROCESS_DETACH.  That causes a call to ExitInstance and a subsequent call to teh DLLApp object destructor.

-- Dan
ok, i'll award the points tomorrow. I'm curious however how you would implement this without changing the baud rate. Could you please post 2 examples. One that would cause the device to display 3000 and another that would cause it to display 5000.

Note: Points changed to 300.
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
thank you for your clear explanation.
I now see how your way works, however I find that changing the baud rate is more practical for this application (it's very strange). I figured out the problem. The serial port was in use when I quit my application, creating the system error. I solved this problem by modifying the LoadLibrary routine i used, to store the HMODULE value for each DLL in an array, and then when the application was exited, i looped through the array calling FreeLibrary for each DLL. I added ExitInstance functions to my DLLs and inserted code to stop the threads in my DLL and terminate any further serial port access.
This solved the problem. (i got a bit confused when diagnosing this problem, so i apologize for throwing out some inaccurate information)
Note: I ended up using a variation on the cbOutQue thing to space the writes properly.