Автор: Electron18
www.softelectro.ru    
2009              
electron18@softelectro.ru
Работа коммуникационных портов реализована на универсальных асинхронных приемопередатчиках UART.
UART- это микросхемы, которые работают по стандарту RS-232C.
Для СОМ порта компьютера используется 9-ти штырьковый разъем DE9p согласно стандарта TIA-574.
В этом разъеме используется шесть сервисных сигналов и два канала обмена последовательными данными.
Рис.1 Вид разъёмов СОМ1 и СОМ2 на материнской плате.
UART использует уровни сигналов -12в....+12в . Зона нечувствительности, то есть отсутствие сигналов считается напряжение -3в...+3в. При этом обратите внимания, что принимаемые/передаваемые данные инвертированы.
Рис.3 Уровни сигналов UART по стандарту RS-232c
При передаче данных символы передаются из буфера передатчика последовательно (первым пришел- первым вышел). Специально назвал символами , а не байтами, так как символы могут иметь размер от 5 до 8 бит. Каждый переданный символ снабжается стартовым и стоповым битами, предназначенным для синхронизации на приемной стороне. После стартового бита следуют биты данных, начиная с младшего бита и заканчивая старшим. За последним битом данных символа может следовать бит паритета, служащий для обнаружения ошибки передачи битов данных. Последним передается стоповый бит, который необходим для временного разделения переданных символов
Рис.4 Показана передача символов "0" "0" без паритета, с одним стоповым битом
На рисунке 4 хорошо видно, что стоповый бит разделяет два переданных символа. При необходимости можно увеличить этот интервал до 2 стоповых битов, если конечное устройство не успевает разделять символы.
Рис.5 Показана передача символов "0" "0" с проверкой на четность (EVEN), с одним стоповым битом
Нуль-модемное соединение двух COM портов.
При таком соединении компьютеры(терминалы) соединяются между собой непосредственно через СОМ-порты, без использования модемов. Так как компьютеры обладают большой скоростью обработки данных, то синхронизировать их работу не нужно. Поэтому предполагается, что режим синхронизации обмена (Handshaking): 0-None, то есть сервисные сигналы не влияют на процедуры обмена данными. Для этого используется нуль-модемный кабель.
Рис.6 Нуль-модемный кабель для Handshaking = 0 (None)
Так как режим синхронизации обмена на СОМ портах может быть включен, то часто сервисные сигналы СОМ портов замыкают самих на себя, тем самым исключая их влияния на процедуру обмена.
Рис.7 Нуль-модемный кабель для любых режимов Handshaking
Если необходимо можно использовать полный кабель, но при этом СОМ-порты должны быть настроены на аппаратную синхронизацию обмена.
Данный режим используют когда устройство не успевает перерабатывать информацию полученную по СОМ-порту (меделенное устройство или PC).
Этот режим позволяет останавливать обмен данных на время обработки полученной информации.
Рис. 8 Нуль-модемный кабель для аппаратного режима синхронизации Handshaking=2
Модемное соединение.
Модемное соединение подразумевает соединение двух компьютеров(DTE) через модемы(DCE). Модемы (модуляторы-демодуляторы) - специальные устройства, позволяющие вести обмен данными практически на неограниченное расстояния, используя для этого модуляцию и демодуляцию информационных сигналов. Поэтому модемное соединение подразумевает подключение СОМ-порта компьютера(DTE) к конечному устройству модему (DCE). Обычно в таком соединении используют аппаратный режим синхронизации Handshaking =2 (модемы - это медленные устройства). Этот режим позволяет модемам управлять процессом передачи данных.
Рис. 9 Типичный модемный кабель.
Аппаратный режим синхронизации обмена данными RTS/CTS (hardware flow control) Handshaking =2, использует сервисные сигналы RS-232C для управления потоком данных.
Рис.10 Организация обмена при аппаратной синхронизации.
Как видно из рис.10 модем использует сигнал CTS, который позволяет остановить передачу данных, если приемник не готов к их приему. Передатчик «выпускает» очередной байт только при включенной линии CTS. Байт, который уже начал передаваться, задержать сигналом CTS невозможно (это гарантирует целостность посылки). Аппаратный протокол обеспечивает самую быструю реакцию передатчика на состояние приемника.
Программный протокол управления потоком XON/XOFF( Handshaking =1). Работает протокол следующим образом: если устройство, принимающее данные, обнаруживает причины, по которым оно не может их дальше принимать, оно по обратному последовательному каналу посылает байт-символ XOFF (13hex). Противоположное устройство, приняв этот символ, приостанавливает передачу. Когда принимающее устройство снова становится готовым к приему данных, оно посылает символ XON (11hex), приняв который противоположное устройство возобновляет передачу. Время реакции передатчика на изменение состояния приемника по сравнению с аппаратным протоколом увеличивается, по крайней мере, на время передачи символа (XON или XOFF) плюс время реакции программы передатчика на прием символа. Преимущество программного протокола заключается в отсутствии необходимости передачи управляющих сигналов интерфейса — минимальный кабель для двустороннего обмена может иметь только 3 провода. Недостатком данного метода является большее время реагирования и исключения из передаваемого потока двух символов (13hex, 11hex).
Существует смешанный метод синхронизации обмена данными RTS/XOn/Xoff (Handshaking =3), который представляет собой объединение двух предыдущих методов.
Для кодирования символов передаваемых по RS-232С используется таблица, кодирующая использованные символы и управляющие знаки.
Рис.11 Стандартная кодовая таблица ASCII
Первые 32 символа этой кодовой страницы представляют собой управляющие символы, которые предназначены для управления модемом. Например, использование символов 17(11hex) и 19(13hex) были изложены выше, в программном способе управления обменом. Эти символы были разработаны в основном для управления печатающими устройствами и модемами
00(00hex) - NUL пустой символ | 08(08hex)- BS возврат на одну позицию | 16(10hex)- DLE переключение кода | 24(18hex)- CAN отмена |
01(01hex)- SOH начало заголовка | 09(09hex)- HT горизонтальная табуляция | 17(11hex)- DC1 управление первым устройством (XON) | 25(19hex)- EM конец носителя |
02(02hex)- STX начало текста | 10(0Ahex)- LF перевод строки | 18(12hex)- DC2 управление вторым устройством | 26(1Ahex)- SUB замена |
03(03hex)- ETX конец текста | 11(0Bhex)- VT вертикальная табуляция | 19(13hex)- DC3 управление третьим устройством (XOFF) | 27(1Bhex)- ESC переход |
04(04hex)- EOT конец передачи | 12(0Chex)- FF подача бланка (новый лист) | 20(14hex)- DC4 управление четвертым устройством | 28(1Chex)- FS разделитель файла |
05(05hex)- ENQ запрос | 13(0Dhex)- CR возврат каретки | 21(15hex)- NAK переспрос | 29(1Dhex)- GS разделитель группы |
06(06hex)- ACK подтверждение | 14(0Ehex)- SO переход на верхний регистр | 22(16hex)- SYN режим синхронного ожидания | 30(1Ehex)- RS разделитель записи |
07(07hex)- BEL звонок | 15(0Fhex)- SI переход на нижний регистр | 23(17hex)- ETB конец передачи блока | 31(1Fhex)- US разделитель блока |
Для аппаратной реализации СОМ портов по стандарту RS-232 используется специализированная микросхема UART. UART (Universal Asynchronous Receiver-Transmitter )- универсальный асинхронный приёмо-передатчик. Микросхема i8250 установленная в IBM XT послужила началом целой серии микросхем UART, которые устанавливались на материнские платы PC.
Микросхемы выпускались разными фирмами производителями: Intel, National Semiconductor, Maxim и др.
Микросхема представляет собой управляемую логическую схему с буферными регистрами для приёма и передачи последовательных данных.
Буферные регистры позволяют вести передачу и приём данных без участия CPU.
Соответственно чем больше ёмкость буферных регистров, тем реже микросхема прерывает работу CPU.
Буферные регистры устроены по принципу "очереди" (FIFO) - первым пришел, первым вышел.
Получив порцию данных в передающий буферный регистр, UART начинает передавать её в сеть RS-232, одновременно он может принимать данные из сети RS-232 в приёмный буферный регистр.
Программное обеспечение в любой момент может обратиться к приёмному буферу UART, тем самым освободив его для приёма следующих данных.
При заполнении приёмного буфера UART может прервать работу CPU, сообщив ему о заполнении буфера.
Заполнение приёмного буфера вызовет остановку приёма данных из сети RS-232, до тех пор пока он не будет прочитан.
Рассмотрим работу UART на примере микросхемы PC16550D
Рис.12 Стандартная схема включения UART PC16550D с микропроцессором Intel 8088
Обращение к микросхеме осуществляется через адресное пространство портов ввода-вывода CPU.
Микросхема подключается к системной шине при активизации сигнала CS0, который вырабатывается при обращении CPU к заданному диапазону адресов порта.
Адреса портов ввода-вывода заданы в BIOS. Обычно они имеют значения: COM1=3F8h, COM2=2F8h, COM3=3E8h, COM4=2E8h .
На входы UART A0,A1,A2 подаются три младших разряда адресной шины CPU.
Адрес заданный в BIOS является начальным адресом диапазона адресов (A2A1A0=000).
Следовательно полный диапазон адресов для каждого порта равен 8 адресам (от A2A1A0=000 до A2A1A0=111).
Например, для СОМ4 2E8h,2E9h,2EAh,2EBh,2ECh,2EDh,2EEh,2EFh.
Расстояние между начальными адресами портов равно 16, что допускает в дальнейшем использования микросхем с четырьмя начальными адресными линиями.
Обращение к микросхеме по определённому адресу открывает доступ к группе регистров управления или буферных регистров приёма и передачи.
CPU может записать данные в регистры UART выставив сигнал WR=0, или прочитать данные, выставив сигнал RD=0.
UART состоит из 12 регистров, к которым можно обратиться по восьми адресам портов ввода-вывода.
Так как индивидуального адреса для каждого регистра не хватает, то используют расщепление адресного пространства с помощью следующих методов:
    1.Разделение одного адресного пространства на два регистра по записи/чтению.
        По сигналу чтения RD=0 читается один регистр, по сигналу записи WR=0 записывается второй регистр.
        То есть данные по одинаковому адресу записываются или читаются с разных регистров.
        Таких регистров четыре:
                THR, RBR - по адресу UART 00h(A2A1A0=000)
                IIR, FOR - по адресу UART 02h(A2A1A0=010)
        Эти регистры односторонние, то есть в одни можно только записывать, в другие только читать данные.
    2.Использование дополнительного адресного бита
        Используют 7-ой бит регистра LCR-находящегося по адресу UART 03h(A2A1A0=011).
        Этот бит называют DLAB, если DLAB=0, то для чтения/записи используется один регистр,
        если DLAB=1, то для чтения/записи используется второй регистр.
        Таких регистров пять:
                (THR & RBR),DLL - по адресу UART 00h(A2A1A0=000)
                DIM, IER - по адресу UART 01h(A2A1A0=001)
