Delphi com port android

Работа с COM портом в Android приложениях

Все доброго времени суток!

Однажды я задумался над разработкой приложения, которое требовало бы общения с hardware устройством, через COM порт. Хотя Android заботливо предоставляет api для работы с usb устройствами, оно мне к сожалению не подходило, так как хотелось осуществлять поддержку моего старого планшета на базе андроид версии 2.2. Стандартное api ото Гугла, не подходило

по двум причинам:

  • во первых оно не сработало для меня.
  • девайс у меня все-таки старенький

Итак, после длительного поиска в интернете, я наткнулся на очень интересное решение android-serialport-api. Данное решение представляет собой Java обертку в которой через JNI в которой осуществляются вызовы к USB устройству.

Разработчики библиотеки предлагают 4 различных варианта подключения Android устройства к COM порту через USB.

Так как я имел в своем распоряжении планшет с USB host портом, и у меня под рукой имеется USB to RS232 преобразователь, то я пошел по варианту номер 2.

Преимущества и недостатки такого решения заключаются в

Преимущества

  • USB — RS232 преобразователь можно легко найти на ближайшем радио рынке
  • никаких извращений с моим дорогим моему сердцу планшетом
  • возможная высокая скорость передачи информации

Недостатки

  • потребуется USB хост коннектор
  • возможно, что девайс придется рутануть

Тут я опишу подход в использовании этой библиотеки.

Для корректной работы serialPortFinder необходимо чтобы переходник USB-RS232 был подключен, определен в /dev.

Вот таким нехитрым способом можно осуществлять общение с очень большим количеством железа через COM порт,

Источник

Проекты Логинова Дмитрия

Выбор библиотеки для работы с COM-портами в Delphi

Версия от 30.10.2011

Данная тема в Интернете раскрыта уже до такой степени, что мне остается добавить от себя лишь несколько слов.
Существует огромное количество библиотек для работы с COM-портами, как платных, так и бесплатных. Разумеется, обзором платных библиотек заниматься мы здесь не будем, ограничимся только бесплатными. Среди них наиболее популярными являются: AsyncPro и ComPort Library.

AsyncPro

AsyncPro — это известная, в былые времена популярная библиотека для работы с различным подключаемым к компьютеру оборудованием, обладающая огромными возможностями. Библиотека весьма сложная и громоздкая. Поддерживает большое количество устройств, конвертеров, адаптеров, драйверов и т.п. Библиотека упрощает работу с обычными модемами, GSM-модемами (в том числе отправка SMS) и т.д.
В части работы с COM-портами (и не только) выявлены следующие недостатки библиотеки AsyncPro:

  • Вся синхронизация выполняется в основном потоке. Попытка реализовать прием данных в дополнительном потоке не увенчалась успехом. Плохо то, что COM-порт приходится открывать в основном потоке. Для встроенных COM-портов и PCI-to-Serial-плат в этом нет ничего страшного, однако при использовании популярных конвертеров Ethernet-to-Serial Moxa подвисания основного потока составляют секунды. Кроме того, основной поток весьма существенно подвисает при попытке работы с модемом в том случае, если он не подключен, либо некорректно выставлена скорость.
  • В связи с необходимостью поддержки большого числа COM-устройств и всевозможных драйверов работы с COM-портами неограниченного радиуса кривизны, в библиотеке APRO реализована обработка всех возможных событий и ситуаций (особенно радуют «выкрутасы» с «недокументированным» EV_RINGTE). Набор событий «EV_», на которые реагирует APRO, задан с помощью константы, поэтому программист поменять его не может. Для чтения одной стандартной порции из 14 байт (размер буфера FIFO) библиотека APRO выполнит сотни вызовов WaitCommEvent и GetModemStatus. Даже после окончания обмена данными библиотека продолжит каждые 50 мс осуществлять вызов функций GetModemStatus и ReadFile (опять же, с целью поддержки кривых драйверов). Таким образом, библиотека APRO существенно ухудшает производительность приложения. Особенно это становится заметным, если Вам требуется произвести обмен данными с несколькими устройствами одновременно. На практике, пользователю очень быстро надоедает работать с подобными приложениями.
  • Библиотека давно перестала развиваться. Представленная на сайте sf.net последняя версия 5.00 хоть и портирована под юникодные Delphi, однако доступны лишь несколько компонентов (к счастью, в их числе компонент для работы с COM-портами TApdComPort). Качество портирования вызывает большие сомнения, поскольку при компиляции постоянно лезут варнинги, преимущественно связанные с преобразованием строк. В правильно портированной библиотеке не должно быть никаких варнингов.
  • Имеются трудности в установке библиотеки APRO на юникодные Delphi (однако они легко решаются).

