在 Windows 10 上无法从串口读取超过 32 个字节

问题描述 投票:0回答:1

我现在有一种解决办法,见下文

首先,我很抱歉再次问一个关于该死的串行通信问题的问题。我在这里读到了一些关于此类问题的威胁,并希望我现在了解这些东西是如何工作的。但看来我错了。

我正在为我的自制 RS485 家庭自动化系统编写一个简单的总线监视器。在控制器端一切正常,但 Windows 部分无法正常工作。

我的总线协议通过 RS484 线路发送最大 260 字节、1200Bd 的数据包。数据包结束由一个小中断(三个字符时间)来报告。跟踪数据包应该很简单:

  1. 将 ReadIntervalTimeout 设置为比两个常规字节之间的暂停稍大的值。所以当出现包间间隙时就会触发超时。
  2. 启动一个使用readfile读取最多260字节的线程

它适用于小于 32 字节的小数据包。但读文件在 32 字节处结束,所以我读到了一个不完整的数据包。在下一个读取文件中,我得到了数据包的其余部分。我可以将两个部分组装在一起,但为什么它在 32 字节后停止?

** 一种解决方案**

Windows 上似乎无法安全地使用超时设置。特别是 ReadIntervalTimeout 无法按预期工作。无论是否有更多字节到来,它都会在 32 字节后返回。我也无法读取 max 的完整数据块。一块 260 字节。即使我将 ReadTotalTimeoutConstant 设置为很长,WaitForSingleObject 函数也会在读取所需字节数之前返回,并且您必须启动一个新的 ReadFile。

现在的解决方案是,忽略所有这些超时内容,首先读取包含长度字段的四字节标头,然后使用此长度信息读取其余数据。这是“一些程序员老兄”建议的解决方案。

下面的代码可以工作,但由于没有通过超时检测数据包间间隙,如果标头损坏,例如,可能会读取错误的数据量。由于总线冲突或在传输过程中启动程序。

我完全重写了代码,所以现在它是一个完全可用的代码。

/*
 *  Test program for reading homenet packets from RS485 bus via USB-to-RS485 adapter
 *
 *  Restrictions:
 *     - program do not use timeouts to detect packets, so it may misinterpret data under heavy bus load
 *     - in case of transmission errors or bus collisions the packet header may be corrupted and therefor a false length
 *       information is read. This may lead to misread next packets
 */

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <process.h>

#define BAUDRATE 1200   // RS485 bus soeed

struct HN_PACKET
{
    uint8_t dest;
    uint8_t src;
    uint8_t socket;
    uint8_t length;
    uint8_t d[256];
} rcvbuffer;

HN_PACKET RS485PacketBuffer;

/*
 *  dump packet to console 
 */
void PrintPacket(HN_PACKET *packet)
{
    DWORD adr = 0;
    DWORD i;
    printf("\nSrc: %u, dest: %u, socket:%u, length: %u:", packet->src, packet->dest, packet->socket, packet->length);
    if (!packet->dest)
    {
        packet->d[packet->length] = 0; // for security put a 0-character at the end of the packet
        printf(" DEBUGMSG : %s", packet->d);
    }
    do
    {
        printf("\n  ");
        for (i = 0; i < 16; i++)
        {
            if (adr + i < packet->length) printf(" %02x", packet->d[adr+i]);
            else printf("   ");
        }
        printf("  ");
        for (i = 0; i < 16; i++)
        {
            if (adr + i < packet->length) putchar(isprint(packet->d[adr+i])?packet->d[adr+i]:'.');
        }
        adr += 16;
    } while (adr < packet->length);
    printf("\n");
}

// Opens the specified serial port, configures its timeouts, and sets its
// baud rate.  Returns a handle on success, or INVALID_HANDLE_VALUE on failure.
HANDLE uart_init(LPCWSTR szPortName, uint32_t baud_rate)
{
    HANDLE hComm;

    hComm = CreateFile(szPortName,
        GENERIC_READ | GENERIC_WRITE,//access ( read and write)
        0,    //(share) 0:cannot share the COM port                        
        0,    //security  (None)                
        OPEN_EXISTING,// creation : open_existing
        FILE_FLAG_OVERLAPPED,// we want overlapped operation
        0// no templates file for COM port...
    );

    if (hComm == INVALID_HANDLE_VALUE) return hComm;

    DCB dcb = { 0 };
    dcb.DCBlength = sizeof(DCB);
    if (!GetCommState(hComm, &dcb))
    {
        printf("CSerialCommHelper : Failed to Get Comm State Reason: %d\n", GetLastError());
        CloseHandle(hComm);
        return NULL;
    }
    dcb.BaudRate = baud_rate;
    dcb.ByteSize = 8;
    dcb.Parity = NOPARITY;
    dcb.StopBits = ONESTOPBIT;
    if (!SetCommState(hComm, &dcb))
    {
        printf("CSerialCommHelper : Failed to Set Comm State Reason: %d\n", GetLastError());
        CloseHandle(hComm);
        return NULL;
    }
    SetCommMask(hComm, EV_RXCHAR);

    COMMTIMEOUTS timeouts;
    timeouts.ReadIntervalTimeout = MAXDWORD;
    timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
    timeouts.ReadTotalTimeoutConstant = 7000;
    timeouts.WriteTotalTimeoutMultiplier = 0;
    timeouts.WriteTotalTimeoutConstant = 0;
    if (!SetCommTimeouts(hComm, &timeouts))
    {
        printf("CSerialCommHelper :  Error setting time-outs. %d\n", GetLastError());
        CloseHandle(hComm);
        return NULL;
    }
    return hComm;
}