адрес | DLAB | чтение/запись | Название регистра |
00h | 0 | WR | THR(Transmit Holding Register)-регистр данных ожидающих передачи |
00h | 0 | RD | RBR(Receiver Buffer Register)- буферный регистр приемника |
00h | 1 | RD/WR | DLL(Divisor Latch LSB)-младший байт делителя частоты |
01h | 1 | RD/WR | DIM(Divisor Latch MSB)-старший байт делителя частоты |
01h | 0 | RD/WR | IER(Interrupt Enable Register)-регистр разрешения прерывания |
02h | х | RD | IIR(Interrupt Identification Register)-регистр идентифицирующий прерывания |
02h | х | WR | FCR(FIFO Control Register)-регистр управления режимом FIFO |
03h | x | RD/WR | LCR(Line Control Register)-регистр управления линией связи |
04h | x | RD/WR | MCR(Modem Control Register)-регистр управления модемом |
05h | x | RD/WR | LSR(Line Status Register)-регистр состояния линии связи |
06h | x | RD/WR | MSR(Modem Status Register)-регистр состояния модема |
07h | x | RD/WR | SCR(Scratch Pad Register)-регистр временного хранения |
Рис.13 Функциональная схема UART PC16550.
THR-регистр данных ожидающих передачи(только для записи)
(Transmit Holding Register)
        В этот регистр записывают байт данных, определённый как символ (от 5 до 8 бит) который будет передан в линию связи.
Символ, принятый в THR передаётся далее в сдвигающий регистр младшем битом вперед (см. рис.5).
В начало символа добавляется стартовый бит, в конец символа добавляется стоповый бит.
Перед стоповым битом может находиться бит паритета.
Если символ короче 8 бит, то старшие биты регистра THR игнорируются (не используются, хотя записываются в этот регистр).
Регистр THR может принять всего один байт данных и передать его в регистр последовательного сдвига.
В большинстве UART имеется режим FIFO, в котором данные загружаются не в THR, а в регистр FIFO.
Например, UART PC16550 имеет регистр FIFO, который может принять 16 байт данных.
Кроме этого у некоторых UART существует режим DMA, в этом режиме сдвигающий регистр заполняется байтами данных непосредственно из оперативной памяти без участия микропроцессора.
Для указания того, что регистр THR пуст и в него можно загрузить очередной байт данных используют бит 5 регистра LSR.
Этот бит называется THRE(Transmitter Holding Register Empty) -"регистр данных ожидающих передачи пуст".
Если THRE=1, то в регистр THR можно посылать очередной байт данных, в режиме FIFO этот бит говорит о том, что регистр FIFO пуст и можно посылать следующий пакет байтов данных.
Бит THRE может быть источником прерывания CPU.
RBR- буферный регистр приемника(только для чтения)
(Receiver Buffer Register)
        В этот регистр байты(символы) принимаются из приемного сдвигающегося регистра.
Регистр RBR может принять только один байт из сдвигающего приемного регистра.
Аналогично передающей части UART здесь есть регистр FIFO, который может принимать больше одного байта данных минуя регистр RBR.
К моменту заполнения сдвигающего приёмного регистра регистр RBR должен быть освобожден для приема очередного байта, иначе произойдет ошибка переполнения.
Освобождение регистра RBR происходит, когда данные из него читаются микропроцессором.
О том, что символ потерян в результате переполнения сообщает бит 1 регистра LSR.
Этот бит называется ОЕ (Overrun Error)-"ошибка переполнения", OE=1 означает что один из переданных символов потерян.
О том, что байт готов к прочтению микропроцессором (т.е. полностью выгрузился из приемного сдвигающегося регистра или FIFO) сообщает бит 0 регистра LSR.
Этот бит называется DR (Receiver Data Ready) -"Данные приёмника готовы".
DR=1 говорит о том, что регистр RBR(или FIFO) содержит принятый байт и его необходимо прочитать, DR сбрасывается в ноль после прочтения регистра RBR микропроцессором.
Это бит также может инициировать прерывание микропроцессора.
DLL-младший байт делителя частоты :16 (чтение/запись)
(Divisor Latch LSB)
        В это регистре находится младший байт делителя частоты деленного на 16.
DIM-старший байт делителя частоты :16 (чтение/запись)
(Divisor Latch MSB)
        В этом регистре находится старший байт делителя частоты деленного на 16.
В микросхеме UART частота задающего кварца делится на делитель частоты(Decimal Divisor),который получается из двухбайтового числа (DIM,DLL) умноженного на 16.
Таким образом делитель частоты задает скорость обмена данных через UART.
Записью в регистры DIM и DLL старшего и младшего байта этого двухбайтового числа вы зададите скорость обмена СОМ-порта в бит/сек.
Для кварца UART частотой f=1,8432 МГц, делитель частоты:16 считается по формуле:
D=115200/V, где V-скорость в бит/сек, D=делитель частоты:16
Для кварца UART частотой f=24 МГц, делитель частоты:16 считается по формуле:D=1 500 000/V, где V-скорость в бит/сек, D=делитель частоты:16
1,8432 МГц | 24 МГц | |||||
---|---|---|---|---|---|---|
Скорость, бит/сек | делитель:16 | DIM | DLL | делитель:16 | DIM | DLL |
50 | 2304 | 09h | 00h | 30000 | 75h | 30h |
75 | 1536 | 06h | 00h | 20000 | 4Eh | 20h |
110 | 1047 | 41h | 07h | 13636 | 35h | 44h |
150 | 768 | 03h | 00h | 10000 | 27h | 10h |
300 | 384 | 01h | 80h | 5000 | 13h | 88h |
600 | 192 | 00h | C0h | 2500 | 09h | C4h |
1 200 | 96 | 00h | 60h | 1250 | 04h | E2h |
1 800 | 64 | 00h | 40h | 833 | 03h | 41h |
2 000 | 58 | 00h | 3Ah | 750 | 02h | EEh |
2 400 | 48 | 00h | 30h | 625 | 02h | 71h |
3 600 | 32 | 00h | 20h | 417 | 0h | A1h |
4 800 | 24 | 00h | 18h | 312 | 01h | 38h |
7 200 | 16 | 00h | 10h | 208 | 00h | D0h |
9 600 | 12 | 00h | 0Ch | 156 | 00h | 9Ch |
14 400 | 8 | 00h | 08h | 104 | 00h | 68h |
19 200 | 6 | 00h | 06h | 78 | 00h | 4Eh |
28 800 | 4 | 00h | 04h | 52 | 00h | 34h |
38 400 | 3 | 00h | 03h | 39 | 00h | 27h |
57 600 | 2 | 00h | 02h | 26 | 00h | 1Ah |
115 200 | 1 | 00h | 01h | 13 | 00h | 0Dh |
250 000 | x | x | x | 6 | 00h | 06h |
300 000 | x | x | x | 5 | 00h | 05h |
375 000 | x | x | x | 4 | 00h | 04h |
500 000 | x | x | x | 3 | 00h | 03h |
750 000 | x | x | x | 2 | 00h | 02h |
1 500 000 | x | x | x | 1 | 00h | 01h |
IER-регистр разрешения прерывания(чтение/запись)
(Interrupt Enable Register)
        Регистр разрешения прерываний дает разрешения определённым событиям вызывать прерывание микропроцессора.