В качестве особенности библиотеки APRO следует упомянуть кэширование принимаемых из порта данных (вернее сказать — от драйвера). В процессе приема байтов участвуют как минимум 3 кэша: буфер FIFO COM-порта (для обычных UART — до 14 байт, как указано в окне настройки COM-порта в Windows; для других устройств, видимо, имеется что-то похожее), затем данные из буфера COM-порта поступают в кэш драйвера (обычно не более 4КБ, для драйверов память — это всегда большой дефицит), после этого программа считывает принятые данные из кэша драйвера в свой собственных кэш (с помощью функции ReadFile). У библиотеки APRO имеется еще один кэш, в который складируются данные, считанные с помощью череды вызовов ReadFile. Наличие собственного большого кэша снижает вероятность ошибки переполнения кэша драйвера (ведь если эти данные не считывать, то кэш драйвера может заполниться очень быстро — это 400 мс на скорости 115200). Даже если в обработчике OnTriggerAvail вызвать ShowMessage, библиотека APRO все равно будет параллельно считывать данные с COM-порта (пока не переполнится ее собственный кэш).

ComPort Library

ComPort Library — это проверенная годами (более 10 лет) библиотека для работы с COM-портами. Ее реализация во много раз проще и понятнее, чем APRO. Библиотека имеет следующие особенности:

  • По умолчанию используется синхронизация через основной поток, однако ее можно легко отключить (с помощью свойства SyncMethod, его следует вызывать до метода Open).
  • Важной особенностью данной библиотеки является то, что она не кэширует принимаемые данные, а вместо этого попросту вызывает обработчик события OnRxChar (либо override-метод DoRxChar), причем в контексте основного потока (по умолчанию), а уже из этого обработчика следует вызвать одну из функций приёма данных, например, Read или ReadStr и только после этого будут считаны данные, накопившиеся в кэше драйвера. Очевидно, что если основной поток в это время занят чем-то другим, то он может не успеть считать кэш драйвера, произойдет его переполнение, что может привести к потере данных и нарушению протокола обмена с устройством. Может быть другая ситуация: устройство (например, касса Штрих или Феликс) отправляет на компьютер данные и ожидает от него не более 100 мс байта подтверждения приёма ACK. Если основной поток подвит, то он не успеет ответить за 100 мс и произойдет нарушение обмена данными. В таких случаях следует отключать синхронизацию. При этом важно помнить, что обработчик OnRxChar будет вызван из дополнительного потока, поэтому в этом обработчике не должно быть кода, обращающегося к объектам основного потока без синхронизации. Следует тщательно следить за кодом отправки данных — нельзя вызывать метод Write одновременно из разных потоков.
  • Важным преимуществом библиотеки ComPort Library является возможность установить набор событий COM-порта, на которые библиотека будет реагировать, например, Port.Events := [evRxChar]. Благодаря этому, работа с COM-портом не приводит к ухудшению производительности (даже если осуществляется одновременный обмен с множеством устройств, подключенных к разным COM-портам).
  • Следующей особенностью библиотеки является то, что метод Write осуществляет синхронную запись в порт. Функция не завершится, пока в порт не будут переданы все байты. Для асинхронной записи служит метод WriteAsync, однако перед его вызовом необходимо инициализировать указатель PAsync с помощью процедуры InitAsync (см. исходники библиотеки).

Ссылки

Разумеется, при работе с COM-портами следует иметь общее представление об используемым механизмах. Предлагаю следующую подборку ссылок:

А для простых экспериментов достаточно закоротить Tx на Rx (замкнуть между собой выходы 2 и 3 в разъеме COM-порта DB9).

Источник

Работа с COM-портами в Delphi.

