Search code examples
serial-portwindows-xp-embeddedrs485

Windows XP embedded - RS485 problems


We've got a system running XP embedded, with COM2 being a hardware RS485 port.

In my code, I'm setting up the DCB with RTS_CONTROL_TOGGLE. I'd assume that would do what it says... turn off RTS in kernel mode once the write empty interrupt happens. That should be virtually instant.

Instead, We see on a scope that the PC is driving the bus anywhere from 1-8 milliseconds longer than the end of the message. The device that we're talking to is responding in about 1-5 milliseconds. So... communications corruptions galore. No, there's no way to change the target's response time.

We've now hooked up to the RS232 port and connected the scope to the TX and RTS lines, and we're seeing the same thing. The RTS line stays high 1-8 milliseconds after the message is sent.

We've also tried turning off the FIFO, or setting the FIFO depths to 1, with no effect.

Any ideas? I'm about to try manually controlling the RTS line from user mode with REALTIME priority during the "SendFile, clear RTS" cycle. I don't have many hopes that this will work either. This should not be done in user mode.


Solution

  • RTS_CONTROL_TOGGLE does not work (has a variable 1-15 millisecond delay before turning it off after transmit) on our embedded XP platform. It's possible I could get that down if I altered the time quantum to 1 ms using timeBeginPeriod(1), etc, but I doubt it would be reliable or enough to matter. (The device responds @ 1 millisecond sometimes)

    The final solution is really ugly but it works on this hardware. I would not use it on anything where the hardware is not fixed in stone.

    Basically:

    1) set the FIFOs on the serial port's device manager page to off or 1 character deep

    2) send your message + 2 extra bytes using this code:

    int WriteFile485(HANDLE hPort, void* pvBuffer, DWORD iLength, DWORD* pdwWritten, LPOVERLAPPED lpOverlapped)
    {
      int iOldClass = GetPriorityClass(GetCurrentProcess());
      int iOldPriority = GetThreadPriority(GetCurrentThread());
      SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
      SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
    
      EscapeCommFunction(hPort, SETRTS);
    
      BOOL bRet = WriteFile(hPort, pvBuffer, iLength, pdwWritten, lpOverlapped);
    
      EscapeCommFunction(hPort, CLRRTS);
    
      SetPriorityClass(GetCurrentProcess(), iOldClass);
      SetThreadPriority(GetCurrentThread(), iOldPriority);
    
      return bRet;
    }
    

    The WriteFile() returns when the last byte or two have been written to the serial port. They have NOT gone out the port yet, thus the need to send 2 extra bytes. One or both of them will get trashed when you do CLRRTS.

    Like I said... it's ugly.