Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Обмен по COM порту, Проблема пересылки больших сообщений 
:(
    Опции темы
TheDestroyer
Дата 3.4.2009, 10:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Здравствуйте.
Для тестирования и отработки протокола обмена между микроконтроллером и управляющим блоком необходимо сделать обмен по RS-232 между двумя компьютерами. 
Сделано так: помимо основного потока программы, есть еще 3 потока - чтение, запись и управляющий поток (чтобы не зависало окно приложения).
У программы есть два режима работы - master и slave.  Master запускаю на одном компьтере, slave на другом. 
Master отсылает сообщение (его длина указывается в #define BUFSIZE 100 - количество байт), и ждет пока не прийдет сообщение назад, как только сообщение приходит назад, он отправляет новое сообщение и т.д. 
Slave ждет сообщение от Master-а, как только он его получает - тут же отправляет назад Master-у.
Также есть возможность посылать Master-ом как единичные сообщения, так и посылать сообщения циклически. (Размер сообщения, как было сказано выше, можно менять).

Запуск разового и циклического обмена происходит  соответствующими кнопками. Среда Borland C++ 6, но стараюсь писать на чистом с++, т.к. часть кода будет использоваться при написании программы под микроконтроллер.

Проблема: при пересылке небольших сообщений 1байт, 10 байт, как единично так и циклически - все работает  нормально. При пересылке больших сообщений (100 байт) даже при единичной пересылке возникают проблемы - иногда Master- ом принимается все нормально, но чаще принимается не 100 байт, а 186 байт. При отладке стало видно, что прием происходит не за один цикл (пришло сообщение, определилось, что пришло 100 байт и считалось), а за два цикла (происходит событие прихода сообщения, определяется, что принято 14 байт, считывается, затем снова происходит событие приема, определяется что принято 86 байт и считывается. Иногда во втором цикле приема приходит больше чем 86 байт).
Заметил, что если поставить точку останова на строке сразу после отправки сообщения в порт:
Код

WriteFile(COMport, bufwrHEX, sizeof(bufwrHEX), &temp, &overlappedwr);  //записать байты в порт (перекрываемая операция!)  
ОСТАНОВ. signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);      //приостановить поток, пока не завершится перекрываемая операция WriteFile

то приходит 100 байт, т.е. ровно столько сколько было отправлено и приходит за один раз.

Догадка: возможно прием большого сообщения происходит не сразу из-за того, что в цикле чтения стоит ожидание события приема байта (EV_RXCHAR), а надо бы дождаться когда прийдет все сообщение.

Как решить данную проблему?
Возможно ли вообще передавать таким образом большие сообщения? (Большие файлы-то по COM порту качать можно)
Как связана данная передача с протоколом UART? При пересылке большого сообщения оно перепаковывается в небольшие слова со стоп и старт битами и передается, или же сразу пересылается все сообщение разом?

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


//=============================================================================
//..................... объявления глобальных переменных ......................
//=============================================================================

#define BUFSIZE 100     //ёмкость буфера приемо-передачи

unsigned char bufwrHEX[BUFSIZE] = {123}; // передающий буфер для обмена в HEX
unsigned char bufrdHEX[sizeof(bufwrHEX)]; // приёмный буфер для обмена в HEX
//---------------------------------------------------------------------------

HANDLE COMport;        //дескриптор порта

//структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры
//эти структуры необходимо объявить глобально, иначе программа не будет работать правильно
OVERLAPPED overlapped;        //будем использовать для операций чтения (см. поток ReadThread)
OVERLAPPED overlappedwr;        //будем использовать для операций записи (см. поток WriteThread)

//---------------------------------------------------------------------------

int handle;              //дескриптор для работы с файлом с помощью библиотеки <io.h>

//---------------------------------------------------------------------------

bool fl=0;    //флаг, указывающий на успешность операций записи (1 - успешно, 0 - не успешно)

unsigned long counter;    //счётчик принятых байтов, обнуляется при каждом открытии порта

//---------------------------------------------------------------------------

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 COMOpen(void);             //открыть порт
void COMClose(void);            //закрыть порт
void Cycle_stop(void);            //остановка цикла обмена

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

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

DWORD WINAPI ReadThread(LPVOID);
DWORD WINAPI WriteThread(LPVOID);
DWORD WINAPI MainThread(LPVOID);


//=============================================================================
//.............................. реализация потоков ...........................
//=============================================================================

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

void ReadPrinting(int btr);
void Retranslate(void);

//---------------------------------------------------------------------------

//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;        //структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
 DWORD btr, temp, mask, signal;    //переменная temp используется в качестве заглушки

 overlapped.hEvent = CreateEvent(NULL, true, true, NULL);    //создать сигнальный объект-событие для асинхронных операций
 SetCommMask(COMport, EV_RXCHAR);                            //установить маску на срабатывание по событию приёма байта в порт
 //SetCommMask(COMport, EV_TXEMPTY);
 while(1)                        //пока поток не будет прерван, выполняем цикл
  {
   WaitCommEvent(COMport, &mask, &overlapped);                //ожидать события приёма байта (это и есть перекрываемая операция)
   signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
   if(signal == WAIT_OBJECT_0)                        //если событие прихода байта произошло
    {
     if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
      if((mask & EV_RXCHAR)!=0)                    //если произошло именно событие прихода байта
       {
        ClearCommError(COMport, &temp, &comstat);        //нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                           //и получить из неё количество принятых байтов
        if(btr)                                  //если действительно есть байты для чтения
        {
         ReadFile(COMport, bufrdHEX, btr, &temp, &overlapped);     //прочитать байты из порта в буфер программы
         counter+=btr;        // увеличиваем счётчик байт за все время работы программы
         Bytes_received+=btr; // счетчик принятых байт за один пуск
         ReadPrinting(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)
}

//---------------------------------------------------------------------------

//выводим принятые байты на экран и в файл (если включено)
void ReadPrinting(int btr)
{
 for (int i=0;i < btr;i++)
 {
 Form1->Memo1->Lines->Text = Form1->Memo1->Lines->Text + "  " + bufrdHEX[i];
 }
 Form1->Memo1->Lines->Add("");
 Form1->StatusBar1->Panels->Items[2]->Text = "Всего принято " + IntToStr(counter) + " байт";    //выводим счётчик в строке состояния
 // если единичный запуск, то выведем информацию на форму
 if (show_info)
 {
 Form1->Label11->Caption = IntToStr(Bytes_received);
 Bytes_received = 0;
 }
 if(Form1->CheckBox3->Checked == true)  //если включен режим вывода в файл
  {
   write(handle, bufrdHEX, sizeof(bufrdHEX)); //записать в файл данные из приёмного буфера
  }
}

//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------

//главная функция потока, выполняет передачу байтов из буфера в COM-порт
DWORD WINAPI WriteThread(LPVOID)
{
 DWORD temp, signal;    //temp - переменная-заглушка

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

//---------------------------------------------------------------------------


//-----------------------------------------------------------------------------
//............................... поток MainThead ............................
//-----------------------------------------------------------------------------

//---------------------------------------------------------------------------

//поток ждет ответа (чтения из COM порта) и пишет информацию в COM порт
DWORD WINAPI MainThread(LPVOID)
{
 // ============= переменные =============
int i = 0; // счетчик циклов передачи
int size=0;
// ============= программа =============
stopfl = false;
info_read = CreateEvent(NULL, true, true, NULL); // создаем событие чтения информации из порта в сигнальном состоянии, чтобы начать первый цикл
Form1->StatusBar1->Panels->Items[1]->Text  = "Идет обмен";
// ============= цикл чтения и записи информации в порт =============
 while (stopfl != true)
 {
// ============= ожидание записи информации в порт и подготовка на след. цикл =============
    // ждем пока информация не будет записана в порт
    //WaitForSingleObject(info_sent, INFINITE);
   if (WaitForSingleObject(info_read, 5000) == WAIT_OBJECT_0 ) // ждем чтения ответа 5 сек. максимум
     {
      sleep(1);
      ResetEvent(info_read);              // переключаем событие чтения информации в порт в несигнальное положение
      // заполним массив информацией
      for (int j=0;j < sizeof(bufwrHEX);j++)
      {
        bufwrHEX[j] = char(i);
        if (i > 250) {i=0;}
      } // for (int j=0;j < sizeof(bufwrHEX);j++)

      // вывод на экран
      for (int i=0;i<sizeof(bufwrHEX);i++)
           {
            Form1->Memo2->Lines->Text = Form1->Memo2->Lines->Text + "  " + bufwrHEX[i];
           }
      Form1->Memo2->Lines->Add("");

// ============= запись информации в порт =============
       PurgeComm(COMport, PURGE_TXCLEAR);  //очистить передающий буфер порта
       ResumeThread(writer);               //активировать поток записи данных в порт
       i++;
      } //  if (WaitForSingleObject(info_sent, 5000) == WAIT_OBJECT_0 ) // ждем 5 сек. максимум
   else
     { // если таймаут ожидания, остановим цикл и выведем сообщение
      stopfl = true;
      Form1->StatusBar1->Panels->Items[0]->Text  = "Таймаут ожидания записи в порт";
      Form1->Memo2->Lines->Add("Таймаут ожидания записи в порт");
      Cycle_stop();
     }

} // while (stopfl != true)
Cycle_stop();

} //  DWORD WINAPI MainThread(LPVOID)




//функция открытия и инициализации порта
void COMOpen()
{
 String portname;       //имя порта (например, "COM1", "COM2" и т.д.)
 DCB dcb;                //структура для общей инициализации порта DCB
 COMMTIMEOUTS timeouts;  //структура для установки таймаутов
 
 portname = Form1->ComboBox1->Text;    //получить имя выбранного порта

 //открыть порт, для асинхронных операций обязательно нужно указать флаг FILE_FLAG_OVERLAPPED
 COMport = CreateFile(portname.c_str(),GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 //здесь:
 // - portname.c_str() - имя порта в качестве имени файла, c_str() преобразует строку типа String в строку в виде массива типа char, иначе функция не примет
 // - GENERIC_READ | GENERIC_WRITE - доступ к порту на чтение/записть
 // - 0 - порт не может быть общедоступным (shared)
 // - NULL - дескриптор порта не наследуется, используется дескриптор безопасности по умолчанию
 // - OPEN_EXISTING - порт должен открываться как уже существующий файл
 // - FILE_FLAG_OVERLAPPED - этот флаг указывает на использование асинхронных операций
 // - NULL - указатель на файл шаблона не используется при работе с портами

 if(COMport == INVALID_HANDLE_VALUE)            //если ошибка открытия порта
  {
   Form1->SpeedButton1->Down = false;           //отжать кнопку
   Form1->StatusBar1->Panels->Items[0]->Text = "Не удалось открыть порт";       //вывести сообщение в строке состояния
   return;
  }

 //инициализация порта

 dcb.DCBlength = sizeof(DCB);    //в первое поле структуры DCB необходимо занести её длину, она будет использоваться функциями настройки порта для контроля корректности структуры

 //считать структуру DCB из порта
 if(!GetCommState(COMport, &dcb))    //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
  {
   COMClose();
   Form1->StatusBar1->Panels->Items[0]->Text  = "Не удалось считать DCB";
   return;
  }

 //инициализация структуры DCB
 dcb.BaudRate = StrToInt(Form1->ComboBox2->Text);       //задаём скорость передачи 115200 бод
 dcb.fBinary = TRUE;                                    //включаем двоичный режим обмена
 dcb.fOutxCtsFlow = FALSE;                              //выключаем режим слежения за сигналом CTS
 dcb.fOutxDsrFlow = FALSE;                              //выключаем режим слежения за сигналом DSR
 dcb.fDtrControl = DTR_CONTROL_DISABLE;                 //отключаем использование линии DTR
 dcb.fDsrSensitivity = FALSE;                           //отключаем восприимчивость драйвера к состоянию линии DSR
 dcb.fNull = FALSE;                                     //разрешить приём нулевых байтов
 dcb.fRtsControl = RTS_CONTROL_DISABLE;                 //отключаем использование линии RTS
 dcb.fAbortOnError = FALSE;                             //отключаем остановку всех операций чтения/записи при ошибке
 dcb.ByteSize = 8;                                      //задаём 8 бит в байте
 dcb.Parity = 0;                                        //отключаем проверку чётности
 dcb.StopBits = 0;                                      //задаём один стоп-бит
 //dcb.EvtChar = EV_TXEMPTY;

 //загрузить структуру DCB в порт
 if(!SetCommState(COMport, &dcb))    //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
  {
   COMClose();
   Form1->StatusBar1->Panels->Items[0]->Text  = "Не удалось установить DCB";
   return;
  }

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

 //записать структуру таймаутов в порт
 if(!SetCommTimeouts(COMport, &timeouts))    //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния
  {
   COMClose();
   Form1->StatusBar1->Panels->Items[0]->Text  = "Не удалось установить тайм-ауты";
   return;
  }

 //установить размеры очередей приёма и передачи
 SetupComm(COMport,2000,2000);
 //SetupComm(COMport,50,50);

 //создать или открыть существующий файл для записи принимаемых данных
 handle = open("test.txt", O_CREAT | O_APPEND | O_BINARY | O_WRONLY, S_IREAD | S_IWRITE);

 if(handle==-1)        //если произошла ошибка открытия файла
  {
   Form1->StatusBar1->Panels->Items[1]->Text = "Ошибка открытия файла";    //вывести сообщение об этом в командной строке
   Form1->Label6->Hide();                                               //спрятать надпись с именем файла
   Form1->CheckBox3->Checked = false;                                   //сбросить и отключить галочку
   Form1->CheckBox3->Enabled = false;
  }
 else { Form1->StatusBar1->Panels->Items[0]->Text = "Файл открыт успешно"; } //иначе вывести в строке состояния сообщение об успешном открытии файла

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

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

}

//---------------------------------------------------------------------------

//функция закрытия порта
void COMClose()
{
//Примечание: так как при прерывании потоков, созданных с помощью функций WinAPI, функцией TerminateThread
//          поток может быть прерван жёстко, в любом месте своего выполнения, то освобождать дескриптор
//          сигнального объекта-события, находящегося в структуре типа OVERLAPPED, связанной с потоком,
//          следует не внутри кода потока, а отдельно, после вызова функции TerminateThread.
//          После чего нужно освободить и сам дескриптор потока.

 if(writer)        //если поток записи работает, завершить его; проверка if(writer) обязательна, иначе возникают ошибки
  {TerminateThread(writer,0);
   CloseHandle(overlappedwr.hEvent);    //нужно закрыть объект-событие
   CloseHandle(writer);
   writer = NULL;                       // надо обнулить, иначе после закртия хэндла переменная всеравно остается
  }
 if(reader)        //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки
  {TerminateThread(reader,0);
   CloseHandle(overlapped.hEvent);    //нужно закрыть объект-событие
   CloseHandle(reader);
   reader = NULL;
  }
  if(MainThr)        //если поток чтения работает, завершить его; проверка if(MainThread) обязательна, иначе возникают ошибки
  {
   TerminateThread(MainThr,0);
   //CloseHandle(info_sent);    //нужно закрыть объект-событие
   CloseHandle(info_read);      //нужно закрыть объект-событие
   CloseHandle(MainThr);
   MainThr = NULL;
  }

 CloseHandle(COMport);                  //закрыть порт
 COMport=0;                //обнулить переменную для дескриптора порта
 close(handle);                //закрыть файл, в который велась запись принимаемых данных
 handle=NULL;                //обнулить переменную для дескриптора файла
                                        // надо обнулить, иначе после закртия хэндла переменная всеравно остается
}

//---------------------------------------------------------------------------
// функция остановки цикла обмена
void Cycle_stop()
{
int timer_time = 0;
float speed = 0;
stopfl = true;
Form1->StatusBar1->Panels->Items[1]->Text  = "";

if(MainThr)        //если поток чтения работает, завершить его; проверка if(MainThread) обязательна, иначе возникают ошибки
  {
   TerminateThread(MainThr,0);
   //CloseHandle(info_sent);    //нужно закрыть объект-событие
   CloseHandle(info_read);      //нужно закрыть объект-событие
   CloseHandle(MainThr);
   MainThr = NULL;
  }

// если таймер был запущен - выведем информацию
if (Form1->Timer1->Enabled)
{
Form1->Timer1->Enabled = false;
_sleep(1); // поток чтения, как правило, к этому моменту еще не завершился, ждем
timer_time = Timer_cycles * Form1->Timer1->Interval; // время работы таймера, мс
Form1->Label9->Caption = IntToStr(timer_time) + "мс";
Form1->Label11->Caption = IntToStr(Bytes_received);
Form1->Label14->Caption = IntToStr(Bytes_written);
speed = float(Bytes_written)/float(timer_time)*1000*8; // Бит/сек
speed = RoundTo(speed, 0);
Form1->Label16->Caption = FloatToStr(speed) + " Бит/сек";

// обнулим переменные статистики
Timer_cycles = 0;
Bytes_received = 0;
Bytes_written = 0;
}
}
//---------------------------------------------------------------------------

// ============= Кнопка запуска разового обмена =============
void __fastcall TForm1::Button2Click(TObject *Sender)
{
if (Form1->CheckBox5->Checked)
{show_info = true;}
else
{show_info = false;}
 PurgeComm(COMport, PURGE_TXCLEAR);             //очистить передающий буфер порта
 ResumeThread(writer);               //активировать поток записи данных в порт
}
//---------------------------------------------------------------------------

// =============  Кнопка запуска циклического обмена  =============
void __fastcall TForm1::Button4Click(TObject *Sender)
{
// ============= Запустить таймер статистики =============
Form1->Timer1->Enabled = true;
// ============= Запустить поток управления чтением и записью =============
MainThr = CreateThread(NULL, 0, MainThread, NULL, 0, NULL);    //создаём основной поток в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED)
Form1->Button4->Enabled = false;
Form1->Button5->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button5Click(TObject *Sender)
{
Cycle_stop();
Form1->Button4->Enabled = true;
Form1->Button5->Enabled = false;
}


Заранее спасибо.

PM MAIL   Вверх
Dem_max
Дата 3.4.2009, 11:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



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


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
TheDestroyer
Дата 3.4.2009, 13:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



На счет буфера, обращаю внимание, что он выставляется
Код

 //установить размеры очередей приёма и передачи
 SetupComm(COMport,2000,2000);

Если конечно про этот буфер идет речь.
PM MAIL   Вверх
Dem_max
Дата 3.4.2009, 16:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1780
Регистрация: 12.4.2007

Репутация: 16
Всего: 39



Это промежуточный виндовый буффер, а я упоминал про реальный железковый.
Кстати программый SetupComm(COMport,2000,2000); задается чтобы не терять байты если вдруг ваша прога немножко подзависла, винда сохранит байты у себя.

Это сообщение отредактировал(а) Dem_max - 3.4.2009, 16:06


--------------------
Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte")
PM MAIL   Вверх
TheDestroyer
Дата 3.4.2009, 17:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Обнаружил интересный эффект: Master и Slave режим пока не рассматриваем. Просто делаю передачу с одного компьютера на другой.
При разовой передаче 15 байт максимальное число байт, которое передается за один цикл равно 14, потом идет второй цикл приема (событие приема опять в сигнальном подложении) и принимается еще 1 байт. Итого всего 15 байт принято, но за два цикла. Число 14 - какое-то магическое. Больше никак - меньше пожалуйста.
Если сделать точку останова на ClearCommError(COMport, &temp, &comstat); , то число принятых байт сразу будет равно 15-ти, т.е. сразу определится, правильное число принятых байт, а не 14+1.

Код


//...
if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
      if((mask & EV_RXCHAR)!=0)                    //если произошло именно событие прихода байта
       {
        ClearCommError(COMport, &temp, &comstat);        //нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                           //и получить из неё количество принятых байтов
        if(btr)                                  //если действительно есть байты для чтения
        {
         ReadFile(COMport, bufrdHEX, btr, &temp, &overlapped);     //прочитать байты из порта в буфер программы
         //...
         }
        // ...
        }
//...


Далее, если вообще не проверять количество принятых байт, а просто принудительно читать ровно столько байт, сколько было отослано:
Код

ReadFile(COMport, bufrdHEX, 15, &temp, &overlapped);     //прочитать байты из порта в буфер программы

то прочитается все нормально. Также вместо 15-ти можно ставить и другое число.
В итоге получается, что структура comstat (ее параметр comstat.cbInQue) определяется неправильно. 
Из-за чего это может случаться?
Как это поправить?

Это сообщение отредактировал(а) TheDestroyer - 3.4.2009, 17:07
PM MAIL   Вверх
xvr
Дата 3.4.2009, 17:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Установите timeout'ы (SetCommTimeouts) в какое нибудь осмысленное значение. Все ваши проблемы с недочитанными байтами именно из за этого. UART очень медленное устройство (по сравнению со скоростью работы CPU). Установка timeout'ов в 0 заставляет ReadFile вернуть немедленно столько байтов, сколько было прочитанно из порта к этому моменту. Ваше магическое число 14 байтов - это размер очереди (FIFO) у аппаратного UART. Он осуществляет прием с некоторым timeout'ом, на который ваши нулевые установки не распространяются.
Далее, размер прочтенных данных надо брать из вызова ReadFile (переменная temp, в строке 95), а не из переменной btr.
В ReadFile можно передавать запрос сразу на чтение всего буффера, она прочтет не более того, что реально есть.
В вашей реализации Overlapped ввод/вывод не нужен, используйте обычные блокирующие ReadFile/WriteFile. Я уже не говорю о странной конструкции с WaitCommEvent/WaitForSingleObject/ReadFile - первые 2 части явно лишние, ReadFile и сам умеет работать в Overlapped режиме
Далее, вызывать методы VCL объектов напрямую не из главного потока категорически нельзя - VCL не расчитанна на такое использование, будет масса трудноуловимых глюков (используйте класс TThread и его метод Synchronize)
И последнее - для синхронизации потоков обычно используют Event'ы или семафоры, а уж никак не запуск и остановку этих самых потоков

PM MAIL   Вверх
TheDestroyer
Дата 6.4.2009, 11:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Цитата(xvr @  3.4.2009,  17:38 Найти цитируемый пост)
14 байтов - это размер очереди (FIFO) у аппаратного UART

А размер FIFO какой у контроллера? Почему именно 14 байт... Это стандарт? Никак не могу найти спецификацию, где бы говорилось об этом аппаратном буфере.

Посоветуйте какие таймауты выставить, из каких соображений они выставляются, в инете в разных примерах разные таймауты.

Про вызов VCL из потока согласен, это пока для отладочных целей.

Про странную на ваш взгляд конструкцию - я взял пример из http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html , он оказался наиболее полным и рабочим, т.к. все примеры, что нашел в инете если и работают, то не так как ожидается.
Буду признателен за любые советы.
PM MAIL   Вверх
xvr
Дата 6.4.2009, 12:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 6.4.2009,  11:14)
Цитата(xvr @  3.4.2009,  17:38 Найти цитируемый пост)
14 байтов - это размер очереди (FIFO) у аппаратного UART

А размер FIFO какой у контроллера? Почему именно 14 байт... Это стандарт? Никак не могу найти спецификацию, где бы говорилось об этом аппаратном буфере.

Читатйте datasheet на микросхему UART (16550 кажется)

Цитата

Посоветуйте какие таймауты выставить, из каких соображений они выставляются, в инете в разных примерах разные таймауты.
Таймауты зависят от вашей железки. С каким интервалом она шлет данные, есть ли интервалы между данными в пакете и пр.

Цитата

Про странную на ваш взгляд конструкцию - я взял пример из http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html , 
Не все, что пишут в Интернете стоит повторять - здесь нечно явно излишнее. Возможно там ловили не только принятые байты - тогда эта конструкция имеет смысл.
Сделайте обычный не Overlapped read

PM MAIL   Вверх
TheDestroyer
Дата 6.4.2009, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Нашел в настройках COM порта компьютера - диспетчер устройств - COM1 - параметры порта - дополнительно - использовать буферы FIFO, как раз там стоял размер 14 байт. Выключил этот буфер, результат не поменялся. 

По поводу использования не Overlapped read, в этом случае поток будет непрерывно читать из порта? Хорошо ли это, ведь рациональнее ждать прихода информации и по приходу читать ее.

Код

//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;        //структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
 DWORD btr, temp, mask, signal;
 while(1)                        //пока поток не будет прерван, выполняем цикл
  {
        ClearCommError(COMport, &temp, &comstat);        //нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                           //и получить из неё количество принятых байтов
        if(btr)                                  //если действительно есть байты для чтения
        {
         ReadFile(COMport, bufrd, btr, &temp, NULL);
         counter+=btr;                                          //увеличиваем счётчик байтов
         ReadPrinting();                           //вызываем функцию для вывода данных на экран и в файл
        } // if(btr)
  } // while(1)
}

PM MAIL   Вверх
xvr
Дата 6.4.2009, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Функция ReadFile будет блокироваться, пока в буфере не появится хотя бы один байт. После чего она начнет читать байты, пока не сработает один из timeout'ов или не исчерпается буфер. Насколько я понимаю именно это вам и надо. (Timeout'ы должны быть не нулевые!)

PM MAIL   Вверх
TheDestroyer
Дата 6.4.2009, 15:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Следуя вашим советам сделал так:
установил таймауты, почитав в хэлпе про них:
Код

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


Поток чтения сделал таким:
Код

DWORD WINAPI ReadThread(LPVOID)
{
 DWORD temp;    

 while(1)                        //пока поток не будет прерван, выполняем цикл
  {
         ReadFile(COMport, bufrd, 255, &temp, NULL);     //прочитать байты из порта в буфер программы
         if (temp>0)
         {
         counter+=btr;                                 //увеличиваем счётчик байтов
         ReadPrinting();                           //вызываем функцию для вывода данных на экран и в файл
         } // if (temp>0)

  } // while(1)
}


Цикл в потоке крутится непрерывно, и даже если выставить  timeouts.ReadTotalTimeoutConstant = 10000, т.е. 10-ти секундный таймаут ожидания чтения функцией ReadFile, то визуально ReadFile как мгновенно выполнялась, так и продолжает выполняться мгновенно.
В переменной temp всегда ноль.
Подскажите, плз, что не так делаю.
PM MAIL   Вверх
xvr
Дата 6.4.2009, 15:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



FILE_FLAG_OVERLAPPED из CreateFile убрали?

PM MAIL   Вверх
TheDestroyer
Дата 6.4.2009, 16:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Нет, забыл убрать. Убрал - заработало так, как ожидалось!

Для полного понимания происходящего:
В реализации потока чтения без overlapped:
Код

DWORD WINAPI ReadThread(LPVOID)
{
 DWORD temp;    //переменная temp используется в качестве заглушки
 while(1) //пока поток не будет прерван, выполняем цикл
  {
         ReadFile(COMport, bufrd, 255, &temp, NULL);  //прочитать байты из порта в буфер программы
         if (temp>0)
         {
         counter+=temp;      //увеличиваем счётчик байтов
         ReadPrinting();     //вызываем функцию для вывода данных на экран и в файл
         } // if (temp>0)
  } // while(1)
}

Есть вопросы:
1. С вышеуказанными таймаутами ReadFile ждет появления принятых байт 100*15+100мс= 1,6 сек и возвращает false если ничего так и не пришло, true если что-то пришло и считалось, это так?
2. Если было отправлено, например, 1кБ или более, то чтение будет продолжаться, пока передача не прекратится?
3. Если 2 - верно, то возникает вопрос, если передавать, гигабайты, то не объявишь же гигабайтный буфер приема bufrd, как следует поступать?
4. Если COM порт настроен на работу на скорости  9600 бод/с, это скорость передачи полезной информации, невключая транспортные издержки в виде старт, стоп, ... битов?
5. Чтобы добиться максимальной скорости передачи информации, наверное необходимо разбивать ее на части по сколько-то байт, чтобы принимать сразу целые части, или это несущественно?
Из документации по контроллеру:
Код

A standard ASC frame consists of:
• An idle time with the signal level 1.
• One start of frame bit (SOF) with the signal level 0.
• A data field containing a programmable number of data bits (1-63).
• A parity bit (P), programmable for either even or odd parity. It is optionally possible to
handle frames without parity bit.
• One or two stop bits with the signal level 1.

т.е. кадр может содержать от 1 до 63 информационных бита. 
Следует ли перепаковывать информационные кадры специальным образом, если их размер около 16 байт?

В реализации потока чтения c overlapped:
Код

DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;        //структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
 DWORD btr, temp, mask, signal;    //переменная temp используется в качестве заглушки
 bool result = false;

 overlapped.hEvent = CreateEvent(NULL, true, true, NULL);    //создать сигнальный объект-событие для асинхронных операций
 SetCommMask(COMport, EV_RXCHAR);                            //установить маску на срабатывание по событию приёма байта в порт
 while(1)        //пока поток не будет прерван, выполняем цикл
  {
   WaitCommEvent(COMport, &mask, &overlapped);                //ожидать события приёма байта (это и есть перекрываемая операция)
   signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
   if(signal == WAIT_OBJECT_0)                        //если событие прихода байта произошло
    {
     // \/ставим здесь задержку в 1 сек. или останов и все принимается как надо!
     flag = GetOverlappedResult(COMport, &overlapped, &temp, true);
     if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
     if(flag)
     if((mask & EV_RXCHAR)!=0)                    //если произошло именно событие прихода байта
       {
        ClearCommError(COMport, &temp, &comstat);        //нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                           //и получить из неё количество принятых байтов
        if(btr)                                  //если действительно есть байты для чтения
        {
         result = ReadFile(COMport, bufrd, btr, &temp, &overlapped);     //прочитать байты из порта в буфер программы
         counter+=btr;        //увеличиваем счётчик байтов
         //ReadPrinting();    //вызываем функцию для вывода данных на экран и в файл
        } // if(btr)
       }// if((mask & EV_RXCHAR)!=0)
    } // if(signal == WAIT_OBJECT_0)
  } // while(1)
}


1. Так и не удалось получить за один цикл приема все 15 байт, только 14+1. Для чего может понадобиться использовать Overlapped вариант?
Казалось бы, в строке 12 происходит ожидание прихода информации, как раз тут можно было бы дождаться пока вся информация не придет и продолжить.

Спасибо.
PM MAIL   Вверх
xvr
Дата 6.4.2009, 17:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 6.4.2009,  16:21)
Есть вопросы:
1. С вышеуказанными таймаутами ReadFile ждет появления принятых байт 100*15+100мс= 1,6 сек и возвращает false если ничего так и не пришло, true если что-то пришло и считалось, это так?

В первом приближении так. Только возвращается TRUE в любом случае, и еще учитывается межсимвольный timeout, т.е. если данные приходят аостоянно, то он будет постоянно читать. Если данных изначально не было, то он будет ждать первого байта бесконечно
Цитата

2. Если было отправлено, например, 1кБ или более, то чтение будет продолжаться, пока передача не прекратится?
Да
Цитата

3. Если 2 - верно, то возникает вопрос, если передавать, гигабайты, то не объявишь же гигабайтный буфер приема bufrd, как следует поступать?
Прием прекратится как только в буфере bufrd кончится место
Цитата

4. Если COM порт настроен на работу на скорости  9600 бод/с, это скорость передачи полезной информации, невключая транспортные издержки в виде старт, стоп, ... битов?
Включая. Т.е. на байт придется 10-11 битовых интервалов по 9600 бит
Цитата

5. Чтобы добиться максимальной скорости передачи информации, наверное необходимо разбивать ее на части по сколько-то байт, чтобы принимать сразу целые части, или это несущественно?
Несущественно
Цитата

т.е. кадр может содержать от 1 до 63 информационных бита. 
Следует ли перепаковывать информационные кадры специальным образом, если их размер около 16 байт?
Это уж вам решать - надо их перепаковывать, или нет. Зависит от требований приложения

Цитата

1. Так и не удалось получить за один цикл приема все 15 байт, только 14+1. Для чего может понадобиться использовать Overlapped вариант?
Если одновременно с ожиданием надо сделать что то, или если надо ждать одновременно несколько событий (например - прием данных или сигнал на завершение нити приема от главной программы)


PM MAIL   Вверх
TheDestroyer
Дата 7.4.2009, 13:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Да, как только начал тестировать, оказалось, что пока ReadFile ждет, то несмотря на то, что ждет она в отдельном потоке, это ожидание подвешивает все. Например, пока идет ожидание - жму кнопку отправки данных, функции PurgeComm и WriteFile не выполняются пока ReadFile не завершит ожидание. Конечно, можно уменьшить таймауты, чтобы ReadFile "крутилась" быстрее, но это неправильно. Похоже, все-таки, нужно использовать overlapped. 

Если в режиме overlapped передавать 15 байт, то ReadFile читает 14+1 байт.
Код

btr = comstat.cbInQue;
ReadFile(COMport, bufrd, btr, &temp, &overlapped);

Если же указать явно вместо btr - 15 байт, то прочитает сразу 15 байт. Т.е. надо явно указывать сколько читать байт. 
Если указать буфер чтения больше, чем передано байт, то принимается ноль байт.
Как бы принимать в режиме overlapped ровно столько байт сколько было передано за один раз?

PM MAIL   Вверх
xvr
Дата 7.4.2009, 13:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 7.4.2009,  13:02)
Да, как только начал тестировать, оказалось, что пока ReadFile ждет, то несмотря на то, что ждет она в отдельном потоке, это ожидание подвешивает все. Например, пока идет ожидание - жму кнопку отправки данных, функции PurgeComm и WriteFile не выполняются пока ReadFile не завершит ожидание. Конечно, можно уменьшить таймауты, чтобы ReadFile "крутилась" быстрее, но это неправильно. Похоже, все-таки, нужно использовать overlapped. 

Если нужно читать в Overlapped, то WaitCommEvent все равно не нужен. Нужно сразу запускать ReadFile, и если она вернулась с ошибкой IO_STILL_PENDING (кажется так), запускать WaitForSingleObject на event'е из OVERLAPPED, а затем GetOverlappedResult. (А можно сразу GetOverlappedResult и поставить ему TRUE в последнем параметре)
Цитата

Если в режиме overlapped передавать 15 байт, то ReadFile читает 14+1 байт.
Если же указать явно вместо btr - 15 байт, то прочитает сразу 15 байт. Т.е. надо явно указывать сколько читать байт. 
Указывайте по максимуму. И timeout'ы не забудьте выставить
Цитата

Если указать буфер чтения больше, чем передано байт, то принимается ноль байт.
Принимается больше, но надо ждать  smile 
Цитата

Как бы принимать в режиме overlapped ровно столько байт сколько было передано за один раз?
'За один раз' - это за какой раз? UART не режет посылки на пакеты, единственный способ различить 'разы' это timeout'ы. Причем, если ДО вызова ReadFile уже пришло несколько пакетов, то их отличить друг от друга (на уровне ReadFile) уже невозможно

PM MAIL   Вверх
TheDestroyer
Дата 8.4.2009, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



в режиме Overlapped получился такой поток чтения:
Код

DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;    
 DWORD Error, btr, bytes_read, mask, signal;    
 bool result = false;

 overlapped.hEvent = CreateEvent(NULL, true, true, NULL);
 SetCommMask(COMport, EV_RXCHAR);          
 while(1)    
  {
     result = ReadFile(COMport, bufrdHEX, sizeof(bufrdHEX), &bytes_read, &overlapped);
     if (!result)
     {
       Error = GetLastError();
       if (Error == ERROR_IO_PENDING)
        {
          if(GetOverlappedResult(COMport, &overlapped, &bytes_read, true))  // true - Specifies whether the function should wait for the pending overlapped operation to be completed
          { 
           if(bytes_read)      
           {
            counter+=bytes_read;                 
            Bytes_received+=bytes_read;
            if (show_info)
            {
             ReadPrinting(bytes_read);           
            }
            SetEvent(info_read); 
            if (slave_mode)
            {
            memcpy(bufwrHEX,bufrdHEX,bytes_read);
            ResumeThread(writer);           
            } // if (slave_mode)
           } // if(btr)
          } // if(GetOverlappedResult(COMport, &overlapped, &temp, true))
        } // if (Error == ERROR_IO_PENDING)
     } // if (!result)
  } // while(1)
}

