C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 Здравствуйте, гость ( Вход | Регистрация )   Скачать форум | Поиск по Форуму | Участники | Помощь
 Поиск по:   сайтy   
   архиву форума   
  ОтветитьНовая тема
проблема чтением из COM порта, загрузка проца на 100% при опросе CОM по
« предыдущая тема | следующая тема » Подписаться на тему | Отправить тему на E-mail | Распечатать тему
** slan
Отправлено: 06.07.2006, 17:08 Цитировать


Unregistered









в приложении постоянно опрашивается компорт и считываются с него приходящие данные, вот такой функцией

CODE

unsigned char readcom()
{
unsigned char chRead;
DWORD dwRead = 0;
// Начинаем чтение
do
{
  ReadFile(hCom, &chRead, 1, &dwRead, NULL);
  // sleep(5);
}
while ((!dwRead) && (GetLastError() != ERROR_IO_PENDING));

return chRead;
}


именно такая обработка приводит к 100% загрузке процесора, как этого избежать ? чтобы не терять токо данные приходящие в ком порт, это однин из работающих потоков в приложении, другие потоки отправляют данные в ком, но не грузят его.

Если раскоментить // sleep, то загрузка спадает но данные теряются
 
Top
Konstantine
Отправлено: 06.07.2006, 23:43 Цитировать


Мастер участка


Группа: Модератор
Сообщений: 545
Пользователь № 790
Регистрация: 17.08.04



разберитесь с какой скоростью передаются данные?
если 5 мс за шаг - то это 200 Байт/сек т.е. ~25 кБ/сек
если скорость Ваших данных больше, то попробуйте уменьшить время задержки на подходящее значение, и неплохо бы запас иметь.
писать отдельный поток и ставить его в Idle (я так сразу подумал) - нельзя, т.к. при загрузке проца другой программой будет 100% потеря данных.

--------------------
Нет ничего более постоянного чем временное...
 
Послать сообщение E-mail адрес ICQ  Top
Asher
Отправлено: 07.07.2006, 08:49 Цитировать


Мастер участка


Группа: Модератор
Сообщений: 550
Пользователь № 12
Регистрация: 22.05.03



Привет.

Почитайте про overlapped режим работы с Com-портом
Хоть в MSDN, хоть в Internete.

--------------------
Это моё сугубо личное мнение, но я с ним согласен.
 
Послать сообщение E-mail адрес ICQ  Top
slan
Отправлено: 07.07.2006, 09:23 Цитировать


Ученик-кочегар


Группа: Участник
Сообщений: 3
Пользователь № 1476
Регистрация: 20.01.06



Konstantine, скорость 9600

Asher, сейчас почитаю
 
Послать сообщение  Top
Георгий
Отправлено: 22.07.2006, 23:10 Цитировать


Почетный железнодорожник


Группа: Модератор
Сообщений: 874
Пользователь № 3
Регистрация: 20.03.03



у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.

если в кратце, от работаешь с портом в потоке и открываешь порт в режиме чтения с ожиданием. в этом случае загрузка будет около 2-3% на свежих машинах и 5-10% на древних вроде P200mmx. но нужен современный контролер com порта - мультикарта на isa не подойдёт.

гм... только сейчас заметил дату поста smile.gif

Отредактировано Георгий - 23/07/2006, 00:10
 
Послать сообщение E-mail адрес ICQ  Top
Михаил
Отправлено: 18.01.2007, 09:31 Цитировать


Ученик-кочегар


Группа: Участник
Сообщений: 1
Пользователь № 3779
Регистрация: 18.01.07



QUOTE (Георгий @ 22.07.2006, 23:10)
у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.


Георгий!
Вы писали: "в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%."
Можене мне скинуть эти примеры кода (или ссылки на них) по E-mail?
А может у Вас есть готовый модуль(компонент) для работы с СОМ-портом?
Помогите,пожалуйста!!!
Я работаю в С++ Builder 6, под WinXP.
 
Послать сообщение E-mail адрес  Top
Георгий
Отправлено: 19.01.2007, 02:20 Цитировать


Почетный железнодорожник


Группа: Модератор
Сообщений: 874
Пользователь № 3
Регистрация: 20.03.03



QUOTE (Михаил @ 18.01.2007, 10:31)
QUOTE (Георгий @ 22.07.2006, 23:10)
у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.


Георгий!
Вы писали: "в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%."
Можене мне скинуть эти примеры кода (или ссылки на них) по E-mail?
А может у Вас есть готовый модуль(компонент) для работы с СОМ-портом?
Помогите,пожалуйста!!!
Я работаю в С++ Builder 6, под WinXP.

