Форум: "Начинающим";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];
ВнизНьюансы работы с COM-портами и асинхронный ввод/вывод Найти похожие ветки
← →
shkurkin (2010-05-29 15:26) [0]В интернете множество статей и тем на форумах по работе с COM портами, и асинхронному вводу/выводу (а в литературе как ни странно это вопрос подробно не разбирается), но у меня так и не сложилась полная картина по этой теме.
Программу я написал и добился от нее работоспособности, но остались сомнения в грамотности ее построения и неудовлетворенность стабильносте работы.
Итак, ЗАДАЧА:
1. Программа должна взаимодействовать с внешним устройством через один COM-port, внешнее устройсво - микроконтроллер с портами ввода/вывода а также с АЦП и ЦАП.
2. Программа должна уметь отображать состояние внешнего устройства, состояние его портов ввода, а также вводить/выводить иформация с/на АЦП/ЦАП.
3. Через второй COM порт программа должна взаимодействовать с такими-же программами по радиоканалу (к порту подключен приемо/передатчик).
4. При определенных условиях прорамма должна ваступать в роле моста между двумя портами, т.е. данные которые принимаются по одному порту должны быть переданы во второй, и наоборот.
КАК Я ЭТО РЕАЛИЗОВАЛ:
1. Работа с портмами реализована через API.
2. Прием и передача по портам организована асинхронно, в отдельном потоке и прием и передача (4 потока т.к. 2 порта). Все потоки работают постоянно, т.к. данные мгут прийти в любой момент.
3. Программа использует кольцевые буферы (256 байт) на прием и передачу (4 буфера т.к. 2 потра).
4. Данные из порта читаются по байтно, т.к. детектирование команды в буфере данных выполняется побайтно для экономии времени. Команда состоит минимум из 8 байт, маскимум - 136 (до 128 байт данных).
Часть исходника 1-го потока на прием:
repeat
lpEvntRF:=0;
WaitCommEvent(hPortRF,lpEvntRF,@lpOvrRF); //ждем
if (lpEvntRF and EV_RXCHAR)=EV_RXCHAR then //проверяем нужное событие
begin
ClearCommError(hPortRF,lpErrRF,@lpStatRF); //сбрасываем флаг
lpSzInRF := lpStatRF.cbInQue;
ReadFile(hPortRF,bRe,lpSzInRF,lpSzOutRF,@lpOvrRF);
// Побайтное чтение:
if lpSzOutRF <> 0 then
for i:=0 to (lpSzOutRF-1) do
begin
inc(ieReRF); // инкрементируем индекс буфера
bReRF[ieReRF]:=bRe[i];
fndCMDbRF();
end;//for
end;//if
until Terminated;
Часть исходника 1-го потока на передачу:
repeat
//Передача данных из буфера UART:
if (fTrEvCD) then
begin
chrTR := bTrCD[ibTrCD];
inc(ibTrCD);
SzIn:=1;
WriteFile(hPortCD,chrTr,SzIn,SzOut,@lpOvrCD);
//Если буфер на передачу пуст:
if (ieTrCD=ibTrCD)then
begin
fTrEvCD := false; // сброс флага о событии на передачу
//Обнуление индексов буфера. ???Нужно ли???
{ieTr=0;
ieTr=0;}
end//if(ieTrCD=ibTrCD)
end;//if(fTrEvCD)
until Terminated;
ПРОБЛЕМЫ И ВОПРОСЫ:
1. Постоянно работающие потоки загружают проц, и Windows начинает тормозить, при приеме/передаче данных появляются бешенные задержки. Уменьшение приоритетов потоков улучшает ситуацию но не кардинально. Получается ситуация: 8битный микроконтроллер с частотой 16МГц реагирует на команду в течении порядка 1-5мкс. А комп с частотой более 2ГГц реагирует в течении порядка 50-1000мс! Как это можно исправить? Windows не может генерировать событие и запускать поток, чтобы он выполнялся не постоянно (примерно так реализованно у меня в микроконтроллере)?
2. При инициализации порта выставляю событие на прием - "Прием любого символа", т.е. Windows должна генерировать событие при приеме каждого байта, однако, если принимается сразу несколько байт (без паузы) генерируется одно событие по приему последнего байта, и мне в проге приходиться разбирать эти данные по байтно. Как исправить?
3. Как открыть COM порт на скорости больше 115200?
4. Как открыть порт с номером больше 9, например COM10?
5. Как я понял функция WaitCommEvent() "вешает" поток пока не произойдет событие?
6. Функция WriteFile() завершает свою работу когда данные переданны в буфер ОС или когда данные физически будут полностью переданны в
порт?
Заранее СПАСИБО ЗА ПОМОЩЬ.
← →
Loginov Dmitry © (2010-05-29 22:36) [1]
> 1. Постоянно работающие потоки загружают проц, и Windows
> начинает тормозить, при приеме/передаче данных появляются
> бешенные задержки. Уменьшение приоритетов потоков улучшает
> ситуацию но не кардинально. Получается ситуация: 8битный
> микроконтроллер с частотой 16МГц реагирует на команду в
> течении порядка 1-5мкс. А комп с частотой более 2ГГц реагирует
> в течении порядка 50-1000мс! Как это можно исправить? Windows
> не может генерировать событие и запускать поток, чтобы он
> выполнялся не постоянно (примерно так реализованно у меня
> в микроконтроллере)?
Почему бы не использовать ГОТОВЫЕ и РАБОТОСПОСОБНЫЕ компоненты для работы с СОМ-портами. Их очень много, и в них нет таких проблем с производительностью.
Кстати, время реакции компьютера 50мс - это норма. При работающем касперском может и до 500мс доходить :)
← →
Германн © (2010-05-30 02:38) [2]
> а в литературе как ни странно это вопрос подробно не разбирается
1. Могу выслать на почту книгу П.Агурова. В ней этот вопрос достаточно подробно разобран.
2.
> Почему бы не использовать ГОТОВЫЕ и РАБОТОСПОСОБНЫЕ компоненты
> для работы с СОМ-портами
?
Могу посоветовать tpapro. http://sourceforge.net/projects/tpapro/
← →
shkurkin © (2010-05-30 05:25) [3]Спасибо за советы.
Герман, вышли пожалуйста книгу: shkurkins@yandex.ru.
Уже вчера с помощью этого форума нашел компонент "BComPort". Вроде все понятно, но начинаю реализовывать захожу в тупик (после нескольких лет программирования под микроконтроллеры, мой мозг никак не хочет перестраиваться на програмирование под Windows и понимать ООП :[ ).
С приемом данных все понятно: компонент BComPort имеет событие на прием "OnRxChar" создаем обработчик события в теле самой проги и делаем в нем, что нужно. Никаких дополнительных потоков.
ПРОБЛЕМА с передачей данных: почему в моих собственных процедурах не могу обращатся к компоненту "BComPort"? Например:procedure TrPortRF(Sender: TObject);
var
str: string;
begin
//Ожидание завершения предыдущей асинхронной передачи
repeat
until BComPortRF.IsAsyncCompleted(pAsyncRF); // *** ЗДЕСЬ ПРОИСХОДИТ ОШИБКА
BComPortRF.WaitForAsync(pAsyncRF);
DoneAsync(pAsyncRF);
//Новая асинхронная передача:
if (bTrRf <> "") then
begin
str := bTrRf;
InitAsync(pAsyncRF);
BComPortRF.WriteStrAsync(str, pAsyncRF);
bTrRf := "";
end;
end;//TrPortRf
Компилятор сообщает что "BComPortRF" не описанный определитель, хотя если этот же код поместить в обработчик события "OnTxEmpty" компонента BComPort - все отлично. Такая же проблема у меня была и когда делал с помощью потоков, в своих подпрограммах пытался приостановить поток, но имя потока в подпрограмме "Не описанный определитель". Получается, что в своих подпрограммах я могу обращатся только к объектам визуальной формы. А обращаться к потокам и компоненту "BComPort" только в обработчиках событий от объектов визуальной формы. ЧТО Я ДЕЛАЮ НЕ ТАК?
И еще один вопрос: можно ли в программе вызывать обработчик события если событие не произошло?
← →
YurikGL © (2010-05-30 09:34) [4]Попробуй использовать компоненту comm32 ... В свое время на ней много с COM-портом работал.
← →
Loginov Dmitry © (2010-05-30 12:32) [5]
> Могу посоветовать tpapro
Кстати, я бы не стал рекомендовать именно эти компоненты. Может у меня версия этих компонент не самая новая, но в моей версии весь прием данных, полученных по СОМ-порту, повешен на основной поток.
← →
Плохиш © (2010-05-30 16:49) [6]
> Компилятор сообщает что "BComPortRF" не описанный определитель
Значит это не является глобальной переменной, подозреваю, что он лежит на какой-то форме и работу с ним нужно программировать в методах соответствуюшей формы.
За код типа<Форма>.BComPortRF
полагается пожизненный эцик с гвоздями.
> И еще один вопрос: можно ли в программе вызывать обработчик
> события если событие не произошло?
Можно как и обычный метод класса, но про это надо прочитать в описании синтаксиса языка.
← →
shkurkin © (2010-05-30 23:16) [7]РЕШИЛ проблему с использованием компонента BComport. Работает все изумительно. Нагрузка на проц - 0%! А опрос устройств по обоим портам одновременно до 40 раз в секунду (большее количество запросов не пробовал), причем на запрос успевает приходить и ответ от устройства!
Чтобы использовать методы BComPort в любой части проги, нужно создать дочерний объект (часть методов просто описана в "public"), кстати сам компонент на форму я даже не помещал, а просто назначал для нового объекта обработчики событий.
ЗАМЕЧАНИЯ по компоненту BComPort:
Как мне кажется в описании к этому компоненту автор приводит не удачные примеры по асинхронной записи/чтении данных. Я бы не стал применять этот компонент таким образом. Почему:
1. В примере приведен код асинхронной записи:var
Operation1: PAsync;
Str: String;
begin
Str := "Hello";
InitAsync(Operation1);
try
BComPort1.WriteAsync(Str[1], Length(Str), Operation1);
repeat
// Выполнение других действий
until BComPort1.IsAsyncCompleted(Operation1);
BComPort1.WaitForAsync(Operation1);
finally
DoneAsync(Operation1);
end;
end;
Какой смысл в такой асинхронной записи? Пока данные физически не будут переданы в порт, выход из подпрограммы не произойдет (а мне нужно еще успеть данные принять из второго порта и как можно быстрее ответить). Когда просто достаточно вызвать метод "WriteAsync", чтобы поместить данные в буфер, и можно выходить из процедуры, а прога может делать другие действия. Чтобы продолжить передачу данных когда буфер освободиться создаем обработчик события "OnTxEmpty".
2. Асинхронное чтение из примера. Здесь назначен обработчик события "RxChar":procedure TForm1.BComPort1RxChar(Sender: TObject; Count: Integer);
var
Str: String;
Stat: PAsync;
N: Integer;
begin
InitAsync(Stat);
try
BComPort1.ReadStrAsync(Str, Count, Stat);
repeat
// Выполнение других действий
until BComPort1.IsAsyncCompleted(Stat);
N := BComPort1.WaitForAsync(Stat);
if N <> Count then SetLength(Str, Count);
finally
DoneAsync(Stat);
end;
// Действия с переменной Str
end;
Т.к. назначен обработчик события, то если он был вызван значит данные в буфере есть, использование обработчика события это уже асинхронная операция. Зачем тогда делать асинхронное чтение из буфера? Можно заранее узнать кол-во байт в буфере методом "InBufCount" и одним махом это Count кол-во байт считать методом "Read" и сразу выйти из обработчика.
Написал программу с учетом этих замечаний, все работает отлично.
P.S. Всем спасибо за участие.
← →
Германн © (2010-05-31 02:46) [8]
> Loginov Dmitry © (30.05.10 12:32) [5]
>
>
> > Могу посоветовать tpapro
>
>
> Кстати, я бы не стал рекомендовать именно эти компоненты.
> Может у меня версия этих компонент не самая новая, но в
> моей версии весь прием данных, полученных по СОМ-порту,
> повешен на основной поток.
>
Эээ. Поясни, пожалуйста, что именно тебе не нравиться?
Я эти версии давно знаю.
Что есть "весь прием данных, полученных по СОМ-порту,
> повешен на основной поток"?
P.S.
А не надо "весь приём данных" вешать на доппоток! Если "граблей по лбу" не хочешь!
← →
Loginov Dmitry © (2010-05-31 23:28) [9]
> Я эти версии давно знаю.
> Что есть "весь прием данных, полученных по СОМ-порту,
> > повешен на основной поток"?
Это значит примерно то, что для каждого полученного байта вызывается PostMessage в основной поток, и этот самый основной поток подвисает. Может это как-то и отключается, уже не помню, но разбираться в тоннах кода крайне сложно.
> А не надо "весь приём данных" вешать на доппоток! Если "граблей
> по лбу" не хочешь!
Т.е. если я весь прием вешаю на дополнительный поток, то получаю "граблей по лбу"? А что за "грабля" такая?
Страницы: 1 вся ветка
Форум: "Начинающим";
Текущий архив: 2010.08.27;
Скачать: [xml.tar.bz2];
Память: 0.5 MB
Время: 0.065 c