VickyJoe

Hello, everyont:

I met a problem that I didn't thought it was a trouble one,but in fact it troubled me for almost a hole day:

I defined a SerialPort Class in a module. Please look at the following codes:

Module Module1

Dim rdata() as byte

Public WithEvents comport As New System.IO.Port.SerialPort("COM1",9600,IO.Ports.Parity.None,8,IO.Ports.StopBits.One) ''''''''''''''''''the SerialPort Class

Sub SendData(Byval data as byte()) ''''''''''''''''''''''write bytes

comport.DiscardInBuffer()

comport.Write(data,0,data.Length)

End Sub

Public Sub ReceiveData(Byval sender As Object,Byval e as System.IO.Ports.SerialDataReceivedEventArgs) Handles comport.DataReceived '''''''''''''''''''''''''the DataReceived event

comport.ReceivedBytesThreshold = 3

comport.read(rdata,0,3)

End Sub

End Module

Now the problem comes:when I wanted to read the serialport in my Form1 by using the method:comport.Read(array,0,array.Length) ,it reads correctly; but when I changed it to the ReceiveData() event,the program was dead.

My Problem:How can I use the DataReceived event to get the bytes from my serialport COM1

The most important thing is that I have many forms which are all need to receive data from COM1.




Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Bruno Yu - MSFT

VickyLoe,

According to your question on using System.IO.Ports.SerialPort.DataReceived event to get the bytes, I would like to provide you the suggestions as follows:

1. Serial Communications

Usually when I do serial communications, I set up an event handler for the comm port's data received event. Then I get the data whenever it comes in and usually will resend my output until I get a response or send it too many times, at which point I timeout, close the port, and raise a timeout event. The code could look something like this:

Code Snippet

Dim WithEvents port As New SerialPort("COM1")

Private Sub SendData(ByVal data As String)

If Not port.IsOpen Then port.Open()

port.Write(data)

End Sub

Private Sub port_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles port.DataReceived

If e.EventType <> SerialData.Chars Then Exit Sub

Dim inData As String = port.ReadExisting

'process incoming data here

End Sub

2. How does SerialPort handle DataReceived

Please see the answer by nobugz: The call to your DataReceived event may well be delayed, depending on system load. By the time it runs and you use the BytesToRead property, you may be seeing bytes received for which SerialPort has already queued new thread pool worker items. That means that you may well see zero bytes (because they were read by the previous invocation of the event). Also, the the number of bytes may change right after you call it in the middle of your DataReceived event's execution when another byte was received a few microseconds later.

3. Example from code project: Communication on a serial port in NET 2.0

Code Block

Imports System

Imports System.IO.Ports

Public Class Form1

Dim WithEvents port As SerialPort = New _

System.IO.Ports.SerialPort("COM1", 9600, Parity.None, 8, StopBits.One)

Private Sub Form1_Load(ByVal sender As Object, ByVal e As _

System.EventArgs) Handles Me.Load

CheckForIllegalCrossThreadCalls = False

If port.IsOpen = False Then port.Open()

End Sub

Private Sub port_DataReceived(ByVal sender As Object, ByVal e As _

System.IO.Ports.SerialDataReceivedEventArgs) Handles port.DataReceived

TextBox1.Text = (port.ReadTo("!%"))

If port.ReadExisting.Length = 0 Then

ListBox1.Items.Add(TextBox1.Text)

TextBox1.Text = ""

End If

End Sub

End Class

Hope that can help you.




Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

Bruno Yu - MSFT:

Thanks very much for your kind reply,but I still have a question about the SerialPort control: I'd like to know if I set the SerialPort.ReceivedBytesThreshold = 3, and I do a SerialPort.read(rdata,0,3) operation, resume that there are still many bytes data in the InBuffer, does it means after the SerialPort.read(rdata,0,3) operation, system will clear the three bytes' space whice I just read,and the rest bytes will step up to the space,then fire another DataReceived Event

Is my understanding right

if it is right, I have another question: How fast can the DataReceived Event be fired 2mS a time or 1mS a time

In my application, the DataReceived Event need to fire 1.5mS a time,but I found that it can only fire at a frequency of 2mS a time! Where is the Problem

Hoping for your kind reply again!

yours

VickyJoe






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

Bruno Yu and VickyJoe

I don't think that the answer from nobugz, which Bruno Yu quote, is correct (#2).

The short story:

DataReceived fires when the number of received data exceeds the threshold level. Any further events from SerialPort are disabled (by means of a lock on the underlying SerialStream object) until the eventhandler returns, so there will never be more instances running at the same time. Therefore, you will never receive zero bytes (bytes received by a previous instance).

The long story:

Read at least the chapter "Serial Port Events" of this description: http://www.innovatic.dk/knowledg/SerialCOM/SerialCOM.htm . This describes the exact receive sequence according to Kim Hamilton, who is the one, who has programmed SerialPort.

VickyJoe

You cannot expect the DataReceived event to fire faster than 50 mS! The event is handled on a thread pool thread with lower priority than the UI thread. Why do you need it so fast Anything above 115.2 kbit/s requires a better UART, but below that speed, you will have no problems if you do it right.
I know that when you put SerialPort in its own class (or into a module) you need to do something slightly different to make the DataReceived event to fire. I think it has something to do with making a new SerialPortEventHandler delegate in the class, but I have never used SerialPort that way so I cannot give you a precise guidance, but maybe Bruno Yu know what to change when you put some code in a module.





Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

VickyJoe

As the threshold level is checked in the thread pool thread, which is used for the DataReceived event, before the event handler is called (see long story) it is not a good idea to set the threshold level in the eventhandler. Do it outside.

As I wrote, I have never used SerialPort in a module, but have you tried something like this:

... Handles Me.comport.DataReceived

Note the extra "Me."





Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

Thanks for your reply, according to your short story and Kim's long story, I have a new understanding of the SerialPort Class.

But I still have a question about the DataReceived Event's fire-time:I use a baudrate of 19200,and it is much less than 115200bit/s,why can't it received all the data I send to it I'm sure that the lost data are still in the InPutBuffer,because when I send some data to it again, it received the data which I sent before. What a strange phenomenon!!!

So another problem comes:whether the SerialPort clear the space it just read when it fired again

Although I use the loop require method to solve the problems above,but I still want to know why the event cannot fire fast enough How does the SerialPort Control run when it reads the InPutBuffer time and time again

Thanks you all very much,sincerely!






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

Carsten Kanstrup :

I think it is my fault to have not explaint my problem clearly:

I can write the DataReceived Event in a module in the form of :

..........Handles comport.DataReceived (suppose the name of the SerialPort Class is comport)

but my problem is that when I put it in a module , how can I call it to be active

Campared with define a DataReceived Event in a module,suppose I use a SerialPort Control in the main form whose name is Form1, I can define the Event in the code space of Form1 like:

......Handles SerialPort1.DataRecieved (suppose the name of the SerialPort Control is SerialPort1)

And this time it can fire correctly when it receives enough bytes from COMx according to my setting, because it is always active.

So, what I asked was how can I make a DataReceived Event to be active if I define it in a module

Thanks for your attention!






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

It is important to understand that DataReceived does not necessary fire for each byte. All data, which are receiced by the UART, are stored in the receive buffer in the UART driver. This buffer has a minimum size of 4096 bytes. This means that at 19200 bit/s it will take 2.1 seconds to fill this buffer (remember start and stop bit), so DataReceived must just not be slower than that. I am using SerialPort up to 921.6 kbit/s myself with a 16C950 UART.

Your so-called data loss may be coursed by your wrong setting of the receiver threshold in combination with a misunderstanding of how Read(buffer, offset, count) works. Qoute from chapter "Read Method" in my description:

"Because the help files does not describe how Read actually works, it is logical to assume that the method:

  BytesReceived = YourCOMPort.Read(buffer, offset, count)
  

is a blocking method, which does not return before "count" number of bytes are received. It is not!. If there are bytes available on the serial port, Read returns up to "count" bytes, but will not block (wait) for the remaining bytes. If there are no bytes available on the serial port, Read will block until at least one byte is available on the port, up until the ReadTimeout milliseconds have elapsed, at which time a TimeoutException will be thrown."

Because your threshold setting does not work, Read() may return when only one byte is received. The next time you call Read(), you will get the next bytes. I think that this is your "data loss".

I will try to find the time to make a test program where I put SerialPort in a module to see what is necessary to change. In the meantime it is perhaps a good idea for you to read more of my description. In this description I have tried to gather all my present knowledge about SerialPort plus an answer to all the questions I have been asked about SerialPort.





Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

So!!!That is the problem!

Why did it "will not block (wait) for the remaining bytes"

It is define to read the "count" number of bytes,and further more, I had already set the ReceiveThreshold to "count", Why did it still be fired when there were one or two or three bytes available in the inputbuffer if I set the ReceiveThreshold equal to 6

Is the Read method's problem Or it just works in the way you just posted above






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

Of course Read() works the way I have described it - it is actually a quote from Kim Hamilton. If you use Read(), you should always check the actual number of received bytes (the returned value).

As I wrote before (see my above posts), the receiver threshold is checked in the thread pool thread BEFORE it calls your eventhandler for the DataReceived event, so it doesn't work to specify the threshold level in the eventhandler as you do! You need to specify the threshold level outside the eventhandler - typically the same place where you set other parameters for SerialPort like ReadBufferSize, BaudRate, Parity etc.





Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

OK,I will try it in the way you told.Thanks very much! But actually I specify the Threshold in the right place as you said. I think there still someplace I didn't do them right,I will checkout them and reply this post later.

At last,I feel so happy that you helped me so much,Thank you!






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

Sorry, but unless you have changed your code, you set the threshold in the eventhandler.

Public Sub ReceiveData(Byval sender As Object,Byval e as System.IO.Ports.SerialDataReceivedEventArgs) Handles comport.DataReceived '''''''''''''''''''''''''the DataReceived event

comport.ReceivedBytesThreshold = 3 ' Here you set the threshold in the eventhandler - this is wrong!

comport.read(rdata,0,3)

End Sub

End Module





Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

VickyJoe

Oh,yes,I forgot to say that comport.ReceivedBytesThreshold = 3 is an example of my code,but actually I define it with the PortName,Baudrate,StopBits,Parity...and so on,which is outside the DataReceived Event.In Fact they are initialized in the Form1_Load event.I'm so sorry about the mistake which made you misunderstand.

But even I do so, the DataReceived Event can be fired when there were not 3 bytes in the receivebuffer.I use a Loop to do the DataReceive instead of the DataReceived Event,and that works smoothly.

Thanks for your kind help,sincerely!!!






Re: Visual Basic Express Edition How to use the DataReceived event which was defined in a module.

Carsten Kanstrup

I am not sure if this case is solved

Have you solved the module problem and made a program, which works