пример кода работающего по принципу запрос-ответ.
вычищать от остатков моего кода не стал - ничего секретного у нём нет, а ляпы, особенно с оператором goto, полезно посмотреть.

код, с которого начинается эта вертка - правильный, но, наверняка, автор не настроил таймауты - вот и выходит 100% загрузка проца.
я у себя настраиваю таймауты и код работает так:
1 формирую запрос на чтение (вызов readfile)
2 ОС передаёт запрос на чтение драйверу
3 драйвер напрягает контроллер com порта, что б он сгенерил прерывание, как получит 1 байт
4 драйвер ловит все прерывания контроллера и складывает байтики в свой буфер в ОЗУ
5 как только драйвер не получил прерывания от порта в течение таймаута, то он пинает ОС
6 ОС копирует данные из буфера драйвера в буфер переданный функции readfile и пинает (будит) поток
усё.
единственное отличие моего кода от вашего - у меня таймауты не нулевые и, если из порта ничего не приходит, то ЦПУ занимается другими задачами - вот поэтому и нет 100% загрузки.
а в вашем случае вы постоянно дёргаете драйвер - "скажика там есть что-нибудь?", вместо того, что б попросить драйвер - "разбуди меня, когда что-нибудь придёт."
обратите внимание на код настраивающий порт.
а когда будите свою реализацию делать, то не забудьте о правильной, не блокирующей основной поток на длительное время, синхронизации передачи данных из потока опроса порта и основного потока. а то всё распараллеливание работы с портом пойдёт насмарку.

код работы с портом:
CODE
void ShowLastError(void)
    {
  LPVOID lpMsgBuf;

  FormatMessage(
   FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
   NULL,
   GetLastError(),
   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
   (LPTSTR) &lpMsgBuf,
   0,
   NULL
   );

   // Display the string.
   MessageBox( NULL,(char*) lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );

   // Free the buffer.
   LocalFree( lpMsgBuf );
    };

Device ::Device(void)
      {
      this->hCom=INVALID_HANDLE_VALUE;
      this->PackageNumber=0;
      this->OutPutBuffer=0;
      this->InPutBuffer=0;
      };

Device ::~Device(void)
      {
      };

bool Device::Connect(const AnsiString& ComName,const AnsiString& CfgString,char StartByte,char DeviceId)
    {
    this->StartByte=StartByte;
    this->DeviceId=DeviceId;
    _COMMTIMEOUTS ctmo;
    DCB dcb;
    COMMCONFIG cc;
    cc.dwSize=sizeof (COMMCONFIG);
    cc.wVersion=1;
    cc.dwProviderSubType=PST_RS232;
    cc.dwProviderOffset=0;
    cc.dwProviderSize=0;

    //по непонятным причинам функция BuildCommDCB не заполняет ВСЕ поля
    //структуры описания контекста устройства так что заполняем их
    //своими руками (копия того, что устанавливает CommConfigDialog)
    dcb.DCBlength=28;

    dcb.BaudRate=38400;
    dcb.fBinary=1;
    dcb.fParity=1;
    dcb.fOutxCtsFlow=0;
    dcb.fOutxDsrFlow=0;
    dcb.fDtrControl=1;
    dcb.fDsrSensitivity=0;
    dcb.fTXContinueOnXoff=0;
    dcb.fOutX=0;
    dcb.fInX=0;
    dcb.fErrorChar=0;
    dcb.fNull=0;
    dcb.fRtsControl=0;
    dcb.fAbortOnError=0;
    //dcb.wReserved;//это поле по непонятным причинам не находит компилятор
    *(&dcb.BaudRate+2)=0;//приходится обнулять таким образом
    dcb.XonLim=2048;
    dcb.XoffLim=512;
    dcb.ByteSize=8;
    dcb.Parity=0;

    dcb.StopBits=2;
    dcb.XonChar=17;
    dcb.XoffChar=19;
    dcb.ErrorChar=0;
    dcb.EofChar=0;
    dcb.EvtChar=0;

    if (hCom!=INVALID_HANDLE_VALUE) CloseHandle(this->hCom);
    this->hCom=CreateFile(
                           ComName.c_str(),
                           GENERIC_READ|GENERIC_WRITE,
                           0,
                           0,
                           OPEN_EXISTING,
                           0,
                           0);

    if (!BuildCommDCB(CfgString.c_str(),&dcb))
       ShowLastError();

    if (!SetCommState(this->hCom,&dcb))
       ShowLastError();
    return true;
    };

bool Device::DisConnect(void)
    {
    if (hCom==INVALID_HANDLE_VALUE)
       return false;
    return CloseHandle(hCom);
    };
