COM порт под Виндой на Си.

Тема в разделе "Флудилка", создана пользователем NR55RU, 13 май 2013.

  1. NR55RU

    NR55RU Гик

    Я не первый день программирую но все это Web.
    В последнее время приступил к изучению Си (пока Си .. ++ чуть позже, книга хорошая по Си попалась).
    Столкнулся с проблемой, в книге не описанной но очень хочется чтобы программа могла общаться с ардуином, для этого нужен СОМ порт.
    В линуксе там все вроде как просто открываем фаил порта и все, нашел много примеров в инете.
    Но вот под Виндой ни один из найденных примеров не работает, и 90% примеров на С++.
    Может кто может подсказать рабочий пример на Си ?
    Винда 7-ка.

    P.s. Кстати именно из этой книги я узнал про Ардуино, там просто как один из мини проектов предлагается сделать на базе ардуина.
     
  2. Unixon

    Unixon Оракул Модератор

  3. NR55RU

    NR55RU Гик

    Спасибо, покурю сие дело.
     
  4. Megakoteyka

    Megakoteyka Оракул Модератор

  5. NR55RU

    NR55RU Гик

    Можете мне помочь.
    Я читал разбирался но я так и не смог выполнить простую задачу.
    С арждуина я каждые 5 секунд шлю на сериал просто 1.Я смог открыть ком порт я использовал функции считывания но я так и не смогу получить эту единицу.
    Если не затруднит мог бы кто то написать максимально короткий код на Си который позволит считать эту единицу с ком порта.
    Я пока побывал в синхронном режиме через бесконечный цикл, но условие о том что данные считаны так и не срабатывало.
    Использовал вот такой код взятый по одной из ссылок но чуток модифицированный.
    Код (C):

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

    void main()
    {
        HANDLE port = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL,
                                OPEN_EXISTING, 0, NULL);
        unsigned char dst[1024] = {0};
         
        unsigned long size = sizeof(dst);
        if(port!= INVALID_HANDLE_VALUE)
        {
                while(1)
                {
                    if(ReadFile(port,dst,size, &size,0))
                    {
                        printf("\nRead %d bytes",size);
                        break;
                    }
                }
        }
        else
        {
            puts("Error");
        }
    }
     
    Код на ардуине:
    Код (C):

    int ledPin = 13;
    void setup() {
      pinMode(ledPin, OUTPUT);
      Serial.begin(9600);
    }

    void loop() {
      delay(5000);
      digitalWrite(ledPin, HIGH);
      Serial.println(1);
      delay(500);
      digitalWrite(ledPin, LOW);
    }
     
     
  6. Mitrandir

    Mitrandir Гуру

    меня вот это смущает:
    ReadFile(port,dst,size, &size,0))

    синтаксис:
    BOOL ReadFile(
    HANDLE hFile, // дескриптор файла
    LPVOID lpBuffer, // буфер данных
    DWORD nNumberOfBytesToRead, // число байтов для чтения
    LPDWORD lpNumberOfBytesRead, // число прочитанных байтов
    LPOVERLAPPED lpOverlapped // асинхронный буфер
    );

    что происходит:
    1 шаг цикла while
    size = 1024
    ардуина еще не успела кинуть ничего в порт
    функция ReadFile пишет в size 0 возвращает 0

    2 шаг цикла:

    size = 0
    функция ReadFile читает 0 байт и т.д


    попробуй так:
    Код (C):

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

    void main()
    {
        HANDLE port = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL,
                                OPEN_EXISTING, 0, NULL);
        unsigned char dst[1024] = {0};
         
        unsigned long size = sizeof(dst);
        unsigned long recv;
        if(port!= INVALID_HANDLE_VALUE)
        {
                while(1)
                {
                    if(ReadFile(port,dst,size, &recv,0))
                    {
                        printf("\nRead %d bytes",recv);
                        break;
                    }
                }
        }
        else
        {
            puts("Error");
        }
    }
     
  7. NR55RU

    NR55RU Гик

    Попробовал, и в том и другом случае одно.
    Пустота выполнения бесконечного цикла. Никаких выводов ни ошибки ничего, ждал пару минут ради интереса.
    Я даже проверил что программа открыла СОМ3 ибо монитор порта предоставленный со средой разработки Ардуино выдает ошибку о невозможности открытия порт так как он занят.
     
  8. Mitrandir

    Mitrandir Гуру

    хмм попробуй в ручную поставить настройки ком порта такие как скорость. Под виндой не делал, только под линуксом, там при открытии задавал скорость.:
    Код (C):
                    LPDCB m_dcb;
                    m_dcb.BaudRate = 9600;
            m_dcb.ByteSize = 8;
            m_dcb.Parity = NOPARITY;
            m_dcb.StopBits = ONESTOPBIT;
            m_dcb.fAbortOnError = TRUE;

            m_bPortReady = SetCommState(port, &m_dcb);
     
  9. NR55RU

    NR55RU Гик

    Данный кусок я просто так вставить не смог вылезло много ошибок, видимо не хватает какого то заголовочного файла.
    Но мне просто чисто случайно удалось добиться кой какого результата.
    Выяснил что моя програма не а цикле "зависает" а она зависает ожидая ответа от функции ReadFile даже без цикла.
    и случайно выяснилось следующие.
    Если подключить ардуин и запустить мою программу она виснет в ожидании но если подключить ардуин запустить монитор порта что идет в комплекте с иде ардуина потом его закрыть и запустить программу она тут же шустро возвращает сообщение о прочитанных нуле байт.
    В итоге в цикле мне удалось получить долгожданное сообщение о том что считывается разное количество байт от 1 до 3, почему так мне вполне понятно.
    В консоль пока данные высести не смог полученные но это я буду читать искать разбираться докопаюсь до истины.
    Меня интересует вопрос иной, почему функция ReadFile начинает работать только если после подключения ардуина запустить функцию мониторинга порта...

    Такое чувство будто данный монитор при запуске создает какие то настройки порта в системе и тогда ReadFile спокойно отрабатывает.
     
  10. Mitrandir

    Mitrandir Гуру

    Вот это у меня компилится и работает
    Код (C):
    // ComSample.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    #include <windows.h>
    #include <tchar.h>
    #include <stdio.h>

    void PrintCommState(DCB dcb)
    {
        //  Print some of the DCB structure values
        _tprintf( TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
                  dcb.BaudRate,
                  dcb.ByteSize,
                  dcb.Parity,
                  dcb.StopBits );
    }


    int _tmain( int argc, TCHAR *argv[] )
    {
       DCB dcb;
       HANDLE hCom;
       BOOL fSuccess;
       TCHAR *pcCommPort = TEXT("COM4"); //  Most systems have a COM1 port

       //  Open a handle to the specified com port.
       hCom = CreateFile( pcCommPort,
                          GENERIC_READ | GENERIC_WRITE,
                          0,      //  must be opened with exclusive-access
                          NULL,   //  default security attributes
                          OPEN_EXISTING, //  must use OPEN_EXISTING
                          0,      //  not overlapped I/O
                          NULL ); //  hTemplate must be NULL for comm devices

       if (hCom == INVALID_HANDLE_VALUE)
       {
           //  Handle the error.
           printf ("CreateFile failed with error %d.\n", GetLastError());
           return (1);
       }

       //  Initialize the DCB structure.
       SecureZeroMemory(&dcb, sizeof(DCB));
       dcb.DCBlength = sizeof(DCB);

       //  Build on the current configuration by first retrieving all current
       //  settings.
       fSuccess = GetCommState(hCom, &dcb);

       if (!fSuccess)
       {
          //  Handle the error.
          printf ("GetCommState failed with error %d.\n", GetLastError());
          return (2);
       }

       PrintCommState(dcb);    //  Output to console

       //  Fill in some DCB values and set the com state:
       //  57,600 bps, 8 data bits, no parity, and 1 stop bit.
       dcb.BaudRate = CBR_9600;  //  baud rate
       dcb.ByteSize = 8;             //  data size, xmit and rcv
       dcb.Parity   = NOPARITY;   //  parity bit
       dcb.StopBits = ONESTOPBIT;   //  stop bit

       fSuccess = SetCommState(hCom, &dcb);

       if (!fSuccess)
       {
          //  Handle the error.
          printf ("SetCommState failed with error %d.\n", GetLastError());
          return (3);
       }

       //  Get the comm config again.
       fSuccess = GetCommState(hCom, &dcb);

       if (!fSuccess)
       {
          //  Handle the error.
          printf ("GetCommState failed with error %d.\n", GetLastError());
          return (2);
       }

       PrintCommState(dcb);    //  Output to console

       _tprintf (TEXT("Serial port %s successfully reconfigured.\n"), pcCommPort);
       char dst[1024];
       int size = 1024;
       DWORD recv;
          while(1)
                {
                    if(ReadFile(hCom,dst,size, &recv,0))
                    {
                        printf("\nRead %d bytes:%s\n",recv,dst);
                       
                    }
                }
       return (0);



    }

     
     
    Megakoteyka нравится это.
  11. NR55RU

    NR55RU Гик

    Спасиб. Покопаюсь во всем этом на выходных.
     
  12. Mitrandir

    Mitrandir Гуру

    только там у меня от ардуины какая-то хрень иногда проскакивала
    скетч был такой:
    Код (C):

    void setup()
    {
      Serial.begin(9600);
    }
    void loop()
    {
      Serial.println("hello");
    }

     
    но в окне на компе кроме hello
    изредка какие-то иероглифы выскакивали, не было толком времени отлавливать глюки.
     
  13. NR55RU

    NR55RU Гик

    Ну вот это одна из причин почему я хочу максимально компактно без кучи лишнего кода разобраться в считывании данных с сериала ибо в таких случаях никогда не знаешь где беда в тебе или в какой то из кучи сложных функций :)
    Кодом что был приведен выше изначально с твоей поправкой в плане переменной получается считывать, но есть описанные проблемы, думаю они решаются вопросом настройки порта.
    Вот помучаюсь в этим делом на досуге покурю структуру настройки порта.
    Просто я не хочу брать готовую библиотеку, я хочу разобраться как это работает максимально"близко" к порту :)
    Ведь если понимаешь процесс то легко и решаешь проблемы.
     
  14. Mitrandir

    Mitrandir Гуру

    Да у меня в коде библиотек специализированных не используется... просто добавил в твой код заполнение структуры с настройками порта и запихнул их в порт с помощью SetCommState
     
  15. NR55RU

    NR55RU Гик

    Сегодня мне таки удалось получать нормально полноценные строки с ардуина максимально простым способом.
    код на СИ
    Код (C):

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

    void main()
    {
        HANDLE port = CreateFile("\\\\.\\COM3", GENERIC_READ | GENERIC_WRITE, 0, NULL,
                                OPEN_EXISTING, 0, NULL);
        unsigned char dst[1024] = {0};
         
        unsigned long size = sizeof(dst);
        unsigned long recv;
       
       
            COMMTIMEOUTS CommTimeOuts;
            CommTimeOuts.ReadIntervalTimeout = 5;
            CommTimeOuts.ReadTotalTimeoutMultiplier = 0;
            CommTimeOuts.ReadTotalTimeoutConstant = 0;
            CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
            CommTimeOuts.WriteTotalTimeoutConstant = 0;
       
        if(SetCommTimeouts(port, &CommTimeOuts) == 0)
        {
             puts("Error of SetCommTimeouts");
        }
       
        if(port!= INVALID_HANDLE_VALUE)
        {
                int i;
                for (i=1; i< 10; i++)
                {
                    if(ReadFile(port,dst,size, &recv,0))
                    {
                        if(recv > 0) {
                            printf("Read %d bytes\n",recv);
                            printf("%s\n", dst);
                        }
                    }
                    printf("%i\r", i);
                }
        }      
        else
        {
            puts("Error of Handler - INVALID_HANDLE_VALUE");
        }
    }
     
    Код с ардуина
    Код (C):

    int ledPin = 13;
    void setup() {
      pinMode(ledPin, OUTPUT);
      Serial.begin(9600);
    }

    void loop() {
      delay(1000);
      digitalWrite(ledPin, HIGH);
      Serial.print("Hello world");
      delay(500);
      digitalWrite(ledPin, LOW);
    }
     
    10 циклов он вполне комфортно принимает фразу с ардуина Hello Word и выводит ее на экран, целиком без проблем..
    Все что я сделал по сути это установил таймаут в 5мс между принятием 2-х символов, если он больше указного то ReadFile тут же прекращает работу и возвращает все что считал.
    До этого он не успевал считать всю строчку и возвращал лишь то что успел считать за итерацию и все.
    Теперь по сути требуется просто детально разобраться с более большой и сложной структурой настройки порта DCB для нормальный настройки :)
    А потмо уже переходить в ахинхронному режиму.

    Зачем я все это тут пишу, все просто, я нигде не нашел нормального описания работы с КОМ портом в винде для совсем новичка, приходится с кучи ресурсов собирать обрывки и додумывать и пробовать.
    Возможно кто то в будущем будет на моем же месте и это может помочь разобраться.

    P.s. пока все это до сих пор адекватно работает только если перед запуском этого кода запустить монитор порта что идет с ардуин иде который видимо настраивает порт. Для самостоятельной работы кода требуется судя по всему как раз та структура DCB.
     
  16. Megakoteyka

    Megakoteyka Оракул Модератор

    Это решается использованием программного буфера, в котором данные копятся и по накоплении нужной порции (например, той же строки) передаются на обработку, а таймауты нужно считать исходя из скорости коннекта.
     
  17. NR55RU

    NR55RU Гик

    А вот эта порция как настраивается через функцию ReadFile или через структуру DCB ?
     
  18. Mitrandir

    Mitrandir Гуру

    Код (C):
    typedef struct _DCB {
        DWORD DCBlength;      /* sizeof(DCB)                     */
        DWORD BaudRate;    /* Baudrate at which running    */
        DWORD fBinary: 1;    /* Binary Mode (skip EOF check)    */
        DWORD fParity: 1;    /* Enable parity checking        */
        DWORD fOutxCtsFlow:1; /* CTS handshaking on output     */
        DWORD fOutxDsrFlow:1; /* DSR handshaking on output     */
        DWORD fDtrControl:2;  /* DTR Flow control               */
        DWORD fDsrSensitivity:1; /* DSR Sensitivity           */
        DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */
        DWORD fOutX: 1;    /* Enable output X-ON/X-OFF      */
        DWORD fInX: 1;      /* Enable input X-ON/X-OFF       */
        DWORD fErrorChar: 1;  /* Enable Err Replacement       */
        DWORD fNull: 1;    /* Enable Null stripping        */
        DWORD fRtsControl:2;  /* Rts Flow control               */
        DWORD fAbortOnError:1; /* Abort all reads and writes on Error */
        DWORD fDummy2:17;    /* Reserved                        */
        WORD wReserved;    /* Not currently used              */
        WORD XonLim;          /* Transmit X-ON threshold         */
        WORD XoffLim;        /* Transmit X-OFF threshold        */
        BYTE ByteSize;      /* Number of bits/byte, 4-8     */
        BYTE Parity;          /* 0-4=None,Odd,Even,Mark,Space   */
        BYTE StopBits;      /* 0,1,2 = 1, 1.5, 2               */
        char XonChar;        /* Tx and Rx X-ON character        */
        char XoffChar;      /* Tx and Rx X-OFF character       */
        char ErrorChar;    /* Error replacement char          */
        char EofChar;        /* End of Input character        */
        char EvtChar;        /* Received Event character        */
        WORD wReserved1;      /* Fill for now.                 */
    } DCB, *LPDCB;
     
    вот что задает структура DCB
    Megakoteyka говорил немного о другом

    Например, ты на ардуине сказал Serial.println("Hello world");
    Ардуино стал посимвольно пихать в сом порт со скоростью допустим 9600 бод
    с++ начинает делать read.
    если еще ничего не пришло, то read возвращает 0
    если ардуино послал только H и е read их схватит и передаст далее на вывод
    поэтому, надо в цикле делать read и складывать в массив-буффер, когда он достаточно набьется, то передавать дальше массив на обработку

    то что у тебя до запуска IDE не работает, потому что IDE заполняет DCB и настраивает порт за тебя...
     
    Megakoteyka нравится это.
  19. NR55RU

    NR55RU Гик

    Угу, мне идея про складывать в отдельную переменную первое что пришло в голову но я решил покурить тему получше что бы получше разбираться в этом, так что сейчас как раз и пытаюсь разобраться во всех настройках данной структуры.

    Просто думал что есть какой то встроенный в функцию буфер который можно "настроить" :)
     
  20. Nickstalker

    Nickstalker Нерд

    Открывали ли Вы порт в структуре DCB? Ведь он изначально закрыт, а IDE его открывает.
    P.S.: В C# есть класс System.IO.Ports.SerialPort, для его функционирования ОБЯЗАТЕЛЬНО надо вызвать метод SerialPort.Open() возможно и здесь также?