Майстри delphi, робота з com-портами під windows

Робота з COM-портами під Windows

Отже, у нас є в комп'ютері com-порт. І добре б по ньому передавати і приймати байти. У загальному випадку можна поставити задачу так: необхідно відправляти потрібний рядок і необхідно реагувати на прихід рядки в порт від зовнішнього пристрою.

Приклад містить інтерфейсну форму, а так же модуль для роботи з портом. Модуль узятий з реально працюючої програми, але (знову ж для спрощення), з нього були викинуті всілякого виду перевірки, і я постарався залишити один кістяк. У реальній програмі треба ставити купу перевірок (в ідеалі після кожної API-функції), очищати буфер порту і задавати його ємність і т.д. і т.п. Але для початку (наприклад, щоб послати що-небудь в модем) даного матеріалу вистачить. Відмітна особливість даного прикладу - організація роботи прийому даних з порту за подією, якого програма чекає в паралельному потоці.

Модуль для роботи з портом містить чотири процедури:
  • PortInit - ініціалізація роботи і запуск потоку прийому даних з порту.
  • KillComm - власне вбивство потоку прийому і самого порту.
  • WriteComm - запис в порт.

Процедура PortInit.

Для початку необхідно створити порт і отримати його ідентифікаційний номер (хоча, строго кажучи, створити файл і отримати хендл). Робиться це однією функцією CreateFile:

CommHandle - хендл, тобто номер створеного неподобства. Тип - THandle. Надалі працюємо в програмі тільки з нею.
Перший параметр 'COM1' - власне ім'я порту. Його відповідно можна міняти (зверніть увагу це не тип string, а тип PChar). Решта установки досить стандартні і міняти їх часто не доведеться. Хоча звичайно можна (і потрібно) і по Хелп полазити і дати волю цікавості.

Тепер налаштовуємо параметри порту, а так само маску. Маска - це опис такої події, яке порт буде чекати, і за яким вестиметься обробка подій. В даному прикладі розглядаємо окремий випадок - прихід символу "повернення каретки". Але постарайтеся подивитися опис функції SetCommMask (клавіша F1 перебувати зверху зліва клавіатури). Дуже корисно знати, за яким подіям можна ще організувати обробку.

SetCommMask (CommHandle, EV_RXFLAG); - встановлюємо маску EV_RXFLAG - "обробка за певним символу". Іншими словами, як тільки в порт прийде необхідний символ - то програма почне обробляти цю подію. При відстеженні кількох подій вони задаються через or (логічне або).

Сам символ задаємо в DCB-структурі. DCB- структура - це керуюча структура Вашого порту. Ключова штуковина. Необхідно її заповнити. Власне в ній визначаються настройки порту.

GetCommState (CommHandle, DCB); - отримуємо поточний DCB.
DCB.BaudRate: = CBR_9600; - встановлюємо швидкість роботи.
DCB.Parity: = NOPARITY; - встановлюємо відсутність перевірки на парність
DCB.ByteSize: = 8; - 8 біт в переданому байті.
DCB.StopBits: = OneStopBit; - одиночний стоп-біт.
DCB.EvtChar: = chr (13); - ось власне задаємо символ для SetCommMask. В даному випадку - повернення каретки.
SetCommState (cId, DCB); - ну тепер власне прописуємо виправлене DCB.

Природно це не всі параметри DCB, а тільки найважливіші та часто використовувані. Всю структуру DCB, а так само всі значення параметрів можна отримати за допомогою все тієї ж красивою клавіші під назвою F1.

процедура ReadComm

Тепер уважно розглянемо процедуру ReadComm. По-перше, це звичайна процедура, входячи складу модуля. По-друге, всі її вміст зациклюється за допомогою while true do. Але це не смертельно. Все-таки запускаємо процедуру виключно в окремому потоці, так що біди не буде. Потік ж убивається в міру необхідності.

Отже, процедура містить цикл і в циклі чекає подія Тут, власне, наш потік зупиняється і чекає якої-небудь події прописаного в SetCommMask (в процедурі PortInit). Як тільки подія відбулася програма йде далі
(TransMask and EV_RXFLAG) = EV_RXFLAG - цим виразом ми перевіряємо, а чи то подія відбулася, що нам треба. Начебто те саме. Тут треба зазначити, що в SetCommMask можна поставити за допомогою оператора or кілька подій. Тоді в обробнику після очікування треба відповідно передбачити і кілька if then. Що б прописати реакцію на кожну подію.
Йдемо далі.

ClearCommError (CommHandle, Errs, @ Stat); - всупереч назві, тут ця функція очищає не помилки, а власне факт приходу події. Без неї RXFLAG так і залишиться висіти. Можете спробувати вгадати, що буде на наступному циклі прийому.

Ну а потім йде власне прийом Kols: = Stat.cbInQue; - беремо кількість байт в буфері порту
ReadFile (CommHandle, Resive, Kols, Kols, @ Ovr); - зчитуємо все в масив Resive.

Далі все отримане в Resive треба обробити. Як це робиться - питання конкретного розробника, зручності і звичайно протоколу обміну. Одне можу сказати точно - НЕ РОБІТЬ як зроблено в прикладі. Не треба з потоку звертатися до візуальних компонентів (наприклад, до Panel1 J). Краще рішення - щоб юніт по роботі з портом взагалі не бачив головний юніт і головну форму. Висновок даних на екран або на обробку можна зробити або за таймером або, наприклад, пославши призначене для користувача повідомлення (message), ну а в обробнику організувати висновок прийнятої інформації на екран.

Процедура WriteComm.

процедура KillComm

Зав'язуємо використання порту і все підмітаємо за собою. TerminateThread (CommThread, 0); - "вбивство" паралельного потоку прийому
CloseHandle (CommHandle); - "вбивство" власне файлу-порту.

висновок

Схожі статті