void Device::SetTimeOuts(const DeviceData* d)
    {
    _COMMTIMEOUTS ctmo;
    ctmo.ReadIntervalTimeout=0;
    ctmo.ReadTotalTimeoutMultiplier=0;
    ctmo.ReadTotalTimeoutConstant=d->GetInputTimeOut();
    ctmo.WriteTotalTimeoutMultiplier=0;
    ctmo.WriteTotalTimeoutConstant=d->GetOutputTimeOut();
    SetCommTimeouts(hCom,&ctmo);
    };

void Device::TransmitRecive(DeviceData* d)
    {
    DWORD i,size,nBytes;
    size=d->GetOutputSize();
    this->OutPutBuffer=new char[size+4+2];
    this->PackPackage(d);
    this->SetTimeOuts(d);

    PurgeComm(this->hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
    WriteFile(hCom,this->OutPutBuffer,4+size+2,&nBytes,NULL);
    if (nBytes!=(size+2+4))
       {
       AddEvent(InvalidWriteLen,nBytes);
       if (!d->OnWriteFauled())
          goto TransmitReciveAbort;
       };

    size=d->GetInputSize();
    this->InPutBuffer=new char[4+size+2];
    ReadFile(hCom,this->InPutBuffer,4+size+2,&nBytes,NULL);
    if (nBytes!=(size+4+2))
       {
       AddEvent(InvalidLen,nBytes);
       d->OnReadFauled();
       }
    else
        this->AnalizePackage(d);

TransmitReciveAbort:
    delete this->OutPutBuffer;
    delete this->InPutBuffer;
    };

DeviceData ::DeviceData()
          {
          };
DeviceData ::~DeviceData(){};
bool DeviceData::OnWriteFauled(void)
       {
       return false;
       };
void DeviceData::OnReadFauled(void){};


код выполняющийся в параллельном потоке:
CODE
void __fastcall TBackgroundReq::Execute()
    {
    DeviceData *dd;
    QueryData* qd;
    DWORD StartTime,EndTime,WorkTime,SecondReqTime;
    while (this->ContinueWork)
          {
          StartTime=GetTickCount();
          if (!this->ReqQuery[0]->IsEmpty())
             {
             qd=this->ReqQuery[0]->ReadReq();
             dd=dynamic_cast<DeviceData*>(qd);
             if (dd)
                {
                this->Controller->TransmitRecive(dd);
                this->ReqQuery[0]->FreeReq();
                };
             };
          EndTime=GetTickCount();
          WorkTime=this->CalculateWorkTime(StartTime,EndTime);
          if (WorkTime>=(this->CycleTime-this->ReqTime))
             {//нет времени на очередной запрос
             if (WorkTime<this->CycleTime)
                Sleep(this->CycleTime-WorkTime);
             }
          else
              {//как минимум есть время на один запрос
              //ожидание следующего строба
              if (this->ReqTime>WorkTime)
                 Sleep(this->ReqTime-WorkTime);
              while (WorkTime<(this->CycleTime-this->ReqTime))//пока есть хоть один период
                    {
                    SecondReqTime=GetTickCount();
                    if (!this->ReqQuery[1]->IsEmpty())
                       {
                       qd=this->ReqQuery[1]->ReadReq();
                       dd=dynamic_cast<DeviceData*>(qd);
                       if (dd)
                          {
                          this->Controller->TransmitRecive(dd);
                          this->ReqQuery[1]->FreeReq();
                          };
                       }
                    SecondReqTime=this->CalculateWorkTime(StartTime,EndTime);//время работы подзапроса
                    if (this->ReqTime>SecondReqTime)
                       Sleep(this->ReqTime-SecondReqTime);
                    WorkTime=GetTickCount()-StartTime;//общее время работы
                    };//    while
              }//выполнение подзапросов
          };//общий цикл выполнения
    }

ps сейчас я в консалтинг ушёл, так что непосредственно о программировании лучше не меня спрашивать, а тех, кто до сих пор программирует, я же последние 2 года только в ворде пишу.

pps sleep`а в коде потока не пугайтесь - он выполняет разрежение потока запросов в железку - если я свою железку слишком часто опрашивал, то её глючило (сторожевой таймер посылал железку на ресет) и потому я, если с момента ответа на предыдущий запрос прошло слишком мало времени, предпочитал поспать ещё до начала следующего периода опроса, чем дёргать железку.
 
Послать сообщение E-mail адрес ICQ  Top
6 ответов с: 06.07.2006, 17:08 Подписаться на тему | Отправить тему на E-mail | Распечатать тему

Back to Работа с внешними устройствами
ОтветитьНовая тема