Передача команд на Arduino через COM-порт. Visual Studio 2010 c++.

Тема в разделе "Флудилка", создана пользователем GarlenX, 15 сен 2018.

  1. GarlenX

    GarlenX Нерд

    Написал консольное приложение на Visual Studio c++ и возникла проблема в приеме данных Arduino Uno.
    Данные, вроде как, отправляется, но ардуино не реагирует на них... если отправлять команды из монитора порта ардуино, то все работает и загораются диоды rx и xi, когда я отправляю команды из приложения, то загорается только диод xi... так же происходит, когда я меняю настройку NL (добавления новой строки) на любую другую в мониторинге порта... Очень хочу разобраться в проблеме, но никак не могу найти решение... очень прошу помощи.

    Код (C++):
    #include "stdafx.h"
    #include <iostream>
    #include <cstdlib>
    #include <Windows.h>
    #include <string>
    #include "conio.h"
    #include "io.h"
    #include <locale.h>
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    #pragma hdrstop
    #include <io.h>         // Для работы с файлами
    #include <fcntl.h>      // Для работы с файлами
    #include <sys\stat.h>   // Для работы с файлами

    using namespace std;


    //=============================================================================
    //..................... Объявления глобальных переменных ......................
    //=============================================================================
    HANDLE hCOM;            // Дескриптор порта
    int handle;             //дескриптор для работы с файлом с помощью библиотеки <io.h>
    bool fl=0;                //флаг, указывающий на успешность операций записи (1 - успешно, 0 - не успешно)
    OVERLAPPED overlapped;        //будем использовать для операций чтения (см. поток ReadThread)
    OVERLAPPED overlappedwr;           //будем использовать для операций записи
    unsigned long counter;    //счётчик принятых байтов, обнуляется при каждом открытии порта

    #define BUFSIZE 255     // Ёмкость буфера приемо-передачи
    unsigned char bufwrHEX[BUFSIZE] = {1}; // передающий буфер для обмена в HEX
    unsigned char bufrdHEX[sizeof(bufwrHEX)]; // приёмный буфер для обмена в HEX

    bool stopfl = false; // флаг остановки цикла передачи данных
    bool Read_done = true; // флаг окончания чтения информации от ведомого
    bool slave_mode = false; // флаг работы программы в режиме slave
    HANDLE info_read; // сигнальное событие, показывающее прочитана ли информация из порта
    unsigned long Timer_cycles = 0; // переменная количества циклов таймера для счета времени по таймеру
    unsigned long Bytes_received = 0; // число принятых байт за один пуск
    unsigned long Bytes_written = 0;// число записанных байт в порт за один пуск
    bool show_info = false; // флаг вывода информации на экран

    //=============================================================================
    //.............................. Объявления функций ...........................
    //=============================================================================

    void COM_Open(void);        // Открыть порт
    void COM_Setting(void);     // Инициализировать порт
    void COM_Close(void);       // Закрыть порт
    void ReadPrinting(void);    // Создание потока
    void command(void);

    //=============================================================================
    //.............................. Объявления потоков ...........................
    //=============================================================================

    HANDLE reader;    // Дескриптор потока чтения из порта
    HANDLE writer;    // Дескриптор потока записи в порт
    HANDLE MainThr;




    //=============================================================================
    //............................... Тело программы ..............................
    //=============================================================================

    int main()
    {
        setlocale(LC_ALL,"Rus");      // Подключения языка
        char prov;
        int a;    
        string str;
       
        COM_Open();                   // Функция открытия порта
        COM_Setting();                // Функция инициализации порта
        DWORD WINAPI WriteThread(LPVOID);  
        DWORD WINAPI ReadThread(LPVOID);

        do
        {
            printf("===============================\n");
            cout<<"Введите 'n' для завершения ввода\n";
            cin>> prov;
            printf("===============================\n");
            cout<<"Введите команду!\n";
            cout<<"===============================\n"<<endl;
            cin>>str;
            cout<<"==========значения в буффере до очистки программного передающего буффера:==========\n"<<endl;
            cout<<bufwrHEX<<endl;
            cout<<bufrdHEX<<endl;
            cout<<"===============================\n"<<endl;
            memset(bufwrHEX,0,BUFSIZE);                    // Очистить программный передающий буфер, чтобы данные не накладывались друг на друга
            cout<<"==========значения в буффере после очистки программного передающего буффера:==========\n"<<endl;
            cout<<bufwrHEX<<endl;
            cout<<bufrdHEX<<endl;
            PurgeComm(hCOM, PURGE_TXCLEAR);             // Очистить передающий буфер порта
            cout<<"==========значения в буффере после очистки передающего буффера порта:==========\n"<<endl;
            cout<<bufwrHEX<<endl;
            cout<<bufrdHEX<<endl;
            strcpy((char*)bufwrHEX, str.c_str());       // Занести в программный передающий буфер строку из переменной str
           
            cout<<"===============================\n"<<endl;
            cout<<"==========значения в буффере после передачи в него команды:==========\n"<<endl;
            cout<<bufwrHEX<<endl;
            cout<<bufrdHEX<<endl;
            ResumeThread(writer);                       // Активировать поток записи данных в порт
            cout<<"==========значения в буффере после отправки команды:==========\n"<<endl;
            cout<<bufwrHEX<<endl;  
            cout<<bufrdHEX<<endl;
        }
        while (prov != 'n');

        printf("===============================\n");
        printf("Для выхода введите 100!\n");
        printf("===============================\n");
        printf("Поле ввода команд:   ");
        cin >> a;
        if(a=100);
        printf("===============================\n");
        {
        COM_Close();
        }  
    }

     
     
  2. GarlenX

    GarlenX Нерд

    Код (C++):

    //-----------------------------------------------------------------------------
    //......................... поток ReadThead и WriteThead.......................
    //-----------------------------------------------------------------------------

    DWORD WINAPI ReadThread(LPVOID)
    {
    COMSTAT comstat;        //структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
    DWORD btr, temp, mask, signal;    //переменная temp используется в качестве заглушки
    overlapped.hEvent = CreateEvent(NULL, true, true, NULL);    //создать сигнальный объект-событие для асинхронных операций
    SetCommMask(hCOM, EV_RXCHAR);                            //установить маску на срабатывание по событию приёма байта в порт
    //SetCommMask(hCOM, EV_TXEMPTY);
    while(1)                        //пока поток не будет прерван, выполняем цикл
      {
       WaitCommEvent(hCOM, &mask, &overlapped);                //ожидать события приёма байта (это и есть перекрываемая операция)
       signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
       if(signal == WAIT_OBJECT_0)                        //если событие прихода байта произошло
        {
         if(GetOverlappedResult(hCOM, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
          if((mask & EV_RXCHAR)!=0)                    //если произошло именно событие прихода байта
           {
            ClearCommError(hCOM, &temp, &comstat);        //нужно заполнить структуру COMSTAT
            btr = comstat.cbInQue;                           //и получить из неё количество принятых байтов
            if(btr)                                  //если действительно есть байты для чтения
            {
             ReadFile(hCOM, bufrdHEX, btr, &temp, &overlapped);     //прочитать байты из порта в буфер программы
             counter+=btr;        // увеличиваем счётчик байт за все время работы программы
             Bytes_received+=btr; // счетчик принятых байт за один пуск        
             SetEvent(info_read); // устанавливаем событие прочтения информации в сигнальное положение
             if (slave_mode)
             {
             // транслируем приянтое отправителю если в режиме slave
             memcpy(bufwrHEX,bufrdHEX,sizeof(bufrdHEX));
             ResumeThread(writer);               //активировать поток записи данных в порт
             } // if (slave_mode)
             memset(bufrdHEX, 0, BUFSIZE);         //очистить буфер (чтобы данные не накладывались друг на друга)
            } // if(btr)
           } // if((mask & EV_RXCHAR)!=0)
        } // if(signal == WAIT_OBJECT_0)
      } // while(1)
    }

    /*DWORD WINAPI WriteThread(LPVOID)
    {
    DWORD temp, signal;    //temp - переменная-заглушка
    overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL);      //создать событие
    while(1)
      {
      PurgeComm(hCOM, PURGE_TXCLEAR);             //очистить передающий буфер порта
      WriteFile(hCOM, bufwrHEX, sizeof(bufwrHEX), &temp, &overlappedwr);  //записать байты в порт (перекрываемая операция!)
      signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);      //приостановить поток, пока не завершится перекрываемая операция WriteFile
       if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(hCOM, &overlappedwr, &temp, true)))    //если операция завершилась успешно
         {
         Bytes_written += sizeof(bufwrHEX);
       // если единичный запуск, то выведем информацию на форму
       if (show_info)
       {
       Bytes_written = 0;
       }
       SuspendThread(writer);
      }
    }
    }*/





    DWORD WINAPI WriteThread(LPVOID)
    {
    DWORD temp, signal;    //temp - переменная-заглушка
    overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL);         //создать событие
    while(1)
      {
          WriteFile(hCOM, bufwrHEX, strlen((char*)bufwrHEX), &temp, &overlappedwr);  //записать байты в порт (перекрываемая операция!
          signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);      //приостановить поток, пока не завершится перекрываемая операция WriteFile
          if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(hCOM, &overlappedwr, &temp, true)))    //если операция завершилась успешно
           {
               Bytes_written += sizeof(bufwrHEX);
               printf("\nПередача прошла успешно\n");                     //вывести сообщение об этом в строке состояния
               }
               else
               {
                   printf("Ошибка передачи\n");                           //иначе вывести в строке состояния сообщение об ошибке
                }    
               
        if (show_info)
         {
       Bytes_written = 0;
         }
       SuspendThread(writer);
     
      }
    }


     
     
  3. GarlenX

    GarlenX Нерд

    Код (C++):
    //=============================================================================
    //............................... Описание функций ............................
    //=============================================================================

    void COM_Open()
    {      
        DCB dcb;
        COMMTIMEOUTS timeouts;
        hCOM = CreateFile(TEXT("COM3"),GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_FLAG_OVERLAPPED, NULL);

     
        if (hCOM == INVALID_HANDLE_VALUE)
    {
        printf("Ошибка открытия COM-порта (error %d)\n", GetLastError());
        printf("===============================\n");
       
    }
        if (hCOM != INVALID_HANDLE_VALUE)
        {
            printf("COM3 успешно открыт!\n");
            printf("===============================\n");
        }
        else
            {
             printf("COM-порт не доступен\n");
             printf("===============================\n");
             }
        getch ();
        return;
    }

    void COM_Setting()
    {
     
        COMMTIMEOUTS CommTimeOuts;
        DCB dcbSerialParams = {0};
        SetupComm(hCOM, 2000, 2000);
        GetCommState(hCOM, &dcbSerialParams);
    dcbSerialParams.DCBlength=sizeof(dcbSerialParams);  // В первое поле структуры DCB необходимо занести её длину, она будет использоваться функциями настройки порта для контроля корректности структуры

    // Считать структуру DCB из порта
    if (!GetCommState(hCOM, &dcbSerialParams))          // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
        {
        printf("Не удалось считать параметры COM-порта\n");
        COM_Close();
        return;
        }
        else
            {
             printf("Параметры COM-порта считаны:\n");
             printf("===============================\n");
             _tprintf( TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
                  dcbSerialParams.BaudRate,
                  dcbSerialParams.ByteSize,
                  dcbSerialParams.Parity,
                  dcbSerialParams.StopBits );
             printf("\n===============================\n");

             }

    //__________________________________________________________________________________________
    //___________________________________Параметры COM-порта____________________________________
    //__________________________________________________________________________________________

    dcbSerialParams.BaudRate=CBR_57600;
    dcbSerialParams.ByteSize=8;                                        //задаём 8 бит в байте
    dcbSerialParams.StopBits=ONESTOPBIT;                               //задаём один стоп-бит
    dcbSerialParams.Parity=NOPARITY;                                   //отключаем проверку чётности
    dcbSerialParams.fBinary = TRUE;                                    //включаем двоичный режим обмена
    dcbSerialParams.fOutxCtsFlow = FALSE;                              //выключаем режим слежения за сигналом CTS
    dcbSerialParams.fOutxDsrFlow = FALSE;                              //выключаем режим слежения за сигналом DSR
    dcbSerialParams.fDtrControl = DTR_CONTROL_DISABLE;                 //отключаем использование линии DTR
    dcbSerialParams.fDsrSensitivity = FALSE;                           //отключаем восприимчивость драйвера к состоянию линии DSR
    dcbSerialParams.fNull = FALSE;                                     //разрешить приём нулевых байтов
    dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;                 //отключаем использование линии RTS
    dcbSerialParams.fAbortOnError = FALSE;                             //отключаем остановку всех операций чтения/записи при ошибке

    if(!SetCommState(hCOM, &dcbSerialParams))               //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
    {
        printf("Ошибка в установке параметров COM-порта\n");
        COM_Close();  
    }
        else
          {
           printf("Параметры COM порта успешно загружены:\n");
           printf("===============================\n");
             _tprintf( TEXT("\nBaudRate = %d, ByteSize = %d, Parity = %d, StopBits = %d\n"),
                  dcbSerialParams.BaudRate,
                  dcbSerialParams.ByteSize,
                  dcbSerialParams.Parity,
                  dcbSerialParams.StopBits );
             printf("\n===============================\n");
          }

    //__________________________________________________________________________________________
    //___________________________________Параметры таймаутов____________________________________
    //__________________________________________________________________________________________

    CommTimeOuts.ReadIntervalTimeout = 0;                   //таймаут между двумя символами
    CommTimeOuts.ReadTotalTimeoutMultiplier = 0;          //общий таймаут операции чтения
    CommTimeOuts.ReadTotalTimeoutConstant = 0;           //константа для общего таймаута операции чтения
    CommTimeOuts.WriteTotalTimeoutMultiplier = 0;        //общий таймаут операции записи
    CommTimeOuts.WriteTotalTimeoutConstant = 0;          //константа для общего таймаута операции записи

    //записать структуру таймаутов в порт
    if(!SetCommTimeouts(hCOM, &CommTimeOuts))              //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
      {
       COM_Close();
       printf("Не удалось установить параметры timeouts\n");
       printf("===============================\n");
       return;
      }
      else
          {
           printf("Параметры timeouts установлены\n");
           printf("===============================\n");
          }

    //__________________________________________________________________________________________
    //_____________________________Настройка записи в файл______________________________________
    //__________________________________________________________________________________________

    SetupComm(hCOM,2000,2000);          //установить размеры очередей приёма и передачи
    handle = open("test.txt", O_CREAT | O_APPEND | O_BINARY | O_WRONLY, S_IREAD | S_IWRITE);

    if(handle == - 1)        //если произошла ошибка открытия файла
      {
        printf("Не удалось установить параметры timeouts\n");
        printf("Ошибка открытия файла\n");
      }
    else
       {
         printf("Файл открыт успешно\n");
         printf("===============================\n");
        }

    PurgeComm(hCOM, PURGE_RXCLEAR);    //очистить принимающий буфер порта

    reader = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);            //создаём поток чтения, который сразу начнёт выполняться (предпоследний параметр = 0)
    writer = CreateThread(NULL, 0, WriteThread, NULL, CREATE_SUSPENDED, NULL);    //создаём поток записи в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED)

    getch ();
    return;
    }

    void COM_Close()
    {
    if(writer)                                 //если поток записи работает, завершить его; проверка if(writer) обязательна, иначе возникают ошибки
        {
         TerminateThread(writer,0);
         CloseHandle(overlappedwr.hEvent);     //нужно закрыть объект-событие
         CloseHandle(writer);
         printf("Поток записи завершен!\n");
        }
    if(reader)                                 //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки
        {
         TerminateThread(reader,0);
         CloseHandle(overlapped.hEvent);    //нужно закрыть объект-событие
         CloseHandle(reader);
         printf("Поток чтения завершен!\n");
        }
    if(hCOM != INVALID_HANDLE_VALUE)
    {
    CloseHandle(hCOM);                     //закрыть порт
    hCOM=0;                                //обнулить переменную для дескриптора порта
    close(handle);                            //закрыть файл, в который велась запись принимаемых данных
    handle=0;                                //обнулить переменную для дескриптора файла
    printf("COM-порт закрыт!\n");
    }

    getch ();
    return;
    }

    //=============================================================================
    //............................... Конец программы .............................
    //=============================================================================
     
  4. Daniil

    Daniil Гуру

    Попробуйте при отправке на ардуино выводить на экран коды символов того, что отправляете и сверить с тем, что должно быть (см. ascii-таблицу).
    Светодиод xi это tx?
    ещё можете взять 2 usb-uart кабеля, соединить и воткнуть в комп и проверить, что отправляет ваша программа другой программой).
    Дебаг программ на пк должен быть проще, т.к. вы в любой момент можете вывести переменные на экран или в режиме отладки смотреть их значения...
     
  5. GarlenX

    GarlenX Нерд

    Да, xi это tx...
    Я, к примеру, отправляю 20 и в поток записывается 20... и отправляется 20...но вот обратно я ничего не
    получаю...по крайней мере, буфер чтения ничего не выводит, а должен получать в ответ ту же команду...
    Но опять же, так же происходит и в мониторе порта, если я не выбираю параметр NL (новая строка)...
     
  6. Daniil

    Daniil Гуру

    Новая строка это символ "\n" (номер в ascii-таблице "10").
    У вас он в программе на пк отправляется?
    И ещё "20" это 2 символа "2" и "0"?
    аски-таблица
     
  7. GarlenX

    GarlenX Нерд

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

    Daniil Гуру

    Можете привести лог программы, что тут происходит. (желательно в кодах)
    Сколько символов в bufwrHEX (ведь, он хранит то, что потом отправляется?)
    Параметры com-порта проверены?
    Приведите лог-программы из терминала, в котором работает весь процесс.
    Напишите на ардуино программку которая по программному юарту отправляет данные на пк(другой ком порт) и мониторьте обычным терминалом.
     
  9. GarlenX

    GarlenX Нерд

    Я разобрался с проблемой...просто неправильно отправлял "\n"...
     
    Daniil нравится это.
  10. Svyatslav

    Svyatslav Начинающий

    Можно вашу прогу скачать?
     
    botArduinoUNO нравится это.
  11. Vovka

    Vovka Гик

    Попробуйте эту
     
    botArduinoUNO и Svyatslav нравится это.