ReadFile стоит чуть больше секунды и возвращает ERROR_IO_PENDING всегда. Приняты данные или нет можно судить по bytes_read. Так поток и "крутится". В Overlapped режиме, в отличие от обычного, ReadFile не создает описанных ранее подвисаний.
Таймауты такие:
Код

 timeouts.ReadIntervalTimeout = 100;     
 timeouts.ReadTotalTimeoutMultiplier = 15;
 timeouts.ReadTotalTimeoutConstant = 100;  
 timeouts.WriteTotalTimeoutMultiplier = 15;   
 timeouts.WriteTotalTimeoutConstant = 100;    

Цитата(xvr @  7.4.2009,  13:32 Найти цитируемый пост)
'За один раз' - это за какой раз? 

В первом посте я описывал, что принимается 14+1 байт при отправке 15-ти байт. В потоке чтения в цикле принималось сначала 14 байт (как сообщалось в comstat.cbInQue), на втором цикле принимался еще один байт (также сообщалось в comstat.cbInQue).

Делаю замер скорости на двух компьютерах. 
Настройки COM порта такие:
Код

dcb.BaudRate = StrToInt(Form1->ComboBox2->Text);   // 9600
 dcb.fBinary = TRUE;                       
 dcb.fOutxCtsFlow = FALSE;          
 dcb.fOutxDsrFlow = FALSE;          
 dcb.fDtrControl = DTR_CONTROL_DISABLE;      
 dcb.fDsrSensitivity = FALSE;                  
 dcb.fNull = FALSE;                                
 dcb.fRtsControl = RTS_CONTROL_DISABLE;            
 dcb.fAbortOnError = FALSE;                         
 dcb.ByteSize = 8;                                  
 dcb.Parity = 0;                   
 dcb.StopBits = 0;       
       
