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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Настройка COM порта 
:(
    Опции темы
OlegIT
Дата 21.6.2010, 09:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Подскажите, как правильно задать таймауты в структуре COMMTIMEOUTS для инициализации COM порта? Параметры передачи/приема, скорость 57600, 1 раз в секунду 161 байт. 

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


Новичок



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

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



Код

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

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


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


Опытный
**


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

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



Увы, но это не так. При такой инициализации сплошные ошибки.
При
Код

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

Уже лучше, но всё равно ошибки бывают, период произвольный.
PM MAIL   Вверх
GremlinProg
Дата 21.6.2010, 10:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



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

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

если есть проблемы с корректностью ответа, стоит попробовать сначала либо проверить соответствие настроек с обеих сторон, либо снизить скорость


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
OlegIT
Дата 21.6.2010, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Ошибка, по-моему, одна, функция чтения возвращает меньшее количество байт, всегда разное. Когда прочитано байт столько сколько нужно в корректности данных проблем нет.
Как работает функция чтения, если пришли не все байты, а она вызвана?

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


Эксперт
****


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

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



Цитата(OlegIT @  21.6.2010,  13:54 Найти цитируемый пост)
функция чтения возвращает меньшее количество байт, всегда разное. Когда прочитано байт столько сколько нужно в корректности данных проблем нет.

в общем случае, для работы с потоковыми протоколами через RS232 требуется:

либо очень хорошо знать железную реализацию протокола,
чтобы синхронно принять ровно столько, сколько было отправлено,
и наоборот - отправить столько, сколько будет принято устройством

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

Цитата(OlegIT @  21.6.2010,  13:54 Найти цитируемый пост)
Как работает функция чтения, если пришли не все байты, а она вызвана?

это зависит от функции чтения, но знать как она работает в принципе не надо, у нас тут достаточно примеров для работы с компортом:
открываем "поиск",
пишем "com порт",
жмем "найти"

Это сообщение отредактировал(а) GremlinProg - 21.6.2010, 12:16


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
lyucean
  Дата 21.6.2010, 12:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Цитата(OlegIT @ 21.6.2010,  11:54)
Ошибка, по-моему, одна, функция чтения возвращает меньшее количество байт, всегда разное. Когда прочитано байт столько сколько нужно в корректности данных проблем нет.
Как работает функция чтения, если пришли не все байты, а она вызвана?

нет, пока свободного времени, чтоб разбираться с вашей проблемой, вод код (100% рабочий),он поможет разобраться, если возникнут проблемы отпишите 
Код

#define BUFSIZE 255     //ёмкость буфера
unsigned char bufrd[BUFSIZE], bufwr[BUFSIZE]; //приёмный и передающий буферы
//---------------------------------------------------------------------------
HANDLE COMport;        //дескриптор порта
//структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры
//эти структуры необходимо объявить глобально, иначе программа не будет работать правильно
OVERLAPPED overlapped;        //будем использовать для операций чтения (см. поток ReadThread)
//---------------------------------------------------------------------------
unsigned long counter;    //счётчик принятых байтов, обнуляется при каждом открытии порта

void COMOpen(void);             //открыть порт
void COMClose(void);            //закрыть порт
//---------------------------------------------------------------------------
//поток для чтения последовательности байтов из COM-порта в буфер
class ReadThread : public TThread
{
 private:
        void __fastcall Printing();    //вывод принятых байтов на экран и в файл
 protected:
        void __fastcall Execute();    //основная функция потока
 public:
        __fastcall ReadThread(bool CreateSuspended);    //конструктор потока
};
//---------------------------------------------------------------------------
ReadThread *reader;     //объект потока ReadThread
//---------------------------------------------------------------------------
//конструктор потока ReadThread, по умолчанию пустой
__fastcall ReadThread::ReadThread(bool CreateSuspended) : TThread(CreateSuspended)
{}
//---------------------------------------------------------------------------
//главная функция потока, реализует приём байтов из COM-порта
void __fastcall ReadThread::Execute()
{
        COMSTAT comstat;        //структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
        DWORD btr, temp, mask, signal;    //переменная temp используется в качестве заглушки

        overlapped.hEvent = CreateEvent(NULL, true, true, NULL);    //создать сигнальный объект-событие для асинхронных операций
        SetCommMask(COMport, EV_RXCHAR);                            //установить маску на срабатывание по событию приёма байта в порт
        while(!Terminated)                        //пока поток не будет прерван, выполняем цикл
        {
        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==10) // <- тут проверяем равно ли 10 байтам принятое ..я,сука,МОНСТР!!!  //если действительно есть байты для чтения
                                {
                                        ReadFile(COMport, bufrd, btr, &temp, &overlapped);     //прочитать байты из порта в буфер программы
                                        counter+=btr;                                          //увеличиваем счётчик байтов
                                        Synchronize(Printing);                           //вызываем функцию для вывода данных на экран и в файл
                                }
                        }
                }
        }
        CloseHandle(overlapped.hEvent);        //перед выходом из потока закрыть объект-событие
}

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

