【问题标题】:Unable to receive serial data无法接收串行数据
【发布时间】:2016-06-12 09:38:56
【问题描述】:

我在windows平台下修改了别人用C(codeblocks/mingw)编写的串行通信环回代码。我能够正确发送数据。我通过打开终端软件验证了这一点。但我无法接收数据。在下面的代码中,我从 ReadFile() 的输入缓冲区 998 中读取错误消息错误。不知道是什么错误。 我正在使用两个 CP210x USB 转串口模块并将 TxD 连接到 RxD 的一个到另一个。

#include    <windows.h>
#include    <stdlib.h>
#include    <stdio.h>
#include    <string.h>
#include    <commdlg.h>
//#include  <windef.h>
#include <time.h>

int nread,nwrite;

void myDelay(unsigned int mseconds)
{
    clock_t goal = mseconds + clock();
    while (goal > clock());
}

int main(int argc, char* argv[])
{
    HANDLE hSerial;
    COMMTIMEOUTS timeouts;
    COMMCONFIG dcbSerialParams;
    char *words, *buffRead, *buffWrite;
    DWORD dwBytesWritten, dwBytesRead;

    if(argc<3)
    {
        printf("Enter the com port as command line parameter and t for transmitter and r for receiver\n");
        printf("Example: Serial.exe com4 t\n");
        return(1);
    }
    hSerial = CreateFile(argv[1],GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

    if ( hSerial == INVALID_HANDLE_VALUE)
    {
        if (GetLastError() == ERROR_FILE_NOT_FOUND)
        {
            printf(" serial port does not exist \n");
        }
        else
        {
            printf(" some other error occured\n");
        }

        return(1);
    }


    if (!GetCommState(hSerial, &dcbSerialParams.dcb))
    {
        printf("error getting state \n");
        return(1);
    }

    dcbSerialParams.dcb.DCBlength = sizeof(dcbSerialParams.dcb);

    // Set various serial port parameters.

    dcbSerialParams.dcb.BaudRate = CBR_9600;
    dcbSerialParams.dcb.ByteSize = 8;
    dcbSerialParams.dcb.StopBits = ONESTOPBIT;
    dcbSerialParams.dcb.Parity = NOPARITY;

    dcbSerialParams.dcb.fBinary = TRUE;
    dcbSerialParams.dcb.fDtrControl = DTR_CONTROL_DISABLE;
    dcbSerialParams.dcb.fRtsControl = RTS_CONTROL_DISABLE;
    dcbSerialParams.dcb.fOutxCtsFlow = FALSE;
    dcbSerialParams.dcb.fOutxDsrFlow = FALSE;
    dcbSerialParams.dcb.fDsrSensitivity= FALSE;
    dcbSerialParams.dcb.fAbortOnError = TRUE;

    if (!SetCommState(hSerial, &dcbSerialParams.dcb))
    {
        printf(" error setting serial port state \n");
        return(1);
    }


    GetCommTimeouts(hSerial,&timeouts);
    timeouts.ReadIntervalTimeout = 50;
    timeouts.ReadTotalTimeoutConstant = 50;
    timeouts.ReadTotalTimeoutMultiplier = 10;
    timeouts.WriteTotalTimeoutConstant = 50;
    timeouts.WriteTotalTimeoutMultiplier= 10;

    if(!SetCommTimeouts(hSerial, &timeouts))
    {
        printf("error setting port state \n");
        return(1);
    }

    while(1)
    {
        // Use a delay of 500 msec
        myDelay(500);
        if(!strcmp(argv[2],"t"))
        {

            printf("Write Mode\n");
            //****************Write Operation*********************//
            words = "This is a string to be written to serial port COM1";
            nwrite = strlen(words);

            buffWrite = words;
            dwBytesWritten = 0;

            if (!WriteFile(hSerial, buffWrite, nwrite, &dwBytesWritten, NULL))
            {
                printf("error writing to output buffer \n");
                return(1);
            }

        }


//***************Read Operation******************//

        else
        {
            dwBytesRead = 0;
            nread = strlen(words);

            if (!ReadFile(hSerial, buffRead, nread, &dwBytesRead, NULL))
            {
                printf("error reading from input buffer %d\n", GetLastError());
                continue;
            }
            printf("Data read from read buffer is \n %s \n",buffRead);

        }
    }
    CloseHandle(hSerial);
    return(0);

}

【问题讨论】:

  • 错误 998 是 ERROR_NOACCESS,“对内存位置的访问无效”。以 100% 的信心告诉您,您将错误的指针传递给 ReadFile()。你是,你从来没有初始化过buffRead。一个更好的声明是BYTE buffRead[4096]; 并传递一个正确的缓冲区大小值,即 4096。printf() 语句也是错误的,你不会得到一个正确的以零结尾的字符串。
  • 工作。没有注意到内存没有分配。如果您将其写为答案,我可以将其标记为答案。现在它对我有用。我在接收部分也做了一些更改以避免垃圾显示。作为清理代码的一部分,我将做更多的更改' else { if(dwBytesRead) { for(count=0;count

标签: c serial-port mingw codeblocks


【解决方案1】:

对 COM 端口的读取和写入相互阻塞,当 FIFO 用完时,您的读取几乎总是超时。此外,FIFO 中可能已经存在一些数据......如果您发送和接收的数据块太大,则以这种方式更有可能丢失数据。

这里是一个古老的非线程 COM 端口访问的小例子

//---------------------------------------------------------------------------
//--- port class ver: 1.0 ------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _port_h
#define _port_h
//---------------------------------------------------------------------------
class port
        {
public: HANDLE       hnd;
        AnsiString   error;
        DWORD        rlen,wlen,err;
        DCB          rs232_state;
        COMMPROP     properties;
        COMMTIMEOUTS timeouts;
        COMSTAT      stat;
        port();
        ~port();
        int  open(AnsiString name);
        void close();
        int  get_stat();        // err,stat
        int  get_timeouts();    // timeouts
        int  set_timeouts();
        int  get_properties();  // properties
        int  get_rs232_state(); // rs232_state
        int  set_rs232_state();
        void rst_rs232_state();
        void out(BYTE data)  { WriteFile(hnd,&data,1,&wlen,NULL); }
        void in (BYTE *data) { ReadFile (hnd, data,1,&rlen,NULL); }
        void in (char *data) { ReadFile (hnd, data,1,&rlen,NULL); }
        void out(BYTE *data,DWORD len) { WriteFile(hnd,data,len,&wlen,NULL); }
        void in (BYTE *data,DWORD len) { ReadFile (hnd,data,len,&rlen,NULL); }
        };
//---------------------------------------------------------------------------
port::port()
        {
        rlen=0;
        wlen=0;
        err =0;
        error="";
        hnd=NULL;
        rst_rs232_state();
        }
//---------------------------------------------------------------------------
port::~port()
        {
        close();
        }
//---------------------------------------------------------------------------
int port::open(AnsiString name)
        {
        close();
        rlen=0;
        wlen=0;
        hnd=CreateFile( name.c_str(),GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_ALWAYS,0,NULL);
        if (hnd==NULL) return 0;
        get_timeouts();
        get_properties();
        get_rs232_state();

        timeouts.ReadIntervalTimeout;
        timeouts.ReadTotalTimeoutMultiplier;
        timeouts.ReadTotalTimeoutConstant;
        timeouts.WriteTotalTimeoutMultiplier;
        timeouts.WriteTotalTimeoutConstant;

        properties.wPacketLength;
        properties.wPacketVersion;
        properties.dwServiceMask;
        properties.dwReserved1;
        properties.dwMaxTxQueue;
        properties.dwMaxRxQueue;
        properties.dwMaxBaud;
        properties.dwProvSubType;
        properties.dwProvCapabilities;
        properties.dwSettableParams;
        properties.dwSettableBaud;
        properties.wSettableData;
        properties.wSettableStopParity;
        properties.dwCurrentTxQueue;
        properties.dwCurrentRxQueue;
        properties.dwProvSpec1;
        properties.dwProvSpec2;
        properties.wcProvChar[1];

        return 1;
        }
//---------------------------------------------------------------------------
void port::close()
        {
        if (hnd==NULL) return;
        CloseHandle(hnd);
        hnd=NULL;
        }
//---------------------------------------------------------------------------
int port::get_stat()
        {
        if (ClearCommError(hnd,&err,&stat)) return 1;
        error=GetLastError();
        return 0;
        }
//---------------------------------------------------------------------------
int port::get_timeouts()
        {
        if (GetCommTimeouts(hnd,&timeouts)) return 1;
        error=GetLastError();
        get_stat();
        return 0;
        }
//---------------------------------------------------------------------------
int port::set_timeouts()
        {
        if (SetCommTimeouts(hnd,&timeouts)) return 1;
        error=GetLastError();
        get_stat();
        return 0;
        }
//---------------------------------------------------------------------------
int port::get_properties()
        {
        if (GetCommProperties(hnd,&properties)) return 1;
        error=GetLastError();
        get_stat();
        return 0;
        }
//---------------------------------------------------------------------------
int port::get_rs232_state()
        {
        if (GetCommState(hnd,&rs232_state)) return 1;
        error=GetLastError();
        get_stat();
        return 0;
        }
//---------------------------------------------------------------------------
int port::set_rs232_state()
        {
        if (SetCommState(hnd,&rs232_state)) return 1;
        error=GetLastError();
        get_stat();
        return 0;
        }
//---------------------------------------------------------------------------
void port::rst_rs232_state()
        {
        rs232_state.BaudRate    = CBR_9600;
        rs232_state.ByteSize    = 8;
        rs232_state.Parity      = NOPARITY;
        rs232_state.StopBits    = ONESTOPBIT;
        rs232_state.fOutxCtsFlow= FALSE;
        rs232_state.fOutxDsrFlow= FALSE;
        rs232_state.fOutX       = FALSE;
        rs232_state.fInX        = FALSE;
        rs232_state.fBinary     = FALSE;
        rs232_state.fRtsControl = RTS_CONTROL_DISABLE;
        }
//---------------------------------------------------------------------------//---------------------------------------------------------------------------
//---------------------------------------------------------------------------//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

它在 C++/VCL 中,所以只需将 AnsiString 重写为 char* 或您获得的任何字符串类型,并将文件例程更改为您的。如果您无权访问类,请重写为 C。用法很简单:

// globals and init
port com;
com.open("COM2");
com.timeouts.ReadIntervalTimeout        =10;
com.timeouts.ReadTotalTimeoutMultiplier =10;
com.timeouts.ReadTotalTimeoutConstant   =10;
com.timeouts.WriteTotalTimeoutMultiplier=10;
com.timeouts.WriteTotalTimeoutConstant  =10;
com.set_timeouts();

// send/recv
char q;
com.out(`x`); // send x
com.in (&q); // recv ...

您应该使用线程来解决这个问题,因为没有线程通信就会阻塞。无论如何,如果没有用于环回的线程,您应该:

  1. 将两个 COM 端口设置为相同的协议
  2. 始终先发送后接收
  3. 设置足够小的超时时间(不要阻塞太久)
  4. 预计数据丢失和超时(并按此处理...不要阻止它发送)
  5. 只发送BYTES 并非所有 COM 端口都有大 FIFO...

为了更安全,请在线程内进行阅读...例如:

volatile bool _stop=false; // set this to true when you want to exit
unsigned long __stdcall thread_com(LPVOID p)
        {
        char q;
        for (;!_stop;)
         {
         com.in (&q); // recv ...
         if (q<32) q=`.`; // data loss due to timeout if not sending `0..31` of coarse
         Sleep(1); // just not to burn CPU ...
         }
        }

HANDLE thread_com_hnd=CreateThread(0,0,thread_com,NULL,0,0); // do this only after COM port is initialized

【讨论】:

  • 我正在寻找非常简单的 C 代码来接收/发送,但我不熟悉线程等。这段代码主要是为了理解目的。无论如何,我会记住这一点,以防我进行进一步的更改。
猜你喜欢
  • 1970-01-01
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 2018-05-19
  • 1970-01-01
相关资源
最近更新 更多