На 2х компьютерах запускаю написанную программу, на 2-ом устанавливаю флаг slave_mode (т.е. сразу после чтения он отправляет принятое назад).
Передаю циклически буфер из 20 байт, содержимое каждый раз меняю для контроля правильной передачи и приема.Время контролирую с помощью компонента Timer из стандартного набора Borland. Интервал таймера выставлен 55мс, т.к. меньшие интервалы, похоже, таймер не чувствует.
Делаю 15 циклов передачи, соответственно вижу, что на компьютере, с которого отправлял: отправлено 300 байт (15 раз по 20 байт) и принято 300 байт. Все без ошибок. Затраченное время 3740мс = 3,74с.
Подсчитываю скорость: 300 байт * 2 / 3,74 c ~ 160,43 байт/с =  1283 бит/с. Никак не 9600 бит/с
Учтем, что пересылка идет, наверное, по 8 информационных бит, к которым добавляется 1 старт и 1 стоп бит. Итого на 300 байт информационных издержек: 300*2 =600 бит = 75 байт. Т.к. в тесте пересылка идет туда и обратно: 75*2 = 150 байт. 
Итого передано всего (300+75)*2 байт / 3,74 с ~ 200,54 байт/с = 1604,3 бит/с. Опять далеко не 9600 бит/с

