如何将这些DCB结构设置从C映射到C#SerialPort类

时间:2017-09-15 15:36:18

标签: c# c serial-port

我需要复制/移植设置,以便使用其SerialPort类从C ++中的示例连接到C#。由于我不太熟悉COM端口设置的细节,我的问题是如何将这些设置映射到SerialPort类的设置:

HANDLE OpenRS232(const char* ComName, DWORD BaudRate)
{
    HANDLE ComHandle;
    DCB CommDCB;
    COMMTIMEOUTS CommTimeouts;

    ComHandle=CreateFile(ComName, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(GetLastError()!=ERROR_SUCCESS) return INVALID_HANDLE_VALUE;
    else
    {
        GetCommState(ComHandle, &CommDCB);

        CommDCB.BaudRate=BaudRate;
        CommDCB.Parity=NOPARITY;
        CommDCB.StopBits=ONESTOPBIT;
        CommDCB.ByteSize=8;

        CommDCB.fBinary=1;  //Binary Mode only
        CommDCB.fParity=0;
        CommDCB.fOutxCtsFlow=0;
        CommDCB.fOutxDsrFlow=0;
        CommDCB.fDtrControl=0;
        CommDCB.fDsrSensitivity=0;
        CommDCB.fTXContinueOnXoff=0;
        CommDCB.fOutX=0;
        CommDCB.fInX=0;
        CommDCB.fErrorChar=0;
        CommDCB.fNull=0;
        CommDCB.fRtsControl=RTS_CONTROL_TOGGLE;
        CommDCB.fAbortOnError=0;

        SetCommState(ComHandle, &CommDCB);

        //Set buffer size
        SetupComm(ComHandle, 100, 100);

        //Set up timeout values (very important, as otherwise the program will be very slow)
        GetCommTimeouts(ComHandle, &CommTimeouts);

        CommTimeouts.ReadIntervalTimeout=MAXDWORD;
        CommTimeouts.ReadTotalTimeoutMultiplier=0;
        CommTimeouts.ReadTotalTimeoutConstant=0;

        SetCommTimeouts(ComHandle, &CommTimeouts);

        return ComHandle;
    }
}

目前,我的通信工作,但它运行不稳定,与硬件的通信在长时间耐久性/稳定性测试中失速。以下是设置串口的硬件控制器工厂的摘录:

    private static SerialPort _serialPort = new SerialPort();
    private string portName = "COM23";
    private int baudRate = 9600;
    private Parity portParity = Parity.None;
    private int dataBits = 8;
    private StopBits stopBits = StopBits.One;
    int serialPortReceiveTimeout = 30000;
    static private int TxRxBufferLength = 9;

    public ControllerFactory()
    {
        _serialPort = new SerialPort();
        _serialPort.PortName = portName;
        _serialPort.BaudRate = baudRate;
        _serialPort.Parity = portParity;
        _serialPort.DataBits = dataBits;
        _serialPort.StopBits = stopBits;
        _serialPort.ReadTimeout = serialPortReceiveTimeout;
        _serialPort.ReceivedBytesThreshold = TxRxBufferLength;

        try
        {
            _serialPort.Open();
            _serialPort.DiscardOutBuffer();
            _serialPort.DiscardInBuffer();
            Controller = new Controller(_serialPort, Address, TxRxBufferLength, ControllerClockFrequency);
        } catch { }

更新

我以为我会尝试保持控制器的类型模糊,但实际上没有任何理由:

这是Trinamics的TMCM-3110,您可以找到固件手册here。此外,代码示例是here,它是从代码示例here链接的。我还没有联系Trinamics的支持,因为我只是意识到我遇到的稳定性问题很可能是由错误的COM设置引起的。

关于这些稳定性问题:

拖延我意味着由于某些数据字节没有被正确接收而导致软件崩溃。这会导致校验和值不正确的问题(有关其计算的详细信息,请参阅示例代码),并且还会导致串行端口超时,最终导致进程崩溃。不幸的是,我还没有真正确定问题源于何处,但我怀疑这是错误的串口设置。

这是我用来处理与TMCM控制器通信的C#代码。正如您所看到的,它或多或少是C ++代码中的1对1端口:

    static readonly object locker = new object();
    private int sendCmdAndCheckResult(byte Address, byte Command, byte CommandType, byte MotorNr, int Value)
    {
        lock (locker) // this part needs to finish with out another thread interrupting, so that the sent value and the return value belong to the same command from the same thread. See firmware manual.
        {
            sendCmd(Address, Command, CommandType, MotorNr, Value);
            readReturnMessageToBuffer();
            checkReturnedStatus();
            Thread.Sleep(10);
            return getReturnedValue();
        }
    }

    private void sendCmd(byte Address, byte Command, byte CommandType, byte MotorNr, int Value)
    {
        TxBuffer[0] = Address;
        TxBuffer[1] = (byte)Command;
        TxBuffer[2] = (byte)CommandType;
        TxBuffer[3] = (byte)MotorNr;
        TxBuffer[4] = (byte)(Value >> 24);
        TxBuffer[5] = (byte)(Value >> 16);
        TxBuffer[6] = (byte)(Value >> 8);
        TxBuffer[7] = (byte)(Value & 0xff);
        TxBuffer[8] = 0;
        for (int i = 0; i < 8; i++)
            TxBuffer[8] += TxBuffer[i];

        Log($"TmclController: Sending  COM-Port Buffer: {ByteArrayToString(TxBuffer)}");

        if (ComPort.IsOpen)
            ComPort.Write(TxBuffer, 0, TxRxBufferLength);
    }

    private void readReturnMessageToBuffer()
    {
        byte Checksum = 0;
        for (int i = 0; i < TxRxBufferLength; i++)
        {
            if(ComPort.IsOpen)
            RxBuffer[i] = (byte) ComPort.ReadByte(); // read byte for byte synchronously; ReadByte() is block and returns only, when it has read a byte; this method is inefficient, but the easiest way to achieve a blocking read of the returned message; a better way would be to use asychronous reading of serial ports using e.g. the SerialPort.ReceivedBytesThreshold Property and SerialPort.DataReceived Event
        }
        for (int i = 0; i < 8; i++)
            Checksum += RxBuffer[i];

        Log($"TmclController: Received COM-Port Buffer: {ByteArrayToString(RxBuffer)}");

        if (Checksum != RxBuffer[8])
        {
            InvalidOperationException er = new InvalidOperationException("Returned Checksum from TMCL controller is incorrect.");
            throw er;
        }
    }

    private int getReturnedValue()
    {
        return (int)((RxBuffer[4] << 24) | (RxBuffer[5] << 16) | (RxBuffer[6] << 8) | RxBuffer[7]);
    }

1 个答案:

答案 0 :(得分:0)

我是C#的初学者,但是使用串行端口编写了一些代码(例如通过VCP进行通信的示波器应用程序)。不幸的是,简单的方法(DataReceived事件......)不起作用。要使其工作,您需要编写自己的后台工作程序从流中获取数据,或者只是在串口到达时从串行端口读取数据。即使是第二种方法也可行 - 我已经在Windows 8M数据速率上测试了大约两周没有任何问题。