//выводим принятые байты на экран и в файл (если включено)
void __fastcall ReadThread::Printing()
{

 //Form1->Memo1->Clear();    //очистить Memo1
        Form1->Memo1->Lines->Add((char*)bufrd);    //выводим принятую строку в Memo
        Form1->StatusBar1->Panels->Items[2]->Text = "Всего принято " + IntToStr(counter) + " байт";    //выводим счётчик в строке состояния
        memset(bufrd, 0, BUFSIZE);            //очистить буфер (чтобы данные не накладывались друг на друга)
}

//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------

//обработчик нажатия на кнопку "Открыть порт"
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
        if(SpeedButton1->Down)
        {
                COMOpen();                   //если кнопка нажата - открыть порт
                Form1->SpeedButton1->Caption = "Закрыть порт";
                counter = 0;    //сбрасываем счётчик байтов
        }
        else
        {
                COMClose();                  //если кнопка отжата - закрыть порт
                Form1->SpeedButton1->Caption = "Открыть порт";
        }
}

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

//обработчик закрытия формы
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
        if(reader)reader->Terminate();    //завершить поток чтения из порта, проверка if(reader) обязательна, иначе возникают ошибки
        if(COMport)CloseHandle(COMport);       //закрыть порт
}
//---------------------------------------------------------------------------
//кнопка "Очистить поле"
void __fastcall TForm1::Button3Click(TObject *Sender)
{
        Form1->Memo1->Clear();    //очистить Memo1
}

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

        portname = "COM3" ;    //получить имя выбранного порта
        //открыть порт, для асинхронных операций обязательно нужно указать флаг 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);       //задаём скорость передачи по дефолту там 9600 но х.з. как там дальше
        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 в порт
        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);
        PurgeComm(COMport, PURGE_RXCLEAR);    //очистить принимающий буфер порта
        reader = new ReadThread(false);    //создать и запустить поток чтения байтов
        reader->FreeOnTerminate = true;        //установить это свойство потока, чтобы он автоматически уничтожался после завершения
}
//---------------------------------------------------------------------------
//функция закрытия порта
void COMClose()
{
        if(reader)reader->Terminate();         //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки
        CloseHandle(COMport);                  //закрыть порт
        COMport=0;                //обнулить переменную для дескриптора порта
}
//---------------------------------------------------------------------------


в приложен файле сама прога, так проще разобраться ..правда там 10 байт принимает, но код хорошо от комментирован, поэтому поправить не составит труда.  smile  

Это сообщение отредактировал(а) lyucean - 21.6.2010, 12:33

Присоединённый файл ( Кол-во скачиваний: 37 )
Присоединённый файл  ________6.rar 685,26 Kb
PM MAIL   Вверх
xvr
Дата 21.6.2010, 17:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(OlegIT @ 21.6.2010,  11:54)
Ошибка, по-моему, одна, функция чтения возвращает меньшее количество байт, всегда разное. 

Это нормально.
Цитата

Как работает функция чтения, если пришли не все байты, а она вызвана?
Возвращает сколько пришло. А вот сколько она будет ждать, пока не поймет, что байтов больше нет, как раз и зависит от настроек таймаута

Ваша программа должна повторно дочитывать из порта все, что не дочиталось за предыдущие попытки.

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


Опытный
**


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

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



lyucean спасибо. В общем заработало. Но есть ещё одна проблема, когда первый заход на проверку пришедших байт происходит не в начале пакета. Тогда моё количество байт набирается с двух соседних посылок. У меня есть коды опознавания начала и конца посылок, могу их контролировать, но как сделать сброс на начало посылки не соображу?
PM MAIL   Вверх
GremlinProg
Дата 24.6.2010, 13:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(OlegIT @  24.6.2010,  15:04 Найти цитируемый пост)
У меня есть коды опознавания начала и конца посылок, могу их контролировать, но как сделать сброс на начало посылки не соображу?

как обычно - отбрасывай с начала пакета по байтику, пока первым не станет маркер начала пакета либо очередь не станет пустой


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "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.1214 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


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

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