Не учитываю время затраченное компьютерами на запись информации в буфера, т.к. считаю его незначительным по сравнению со временем, затраченным на передачу данных.
В чем ошибка расчета скорости?  Может не по 8 информационных бит передается? 


PM MAIL   Вверх
xvr
Дата 8.4.2009, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Что то не так. Передается по 10 бит (минимум). Попробуй отправить большой пакет данных (все 300 байт за раз), и замерять время не таймером, а функцией замера времени (GetTickCount() например)

PM MAIL   Вверх
TheDestroyer
Дата 8.4.2009, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Сделал таким образом:
Код

DWORD WINAPI MainThread(LPVOID)
{
LARGE_INTEGER startTime, stopTime;
LARGE_INTEGER freq;
bool active = false;
long int t=0;

stopfl = false;
info_read = CreateEvent(NULL, true, true, NULL); 

 while (stopfl != true)
 {

   if (WaitForSingleObject(info_read, 5000) == WAIT_OBJECT_0 )
     {
      if (active) // если была записана инф. в порт и вернулась от slave
      {
      QueryPerformanceCounter(&stopTime);
      QueryPerformanceFrequency(&freq);
      __int64 d = (__int64)freq.QuadPart/1000000; // f(MHz)
      t = __int64(stopTime.QuadPart - startTime.QuadPart)/d;  // t(мкс)
      stopfl = true;
      active = false;
      }
      else
      {
      ResetEvent(info_read); 
      for (int j=0;j < sizeof(bufwrHEX);j++)
      {
        bufwrHEX[j] = char(j);
      } // for (int j=0;j < sizeof(bufwrHEX);j++)

      QueryPerformanceCounter(&startTime);
      active = true;

      PurgeComm(COMport,PURGE_TXCLEAR);
      ResumeThread(writer);  
      }// else if (active)

      } //  if (WaitForSingleObject(info_sent, 5000) == WAIT_OBJECT_0 ) 
   else
     { 
      stopfl = true;
     }

} // while (stopfl != true)
Cycle_stop();
} //  DWORD WINAPI MainThread(LPVOID)