Вопрос «как работать с COM-портами?» стал классическим на многих конференциях по языкам программирования. Рано или поздно чуть не каждому программисту приходится работать с портами ввода/вывода. Сегодня я хочу рассказать про работу с последовательным портом из-под самой распространенной на сегодняшний день 32-разрядной операционной системы — Windows. Статья построена по принципу «от простого к сложному». Сначала будут изложены основы работы с портами из-под Win32 с описанием необходимых функций. Затем рассмотрим применение этих функций на примере Delphi-программы. Конечным результатом будет класс, предназначенный для работы с COM-портом, и пример использующей его программы. По ссылке Serial Net Tools — находится написанная мной программа для работы с COM-портом.

Очень часто программисту приходится управлять с помощью компьютера каким-либо внешним устройством, или просто анализировать состояние этого устройства. Порты ввода/вывода — самый распространенный способ сопряжения компьютера и внешнего устройства. Давным-давно уже написано множество классов, библиотек и компонент для работы с портами, поэтому можно, конечно, воспользоваться уже готовым и к тому же бесплатным решением. Именно так я и поступил лет семь назад, при этом потеряв самое главное — своевременное понимание того, как все-таки работать с портами из-под Win32. Незнание внутренних механизмов — это, во-первых, пробел в стройном ряду знаний, а во-вторых, актуальная возможность ошибок в работе программы.

С портами из-под Win32 работают так же, как и с обычными файлами, используя при этом всего несколько специфичных функций WinAPI. Однако коммуникационный порт — это не совсем обычный файл. Для него, например, нельзя выполнить позиционирование файлового указателя, или же создать порт, если таковой отсутствует. Любая работа с портом начинается с его открытия. Для этого используется файловая функция WinAPI (описания WinAPI-функций взяты из MSDN (Microsoft Developer Network), следовательно, приводятся в синтаксисе C):

HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);lpFileName — указатель на строку с нулевым завершающим символом. Обычно это имя открываемого файла, но в нашем случае это должно быть название порта (COM1, COM2, …).

dwDesiredAccess — тип доступа. В нашем случае должен быть равен GENERIC_READ|GENERIC_WRITE.

dwShareMode — параметр совместного доступа. Для коммуникационных портов всегда равен 0.

lpSecurityAttributes — атрибут защиты. Для коммуникационных портов всегда равен NULL.

dwCreationDistribution — режим автосоздания. Для коммуникационных портов всегда равен OPEN_EXESTING.

dwFlagsAndAttributes — атрибут режима обработки. Для коммуникационных портов должен быть равен 0 или FILE_FLAG_OVERLAPPED.

hTemplateFile — описатель файла-шаблона. Для коммуникационных портов должен быть равен NULL.

При успешном открытии порта функция возвращает его описатель, а в случае ошибки возвращает INVALID_HANDLE_VALUE.

Сразу оговорюсь: все недостающие описания можно найти на http://msdn.microsoft.comи еще по ряду адресов, которые вам подскажет поисковый сервер.

Из всех параметров функции CreateFile() особого пояснения требует dwFlagsAndAttributes. Работа с портом может быть организована в синхронном (nonoverlapped) или асинхронном (overlapped) режимах обработки, что и задается этим флагом. При синхронном режиме (когда параметр dwFlagsAndAttributes = 0) только один поток приложения может либо читать, либо писать в порт. Помните переговорное устройство в лифте? Нажали кнопку — можем только говорить, отпустили кнопку — можем только слушать.

Синхронный режим обработки прост в реализации. Если надо записать данные в порт, то вызываем функцию записи и ожидаем, пока она не завершится. Если же надо читать данные, то вызываем функцию чтения и ждем, пока она не отработает. Для простых задач синхронный режим обработки вполне подходит, однако в мире Windows он почти всегда обречен на неудачу. Ожидание операции чтения или записи воспринимается пользователем программы как «зависание».

Асинхронный режим (когда параметр dwFlagsAndAttributes = FILE_FLAG_OVERLAPPED) позволяет производить операции чтения и записи в порт параллельно из разных потоков. В то время, пока один поток приложения принимает данные, другой поток может параллельно с первым передавать данные — как при разговоре по телефону, когда вы можете слушать и говорить одновременно. Данный режим обработки больше импонирует идее многозадачности Windows. Но за все надо платить: для реализации этого режима обработки требуется в два раза больше написанного кода, вдобавок, умения писать многопоточные программы. Какой режим выбрать — решайте сами. Но если уж разбираться в работе порта, то разбираться «по-взрослому», до конца, а потому и рассмотрим более сложный вариант — асинхронную обработку.

