How to synchronize Half Duplex in Serial Port COM using ReadFile and WriteFile?


HI,

I'm trying to communicate a PC with a microcontroller through RS485. So this means that the communication is Half Duplex. (must only one direction of transmission data at time). The data traffic is very congested in both directions, so I have serious problem with coalition of data.

I have to send a set of bytes from the microcontroller to the PC and the PC has to respond with another set of bytes immediately (it must be separated by 1ms because of RS485 drivers take its time to change direction), and immediately the microcontroller also responds ...and so on.
Also independently each one of them eventually sends its set of bytes suddenly.

I have my ReadFile in an exclusive thread. And in that is where I need ReadFile to gives me a FLAG or any SIGNAL as soon as it start receiving the FIRST byte, so that I can use this Flag to change my bool variable FLAG_write_block as a writer blocker, so that my writer thread is blocked, and do not send anything while reading in the same port.

UINT my_reader_thread(LPVOID param)
{
THREAD_READER_INFO *info=(THREAD_READER_INFO*)param;
CWnd *pWnd=info->pWnd;
HANDLE *com=info->com;
BYTE buf[2000];
....

while(*com!=INVALID_HANDLE_VALUE)
{
ReadFile(*com,buf,2000,&bytesRead,NULL);

/*here I need ReadFile gives me a SIGNAL or a FLAG
when it start receiving the firt byte ...for:
FLAG_write_block=true;
*/

if(bytesRead>0)
pWnd->PostMessageA(MSJ_COM_RECEIVED,(WPARAM)0,(LPARAM)0);

FLAG_write_block=false;

::Sleep(1);
}
return 0;
}

And in another thread dedicated to waiting for the unlocking the FLAG_write_block, while the information to write is accumulating in a CStringA as a write queue, so that when it is not receiving anything, it can flow and deliver everything it has.
Like:

UINT my_writer_thread(LPVOID param)
{
THREAD_WRITER_INFO *info=(THREAD_WRITER_INFO*)param;
CWnd *pWnd=info->pWnd;
HANDLE *com=info->com;
CStringA *writeQueue=info->writeQueue;
bool *FLAG_write_block=info->FLAG_write_block
....

while(*com!=INVALID_HANDLE_VALUE)
{
if(!FLAG_write_block && writeQueue.GetLength())
{
WriteFile(com,(LPCVOID)writeQueue,writeQueue.GetLength(),&i_written,NULL);
writeQueue.Empty();
}

::Sleep(1);
}
return 0;
}


How I can implement this Flag technique in ReadFile?


I really appreciate if someone could give me some hand of help.


Best regards,
Yany
I doubt you'll be able to implement this in user mode. Achieving timing accurate down to 1 ms is really difficult. I can tell you right now that Sleep() is not suitable for this, as it has a ~15 ms granularity. In other words, a call to Sleep(1) may return anywhere from 1 to 15 ms later.
It doesn't matter about the Sleep, because my real codes are in Sleep(100) and it works fine. Internally, the PC accumulates all data arrived at the COM until a ReadFile empties it. I put this Sleep() because any thread running without at least a Sleep(1) it consumes a lot of CPU resources. So I don't need precision in Sleep.

What I really need is how I can obtain a Signal or a Flag in somewhere in the codes when the COM port is receiving the first Byte, as soon as it arrives. So that I can block immediately my writer.
Does your RS-485 network have a "master" or are you trying to operate in "multiple master" mode?

By the way there is usually no need for any delays in the user software because the hardware usually takes care of the necessary delays.

If you're trying to operate in "multiple master" mode (not recommended for beginners) you'll need a way to detect collisions and resend the data if necessary.

I made several Electronic Nodes governed by PIC microcontroller and each one with own electronic RS485 driver. And all of these Nodes fulfill a specific function and each one must to delivery its own information. According to an own designed protocol, it was necessary to create an "Area Master Node", who tells to all of the rest of nodes who is able to send, like a turns polling.
So, it works perfectly! and there is no any data coalition, despite a lot of data traffic. Because, as you said, it's easy to manage data flows in a net when one "master" governs all the rest as "slaves", since a application level.

And the best advantage I had, is because of the Microcontrollers is a low-level programming, like using ASM language, and it can allow manipulate the information at Byte-level, and in real time.