/*
 * Serial data receive thread
 * Thread is started from uartInit function
 */
unsigned int __stdcall RS485Receiver(void* data)
//int SerialThreadFn(HANDLE m_hCommPort)
{
    HANDLE hComm;

    DWORD dwBytes; // number of bytes read
    DWORD dwRead;                // number of bytes read per ReadFile
    DWORD dwRes;                 // result for WaitForSingleObject
    BOOL fWaitingOnRead = FALSE; // flag for pending data reception
    OVERLAPPED osReader = { 0 }; // overlapped structure
    char* buffer = (char*)&RS485PacketBuffer; // pointer to packet buffer

    puts("Serial Thread started");
    if (hComm = uart_init((LPCWSTR)data, 1200))
    {

        // Create the overlapped event. Must be closed before exiting
        // to avoid a handle leak.
        osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (osReader.hEvent == NULL)
        {
            // Error creating overlapped event; abort.
            puts("Error creating overlapped event");
            CloseHandle(hComm);
            return 1;
        }

        dwBytes = 0;
        while (1)
        {
            if (!fWaitingOnRead)
            {
                // Issue read operation.
                if (!ReadFile(hComm, (char*)&RS485PacketBuffer + dwBytes, (dwBytes<4) ? 4 : RS485PacketBuffer.length + 1, &dwRead, &osReader))
                {
                    if (GetLastError() != ERROR_IO_PENDING) // read not delayed ?
                        // Error in communications; report it.
                        printf("Communication error\n");
                    else
                        fWaitingOnRead = TRUE;
                }
                else
                {
                    // read completed immediately
                    if (dwRead)
                    {
                        dwBytes += dwRead;
                        if (dwBytes > RS485PacketBuffer.length + 4U)
                        {
                            PrintPacket(&RS485PacketBuffer);
                            dwBytes = 0;
                        }
                    }
                    else
                    {
                        // packet timeout, restart reading header
                        dwBytes = 0;
                    }
                }
            }

            if (fWaitingOnRead)
            {
                dwRes = WaitForSingleObject(osReader.hEvent, INFINITE);
                switch (dwRes)
                {
                    // Read completed.
                case WAIT_OBJECT_0:
                    if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE))
                        // Error in communications; report it.
                        printf("Communication error\n");
                    else
                    {
                        if (dwRead)
                        {
                            // Read completed successfully.
                            dwBytes += dwRead;
                            if (dwBytes > RS485PacketBuffer.length + 4U)
                            {
                                PrintPacket(&RS485PacketBuffer);
                                dwBytes = 0;
                            }
                        }
                        else
                        {
                            // packet timeout, restart reading header
                            dwBytes = 0;
                        }
                    }
                    fWaitingOnRead = FALSE;
                    break;
                case WAIT_TIMEOUT:
                    // Operation isn't complete yet. fWaitingOnRead flag isn't
                    // changed since I'll loop back around, and I don't want
                    // to issue another read until the first one finishes.
                    //
                    // This is a good time to do some background work.
                    puts("Waittimeout");
                    break;
                default:
                    // Error in the WaitForSingleObject; abort.
                    // This indicates a problem with the OVERLAPPED structure's
                    // event handle.
                    puts("Error");
                    break;
                }
            }
        }
        CloseHandle(osReader.hEvent);
        CloseHandle(hComm);
        // return dwRead;
    }
    puts("Serial Thread stopped");
    return 1;
}

int main()
{
    // Choose the serial port name.
    LPCWSTR device = L"\\\\.\\COM4";

    // Choose the baud rate (bits per second).  This does not matter if you are
    // connecting to the SMC over USB.  If you are connecting via the TX and RX
    // lines, this should match the baud rate in the SMC G2's serial settings.

    _beginthreadex(0, 0, &RS485Receiver, (void *)device, 0, 0);
    puts("Press any key to stop");
    getchar();
    return 0;
}
c windows serial-port
1个回答
0
投票

我现在有了一个解决方案,基于“一些程序员花花公子”在第3条评论中的回答。我无法将其标记为解决方案,没有显示复选标记。

我在文章中添加了描述。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.