Бит 0. RxD_IЕ — если RxD_IЕ=1,то разрешено прерывание для приема данных,это прерывание возникает когда необходимо принять символ из регистра RBR (в режиме FIFO — прерывание по тайм-ауту).
Бит 1. TxD_IE — если TxD_IEЕ=1,то разрешено прерывание для передачи данных, это прерывание возникает когда передающий буфер пуст и необходимо загрузить байт в регистр THR.
Бит 2. RxL_IЕ — если RxL_IЕ=1,то разрешено прерывание при обрыве линии связи или ошибке в приёме данных, это прерывание возникает когда в регистре состояния линии связи LSR будут выставлены биты этих ошибок.
Бит 3. Mod_IЕ — если Mod_IЕ =1,то разрешено прерывание при изменении состояния любого из входных сигналов RST,CTS,DCD,RI, это прерывание возникает когда состояние входных сигналов COM-порта изменились.
Бит 4..7. Не используются и всегда равны 0.
IIR-регистр идентифицирующий прерывания (чтение)
(Interrupt Identification Register)
        Чтобы минимизировать программное обеспечение, UART располагает по приоритетам прерывания в четыре уровня и делает запись этих прерываний в IIR.
Четыре уровня прерывания располагаются в порядке приоритета условий прерывания заданных регистрами - RLS; RDR; THR; и MSR.
Когда CPU обращается к IIR, UART замораживает все прерывания и указывает самое высокое приоритетное отложенное прерывание для CPU.
Во время обработки прерывания, UART делает запись новых прерываний, но не изменяет их текущий признак, до полной обработки.
Бит 0. IP(Interrupt Pending)— если IP=1, то все прерывания обработаны. Если IP=0,то есть необработанные прерывания.
Бит 1. I_ID0(Interrupt ID Bit0)- нулевой бит идентификатора прерываний
Бит 2. I_ID1(Interrupt ID Bit1)- первый бит идентификатора прерываний
Бит 3. I_ID2(Interrupt ID Bit2)- второй бит идентификатора прерываний
I_ID2 | I_ID1 | I_ID0 | Приоритет | идентификация |
x | 0 | 0 | Четвертый | Изменилось состояние модема, сбрасывается прочтением регистра MSR. |
x | 0 | 1 | Третий | Регистр THR пуск, ожидается байт от CPU. Сбрасывается записью байта в THR. |
x | 1 | 0 | Второй | Принят байт данных в регистр RBR, сбрасывается чтением регистра RBR. |
x | 1 | 1 | Наивысший | Обрыв линии или ошибка на линии, сбрасывается прочтением регистра LSR. |
I_ID2 | I_ID1 | I_ID0 | Прирритет | идентификация |
0 | 0 | 0 | Четвертый | Изменилось состояние модема, сбрасывается прочтением регистра MSR |
0 | 0 | 1 | Третий | Буферный регистр передачи FIFO пуск, ожидается данные от CPU. Сбрасывается записью в передающий буфер FIFO |
0 | 1 | 0 | Второй | Приемный буфер FIFO заполнился, сбрасывается чтением приемного буфера FIFO. |
0 | 1 | 1 | Наивысший | Обрыв линии или ошибка на линии, сбрасывается прочтением регистра LSR |
1 | 0 | 0 | ||
1 | 0 | 1 | ||
1 | 1 | 0 | Второй | индикатор тайм-аута (за 4-кратный интервал времени символа не передано и не принято ни одного символа, хотя в буфере FIFO имеется, по крайней мере, один символ). Сброс выполняется чтением приемного буфера FIFO. |
1 | 1 | 1 |
FE_ID1 | FE_ID0 | Режим |
0 | 0 | обычный режим работы, данные передаются побайтно через регистры THR и RBR. |
0 | 1 | |
1 | 0 | режим FIFO для UART 16550. |
1 | 1 | режим FIFO для UART 16550A. |
FCR-регистр управления режимом FIFO (запись)
(FIFO Control Register)
        Это регистр используется только для записи, его данные расположены как в регистре IIR.
Этот регистр используется, чтобы разрешить режимы FIFO, очистить буферы FIFO, задать уровень заполнения буферов FIFO, и выбрать тип DMA(прямого обращения к памяти).
Бит 0. TRFIFOE(Transmit And Receive FIFO Enable)— Запись 1 в этот бит допускает оба режима FIFO передатчика(XMIT) и приемника(RCVR).
Сброс бита в 0 очистит все байты в обоих буферов FIFO.
При изменении режима FIFO к 16450 и наоборот, буферы FIFO автоматически очищаются.
Этот бит должен быть в 1, когда производится запись других битов регистра FCR, иначе они не будут запрограммированы.
Бит 1. RESETRF(Reset Receiver FIFO)-Запись 1 в этот бит очищает все байты в приемном буфере FIFO и сбрасывает его счетчик в 0.
Сдвиговый регистр при этом не очищается.
После этого 1 в этом бите сбрасывается в 0.
Бит 2. RESETTF(Reset Transmitter FIFO)- Запись 1 в этот бит очищает все байты в передающем буфере FIFO и сбрасывает его счетчик в 0.
Сдвиговый регистр при этом не очищается.
После этого 1 в этом бите сбрасывается в 0.
Бит 3. DMAE(DMA Enabled)- Запись 1 в этот бит приводит к изменению сигналов UART RxRDY и TxRDY с 0 к 1,при условии что FCR(bit0)=1.
Эти аппаратные сигналы используются для организации правильной работы режима DMA в микропроцессорной системе.
Бит 4..5.Зарезервированные.
Бит 6. ITL_ID0 (Interrupt Trigger Level ID bit0) - нулевой бит идентификатора триггера уровня прерывания.
Бит 7. ITL_ID1(Interrupt Trigger Level ID bit1)- первый бит идентификатора триггера уровня прерывания.
В этих двух битах задается идентификатор, который задает уровень при котором будет вырабатываться прерывание при приеме данных в режиме FIFO. Уровень задается количеством байт в приемном(RCVR) буфере FIFO.
ITL_ID1 | ITL_ID0 | уровень прерывания, байт |
0 | 0 | 01 |
0 | 1 | 04 |
1 | 0 | 08 |
1 | 1 | 14 |
LCR-регистр управления линией связи(запись/чтение)
(Line Control Register)
        Данный регистр служит для определения(задания) формата асинхронного обмена передачи данных.
Также в этом режиме устанавливается бит DLAB, который позволяет программисту записывать и читать данные из нужных регистров..
Программист может не только записывать, но и читать содержимое регистра LCR.
Способность чтения упрощает системное программирование и устраняет потребность в отдельной области в системной памяти для хранения характеристик линии.
Бит 0. SDB_ID0(Serial Data Bits ID0)- нулевой бит идентификатора количества бит в передаваемом символе.
Бит 1. SDB_ID1(Serial Data Bits ID1)- первый бит идентификатора количества бит в передаваемом символе.
С помощью этих битов задают количество бит в передаваемом или принимаемом символе.
SDB_ID1 | SDB_ID0 | количество бит в символе |
0 | 0 | 5 |
0 | 1 | 6 |
1 | 0 | 7 |
1 | 1 | 8 |
MCR-регистр управления модемом (запись/чтение)
(Modem Control Register)
        Этот регистр управляет интерфейсом модема или периферийным устройством.
Бит 0. DTR(Serial Data Bits ID0)(Data Terminal Ready)- Этот бит управляет выходным сигналом DTR (Готовность терминала данных).
Когда бит DTR=1, вывод DTR UART устанавливается в логический 0, в IBM XT этот сигнал инвертируется буферным инвертором DS1488(см.рис.12) в логическую 1 т.е. U= +12в (сигнал DTR COM-порта считается включенным)
Соответственно когда бит DTR=0, сигнал DTR COM-порта U= -12в логический 0 (сигнал DTR считается выключенным)
Бит 1. RTS(Request To Send )- Этот бит управляет выходным сигналом RTS (Запрос на передачу).
Когда бит RTS=1, вывод RTS UART устанавливается в логический 0, в IBM XT этот сигнал инвертируется буферным инвертором DS1488(см.рис.15) в логическую 1 т.е. U= +12в (сигнал RTS COM-порта считается включенным)
Соответственно когда бит RTS=0, сигнал RTS COM-порта U= -12в логический 0 (сигнал RTS считается выключенным)
Бит 2. OUT1(OUT1 Bit Control) - Управление вспомогательным выходом OUT1.
Бит 3. OUT2(OUT2 Bit Control) - Управление вспомогательным выходом OUT2.
Бит 4. LOOP(Loopback Mode Enable)-Бит режима диагностики. Если LOOP=0, то UART работает в обычном режиме. Если LOOP=1, то URAT работет в режиме диагностики с обратной связью, в этом режиме используются вспомогательные сигналы OUT1 и OUT2.
Бит 5..7. Зарезервированы.
LSR -регистр состояния линии связи (запись/чтение)
(Line Status Register)
        Данный регистр показывает состояние приемопередатчика.