But things get complicated when I want to do the same thing between a Node and a PC.

The physical connection with the PC is only with the "Area Master Node" (not with the rest of Nodes), so it's like a data funnel.

I tried make the PC as the "master" and the Area Master Node as "slave", and also vice-versa where the PC is slave. But didn't work either!

Because Microsoft no longer allowed programming to low-level the PC ports.
So, We are consigned and obliged to use only the ReadFile and WriteFile function to access the ports.

I used first this:
ReadFile(*com,buf,1,&bytesRead,NULL);
capturing byte per byte, but no success

Now, I'm trying, as I show above, make both as "master".
That is to say: "The first byte who arrives takes the control of the bus and blocks writer thread the one who is receiving".
Once finished receiving is unlocked; and both stay in "ready to send state".

I think this technique is better, because it's about only 2 host (no more), and because it would not consume PC resources, since the software is dedicated to other real-time assistance tasks. (I had cases of slowing down with my previous techniques).

But to do this simple thing!...
I need the "ReadFile" function, somehow from internally, makes change a FLAG or eventually send a message when start reading activity, to I know the another host is sending. ...so that I can block my writer thread at that time.

Because Microsoft no longer allowed programming to low-level the PC ports.

If I'm not mistaken that is not entirely true. You can low-level program the PC ports inside device drivers, but possibly not in user mode.

Now, I'm trying, as I show above, make both as "master".
That is to say: "The first byte who arrives takes the control of the bus and blocks writer thread the one who is receiving".
Once finished receiving is unlocked; and both stay in "ready to send state".

The problem with this will be collisions and how to properly handle those collisions. Having a single master that controls the messaging is much easier and will usually give you more bandwidth without collisions.

Do you realize that in a single master system the Master is responsible for requesting information from individual nodes and when a node receives a request addressed to itself it should send the requested information? Remember normally every node is listening for a message addressed to themselves. When the message is received the addressed node should then transmit the counter message.

But things get complicated when I want to do the same thing between a Node and a PC.

Actually if you're using commercial RS-485 hardware for the PC that has the proper drivers it should be fairly easy to make the PC the master. However sometimes making the PC a slave can be tricky.

Could you please provide me a link about "user mode"?, so I can understand what it is that you referring about.
Maybe it could be the solution, if creating my own low-level COM serial port handlers.

I'm not using commercial RS-485 hardware for the PC, because I made mine. Where I convert the RS485 to RS232 and vice-versa adding electronic protection properties to protect the PC from electrical breakdowns.

Anyway. Commercial or homemade, the amphitryon of RS485 at hardware level is the electronic component: SN65176
This integrated circuit has 8 simple pinouts. The most important that interests in this topic is: the R (Read serialdata) pin, D (Write serialdata) pin and the RE-DE (where Microcontroler or PC indicates to put it in Read mode and Write mode)

PC or microcontroler in inactivity put it in Read mode as default always. Only if it's going to send is when it toggle to Write mode, through the DE-RE pins (the two are soldier as one)

On the PC side, this DE-RE pin is governed by the RTS (request to send) pin of the PC COM serial port.

And by software I enable this pin and its handler.

dcb.BaudRate=baudRate;
dcb.ByteSize=byteSize;
dcb.Parity=parity;
dcb.StopBits=stopBit;
dcb.fRtsControl=RTS_CONTROL_TOGGLE; // << here
SetCommState(com,&dcb);

To automatically it set HIGH the pin when the PC sends data and set LOW when not.


So,
It means that at the hardware level there is no establishment of master-slaves by default.
It must to implemented by application layer, that is: by software.

Making the PC as a master is the logical thing to do.

But if I do "master or slave", I will have to implement a loop: to generate questions or to receive questions correspondingly every 10 ms or 100ms, since it is a real-time control system.
But to make so intense activities in the port, it also implies that messages work sequentially in the message map system unceasingly each 10 or 100ms I'm afraid it will subtract CPU resources so much.

What would be the most advisable?
Could you please provide me a link about "user mode"?, so I can understand what it is that you referring about.
"User mode" refers to code running as a normal application, not as a device driver or a kernel module.

https://en.wikipedia.org/wiki/CPU_modes
https://en.wikipedia.org/wiki/Protection_ring
Last edited on
Topic archived. No new replies allowed.