Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[44.202.90.91] |
|
Страницы: (8) [1] 2 3 ... 7 8 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
В С++ Builder 6 создана программа обмена по COM порту в асинхронном режиме. Программа выполнена на трех потоках VinApi. Ничего особенного. Все просто. Основные функции ниже по тексту
#include <vcl.h> #pragma hdrstop #include "Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" #include <iostream> #include <windows.h> #include <stdio.h> #include <Math.hpp> #include <math.h> TForm1 *Form1; //--------------------------------------------------------------------------- // дефайны //ёмкость буфера приема #define BUFSIZE_RD 5000 //ёмкость буфера передачи #define BUFSIZE_WR 128 //--------------------------------------------------------------------------- // дескрипторы HANDLE COMport; HANDLE writer; HANDLE reader; HANDLE main; ///будем использовать для операций чтения (см. поток ReadThread) OVERLAPPED overlapped; //будем использовать для операций записи (см. поток WriteThread) OVERLAPPED overlappedwr; //--------------------------------------------------------------------------- // глобальные переменные // строка определения com порта AnsiString com_port; // флаг приема bool FlComRead; // флаг цикла в потоке приема bool flag; bool FlBeg; //приёмный и передающий буферы unsigned char bufrd[BUFSIZE_RD], bufwr[BUFSIZE_WR]; // контрольная сумма пакета передачи unsigned char TxBufCrc; unsigned char PrCanal; unsigned char PrSect; // счетчик приемов unsigned int counterRX; unsigned int counterTX; unsigned int counterRP; unsigned int Dist; unsigned int Distm; // скорость бод unsigned long BaudR; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- // функции //--------------------------------------------------------------------------- // задержка void wait (int wt) { while (wt--); } //--------------------------------------------------------------------------- // Принятые байты в HEX void hexdumpstrRead ( char src[], size_t size) { Form1->Memo2->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo2->Lines->Add(result); } //--------------------------------------------------------------------------- // Переданные байты в HEX void hexdumpstrWrite ( char src[], size_t size) { Form1->Memo1->Clear(); static const char hex[] = "0123456789abcdef"; char *result = (char *)malloc((size > 0)? size*3: 1), *p = result; if (result) { while (size-- > 0) { *p++ = hex[(*src >> 4) & 0x0f]; *p++ = hex[*src++ & 0x0f]; *p++ = ' '; } p[(p > result)? -1: 0] = 0; } Form1->Memo1->Lines->Add(result); } //--------------------------------------------------------------------------- // Вывод принятых байтов на экран void ReadPrinting(DWORD btr) { char Buffer[20]; unsigned int nb, tm; String stroka, temp_str; counterRP++; // sprintf(Buffer," %d", counterRP); // Form1->Label9->Caption = Buffer; // Перенос буфера в строку. for(UINT32 i=0; i<btr ; i++) { stroka = stroka + IntToHex(bufrd[i], 2) + ' '; } // кличество принятых байт // Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // контрольные символы for (UINT32 i = 0; i < sizeof(bufrd); i++) { if(bufrd[i] == 0xa1 && bufrd[i + 1] == 0xa2 && !FlBeg) { FlBeg = 1; nb = ((bufrd[i + 2] << 8) | bufrd[i + 3]); sprintf(Buffer,"Кол. инфо байт в пакете: %d", nb); Form1->Panel2->Caption = Buffer; Form1->Memo2->Clear(); // wait(30000000); // ЗАПЛАТКА. if(Form1 -> CheckBox4 -> Checked) Sleep(100); else Sleep(400); Form1->Panel1->Caption = ""; // if(!FlComRead) FlComRead = 1; } // определение последнего канала else if(bufrd[i] == 0x55 && bufrd[i + 1] == 0xaa && bufrd[i + 3] == 0x75) { // tm = bufrd[i + 6]; // Sleep(100); // wait(300000); // ЗАПЛАТКА. if(!FlComRead) FlComRead = 1; break; } } // количество принятых байт Form1->Panel3->Caption = "Всего принято байт: " + IntToStr(counterRX); // Вывод Form1->Memo2->Lines->Add(stroka); // Очистить буфер (чтобы данные не накладывались друг на друга). memset(bufrd, 0, BUFSIZE_RD); } //--------------------------------------------------------------------------- // Поток чтения DWORD WINAPI ReadThread( LPVOID lpParam ) { // буфер для вывода строк char Buffer[20]; // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл // while(1) while(!flag) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // Sleep(10); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // Sleep(10); // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // sprintf(Buffer," %d", counterRX); // Form1->Label9->Caption = Buffer; // вызываем функцию для вывода данных на экран ReadPrinting(btr); // hexdumpstrRead(bufrd, 846); } } } } Sleep(1); } // таймаут потока // Sleep(1); CloseHandle(overlapped.hEvent); } //--------------------------------------------------------------------------- // Поток записи WriteThread //главная функция потока, выполняет передачу байтов из буфера в COM-порт DWORD WINAPI WriteThread(LPVOID lpParam) { CONST HANDLE hMutex = (CONST HANDLE)lpParam; // temp - переменная-заглушка DWORD temp, signal; // текущий рабочий буфер char Buffer[20]; //создать событие overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL); while(1) { //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); //записать байты в порт (перекрываемая операция!) // WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr); WriteFile(COMport, bufwr, sizeof(bufwr), &temp, &overlappedwr); //приостановить поток, пока не завершится перекрываемая операция WriteFile signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE); //если операция завершилась успешно if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true))) { //вывести сообщение об этом в строке состояния sprintf(Buffer,"Передача %d прошла успешно", counterTX); Form1->Panel1->Caption = Buffer; } //иначе вывести в строке состояния сообщение об ошибке else {Form1->Panel1->Caption = "Ошибка передачи";} // Приостановить поток записи SuspendThread(writer); Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Поток main DWORD WINAPI MainThread(LPVOID lpParam) { // текущий рабочий буфер char Buffer[20]; while(1) { // если принят пакет if(FlComRead) { FlBeg = 0; FlComRead = 0; sprintf(Buffer,"Дистанция: %d м", Distm); Form1->Label16->Caption = Buffer; // включение режима снятия характеристики if(Form1->CheckBox1 -> Checked) { bufwr[126] |= (1 << 7); Form1->CSpinEdit3->Enabled = true; Form1->CSpinEdit4->Enabled = true; } else { bufwr[126] &= ~(1 << 7); Form1->CSpinEdit3->Enabled = false; Form1->CSpinEdit4->Enabled = false; } // включение режима снятия характеристики if(Form1->CheckBox2 -> Checked) { bufwr[123] |= (1 << 2); bufwr[126] = 80; } else { bufwr[123] &= ~(1 << 2); } // контрольная сумма в 0 TxBufCrc = 0; // подсчет контрольной суммы пакета управл. for(int i = 1; i < 127; i++) { TxBufCrc = TxBufCrc + bufwr[i]; } // контрольная сумма упр. пакета bufwr[127] = TxBufCrc; // вывод управл пакета в Memo 1 hexdumpstrWrite(bufwr,128); // номер передачи counterTX++; // количество принятых байт в 0 counterRX = 0; //чтобы данные не накладывались друг на друга //очистить передающий буфер порта PurgeComm(COMport, PURGE_TXCLEAR); // включение передачи if(Form1->CheckBox3 -> Checked) { //активировать поток записи данных в порт ResumeThread(writer); } } Sleep(1); } // таймаут в потоке // Sleep(1); } //--------------------------------------------------------------------------- // Com установки void Com_Setting() { COMMTIMEOUTS timeouts; com_port += "\\\\.\\"; // com_port.c_str() com_port += Form1->ComboBox1->Text; COMport = CreateFile(Form1->ComboBox1->Text.c_str(),GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if(COMport==INVALID_HANDLE_VALUE) { if(GetLastError()==ERROR_FILE_NOT_FOUND) { MessageBox(NULL,"serial port does not exist.","",MB_OK); } MessageBox(NULL,"serial port error","", MB_OK); } DCB dcbSerialParams = {0}; dcbSerialParams.DCBlength=sizeof(dcbSerialParams); if (!GetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"getting state error\n","",MB_OK); } dcbSerialParams.BaudRate=BaudR; dcbSerialParams.ByteSize=8; dcbSerialParams.StopBits=ONESTOPBIT; dcbSerialParams.Parity=NOPARITY; if(!SetCommState(COMport, &dcbSerialParams)) { MessageBox(NULL,"error setting serial port state\n","",MB_OK); } // Установка таймаутов. timeouts.ReadIntervalTimeout = 10; // таймаут между двумя символами timeouts.ReadTotalTimeoutMultiplier = 0; // общий таймаут операции чтения timeouts.ReadTotalTimeoutConstant = 0; // константа для общего таймаута операции чтения timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи timeouts.WriteTotalTimeoutConstant = 0; // константа для общего таймаута операции записи // Загрузить структуру таймаутов в порт. if(!SetCommTimeouts(COMport, &timeouts)) { // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния. MessageBox(NULL,"Не удалось установить тайм-ауты\n","",MB_OK); return; } // Установить размеры очередей приёма и передачи SetupComm(COMport,2000,2000); // Очистка буферов порта PurgeComm(COMport,PURGE_RXCLEAR); // очистить строку имени порта com_port = ""; } Теперь стоит задаче переместить все в VisualStudio 2008 поскольку к данному коду необходимо будет приклепать сложную графику GLUT. Подскажите пожалуйста как данный код заставить работать в VisualStudio 2008? Пока совершенно не врубаюсь каким образом это можно сделать. Пробовал делать в WindowsForm используя компонент serialPort. Может я не прав но похоже полная ерунда. Компонент не приспособлен для асинхронного режима. А нужно повторить именно асинхронный режим COM порта. Это связано с жесткими временными рамками приема и ответной передачи данных. Если не сложно, подскажите пожалуйста коротко путь по которому функции данного кода + потоки можно приспособить в WindowsForm VisualStudio 2008. Спасибо. |
Сообщ.
#2
,
|
|
|
тупо копипаст не пробовал?
|
Сообщ.
#3
,
|
|
|
В VisualStudio 2008 можно писать как на практически том же C++, что и Билдере, так и на шарпе.
Воспроизвести С++ код из Билдера несложно, т.к. в данном случае VCL-ность не задействована (правда, и синхронизации с главным потоком я не заметил) Компонент serialPort относится к шарпу, и работает именно асинхронно. |
Сообщ.
#4
,
|
|
|
Для обмена в асинхронном режиме зачем три потока? Ну один, ну два, ну никак не три.
Можно это в шарпе сделать с SerialPort, легко и удобно, только со сложной графикой windowsforms не очень подходит в плане производительности. |
Сообщ.
#5
,
|
|
|
Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать.
|
Сообщ.
#6
,
|
|
|
Поток приёма уже есть внутри serialPort. Его событие DataReceived вызывается из этого (вторичного) потока, так что не забыть про синхронизацию.
|
Сообщ.
#7
,
|
|
|
Цитата Acvarif @ Спасибо. Если так, тогда как это делается на практике? WindowsForm необходим, поскольку нужен интерфейс с кнопками и окнами вывода информации. Сам интерфейс составить несложно. Не врубаюсь как теперь туда подключить потоки с кодом внутри и необходимыми функциями. Если можно коротко по пунктам как это сделать. Сделай сначала каркас на шарпе с сериалпортом, чтением записью кнопками, а потом переноси туда свои функции с байтовыми операциями, ну тут рутина, менять кое-что придется, это ты сам. |
Сообщ.
#8
,
|
|
|
Пробововал serialPort Может толком не разобрался. Но получилась ерунда. Полнейшая временная разсинхронизация. В чем суть. Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления. Время между окончанием приема и началом обратной передачи должно уложиться в 5..10 ms. Там организован прием по событию прихода байта на порт. Поэтому скорее всего придется запускать подобные потоки в WindowsForm Вопрос. Можно-ли будет встроить в потоки код типа, где имеются функции типа WaitCommEvent и т.п.?
// буфер для вывода строк char Buffer[20]; // структура текущего состояния порта, // используется для определения количества принятых в порт байт COMSTAT comstat; // переменная temp используется в качестве заглушки DWORD btr, temp, mask, signal; // создать сигнальный объект-событие для асинхронных операций overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // установить маску на срабатывание по событию приёма байта в порт SetCommMask(COMport, EV_RXCHAR); // пока поток не будет прерван, выполняем цикл // while(1) while(!flag) { //ожидать события приёма байта (это и есть перекрываемая операция) WaitCommEvent(COMport, &mask, &overlapped); //приостановить поток до прихода байта signal = WaitForSingleObject(overlapped.hEvent, INFINITE); //если событие прихода байта произошло if(signal == WAIT_OBJECT_0) { //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent if(GetOverlappedResult(COMport, &overlapped, &temp, true)) { //если произошло именно событие прихода байта if((mask & EV_RXCHAR)!=0) { // Sleep(10); // нужно заполнить структуру COMSTAT ClearCommError(COMport, &temp, &comstat); // и получить из неё количество принятых байтов btr = comstat.cbInQue; // если действительно есть байты для чтения if(btr) { // Sleep(10); // прочитать байты из порта в буфер программы ReadFile(COMport, bufrd, btr, &temp, &overlapped); // увеличиваем счётчик байтов counterRX+=btr; // sprintf(Buffer," %d", counterRX); // Form1->Label9->Caption = Buffer; // вызываем функцию для вывода данных на экран ReadPrinting(btr); // hexdumpstrRead(bufrd, 846); } } } } Sleep(1); Кстати, а почему WindowsForm c графикой GLUT непроизводителен? Что производительнее? Да, вот источник который использовался для построения кода http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html#22 |
Сообщ.
#9
,
|
|
|
>Пробововал serialPort
Как именно пробовал? |
Сообщ.
#10
,
|
|
|
Цитата Acvarif @ Потоки приема и передачи в программе на Builder жестко привязаны по времени. Тоесть как только закончился прием непрерывного пакета данных формируется флаг разрешения для передачи обратно так называемого пакета управления. Вот в этом-то тебе и повезо. Дело в том, что ф-ии ReadFile/WriteFile потоко-не-безопасны. В твоём примере синхронизации я не заметил, т.е. ты фактически синхронизируешь операции общим ходом алгоритма. Значит, что-нибудь когда-нибудь заглючит.. |
Сообщ.
#11
,
|
|
|
Вот поток в котором осуществляется прием пакета с последующей передачей. Прием принимает, но не всегда, а передача идет не сразу за примом. Разрывы большие, не стабильные по времени. Иногда до 1 сек. В то время как обмен между компом и устройством производится один раз в сек.
void Form1::ThreadWork(){ buf_rez = Convert::ToInt32( textBox1->Text ); while(ThreadWork_Stop){ //this->Invoke(delInstatnce); mesRead = this->serialPort1->ReadExisting(); BufferSendCom[0] = bit1; BufferSendCom[1] = bit2; BufferSendCom[2] = bit3; //BufferSendCom[3] = bit4; BufferSendCom[124] = bit126; //timer1->Enabled = true; //this->Invoke(delInstatnce); if(mesRead->Length > 1 ){ std::cout << Convert::ToInt32(mesRead[0]) << "ddddsdsd" << std::endl; if( Convert::ToByte(mesRead[0]) == 63){ //form2->Send_Buffer(mesRead); for(int i = 0; i <= 127; i++){ bufer += BufferSendCom[i]; BF += BufferSendCom[i]; } BufferSendCom[127] = bufer; BF += BufferSendCom[127]; this->serialPort1->Write(BF); //Show_Send(); BF = ""; bufer = NULL; } } //std::cout << "//////////////////////" << std::endl; //***************************** mesRead = ""; Sleep(1100); }//while } Да собственно в коде нет ничего секретного, да к тому же он сырой. В срепке проект в архиве. Прикреплённый файлAmuleTest.rar (159,24 Кбайт, скачиваний: 115) |
Сообщ.
#12
,
|
|
|
похоже, что осуществляется поллинг (опрос, нету ли чего в порту) - это нехорошо. Как уже сказано, у порта есть событие OnDataReceived, ему нужно назначить обработчик, и при получении данных обработчик вызовется (по сути - аналог WaitCommEvent)
(и с синхронизхацией опять беда - в поточной, как я понимаю, функции, прямой доступ к GUI Convert::ToInt32( textBox1->Text )) |
Сообщ.
#13
,
|
|
|
В общих чертах понятно. Если OnDataReceived сработает так как WaitCommEvent то должно быть то, что нужно. Если не сложно, можно пример кода serialPort + OnDataReceived.
Да, синхронизация никакая. Буду поправлять. Это второй этам. Пока нужно понять какой дорожкой пойти. Дальше работать с serialPort или.. |
Сообщ.
#15
,
|
|
|
Спасибо. Становится понятней. Для проверки работы по событию сделал так
private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) { counterRX++; std::cout << Convert::ToInt32(counterRX) << std::endl; } Работает. Счетчик считает событя по очереди как и должно быть. Попробую подключить функцию накопления данных в буфере. Добавлено Для дальнейшей проверки пытаюсь вывести часть принятых по сом данных в окно textBox2 (со свойством Multiline - true) private: System::Void serialPort1_DataReceived(System::Object^ sender, System::IO::Ports::SerialDataReceivedEventArgs^ e) { mesRead = this->serialPort1->ReadExisting(); for (int i = 0; i < 30; i++) { this->textBox2->Text += mesRead[i].ToString()+"\r\n"; } } Возникает ошибка Cross-thread operation not valid: Control 'textBox2' accessed from a thread other than the thread it was created on. В чем тут хитрость? Почему операция кросс-поточная? |