Запускается поток MainThread, который запоминает значение точного счетчика, запускает запись в порт, дожидается окончания чтения ответа и сравнивает текущее значение счетчика с запомненным ранее. В итоге, вроде как, точность - 10ки микросекунд.
Пересылаю буфер размером 300 байт.
Результат: время пересылки к slave и обратно = 628623 мкс (в среднем)
Скорость: 300 байт * 2 / 0,63 с = 952,5 байт/с = 7619 бит/с
Транспортных издержек (1 старт и 1 стоп бит):  300 * 2 = 600 бит = 75 байт
Итого: (300 +75)байт * 2 / 0,63 с = 1190,5 байт/с = 9523,8 бит/с
Это уже похоже на правду.
Почему же при пересылке маленькими пачками скорость так падает?

PM MAIL   Вверх
xvr
Дата 8.4.2009, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 8.4.2009,  16:03)
Почему же при пересылке маленькими пачками скорость так падает?

Переключение thread'ов отнимает много времени. Возможно еще где то есть лишние системные вызовы

PM MAIL   Вверх
TheDestroyer
Дата 9.4.2009, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Выяснил, что если в ReadFile ставить приемный буфер больше, чем реально пересылается байт, то возникает задержка.
Так при передаче 300 байт и с 300 байтным буфером приема пересылка туда и обратно происходила за 0,052сек. 
При передаче 17 байт с 300 байтным буфером приема такая пересылка занимает более 0,2сек.
Если же выставить 17 байтный буфер приема, то пересылка занимает 0,0037сек, что соответствует скорости 93176 Бит/с (при настройке порта на 115200 Бит/с).
Видимо на эту задержку в зафисимости от размера приемного буфера влияют таймауты.