Бит 0. DR(Receiver Data Ready) — Готовность данных приемника.DR=1 информирует о том, что данные приняты и загружены в регистр RBR или приемный буфер FIFO.
Бит сбрасывается в ноль, когда все данные будут прочитаны CPU из регистра RBR или буфера FIFO.
Бит 1. OE(Overrun Error) — Бит ошибки переполнения. Бит указывает, что данные в регистре RBR не были прочитаны CPU прежде, чем следующий символ был передан в RBR, что привело к потере предыдущего символа.
Бит устанавливается в OE=1 после обнаружения ошибки переполнения и сбрасывать всякий раз, когда SPU читает содержание регистра LSR.
Бит 2. PE(Parity Error) —Бит ошибки контрольного бита паритета.PE=1 если символ принят с ошибкой паритета.
Бит 3. FE(Framing Error) — ошибка кадра (неверный стопбит).
Бит 4. BD(Break Detected) — индикатор обрыва линии (вход приемника находится в состоянии 0 не менее чем время посылки символа).
Бит 5. THRE(Transmitter Holding Register Empty) — регистр передатчика готов принять байт для передачи. В режиме FIFO указывает на отсутствие символов в FIFO-буфере передачи. Может являться источником прерывания.
Бит 6. TEMPT(Transmitter Empty Status) — регистр передатчика пуст (нет данных для передачи ни в сдвиговом регистре, ни в буферных регистрах THR или FIFO).
Бит 7. FIFOE(FIFO Error Status) —ошибка принятых данных в режиме FIFO (буфер содержит хотя бы один символ, принятый с ошибкой формата, паритета или обрывом). В не FIFO-режиме всегда 0.
MSR-регистр состояния модема
(Modem Status Register)
        Этот регистр позволяет CPU контролировать текущее состояние линий управления модема или периферийного устройства.
В дополнение к этому , четыре бита (0..3) регистра MSR контролируют изменения сигналов на входах CTS,RTS,RI,DCD микросхемы и вырабатывают прерывание микропроцессора.
Бит 0. DCTS(Delta Clear To Send) — Изменение состояния сигнала CTS(очищен для передачи).Бит устанавливается в DCTS=1 при изменении сигнала CTS на входе микросхемы и сбрасывается при прочтении регистра MSR микропроцессором.
При установке бита в 1 генерируется прерывание микропроцессора.
Бит 1. DDSR(Delta Data Set Ready) — Изменение состояния сигнала DSR(установка данных готова).Бит устанавливается в DDSR=1 при изменении сигнала DSR на входе микросхемы и сбрасывается при прочтении регистра MSR микропроцессором.
При установке бита в 1 генерируется прерывание микропроцессора.
Бит 2. ТЕRI(Trailing Edge Of Ring Indicator) — Детектор заднего фронта сигнала RI(индикатор звонка). Бит устанавливается в TERI=1, когда сигнал на выводе микросхемы RI изменяет свой уровень с низкого на высокий. Бит сбрасывается в TERI=0 при прочтении регистра MSR микропроцессором.
При установке бита в 1 генерируется прерывание микропроцессора.
Бит 3. DDCD(Delta Data Carrier Detect) — Изменение состояния сигнала DCD(обнаружен носитель информации).Бит устанавливается в DDCD=1 при изменении сигнала DCD на входе микросхемы и сбрасывается при прочтении регистра MSR микропроцессором.
При установке бита в 1 генерируется прерывание микропроцессора.
Бит 4. CTS(Clear To Send) — Состояние линии CTS. Если CTS=1, то на вход CTS СОМ-порта подано напряжение +12в(сигнал CTS активен).Если CTS=0, то вход СОМ-порта подано напряжение -12В(сигнал CTS пассивен).
В режиме диагностики этот бит эквивалентен биту RTS регистра MCR.
Бит 5. DSR(Data Set Ready) — Состояние линии DSR. Если DSR=1, то на вход DSR СОМ-порта подано напряжение +12в(сигнал DSR активен).Если DSR=0, то вход СОМ-порта подано напряжение -12В(сигнал DSR пассивен).
В режиме диагностики этот бит эквивалентен биту DTR регистра MCR.
Бит 6. RI(Ring Indicator) — Состояние линии RI. Если RI=1, то на вход DSR СОМ-порта подано напряжение +12в(сигнал RI активен).Если RI=0, то вход СОМ-порта подано напряжение -12В(сигнал RI пассивен).
В режиме диагностики этот бит эквивалентен биту OUT1 регистра MCR.
Бит 7. DCD(Data Carrier Detect) — Состояние линии DCD. Если DCD=1, то на вход DCD СОМ-порта подано напряжение +12в(сигнал DCD активен).Если DCD=0, то вход СОМ-порта подано напряжение -12В(сигнал DCD пассивен).
В режиме диагностики этот бит эквивалентен биту OUT2 регистра MCR.
SCR-регистр временного хранения (чтение/запись)
(Scratch Pad Register)
        Регистр временного хранения, на работу UART не влияет, предназначен для временного хранения данных (в UART i8250 отсутствует).
Режим диагностики UART позволяет проверить работоспособность СОМ-портов без подключения к ним периферийных устройств.
Режим диагностики включается битом LOOP=1 регистра MCR.
При этом внутри UART организуется аппаратная "заглушка":
    В MS-DOS программировать СОМ порты можно всем спектром программных средств: прямым кодом микропроцессора(assembler), функциями BIOS, средствами операционной системы, языками программирования высокого уровня.
    Под программированием прямым кодом микропроцессора понимается программирование микросхемы UART через порты ввода-вывода с помощью команд микропроцессора.
В системе команд микропроцессора есть команды OUT и IN, которые позволяют читать/записать байт по указанному адресу порта ввода/вывода.
В п.1.12 описаны 12 регистров микросхемы UART, которые полностью определяют работу указанной микросхемы.
Необходимо просто записать нужные данные в эти регистры, чтобы заставить СОМ порт выполнить нужные действия.
При программировании регистров UART нужно учитывать, что в памяти BIOS находятся адреса портов ввода/вывода для СОМ1...СОМ4.
По умолчанию они равны COM1=3F8h, COM2=2F8h, COM3=3E8h, COM4=2E8h , но бывают "чудики" которые могут их поменять в настройках BIOS.
Поэтому перед началом программирования портов в MS-DOS желательно проверить адреса СОМ портов.
В BIOS адрес СОМ порта занимает 2 байта, и находятся в ячейках памяти по адресам СОМ1: 40...41h, СОМ2: 42...43h, СОМ3: 44...45h, СОМ4: 46...47h.
'записываем в LCR режим работы сом порта: '8 бит всимволе,1 стоп бит, проверка паритета на четность,выдавать 0 в случае обрыва, DLAB=1 mov al,DBh 'записываем в AL значения для регистра LCR=DBh out 3fBh,al'записываем данные в регистр UART LCR 'задаем скорость обмена 115 000 бит/сек DIM=00h, DLL=01h mov al,01h out 3f8h,al 'запись регистра DLL=01h mov al,00h out 03f9h,al 'запись регистра DIM=00h 'снимаем бит DLAB=1 mov al,5Bh 'DLAB=0 out 3fBh,al 'послать байт 03h в линию связи mov al,03h out 3f8h,al 'посылает байт 03h на скорости 115 000 бит/сек
    Перед записью байта данных в регистр передатчика необходимо убедиться в том, что регистр хранения передатчика свободен, то есть убедиться в том, что передача предыдущего символа завершена.
Признаком того, что регистр передатчика свободен, является установленный бит 5(THRE=1) регистра состояния линии LSR.
    Аналогично тому как это делается при передаче данных, перед вводом символа из порта приемника необходимо убедиться в том, что бит 0 регистра LSR установлен (т.е. DR=1).
Это означает, что символ принят из линии и находится в буферном регистре приемника.
    В BIOS имеются функции которые могут выполняться по команде программного прерывания микропроцессора INT 00h...INT 1Fh.
Так как код этих функций находится в BIOS, то их выполнение возможно даже при отсутствии ОС на ПК.
Кроме этого, функции BIOS работают по номерам СОМ портов, а не по адресу ввода/вывода, что существенно удобней.
Функции по прерыванию INT 14h
mov ah,00h 'номер функции int 14h 'вызов функцииРассмотрим функции вызываемые INT 14h:
входные параметры INT14h AH=00h | ||
регистр | старший | младший |
AX | 00h | байт параметров связи |
BX | ||
CX | ||
DX | (n-1), где n-номер COM порта |
байт параметров связи | |||||||||
  7   |   6   |   5   |   4   |   3   |   2   |   1   |   0   | описание | допустимые значения |
