/****************************************************************************** * \file main.cpp.c * \author based on http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html * \attention * Tested On : Windows 7 32-bit Pro ****************************************************************************** */ #include #pragma hdrstop #include #include #include //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //============================================================================= //..................... Global definitions ...................... //============================================================================= #define COMPORT_SETBUFFRXSIZE 16384 //============================================================================= //..................... Global variables ...................... //============================================================================= #define RX_RINGBUFF_SIZE 16384 // RX ringbuffer's length, in bytes typedef struct { unsigned char data[RX_RINGBUFF_SIZE]; uint32_t wrpnt; int rdpnt; }RxBuff_t; RxBuff_t RxBuff; HANDLE COMport; //port descriptor //the OVERLAPPED structure is needed for async operations, two independent structures for Rx and Tx operations must be used OVERLAPPED Overlapped; //for ReadThread int LostRxBytes; unsigned char buff[RX_RINGBUFF_SIZE]; int Dbg_buff_nbytes; bool TranmitIsFinished = false; //============================================================================= //.............................. functions definitions ..................... //============================================================================= void COMOpen(void); //открыть порт void COMClose(void); //закрыть порт //============================================================================= //.............................. TThreads definitions ...................... //============================================================================= class ReadThread : public TThread { private: protected: void __fastcall Execute(); //main function of the thread public: __fastcall ReadThread(bool CreateSuspended); // the constructor of thread }; //--------------------------------------------------------------------------- //----------------------------------------------------------------------------- //............................... ReadThead ............................. //----------------------------------------------------------------------------- ReadThread *reader; //object of the stream ReadThread //the constructor of thread, empty by default __fastcall ReadThread::ReadThread(bool CreateSuspended) : TThread(CreateSuspended) {} //--------------------------------------------------------------------------- //receiving bytes to the ring buffer, starts RxDataProcessing() after new bytes getting void __fastcall ReadThread::Execute() { COMSTAT comstat; //currecnt starte of the port, in this code it's used for taking a quantity of the received bytes DWORD btr, temp, mask, signal; //temp is used just for correct GetOverlappedResult(), the result in temp is not used uint8_t tmpbuf[COMPORT_SETBUFFRXSIZE]; Overlapped.hEvent = CreateEvent(NULL, true, true, NULL); //the signal object for async operations SetCommMask(COMport, EV_RXCHAR); // mask for interrupt by new byte receiving while(!Terminated) //loop till terminationg of this thread { WaitCommEvent(COMport, &mask, &Overlapped); //wait new bytes signal = WaitForSingleObject(Overlapped.hEvent, INFINITE); if(signal == WAIT_OBJECT_0) { if(GetOverlappedResult(COMport, &Overlapped, &temp, true)) { //the correct end of WaitCommEvent() if((mask & EV_RXCHAR)!=0) {// it's event "new byte (bytes) has been receved" ClearCommError(COMport, &temp, &comstat); //fill "comstat" btr = comstat.cbInQue; //and take quantity of the received bytes if(btr) { //something is received. read all bytes to the ring buffer, if something has been received (btr != 0) while (btr!=0) { uint32_t qbytesRd; if (btr > COMPORT_SETBUFFRXSIZE) qbytesRd = COMPORT_SETBUFFRXSIZE-2; else qbytesRd = btr; btr = btr - qbytesRd; ReadFile(COMport, tmpbuf, qbytesRd, &temp, &Overlapped); //reads bytes from port to my buffer //put to the main buffer for (uint32_t idx = 0; idx < qbytesRd; idx++) {// if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; //ring buffer RxBuff.data[RxBuff.wrpnt] = tmpbuf[idx]; RxBuff.wrpnt++; } } if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; //ring buffer test: for keep less than limitation during the called below RxDataProcessing() //the method is changed, just periodic polling in main task by a timer // Synchronize(RxDataProcessing); } } } } } CloseHandle(Overlapped.hEvent); //close it before the exit from the thread } //--------------------------------------------------------------------------- //============================================================================= //............................. Form1 parts ................................ //============================================================================= void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { if(reader)reader->Terminate(); //receive thread if(COMport)CloseHandle(COMport); //close the port } //--------------------------------------------------------------------------- //============================================================================= //........................... internal functions .............................. //============================================================================= //--------------------------------------------------------------------------- //open and init COM port void COMOpen() { AnsiString portname; //port name: "COM1", "COM2" etc. DCB dcb; //structure for common init of the port COMMTIMEOUTS timeouts; //the structure for timeouts settings portname = Config.ComPort.name; //"COM1", "COM2" etc. //open port, for async operation (the flag FILE_FLAG_OVERLAPPED must be set) COMport = CreateFile(portname.c_str(),GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); //where: // - portname is used as a filename, the converted to char by ".c_str()" function // - GENERIC_READ | GENERIC_WRITE - read and write access is allowed // - 0 - the port is not shared // - NULL - address of security descriptor (by default) // - OPEN_EXISTING -the port is opened as already existing // - FILE_FLAG_OVERLAPPED - for async operations must be set // - NULL - handle of file with attributes to copy if(COMport == INVALID_HANDLE_VALUE) {// port opening error AnsiString string = "Error: not possible to open port " + portname; Log_Message( LOG_ERROR, string); ShowMessage (string); return; } //port init dcb.DCBlength = sizeof(DCB); if(!GetCommState(COMport, &dcb)) {//error COMClose(); Log_Message( LOG_ERROR, "Error: not possible to read structure DCB from this port"); return; } //init of DCB structure dcb.BaudRate = Config.ComPort.baudRate; dcb.fBinary = TRUE; //Windows does not support nonbinary mode transfers, so this member must be TRUE dcb.fOutxCtsFlow = FALSE; //CTS dcb.fOutxDsrFlow = FALSE; //DSR dcb.fDtrControl = DTR_CONTROL_DISABLE; //DTR dcb.fDsrSensitivity = FALSE; // DSR sensitivity dcb.fNull = FALSE; //FALSE: enable bytes 0 receiving. If this member is TRUE, null bytes are discarded when received. dcb.fRtsControl = RTS_CONTROL_DISABLE; //RTS dcb.fAbortOnError = FALSE; // If this member is TRUE, the driver terminates all read and write operations with an error status if an error occurs. The driver will not accept any further communications operations until the application has acknowledged the error by calling the ClearCommError function. dcb.ByteSize = 8; dcb.Parity = 0; dcb.StopBits = 0; //load the structure to the port if(!SetCommState(COMport, &dcb)) {//error COMClose(); Log_Message( LOG_ERROR, "not possible to load DCB to this port"); return; } //timeouts setting // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa363190(v=vs.85).aspx //The maximum time allowed to elapse before the arrival of the next byte on the communications line, in milliseconds. //If the interval between the arrival of any two bytes exceeds this amount, the ReadFile operation is completed and any buffered data is returned. //A value of zero indicates that interval time-outs are not used. //A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and ReadTotalTimeoutMultiplier members, specifies that the read operation is to return immediately with the bytes that have already been received, even if no bytes have been received. timeouts.ReadIntervalTimeout = 0; //A value of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant members indicates that total time-outs are not used for read operations. //The multiplier used to calculate the total time-out period for read operations, in milliseconds. //For each read operation, this value is multiplied by the requested number of bytes to be read. timeouts.ReadTotalTimeoutMultiplier = 0; //A constant used to calculate the total time-out period for read operations, in milliseconds. //For each read operation, this value is added to the product of the ReadTotalTimeoutMultiplier member and the requested number of bytes. timeouts.ReadTotalTimeoutConstant = 0; //A value of zero for both the WriteTotalTimeoutMultiplier and WriteTotalTimeoutConstant members indicates that total time-outs are not used for write operations. //The multiplier used to calculate the total time-out period for write operations, in milliseconds. //For each write operation, this value is multiplied by the number of bytes to be written. timeouts.WriteTotalTimeoutMultiplier = 0; //A constant used to calculate the total time-out period for write operations, in milliseconds. //For each write operation, this value is added to the product of the WriteTotalTimeoutMultiplier member and the number of bytes to be written. timeouts.WriteTotalTimeoutConstant = 0; if(!SetCommTimeouts(COMport, &timeouts)) {//error COMClose(); Log_Message( LOG_ERROR, "Error during timeouts setting for this COM port"); return; } //see https://msdn.microsoft.com/en-us/library/windows/desktop/aa363439(v=vs.85).aspx // The recommended size of the device's internal input buffer, in bytes. // The recommended size of the device's internal output buffer, in bytes. SetupComm(COMport,COMPORT_SETBUFFRXSIZE,16384); PurgeComm(COMport, PURGE_RXCLEAR); //clear the input buffer reader = new ReadThread(false); //create and launch the receiving thread reader->FreeOnTerminate = true; ////the thread will be for automatically deleted after finishing } //--------------------------------------------------------------------------- //closing COM port void COMClose() { if(reader)reader->Terminate(); CloseHandle(COMport); COMport=0; //init the descriptor } //--------------------------------------------------------------------------- void __fastcall TForm1::FormCreate(TObject *Sender) { ReadIniFile (&Config); COMOpen(); Form1->Caption = Form1->Caption + " : " + Config.ComPort.name; Logging ( "Application starting on " + Config.ComPort.name + "..."); } //--------------------------------------------------------------------------- // every 50 ms void __fastcall TForm1::Timer1Timer(TObject *Sender) { RxDataProcessing(); } //--------------------------------------------------------------------------- //main processing of the received bytes // every 50 ms uint32_t OldWrPnt = RX_RINGBUFF_SIZE+1; // not valid value for initialization void RxDataProcessing(void) { AnsiString txt_rx; if (OldWrPnt == RxBuff.wrpnt) return; //no new bytes, do nothing OldWrPnt = RxBuff.wrpnt; //for next time //try to find the message in the received stream for new bytes, from RxBuff.rdpnt to RxBuff.wrpnt [skipped] }