Tema: Re: UART programavimas
Autorius: saimhe
Data: 2010-05-26 16:01:51
> kaip ant C# parasyt panasia programele kaip hyper terminal..
> pati problema aprasyta cia: http://www.xoom.lt/2010/05/20/tipo-skopas-voltage-logger/

   Kodėl kodo neduodi, kad iškart matytųsi, kas negerai?

   Nuskaitinėti duomenis be buferizavimo, vos tik jiems atėjus, nėra prasmės.
Čia kompas neužsiima raketos žemė-oras vairavimu, kad reikėtų išjungti vidinį
UART buferį (default'as windowsuose 14 baitų), pačiam aptarnauti INT 0C/0B ir
apskritai naudoti kokią nors realtime operacinę. O kur dar USB/RS232 keitikliai,
juose vėlinimai išvis neprognozuojami. Bet koks PC-based oscilografas ar
panaši duomenų surinkimo sistema neišvengia buferizavimo. Pakanka, kad buferio
pildymas truktų kokią pusę sekundės ar panašiai, nes toliau vėlinimas darosi
pastebimas ir nervina.
   Be to, skaityti iš porto turi atskiras thread'as. Kai jam skirtas buferis
prisipildo, tada duoda signalą skaičiavimo bei atvaizdavimo thread'ui ir ima
pildyti antrą buferį. Paskui atitinkamai sukeičia.

   Kartą dariau GPS logerį savo mobiliakui. Kuo primityvesnį, kad naudotų
mažiau resursų ir nepamestų duomenų net vykstant pokalbiui. Taigi jokių C#.
Štai priimantis thread'as:

---------------------------------------------------------------------------
    while (!stopASAP)
    {
        /* open the port anew and set it up */
        if (hComm != INVALID_HANDLE_VALUE)
            CloseHandle(hComm);
        hComm = CreateFile(tszComm, GENERIC_READ | GENERIC_WRITE, 0, NULL,
            OPEN_EXISTING, 0, NULL);
        if ((hComm != NULL) && (hComm != INVALID_HANDLE_VALUE))
        {
            DCB dcb;
            COMMTIMEOUTS ct;

            SetupComm(hComm, BUFSIZE / 4, BUFSIZE * 2);
            PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
            if (GetCommState(hComm, &dcb))
            {
                dcb.BaudRate = CBR_38400;
                dcb.ByteSize = 8;
                dcb.Parity = NOPARITY;
                dcb.StopBits = ONESTOPBIT;

                if (!SetCommState(hComm, &dcb))
                    if (!dwReadErrorCode || !fReadErrorHold)
                    {
                        dwReadErrorCode = GetLastError();
                        tszReadErrorLoc = _T("s");
                        fReadErrorHold = TRUE;
                    }
            }
            else
                if (!dwReadErrorCode || !fReadErrorHold)
                {
                    dwReadErrorCode = GetLastError();
                    tszReadErrorLoc = _T("g");
                        /* fReadErrorHold not set: unavailability of this call is not
                           critical, especially for virtual ports like "External GPS"
                           that constantly fail with ERROR_NOT_READY
                         */
                }

            /* timeouts will minimize amount of incomplete reads below */
            ct.ReadTotalTimeoutConstant = ct.ReadTotalTimeoutMultiplier = 0;
            ct.WriteTotalTimeoutConstant = ct.WriteTotalTimeoutMultiplier = 0;
            ct.ReadIntervalTimeout = 1000;  /* we expect some data each second */
            SetCommTimeouts(hComm, &ct);

            /* setup finished, we can read now */
            do
            {
                LPBYTE pbReadBuf;
                DWORD nReadAt;
                DWORD nToRead;
                DWORD nReadCount;

                nReadAt = 0;
                if (!fBufIdx)
                    pbReadBuf = abBuffer1;
                else
                    pbReadBuf = abBuffer2;

                /* ensure that the buffer is full */
            fill:
                nToRead = BUFSIZE - nReadAt;
                if (!ReadFile(hComm, pbReadBuf + nReadAt, nToRead, &nReadCount, NULL))
                    /* a blocking call if no Bluetooth connection yet; however
                       after a connection is established for the first time, and
                       no data comes for the given time interval, ReadFile returns
                       with no bytes read after that interval. Handy to detect a
                       lost connection etc.
                     */
                {
                    if (!dwReadErrorCode || !fReadErrorHold || (tszReadErrorLoc[0] == _T('g')))
                    {
                        dwReadErrorCode = GetLastError();
                        tszReadErrorLoc = _T("r");
                        fReadErrorHold = TRUE;
                    }
                    break;
                }
                nReadAt += nReadCount;
                nBytesReadTotal += nReadCount;
                if ((nReadCount != nToRead) && !stopASAP)
                {
                    ClearCommError(hComm, &nReadCount, NULL);
                    goto fill;
                }

                /* reset state but only if the previous code was displayed */
                if (!fReadErrorHold)
                    dwReadErrorCode = 0;

                pbWriteBuf = pbReadBuf;
                nAvailSize = BUFSIZE;
                fBufIdx = !fBufIdx;
                PulseEvent(heDataAvail);
            } while (!stopASAP);
        }
        else
            if (!dwReadErrorCode || !fReadErrorHold)
            {
                tszReadErrorLoc = _T("c");
                dwReadErrorCode = GetLastError();
                fReadErrorHold = TRUE;
            }

        /* comm port may appear again after a while */
        Sleep(1000);
    }
---------------------------------------------------------------------------

   BUFSIZE paskutiniu metu buvo 1024 ir gavosi pats tas.
   "goto fill": bus atiduotas tik pilnas buferis.
   Antras thread'as, kai nebeturi duomenų apdorojimui, su WaitForSingleObject
laukia PulseEvent šitame thread'e.
   Su "overlapped" tipo operacijomis galbūt pavyktų viską padaryti ir
vieninteliame thread'e. Bet Windows Mobile jų nepalaiko.

   Naudoti kaip Hyperterminal -- esą paspausi mygtuką tame ATtiny ir ekrane
iškart atsiras simbolis -- tikrai nepavyks, paskirtis ne tokia.

-- 
  saimhe