На практике открытие порта для асинхронного режима обработки из программы на Delphi выглядит примерно так:

hPort := CreateFile(‘COM1’, GENERIC_READ or GENERIC_WRITE, 0, nil,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if hPort = INVALID_HANDLE_VALUE then
raise Exception.Create(‘Error opening port’);Функция возвращает описатель порта (hPort), который нам потом пригодится для вызова других функций работы с портом. Если в результате открытия порта описатель не получен, то возбуждается исключение с соответствующим текстом ошибки. Открыв порт, мы получаем его в свое распоряжение. Теперь с этим портом может работать только наша программа (точнее, только наш процесс). По окончании работы с портом его следует закрыть, вызвав функцию:

BOOL CloseHandle(
HANDLE hObject
);В качестве единственного параметра надо передать полученный ранее описатель порта (hPort).

Хоть система при завершении выполнения программы и освобождает все выделенные ей ресурсы (в том числе и порты), хорошим тоном программирования считается собственноручное закрытие портов. Открывать/закрывать порт как будто несложно. Кроме того, нам потребуется программная настройка порта. Думаю, все видели диалог настройки последовательного порта в диспетчере устройств системы. Все эти настройки мы можем произвести программно. Для этих целей используется функция WinAPI:

BOOL SetCommState(
HANDLE hFile,
LPDCB lpDCB
);hFile — описатель открытого порта.
lpDCB — указатель на структуру DCB.

Основные параметры последовательного порта описываются структурой DCB. Она содержит массу полей, каждое из которых соответствует определенному параметру настройки порта. Мы рассмотрим несколько полей, которые нам нужны:

BaudRate — скорость передачи данных. Возможно указание констант —CBR_100, CBR_300, CBR_600, CBR_1200, …, CBR_256000.
Parity — схема контроля четности. Может содержать одно из следующих значений: EVENPARITY, MARKPARITY, NOPARITY, ODDPARITY, SPACEPARITY.
ByteSize — число информационных бит в передаваемых и принимаемых байтах.
StopBits — количество стоповых бит. Может быть ONESTOPBIT, ONE5STOPBIT, TWOSTOPBIT.
Чтобы не заполнять структуру DCB вручную, ее можно заполнить информацией о текущем состоянии порта вызовом функции GetCommState(), затем изменить необходимые поля и установить настройки вызовом функции SetCommState(). Настройку порта желательно производить сразу после его открытия. На Delphi это выглядит так:

var
Dcb: TDcb;

if not GetCommState(hPort, Dcb) then
raise Exception.Create(‘Error setting port state’);

Dcb.BaudRate := CBR_9600;
Dcb.Parity := NOPARITY;
Dcb.ByteSize := 8;
Dcb.StopBits := ONESTOPBIT;

if not SetCommState(hPort, Dcb) then
raise Exception.Create(‘Error setting port state’);
Еще одна операция, которая нам понадобится сразу после открытия порта — его сброс.

BOOL PurgeComm(
HANDLE hFile,
DWORD dwFlags
);Вызов этой функции очищает очередь приема/передачи и завершает все находящиеся в ожидании запросы ввода/вывода.

hFile — описатель открытого порта.
dwFlags — производимые действия в виде набора флагов PURGE_TXABORT, PURGE_RXABORT, PURGE_TXCLEAR, PURGE_RXCLEAR.
Пример на Delphi:

if not PurgeComm(hPort, PURGE_TXCLEAR or PURGE_RXCLEAR) then
raise Exception.Create(‘Error purging port’);На этом подготовительная фаза заканчивается, и можно приступать непосредственно к приему/передаче данных. Прием данных у нас будет происходить по событийной схеме; программа будет ожидать прием одного или нескольких символов (байт). Для перевода порта в этот режим необходимо вызвать функцию SetCommMask() с флагом EV_RXCHAR:

if not SetCommMask(hPort, EV_RXCHAR) then
raise Exception.Create(‘Error setting port mask’);Прием и передача данных выполняется функциями ReadFile() и WriteFile(), то есть теми же самыми функциями, которые используются для работы с дисковыми файлами. Вот их описание:

BOOL ReadFile(
HANDLE hFile,
LPVOID lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);

BOOL WriteFile(
HANDLE hFile,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
LPOVERLAPPED lpOverlapped
);
hFile — описатель открытого порта.
lpBuffer — адрес буфера.
nNumberOfBytesToRead/nNumberOfBytesToWrite — число ожидаемых к приему или предназначенных для передачи байт.
lpNumberOfBytesRead/lpNumberOfBytesWritten — число фактически принятых или переданных байт.
lpOverlapped — адрес структуры OVERLAPPED, используемой для асинхронных операций.
Передача данных является довольно быстрой операцией, поэтому как правило ее выполняют из главного потока приложения. На Delphi это выглядит так:

var
dwWrite: DWORD;
OverWrite: TOverlapped;
WriteBytes: array of Byte;

begin
OverWrite.hEvent := CreateEvent(nil, True, False, nil);
if OverWrite.hEvent = Null then
raise Exception.Create(‘Error creating write event’);

if (not WriteFile(hPort, WriteBytes, SizeOf(WriteBytes),
dwWrite, @OverWrite))
and (GetLastError <> ERROR_IO_PENDING) then
raise Exception.Create(‘Error writing port’);
end;В данном примере функция WriteFile() выполняет асинхронную запись массива байтов WriteBytes в порт. Она сразу возвращает управление, и запись в порт происходит параллельно с выполнением основного кода потока. Если результат WriteFile() равен False, то это значит, что на момент возврата управления передача массива байтов еще не закончилась. Поэтому код ошибки выполнения WriteFile() в данном случае должен быть равен ERROR_IO_PENDING. Переменная OverWrite — overlapped-структура, необходимая для асинхронных операций.

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

Рассмотрение работы с потоками в Windows, в частности того, как это реализовано в Delphi, выходит за рамки данной статьи. Предполагаю, что читатель встречался или по крайней мере знаком с этим. Скажу лишь, что у любого потока есть главная функция, которая начинает выполняться после его создания. В Delphi для потоков существует класс TThread, а его главная процедура называется TThread.Execute().

Вот так выглядит главная процедура отдельного потока, которая ожидает появление одного или нескольких символов и считывает их:

procedure TReadThread.Execute;
var
ComStat: TComStat;
dwMask, dwError: DWORD;
OverRead: TOverlapped;
Buf: array[0..$FF] of Byte;
dwRead: DWORD;
begin
OverRead.hEvent := CreateEvent(nil, True, False, nil);
if OverRead.hEvent = Null then
raise Exception.Create(‘Error creating read event’);

while not Terminated do
begin
if not WaitCommEvent(hPort, dwMask, @OverRead) then
begin
if GetLastError = ERROR_IO_PENDING then
WaitForSingleObject(OverRead.hEvent, INFINITE)
else
raise Exception.Create(‘Error waiting port event’);
end;

if not ClearCommError(hPort, dwError, @ComStat) then
raise Exception.Create(‘Error clearing port’);

if dwRead > 0 then
begin
if not ReadFile(hPort, Buf, dwRead, dwRead, @OverRead) then
raise Exception.Create(‘Error reading port’);
// В Buf находятся прочитанные байты
// Далее идет обработка принятых байтов
end;
end;
end;
В приведенном примере в потоке крутится цикл, тем самым инициируется ожидание события порта вызовом функции WaitCommEvent(), ожидание же самого этого события задается функцией WaitForSingleObject(). Для определения количества принятых символов используется функция ClearCommError(). Когда количество принятых символов (dwRead) известно, непосредственное чтение символов выполняется функцией ReadFile().

Используя вышеописанные выкладки, я написал на Borland Delphi 7 класс TComPort для работы с COM-портами. Для проверки работоспособности программы я просто соединил нуль-модемным кабелем два COM-порта на своем компьютере и запустил два экземпляра программы для каждого порта. Данные передаются через один порт и одновременно принимаются через другой.

Для передачи и приема данных предусмотрены отдельные окна. Формат передаваемых данных — строка. Принимаемые данные представляются в виде массива байт.

Источник

Читайте также:  Настройка телевизора через android
Оцените статью