x | x | x | скорость, бод | 000- 110 001- 150 010- 300 011- 600 100- 1200 101- 2400 110- 4800 111- 9600 | |||||
x | x | проверка паритета | 00- нет 01- нечетность 10- нет 11- четность | ||||||
x | длина стопового бита | 0- 1 1- 2 | |||||||
x | x | кол. бит в символе | 10- 7 11- 8 |
mov ah,00h 'номер функции mov al,EBh '9600 бод,нечетность,1 стоп, 8 бит int 14h 'вызов функцииПосле выполнения функция возвращает выходные параметры :
выходные параметры INT14h AH=00h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | байт состояние модема MSR |
BX | ||
CX | ||
DX |
входные параметры INT14h AH=01h | ||
регистр | старший | младший |
AX | 01h | символ |
BX | ||
CX | ||
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=01h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | символ |
BX | ||
CX | ||
DX |
входные параметры INT14h AH=02h | ||
регистр | старший | младший |
AX | 02h | |
BX | ||
CX | ||
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=02h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | символ |
BX | ||
CX | ||
DX |
выходные параметры INT14h AH=03h | ||
регистр | старший | младший |
AX | 03h | |
BX | ||
CX | ||
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=03h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | байт состояние модема MSR |
BX | ||
CX | ||
DX |
входные параметры INT14h AH=04h | ||
регистр | старший | младший |
AX | 04h | установка прерывания |
BX | установка паритета | установка стопового бита |
CX | бит в символе | скорость, бит/сек |
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=04h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | байт состояние модема MSR |
BX | ||
CX | ||
DX |
выходные параметры INT14h AH=05h | ||
регистр | старший | младший |
AX | 05h | 00h |
BX | ||
CX | ||
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=05h | ||
регистр | старший | младший |
AX | ||
BX | регистр MCR | |
CX | ||
DX |
выходные параметры INT14h AH=05h | ||
регистр | старший | младший |
AX | 05h | 01h |
BX | регистр MCR | |
CX | ||
DX | (n-1), где n-номер COM порта |
выходные параметры INT14h AH=05h | ||
регистр | старший | младший |
AX | байт состояние линии LSR | байт состояние модема MSR |
BX | ||
CX | ||
DX |
Хотя СОМ порт и является основным коммуникационным средством ПК, в MS-DOS практически очень мало программных средств для эффективной работы с портом. Рассмотрим основные программные средства операционной системы MS-DOS:
Функции по прерыванию INT 21h
Существуют четыре функции программного прерывания INT 21h для работы с СОМ портом: 03h,04h,3Fh,40h.
Перед началом описания работы этих функций ознакомимся с понятием "описатель". Описатель- это идентификатор последовательного устройства(объекта) или файла в системе MS-DOS.
С точки зрения программы описатель это целое число, которое указывает на определённую программную структуру (объект), которая(который) обеспечивает работу этого устройства(объекта) с ОС.
Кто "дружит" с Windows, знает насколько важную роль имеет описатель(дескриптор) в этой системе, но начало этого было в MS-DOS.
В MS-DOS первые номера описателей отданы стандартным последовательным устройствам:
описатель | имя | устройство |
0 | CON | стандартное устройство ввода (клавиатура) |
1 | CON | стандартное устройство вывода (дисплей) |
2 | CON | стандартное устройство вывода ошибок (всегда CON) |
3 | AUX | вспомогательное устройство (по умолчанию COM1) |
4 | PRN | стандартный вывод на печать (по умолчанию LPT1) |
входные параметры INT21h AH=03h | ||
регистр | старший | младший |
AX | 03h | |
BX | ||
CX | ||
DX |
выходные параметры INT21h AH=03h | ||
регистр | старший | младший |
AX | символ | |
BX | ||
CX | ||
DX |
входные параметры INT21h AH=04h | ||
регистр | старший | младший |
AX | 04h | |
BX | ||
CX | ||
DX | символ |
Использование аппаратных прерываний
    Так как процесс последовательной передачи данных протекает медленно, можно выполнять его в фоновом режиме, используя прерывания по окончанию передачи или приема символа. Напомним, что порту COM1 соответствует аппаратное прерывание IRQ4 с вектором INT 0Ch, а COM2 - IRQ3 с вектором INT 0Bh. Для разрешения прерываний необходимо установить биты регистра управления прерыванием IER (UART), соответствующие тем прерываниям, которые нужно обрабатывать. Когда происходит прерывание, программа-обработчик, расположенная по указанному вектору прерывания, должна проанализировать причину прерывания, прочитав регистр, идентифицирующий прерывания IIR. Не забудьте, что в конце обработчика аппаратного прерывания должна находится последовательность команд:
mov al, 20h out 20h, al iretЧтобы была возможность обработки нескольких прерываний.
Использование команд MS-DOS
В MS-DOS имеется ряд встроенных команд для работы и настройки СОМ порта. Команды можно вставлять в пакетные файлы с расширением .bat для исполнения их по заданному сценарию.
Команда MODE
Команда Mode предназначена для изменения режима работы периферийных устройств.
Формат:
Mode COMx,бод,паритет,кадр,стоп,P
где: x-номер СОМ порта; бод- скорость: 110,150,300,600,1200,2400,4800,9600,19200 бод; паритет-n-нет, o-четность, e-нечетность; кадр- бит на символ: 7,8; стоп- число стоп битов: 1,2 P- задает режим повторения попыток передачи при неудаче.
Mode COM1,9600,n,8,1,P type c:\data.txt>com1
mode com1 baud=9600 parity=n data=8 stop=1 type c:\data.txt>com1Кроме того, в Windows строку заканчивать символом Enter не обязательно.
    Программирование в операционных системах Windows 2000 и выше отличается от программирования в MS-DOS. Во-первых, COM1-COM4 в этих системах не имеет стандартных адресов ввода вывода и стандартных номеров прерываний, Windows автоматически распределяет ресурсы для COM-портов. Поэтому если вы захотите программировать СОМ-порт через порты ввода-вывода, вам понадобиться, сначала определить ресурсы которые занимает СОМ-порт на данном ПК. Во-вторых, Windows не дает прямой возможности работать с портами ввода-вывода, это возможно только при программировании на уровне ядра ОС (что тоже не просто). В принципе такой вариант программирования возможен, то есть пишите драйвер ядра для работы с портами ввода-вывода и программу для работы с СОМ-портом работающем через этот драйвер.
    Но, не всё так плохо. Естественно разработчики Windows предусмотрели возможность работы с коммуникационными портами через пользовательский интерфейс Windows. Этот способ, наверное, даже проще чем программирование Сом-порта через порты ввода-вывода. В Windows к Сом-порту можно обратиться как к файлу(потоку). Достоинство этого способа очевидны: вам не надо думать о типе микросхемы UART, о номерах портов ввода-вывода и о номерах прерываний. ОС незаметно для программиста работает с аппаратной частью коммуникационного порта.