PM MAIL   Вверх
xvr
Дата 9.4.2009, 17:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 9.4.2009,  14:47)
Выяснил, что если в ReadFile ставить приемный буфер больше, чем реально пересылается байт, то возникает задержка.

Естественно
Цитата

Видимо на эту задержку в зафисимости от размера приемного буфера влияют таймауты.
Влияют, причем вне зависимости от размера  smile 
Если в процессе приема данных закончился буфер, то ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы (и timeout'ы на это не влияют). 
Если же в процессе приема закончился входной поток, то система будет как минимум ждать ReadIntervalTimeout ms после приема последнего байта (ну нету другого способа узнать, что поток прекратился), и только потом ReadFile прекратит чтение.

PM MAIL   Вверх
TheDestroyer
Дата 10.4.2009, 11:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Цитата(xvr @  9.4.2009,  17:41 Найти цитируемый пост)
ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы

Имеется ввиду буфер системы, который устанавливается  SetupComm(COMport,2000,2000) ?
Правильно ли я понимаю, что на пути принятой информации всего два буфера: аппаратный FIFO буфер, который можно отключить или задать размер 1-14 байт на прием и 1-16 на запись и программный, устанавливаемый SetupComm?
Что будет если отключить FIFO буфер? Данные сразу будут писаться в программный буфер? Тогда для чего вообще нужен FIFO буфер, он как-то ускоряет обмен?
PM MAIL   Вверх
xvr
Дата 10.4.2009, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 10.4.2009,  11:43)
Цитата(xvr @  9.4.2009,  17:41 Найти цитируемый пост)
ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы

Имеется ввиду буфер системы, который устанавливается  SetupComm(COMport,2000,2000) ?

Да
Цитата

Правильно ли я понимаю, что на пути принятой информации всего два буфера: аппаратный FIFO буфер, который можно отключить или задать размер 1-14 байт на прием и 1-16 на запись и программный, устанавливаемый SetupComm?
Да
Цитата

Что будет если отключить FIFO буфер? Данные сразу будут писаться в программный буфер? 
Да
Цитата

Тогда для чего вообще нужен FIFO буфер, он как-то ускоряет обмен?
Нет. Без него могут теряться байты на больших скоростях. UART будет посылать прерывания в CPU на каждый принятый байт, и если драйвер порта не прочтет этот байт до прихода следующего, то он потеряется (и установится ошибка приема)
Это было актуально на старых машинах, на современных машинах это может проявится только на мегабодных скоростях и 100% загрузке процессора  smile Хотя отключать его (FIFO) все же не стоит - надежности оно все таки добавляет

PM MAIL   Вверх
TheDestroyer
Дата 13.4.2009, 16:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Спасибо большое за ответы. Поставил бы плюс, если б у меня было достаточно постов. 
Похоже, данная тема форума единственное место, где можно найти ответы на почти все возникающие вопросы при написании обмена по COM порту во всем инете. Все примеры, что раскиданы по инету (по большому счету их около 3х видов) не помогли мне, как помогли здесь.
PM MAIL   Вверх
GremlinProg
Дата 13.4.2009, 17:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(TheDestroyer @  13.4.2009,  18:20 Найти цитируемый пост)
Поставил бы плюс, если б у меня было достаточно постов. 

xvr +1


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Страницы: (2) [Все] 1 2 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1900 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.