Попробуйте, создать в проводнике папку или файл с именем "СОМ1", сделать это не получится. ОС Windows зарезервировала имена от СОМ1 до СОМ9 для работы с СОМ-портами.
Рассмотрим подробнее программирование СОМ-порта с помощью API-функций:
1. Для работы с СОМ-портом первое что надо сделать, это открыть порт.
Сделать это можно с помощью API функции CreateFile из библиотеки "kernel32" :
Эта функция создает новый объект и присваивает ему описатель, по которому с этим объектом можно будет работать.
Пример описания функции CreateFile на языке Си:
HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );Пример декларирования функции CreateFile на языке VB6:
Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Чтобы открыть СОМ-порт вы должны выполнить эту функцию в коде своей программы, с заданными входными параметрами. Результатом работы этой функции будет 32-битное число handle(описатель), по которому вы сможете обращаться к созданному функцией программному объекту связанному с выбранным СОМ-портом.
Com_Handle = CreateFile("COM1:", &HC0000000, 0, 0&, &H3, 0, 0)
Пример открытия СОМ1 в Си:Com_Handle = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
2. После открытия СОМ порта можно передавать и принимать данные через этот СОМ-порт.
Для передачи данных используется API функция WriteFile из библиотеки kernel32.
Для приёма данных используется API функция ReadFile из библиотеки kernel32.
BOOL ReadFile( HANDLE hFile, // описатель сОМ порта LPVOID lpBuffer, // Указатель на буфер, который принимает прочитанные данные из порта DWORD nNumberOfBytesToRead,// Число байтов, которые читаются из порта LPDWORD lpNumberOfBytesRead, // Указатель на переменную, которая получает число прочитанных байтов LPOVERLAPPED lpOverlapped // Указатель на структуру OVERLAPPED. ); BOOL WriteFile( HANDLE hFile,// описатель сом порта LPCVOID lpBuffer,// Указатель на буфер, содержащий данные, которые будут записаны в файл. DWORD nNumberOfBytesToWrite,// Число байтов, которые будут записаны в файл. LPDWORD lpNumberOfBytesWritten,// Указатель на переменную, которая получает число записанных байтов LPOVERLAPPED lpOverlapped // Указатель на структуру OVERLAPPED );Пример декларирования функции ReadFile и WriteFile на языке VB6:
Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Long) As Boolean
Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, lpOverlapped As Long) As Boolean
Dim File_Buffer(255) As Byte 'приемный буфер
Dim Com_Byte_Read As Long 'количество принятых байт
Dim Retval As Boolean
Retval = ReadFile(Com_Handle, File_Buffer(0), 255, Com_Byte_Read, 0)
3. После окончания работы с портом его нужно закрыть.
Закрытие порта осуществляется API функцией CloseHandle из библиотеки kernel32.
BOOL CloseHandle( HANDLE hObject // описатель порта );Пример декларирования функции CloseHandleна языке VB6:
Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Boolean
Пример закрытия порта на языке VB6:
Dim Com_Exit as Boolean
Com_Exit = CloseHandle(Com_Handle)
DCB структура
Структура DCB определяет основные настройки СОМ порта.typedef struct _DCB { DWORD DCBlength; // длина структуры (DCB) DWORD BaudRate; // скорость в бит/сек DWORD fBinary:1; // бинарный режим DWORD fParity:1; // разрешение контроля четности DWORD fOutxCtsFlow:1; // слежение за CTS DWORD fOutxDsrFlow:1; // слежение за DSR DWORD fDtrControl:2; // режим работы сигнала DTR DWORD fDsrSensitivity:1; // чувствительность к DSR DWORD fTXContinueOnXoff:1; // продолжение передачи при XOFF DWORD fOutX:1; // программное управление потоком при передачи (XON/XOFF) DWORD fInX:1; // программное управление потоком при приеме (XON/XOFF) DWORD fErrorChar:1; // замена ошибочных символов DWORD fNull:1; // действия при приёме нулевого символа DWORD fRtsControl:2; // Задает режим управления потоком для сигнала RTS DWORD fAbortOnError:1; // игнорирование запись/чтение при ошибке DWORD fDummy2:17; // зарезервировано WORD wReserved; // не используется, равно 0 WORD XonLim; // мин. количество символов для посылки XON WORD XoffLim; // макс. кол-во символов для посылки XOFF BYTE ByteSize; // количество бит в символе BYTE Parity; // режим паритета 0-4=no,odd,even,mark,space BYTE StopBits; // длина стопового бита 0,1,2 = 1, 1.5, 2 char XonChar; // символ для XON char XoffChar; // символ для XOFF char ErrorChar; // символ для замены ошибок char EofChar; // символ конца данных char EvtChar; // символ события WORD wReserved1; // резервный } DCB;Для работы с DCB структурой используют API функции из библиотеки kernel32.:
COMMTIMEOUTS структура
Данная структура задает временные параметры(задержки и таймауты) работы СОМ порта и определяет поведение функций ReadFile и WriteFile.typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; //интервал между символами DWORD ReadTotalTimeoutMultiplier; //множитель для периода простоя чтения DWORD ReadTotalTimeoutConstant; //постоянная для периода простоя чтения DWORD WriteTotalTimeoutMultiplier; //множитель для периода простоя записи DWORD WriteTotalTimeoutConstant; //постоянная для периода простоя записи } COMMTIMEOUTS, *LPCOMMTIMEOUTS;Для работы с COMMTIMEOUTS структурой используют API функции из библиотеки kernel32.:
COMMSTAT структура
Структура которая сообщает статус СОМ порта после обнаружения ошибки связи.typedef struct _COMSTAT { DWORD fCtsHold :1;//ожидание символа CTS(готовности к приёму) DWORD fDsrHold :1;//ожидание сигнала DSR(готовности модема) DWORD fRlsdHold :1;//ожидание RSLD(детектор сигнала с линии) DWORD fXoffHold :1;//ожидание передачи (был принят XOFF) DWORD fXoffSent :1;//передача символа XOFF DWORD fEof :1;//принят символ конец данных EOF DWORD fTxim :1;//имеется очередь символов для передачи DWORD fReserved :25;//резерв DWORD cbInQue;//число байтов полученых от ReadFile DWORD cbOutQue;//число байтов для WriteFile } COMSTAT, *LPCOMSTAT;Для работы с COMMSTAT структурой используют API функции из библиотеки kernel32.:
COMMPROP структура
Структура которая сообщает информацию о свойствах коммуникационного устройства.typedef struct _COMMPROP { WORD wPacketLength;//размер пакета данных WORD wPacketVersion;//версия структуры DWORD dwServiceMask;//битовая маска услуг поставщика DWORD dwReserved1;//резерв DWORD dwMaxTxQueue;//макс. размер передающего буфера DWORD dwMaxRxQueue;//макс. размер приёмного буфера DWORD dwMaxBaud;//макс. скорость в бит/сек DWORD dwProvSubType;//тип коммуникационного устройства DWORD dwProvCapabilities;//возможности предлагаемые поставщиком DWORD dwSettableParams;//параметр который может изменяться DWORD dwSettableBaud;//скорость разрешенная к использованию WORD wSettableData;//число битов в символе которые разрешено задавать WORD wSettableStopParity;//стоповые биты и паритет которые могут быть выбраны DWORD dwCurrentTxQueue;//текущий размер передающего буфера DWORD dwCurrentRxQueue;//текущий размер приёмного буфера DWORD dwProvSpec1;//данные определяемые поставщиком DWORD dwProvSpec2;//данные определяемые поставщиком WCHAR wcProvChar;//знаки определяемые поставщиком } COMMPROP;Для работы с COMMPROP структурой используют API функции из библиотеки kernel32:
    Программирование работы СОМ порта с помощью внешних компонент один из наиболее распространённых и простых способов работы с СОМ портом. Внешний компонент это программный модуль, который выполняет заданные функций и обладает всеми параметрами программного объекта. Внешний компонент разрабатывается по технологии ActiveX, что позволяет ему встраиваться в любые проекты программ, написанных на языках программирования поддерживающих эту технологию. Практически все современные средства разработки программ поддерживают технологию ActiveX. Вы можете создавать проект своего приложения на C++, Delphi, VB, 1C,MS-Office и для работы с СОМ портом подключить готовую внешнюю компоненту. При этом вам не нужно разбираться, как работает СОМ порт, это делает программный объект внешней компоненты, разработчик только использует свойства, методы и события этого объекта. Технология ActiveX является логическим продолжением dll, DDE, OLE, COM технологий.
    Внешняя компонента(элемент ActiveX) является законченным программным продуктом и обладает всеми авторскими правами. Поэтому разработчику необходимо помнить, что подключая элемент ActiveX, вы подключаете код чужой программы к своему проекту, и соответственно часть вашей программы будет написана разработчиком компоненты, что требует оплаты. Внешних компонент для работы с СОМ портами написано большое количество. Необходимо определиться какую компоненту, и на каких условиях вы будете применять в своём проекте.
Внешние компоненты оформлены в виде файлов и имеют расширение .ocx либо более раннее .dll. Для того чтобы внешнюю компоненту можно было использовать в проекте, она должна быть зарегистрирована в ОС. Регистрация компоненты осуществляется записью ключей в реестр ОС, с помощью специальной программы или команды "Зарегистрировать" из контекстного меню файла.
Для рассмотрения возьмём известную компоненту MSCOMM32.ocx написанную Microsoft и включенную в пакет разработчика Visual Studio Enterprise.
Подключите MSCOMM32.ocx к вашему проекту.
После подключения компоненты к проекту вы сможете работать с объектом MSComm1.
Private Sub Command1_Click() Dim Data_S As String MSComm1.CommPort = 1 'номер сом порта MSComm1.PortOpen = True 'открыть порт MSComm1.Settings = "9600,n,8,1" 'скорость 9600 бит/сек, без паритета, 8 бит в симв/, 1 стоп. Data_S = MSComm1.Input 'принять данные из порта MSComm1.Output = "Hello" ' передать данные в порт MSComm1.DTREnable = True ' включить сигнал DTR MSComm1.PortOpen = False 'закрыть порт End SubВ данной программе при нажатии кнопки 1 открывается порт СОМ1 на скорости 9600 бит/сек, без проверки паритета, 8 бит в символе, с одним стоповым битом. В переменную DATA_S считывается строка символов из приёмного буфера СОМ1 и в передающий буфер СОМ1 выводится слово "Hello". После этого включается сигнал DTR и порт закрывается.
Как видно из приведенного примера работа с Сом портом, при использовании элемента ActiveX, достаточно проста.
Кроме указанных свойств компонент MSCOMM32.ocx имеет большое количество других свойств, событий и методов, которые полнофункционально реализуют работу СОМ порта в ОС Windows.
Реализация простых функций com-порта:
   -открытие порта
   -настройка порта
   -запись текста в порт
   -чтение текста из порта
   -закрытие порта
Напишем программу реализующие эти функции на различных языках программирования: VB6, MASM32, C
Исходник на VB6 COMAPIvb v.1.00   Скачать   Рис.28 Окно проекта COMAPI
Программа работает следующим образом:
    -при нажатии кнопки Open port окрывается сом-порт СОМ1
    -настройка СОМ1: 1200 бод, 8 бит на символ, с 1 стоповым битом, без проверки паритета
    -при нажатии кнопки Write port , стока записанная в Text2(Hello World!) посылается в СОМ1
    -при нажатии кнопки Read port, в Text1 помещается строка из приемного буфера СОМ1
    -при нажатии кнопки Close port, порт СОМ1 закрывается
Вводим код модуля:
'COMAPIvb v.1.01 'Yahkardin Vladimir 'www.softelectro.ru 'Russia, Peterburg '06.04.2012 Option Explicit 'The declaration of used functions API Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, lpOverlapped As Long) As Boolean Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Long) As Boolean Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Declare Function SetCommState Lib "kernel32" (ByVal hFile As Long, lpDCB As DCB) As Boolean Declare Function SetCommTimeouts Lib "kernel32" (ByVal hFile As Long, lpCommTimeouts As COMMTIMEOUTS) As Boolean Declare Function GetLastError Lib "kernel32" () As Long Public Com_Handle As Long 'Descriptor of port Public Buf(255) As Byte 'The buffer of port Public Com_Byte_Read As Long 'количество принятых байт 'Structure for parameter setup of port Type DCB DCBlength As Long 'Length of structure in byte. BaudRate As Long 'Speed of data exchangeб, bps fBitFields As Long 'Bit field for setting of port wReserved As Integer 'Reserved XonLim As Integer 'minimum number of bytes in the buffer to send Xon XoffLim As Integer 'maximum number of bytes in the buffer for sending Xof ByteSize As Byte 'the number of bits in a symbol Parity As Byte 'parity check mode StopBits As Byte 'Stop bit length XonChar As Byte 'character code Xon XoffChar As Byte 'character code Xof ErrorChar As Byte 'code symbol error EofChar As Byte 'character code data end EvtChar As Byte 'character code Events wReserved1 As Integer 'Reserved End Type Public DCB1 As DCB 'Structure for parameter setup of port Type COMMTIMEOUTS ReadIntervalTimeout As Long 'интервал между символами ReadTotalTimeoutMultiplier As Long 'множитель для периода простоя чтения ReadTotalTimeoutConstant As Long 'постоянная для периода простоя чтения WriteTotalTimeoutMultiplier As Long 'множитель для периода простоя записи WriteTotalTimeoutConstant As Long 'постоянная для периода простоя записи End Type Public COMMTIMEOUTS1 As COMMTIMEOUTS 'Function of opening of port Function Com_Open() Dim Retval As Long Dim Retval_B As Boolean Dim Retval_S As Boolean Retval = CreateFile("COM1:", &HC0000000, 0, 0, 3, 0, 0) If Retval = -1 Then MsgBox ("The open error of port:" & GetLastError) Else Com_Handle = Retval 'set dcb DCB1.BaudRate = 1200 DCB1.ByteSize = 8 DCB1.DCBlength = 28 DCB1.EofChar = 0 DCB1.ErrorChar = 0 DCB1.EvtChar = 0 DCB1.fBitFields = 1 DCB1.Parity = 0 DCB1.StopBits = 0 DCB1.wReserved = 0 DCB1.wReserved1 = 0 DCB1.XoffChar = 0 DCB1.XoffLim = 0 DCB1.XonChar = 0 DCB1.XonLim = 0 Retval_S = SetCommState(Com_Handle, DCB1) 'set timeout COMMTIMEOUTS1.ReadIntervalTimeout = -1 COMMTIMEOUTS1.ReadTotalTimeoutConstant = 0 COMMTIMEOUTS1.ReadTotalTimeoutMultiplier = 0 COMMTIMEOUTS1.WriteTotalTimeoutConstant = 5000 COMMTIMEOUTS1.WriteTotalTimeoutMultiplier = 0 Retval_B = SetCommTimeouts(Com_Handle, COMMTIMEOUTS1) If Retval_B = False Or Retval_S = False Then MsgBox ("Error DCB&Timout:" & GetLastError) End If MsgBox ("Open port HANDLE:" & Com_Handle) End If End Function 'Function of closing the port Function Com_Close() Dim Retval As Long Retval = CloseHandle(Com_Handle) If Retval = 0 Then MsgBox ("Close port ERROR:" & GetLastError) Else MsgBox ("Close port") End If End Function 'Recording function to the port Function Com_Write() Dim Len_Buf As Long Dim Retval As Boolean PurgeBuf Retval = WriteFile(Com_Handle, Buf(0), 255, Len_Buf, 0) If Retval = False Then MsgBox ("Write port ERROR:" & GetLastError) Else MsgBox ("Data write: Ok") End If End Function 'function of reading from the port Function Com_Read() Dim Retval As Boolean PurgeBuf Retval = ReadFile(Com_Handle, Buf(0), 255, Com_Byte_Read, 0) If Retval = False Then MsgBox ("Read port ERROR:" & GetLastError & Err.Description) Else MsgBox ("Data read: Ok") End If End Function 'clear buffer Sub PurgeBuf() Dim a As Integer For a = 0 To 255 Buf(a) = &H20 Next a End SubВводим код формы:
'COMAPIvb v.1.01 'Yahkardin Vladimir 'www.softelectro.ru 'Russia, Peterburg '06.04.2012 Option Explicit 'Open port Private Sub Command1_Click() Dim Retval As Boolean Retval = Com_Open End Sub 'Close port Private Sub Command2_Click() Dim Retval As Boolean Retval = Com_Close End Sub 'Read port Private Sub Command3_Click() Dim s As String Dim a As Long Com_Read For a = 1 To Com_Byte_Read s = s & Chr(Buf(a - 1)) Next a Text1.Text = s End Sub 'Write port Sub Command4_Click() Dim s As String Dim n As Long Dim a As Integer Dim Retval As Long s = Text2.Text s = Mid(s, 1, 255) n = Len(s) For a = 1 To n Buf(a - 1) = Asc(Mid(s, a, 1)) Next a Com_Write End Sub 'Exit programm Private Sub Form_Unload(Cancel As Integer) Dim Retval As Long Retval = CloseHandle(Com_Handle) End Sub
;COMAPIas v.1.00 ;htpp:\\www.softelectro.ru ;Electron18 .386 .model flat,stdcall option casemap:none STYLBTN equ WS_CHILD + BS_DEFPUSHBUTTON + WS_VISIBLE STYLEDT equ WS_CHILD+WS_VISIBLE+WS_BORDER+WS_TABSTOP WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD PurgeBuf PROTO include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .DATA ClassName db "SimpleWinClass",0 AppName db "COMAPIas v.1.00",0 ;data button CLSBUTN DB 'BUTTON',0 CPBUT1 DB 'Open port',0 HWNDBTN1 DD 0 CPBUT2 DB 'Close port',0 HWNDBTN2 DD 0 CPBUT3 DB 'Read port',0 HWNDBTN3 DD 0 CPBUT4 DB 'Write port',0 HWNDBTN4 DD 0 ;data edit CLSEDIT DB 'EDIT',0 CPEDT1 DB ' ',0 HWNDEDT1 DD 0 CPEDT2 DB 'Hello World!',0 HWNDEDT2 DD 0 TEXT DB 'Строка редактирования',0 ;Message App ;App message uType EQU 0 lpCapApp DB "App message",0 lpApp1 DB "Open port HANDLE:", 6 dup(" "),0 lpApp2 DB "Close port",0 lpApp3 DB "Data read: Ok",0 lpApp4 DB "Data write: Ok",0 ;error message lpCapERR DB "Error Message",0 lpERR1 DB "Open port Error:",10 dup(" "),0 lpERR2 DB "DCB Structure ERROR:",10 dup(" "),0 lpERR3 DB "SetComm Function ERROR:",10 dup(" "),0 lpERR4 DB "Read port ERROR:",10 dup(" "),0 lpERR5 DB "Write port ERROR:",10 dup(" "),0 lpERR6 DB "Close port ERROR:",10 dup(" "),0 ;data com port Mem1 DD 0 Par1 DB "%lu",0 Buf DB 255 dup(" "),0 HWNDCOM DD 0 LenBuf DD 0 NumCOM DB "COM1:",0 COMSETTING DB "Com1: baud=1200 parity=N data=8 stop=1",0 DCB1 DCB <> .DATA? hInstance HINSTANCE ? CommandLine LPSTR ? .CODE START: INVOKE GetModuleHandle, NULL mov hInstance,eax ;handle app INVOKE GetCommandLine ;handle command line INVOKE WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT ;input to app INVOKE ExitProcess,eax ;exit app ;main window WinMain PROC hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,4;COLOR_MENU;COLOR_WINDOW mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName INVOKE LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax ;handle icon mov wc.hIconSm,0 INVOKE LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax ;handle cursor INVOKE RegisterClassEx, addr wc ;register class window ;create main window INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,20,20,800,200,NULL,NULL,hInst,NULL mov hwnd,eax INVOKE ShowWindow, hwnd,SW_SHOWNORMAL ;show window INVOKE UpdateWindow, hwnd ;update window ;loop message .WHILE TRUE INVOKE GetMessage, ADDR msg,NULL,0,0 ;get message .BREAK .IF (!eax) INVOKE TranslateMessage, ADDR msg ;get key char INVOKE DispatchMessage, ADDR msg ;call Win Proc .ENDW mov eax,msg.wParam ret WinMain endp ;procedure window WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF uMsg==WM_DESTROY INVOKE PostQuitMessage,NULL .ELSEIF uMsg==WM_COMMAND mov EAX, HWNDBTN1 cmp lParam, EAX je BUTTON1_CLICK mov EAX, HWNDBTN2 cmp lParam, EAX je BUTTON2_CLICK mov EAX, HWNDBTN3 cmp lParam, EAX je BUTTON3_CLICK mov EAX, HWNDBTN4 cmp lParam, EAX je BUTTON4_CLICK .ELSEIF uMsg==WM_CREATE ;create button1..4 INVOKE CreateWindowEx, 0,OFFSET CLSBUTN,OFFSET CPBUT1,STYLBTN,10,10,100,20,hWnd,0,hInstance,0 mov HWNDBTN1, EAX ; handle button1 INVOKE CreateWindowEx, 0,OFFSET CLSBUTN,OFFSET CPBUT2,STYLBTN,10,40,100,20,hWnd,0,hInstance,0 mov HWNDBTN2, EAX ; handle button2 INVOKE CreateWindowEx, 0,OFFSET CLSBUTN,OFFSET CPBUT3,STYLBTN,10,70,100,20,hWnd,0,hInstance,0 mov HWNDBTN3, EAX ; handle button3 INVOKE CreateWindowEx, 0,OFFSET CLSBUTN,OFFSET CPBUT4,STYLBTN,10,100,100,20,hWnd,0,hInstance,0 mov HWNDBTN4, EAX ; handle button4 ;create edit1 and edit2 INVOKE CreateWindowEx, 0,OFFSET CLSEDIT,OFFSET CPEDT1,STYLEDT,120,70,600,20,hWnd,0,hInstance,0 mov HWNDEDT1, EAX ; handle edit1 INVOKE CreateWindowEx, 0,OFFSET CLSEDIT,OFFSET CPEDT2,STYLEDT,120,100,600,20,hWnd,0,hInstance,0 mov HWNDEDT2, EAX ; handle edit2 .ELSE INVOKE DefWindowProc,hWnd,uMsg,wParam,lParam ;default message ret .ENDIF jmp ENDMESS BUTTON1_CLICK: ;open port INVOKE CreateFile,OFFSET NumCOM, GENERIC_READ or GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL,NULL mov HWNDCOM, eax .IF EAX==-1 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR1(17),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR1,OFFSET lpCapERR,uType .ELSE INVOKE wsprintf,OFFSET lpApp1(18),OFFSET Par1,HWNDCOM INVOKE MessageBoxA,NULL,OFFSET lpApp1,OFFSET lpCapApp,uType INVOKE BuildCommDCB,OFFSET COMSETTING, OFFSET DCB1 .IF EAX==0 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR2(21),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR2,OFFSET lpCapERR,uType .ENDIF INVOKE SetCommState,HWNDCOM,OFFSET DCB1 .IF EAX==0 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR3(24),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR3,OFFSET lpCapERR,uType .ENDIF .ENDIF jmp ENDMESS BUTTON2_CLICK: ;close port INVOKE CloseHandle,HWNDCOM .IF EAX==0 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR6(18),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR6,OFFSET lpCapERR,uType .ELSE INVOKE MessageBoxA,NULL,OFFSET lpApp2,OFFSET lpCapApp,uType .ENDIF jmp ENDMESS BUTTON3_CLICK: CALL PurgeBuf ;read port INVOKE ReadFile,HWNDCOM,OFFSET Buf, SIZEOF Buf,OFFSET LenBuf, NULL .IF EAX==0 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR4(17),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR4,OFFSET lpCapERR,uType .ELSE INVOKE SendMessage,HWNDEDT1,WM_SETTEXT,SIZEOF Buf,OFFSET Buf INVOKE MessageBoxA,NULL,OFFSET lpApp3,OFFSET lpCapApp,uType .ENDIF jmp ENDMESS BUTTON4_CLICK: ;write port CALL PurgeBuf INVOKE SendMessage,HWNDEDT2,WM_GETTEXT,SIZEOF Buf,OFFSET Buf INVOKE WriteFile,HWNDCOM,OFFSET Buf, SIZEOF Buf,OFFSET LenBuf, NULL .IF EAX==0 INVOKE GetLastError mov Mem1,eax INVOKE wsprintf,OFFSET lpERR5(18),OFFSET Par1,Mem1; INVOKE MessageBoxA,NULL,OFFSET lpERR5,OFFSET lpCapERR,uType .ELSE INVOKE MessageBoxA,NULL,OFFSET lpApp4,OFFSET lpCapApp,uType .ENDIF jmp ENDMESS ENDMESS: xor eax,eax ret WndProc ENDP ; clear buffer PurgeBuf PROC push ebx push ecx mov cx,255 mov ebx,offset Buf mov al,20h L: mov [ebx],al inc ebx LOOP L pop ecx pop ebx xor eax,eax ret PurgeBuf ENDP END START
// COMAPIc.cpp // www.softelectro //Yshkardin V. #include "stdafx.h" #define STYLBTN WS_CHILD|BS_DEFPUSHBUTTON|WS_VISIBLE #define STYLEDT WS_CHILD|WS_VISIBLE|WS_BORDER|WS_TABSTOP //data main window HINSTANCE hInst; LPCTSTR szTitle="COMAPIc v.1.00"; LPCTSTR szWindowClass="SimpleWinClass"; //data control LPCTSTR CLSBUTN="BUTTON"; LPCTSTR CPBUT1="Open port"; LPCTSTR CPBUT2="Close port"; LPCTSTR CPBUT3="Read port"; LPCTSTR CPBUT4="Write port"; LPCTSTR CLSEDIT="EDIT"; LPCTSTR CPEDT1; LPCTSTR CPEDT2="Hello World!"; HWND HWNDBTN1; HWND HWNDBTN2; HWND HWNDBTN3; HWND HWNDBTN4; HWND HWNDEDT1; HWND HWNDEDT2; HMENU ID_BTN1=(HMENU) 101; HMENU ID_BTN2=(HMENU) 102; HMENU ID_BTN3=(HMENU) 103; HMENU ID_BTN4=(HMENU) 104; //App message LPCTSTR lpCapApp = "App message"; char lpApp1[40] = "Open port HANDLE:"; LPCTSTR lpApp2 ="Close port"; LPCTSTR lpApp3 ="Data read: Ok"; LPCTSTR lpApp4 ="Data write: Ok"; //error message LPCTSTR lpCapERR ="Error Message"; char lpERR1[40]={"Open port Error:"}; char lpERR2[40]={"DCB Structure ERROR:"}; char lpERR3[40]={"SetComm Function ERROR:"}; char lpERR4[40]={"Read port ERROR:"}; char lpERR5[40]={"Write port ERROR:"}; char lpERR6[40]={"Close port ERROR:"}; DWORD Mem1; LPCTSTR Par1 ="%lu"; //data com port HANDLE HWNDCOM; LPCTSTR lpNumCOM="COM1:"; LPCTSTR COMSETTING="Com1: baud=1200 parity=N data=8 stop=1"; char Buf[255]; DWORD LenBuf; DCB DCB1; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void PurgeBuf() { int a; for (a=0;a<255;a++) { Buf[a]=0x20; } return; } int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { MSG msg; WNDCLASSEX wcex; HWND hWnd; hInst=hInstance; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); wcex.hIconSm = 0; wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(4); wcex.lpszMenuName = 0; wcex.lpszClassName = szWindowClass; RegisterClassEx(&wcex); hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,20, 20, 800, 200, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, SW_SHOWNORMAL); UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; int Retval; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case 101: //open port HWNDCOM=CreateFile(lpNumCOM, GENERIC_READ|GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, NULL,NULL); if (HWNDCOM==INVALID_HANDLE_VALUE) { Mem1= GetLastError(); wsprintf(&lpERR1[16],Par1,Mem1); MessageBox(0,lpERR1,lpCapERR,0); } else { wsprintf(&lpApp1[17],Par1,HWNDCOM); MessageBoxA(0,lpApp1,lpCapApp,0); Retval=BuildCommDCB(COMSETTING,&DCB1); if (Retval==0) //error DCB { Mem1= GetLastError(); wsprintf(&lpERR2[20],Par1,Mem1); MessageBox(0,lpERR2,lpCapERR,0); } Retval=SetCommState(HWNDCOM,&DCB1); if (Retval==0) //error SetCom { Mem1= GetLastError(); wsprintf(&lpERR3[23],Par1,Mem1); MessageBox(0,lpERR3,lpCapERR,0); } } break; case 102: //close port Retval=CloseHandle(HWNDCOM); if (Retval==0) //error close port { Mem1= GetLastError(); wsprintf(&lpERR6[17],Par1,Mem1); MessageBox(0,lpERR6,lpCapERR,0); } else { MessageBox(0,lpApp2,lpCapApp,0); } break; case 103: //read port PurgeBuf(); Retval= ReadFile(HWNDCOM,&Buf, 255,&LenBuf, NULL); if (Retval==0) //error read port { Mem1= GetLastError(); wsprintf(&lpERR4[16],Par1,Mem1); MessageBox(0,lpERR4,lpCapERR,0); } else { SendMessageA(HWNDEDT1,WM_SETTEXT,sizeof Buf,(LPARAM) Buf); MessageBox(0,lpApp3,lpCapApp,0); } break; case 104: //write port PurgeBuf(); SendMessage(HWNDEDT2,WM_GETTEXT,sizeof Buf,(LPARAM) Buf); Retval= WriteFile(HWNDCOM,&Buf, sizeof Buf,&LenBuf, NULL); if (Retval==0) //error write port { Mem1= GetLastError(); wsprintf(&lpERR5[17],Par1,Mem1); MessageBox(0,lpERR5,lpCapERR,0); } else { MessageBox(0,lpApp4,lpCapApp,0); } break; default: { return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_CREATE: HWNDBTN1= CreateWindowEx( 0,CLSBUTN,CPBUT1,STYLBTN,10,10,100,20,hWnd,ID_BTN1,hInst,0); HWNDBTN2= CreateWindowEx( 0,CLSBUTN,CPBUT2,STYLBTN,10,40,100,20,hWnd,ID_BTN2,hInst,0); HWNDBTN3= CreateWindowEx( 0,CLSBUTN,CPBUT3,STYLBTN,10,70,100,20,hWnd,ID_BTN3,hInst,0); HWNDBTN4= CreateWindowEx( 0,CLSBUTN,CPBUT4,STYLBTN,10,100,100,20,hWnd,ID_BTN4,hInst,0); HWNDEDT1= CreateWindowEx(0,CLSEDIT,CPEDT1,STYLEDT,120,70,600,20,hWnd,0,hInst,0); HWNDEDT2= CreateWindowEx(0,CLSEDIT,CPEDT2,STYLEDT,120,100,600,20,hWnd,0,hInst,0); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // stdafx.cpp : source file that includes just the standard includes // COMAPIc.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) #define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include// TODO: reference additional headers your program requires here //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)