Дата публикации статьи: 29.07.2006 15:36

Глава 11. Некоторые аспекты ввода информации по интерфейсу RS – 232

    Данные для поворота стрелки на определенный угол, показания прибора, режим работы механизма жестко зашиты в программу. Для отображения реальных физических величин в графической форме, необходим способ доставки данных в компьютер от внешних источников, а программа должна уметь прочитать эти данные. Процессор и память находятся в постоянном общении. Для передачи данных процессор и память используют системную шину (физически набор проводов). Процессор берет данные в памяти, обрабатывает в своих регистрах и записывает результат обратно в память. Для подключения к общению (между процессором и памятью) необходим способ выйти на системную шину из вне и выставить на нее свои данные. На рисунке 1 показана упрощенная схема системы ввода/вывода для общения персонального компьютера с внешним миром через стандартный COM порт (до появления PS/2 в более старых ПК мышь подключалась к COM порту).


Рис. 1

    Любая программа представляет из себя инструкции для процессора. Смысл инструкций заключается в том, что надо взять по определенному адресу данные в памяти (выставив на шине адрес), поместить в регистры процессора, обработать и вернуть обратно в память. Языки высокого уровня скрывают нюансы общения процессора с памятью. Многие устройства компьютера такие, как мышь, клавиатура, встроенные часы, таймер, жесткий диск итп имеют выход на системную шину. Если им надо вмешаться в программу и выставить на шину свои данные, они сигнализируют об этом процессору через контроллер прерываний. Контроллер выставляет сигнал на линии INT, приостанавливает обслуживание основной программы и засылает в регистры процессора векторы прерываний (вектор прерываний это адрес). Если процессор выставит на шине адрес вектора прерываний то найдет (в памяти) маленькую программку по обработке прерывания (драйвер). После ее обработки необходимо вернуться в основную программу. Для этого процессор должен знать адрес возврата, то есть эти данные должны где то хранится на время обработки прерывания. Местом для хранения временной информации на период обработки прерываний является защищенная область, которая тоже находится в памяти и называется стек. Этот процесс во многом характерен для шестнадцати разрядных процессоров работающих под управлением DOS и восьмиразрядных процессоров используемых во встроенных системах (мобильные телефоны, бытовая техника, сбор данных). Для тридцатидвухразрядных процессоров (в защищенном режиме) процесс прохождения прерывания усложняется необходимостью защитой влияния задач друг на друга (Windows многозадачная OC).
    К устройствам которые (как правило) уже встроены в системную плату компьютера и имеют выход на системную шину является два COM порта. На задней панели найдите два девяти(стандарт предусматривает и двадцати пяти штырьковый разъем) штырьковых разъема. На рисунке 2 указано физическое расположение линий для ввода данных RD и возврат обработанных данных TD по протоколу RS-232.


Рис. 2

    Необходимо внести некоторую ясность с терминологию. Когда говорят RS-232 подразумевают протокол передачи последовательных данных по стандарту разработанному ассоциациями промышленной связи и электронной промышленности. При этом логический ноль находится в диапазоне от +5 до +12 вольт, а логическая единица от –5 до –12 вольт. На схеме рисунка 1 вы можете видеть обозначение UART, так называется микросхема с помощью которой реализован каждый COM порт компьютера. Эта микросхема физически выходит на системную шину компьютера и может управляться прерываниями, то есть передавать данные из своих внутренних буферов в компьютер. Теперь нужно определиться с данными, которые поступают через COM порт по протоколу RS-232 и передаются с помощью UART на системную шину, а далее в память. Данные представляют различные уровни напряжения, которые в графической форме представлены на рисунке 3.


Рис. 3

    Данные начинаются со старт бита после которого микросхема UART начинает отсчет восьми (можно задать от5 до 8) битов данных, завершающихся стоп битом (можно задать перед ним бит четности). На рисунке биты данных будут 11100110, значит принят/передан один байт, который в шестнадцатеричной форме имеет значение E6, а в десятичной 230 (проверьте по инженерному калькулятору встроенному в Windows). Интерпретация этих данных зависит от вашей программы. В подавляющем числе случаев в одном байте данных передают коды ASCII.
    Для практической работы с COM портами соедините выводы TD и RD проводником(подойдет жила от телевизионного кабеля), но лучше всего купить в магазине радиотоваров разъем “мама” на COM порт и с обратной стороны соединить (лучше пайкой) 2 и 3 выводы. Конструкция представлена на рисунке 4.


Рис. 4

    Получается, что данные из выходного буфера тут же будут поступать в входной буфер. Для демонстрации приема/передачи напишем DLL на языке C++. Если вас не интересует построение DLL можете пропустить этот раздел.
    Программируя графику в Windows вы постоянно обращаетесь к функциям операционной системы, расположенным в DLL. Самой Windows не важно на каком языке Вы обращаетесь к ее сервисам (размещенным как правило в DLL) главное, чтобы запросы (например типы в параметрах функций) соответствовали тому, что Windows ожидает получить, как пример можно привести очень простой пример смешанного программирования VB/VC++ с использованием DLL. Для написания DLL используется С++ и мастер MFC DLL из последней версии Visual C++.NET (логотип .NET пусть не вводит в заблуждение – это обычный С++ стандарта ANSI, просто используется последняя версия MFC). Проект приложения находится в папке BuildDll/VCDLLVB.
    Выполните пошаговые инструкции:
1.Создайте новый проект. File/New/Project/Visual C++ Projects/MFC DLL.
2.В поле Name введите имя проекта - VCDLLVB.
3.Нажмите кнопку ОК (Рис 5).


Рис. 5

4.В появившемся окне выберите пункт Regular DLL with MFC statically linked (рис. 6).


Рис. 6

    Следующим шагом будет добавление функциональности в заготовку для DLL, а именно ввод в проект экспортируемых функций. Классы С++ (порожденные в том числе от CObject) можно использовать только внутри проекта. В фале “VCDLLVB.H” присутствует объявление следующего класса – “class CVCDLLVBApp : public CwinApp{…}”, а в фале “VCDLLVB.CPP” создается глобальный объект этого класса “CVCDLLVBApp theApp;”. По сути это минимальное по функциональности приложение MFC. 5.Добавте в проект файлы MyFunction.h и MyFunction.cpp с помощью меню Project/AddNewItem. Появится окно в котором выберите файл (.h) , а затем (.cpp). Присвойте обоим файлам имена MyFunction. Добавление в проект файлов, с одновременным их созданием, производится кнопкой Open (рис. 7).


Рис. 7

6.После всех манипуляций окно Solution Explorer должно иметь следующий вид (рис. 8).


Рис. 8

    Для редактирования произведите двойной щелчок мышью на именах функций. Введите следующий текст в файлы MyFunction.h и MyFunction.ccp:

// #include "afx.h" MFC не используется
////////////////////////////MyFunction.h////////////////////////
extern "C" LONG  PASCAL EXPORT AddLong(LPLONG a,LPLONG b);
extern "C" void  PASCAL EXPORT DrawCircl(HWND hOkna);

////////////////////////////MyFunction.ccp/////////////////////
#include "StdAfx.h"   //Необходимые директивы препроцессора
#include "MyFunction.h"
extern "C" LONG PASCAL EXPORT AddLong(LPLONG a,LPLONG b)
{
	 return (*a + *b);
}
extern "C" void  PASCAL EXPORT DrawCircl(HWND hOkna)
{
	::HDC hdc = ::GetDC(hOkna);
	::Ellipse(hdc,10,10,50,50);
	::ReleaseDC(hOkna, hdc);
}

    Редактированию необходимо подвергнуть и файл VCDLLVB.def, в нем хранятся имена экспортируемых функций. Так должен выглядеть этот файл:

; VCDLLVB.def : Declares the module parameters for the DLL.

LIBRARY      "VCDLLVB"

EXPORTS
    ; Explicit exports can go here
    AddLong
    DrawCircl

7.Измените режим компиляции с Debug на Release и выполните команду Build/Build VCDLLVB. В результате (если этап компиляции пройдет успешно) в папке “Release” проекта появится файл VCDLLVB.DLL. Теперь эту библиотеку можно протестировать в VB6. Проект тестового приложения находится в папке BuildDll/ VBDLL.
8.Для создания тестового приложения запустите VB и создайте проект Standard EXE. Нарисуйте форму примерно как на рисунке 9.


Рис. 9

Введите следующий код:

Option Explicit
Private Declare Function Ellipse& Lib "gdi32" (ByVal hDC As Long, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long)
Private Declare Function AddLong Lib "VCDLLVB.DLL" (ByRef v1 As Long, ByRef v2 As Long) As Long
Private Declare Sub DrawCircl Lib "VCDLLVB.DLL" (ByVal hWND As Long)

Private Sub Form_Load()
Dim L As Long
Dim L1 As Long
Dim L2 As Long
L1 = 2
L2 = 1
Text1.Text = AddLong(L2, L1)
End Sub

Private Sub Command1_Click()
DrawCircl Form1.hWND
End Sub


Private Sub Command2_Click()
Ellipse Form1.hDC, 10, 50, 50, 90
End Sub

9.Сохраните проект. Поместите файл VCDLLVB.DLL в папку проекта(см. BuildDll/ VBDLL). Если запустить приложение на выполнение и нажать в появившейся форме кнопки, его вид должен соответствовать рисунку 10.


Рис. 10

10.Оба круга нарисованы одной и той же функцией Win32 API, а число три получилось сложением внутри DLL параметров, переданных по ссылке из VB.
    В прототипе функции для сложения AddLong явно указано, с помощью ключевого слова ByRef, что мы передаем значение по ссылке.  Единственный параметр функции для рисования круга в DrawCircl объявлен как ByVal, то есть параметр передается по значению. В прототипе API функции Ellipse первый параметр имеет вид ByVal hDC, значит он тоже передается по значению. Оба параметра являются описателями – первый окна(формы), а второй контекста устройства. Использование описателей находится под контролем ядра Windows, а в параметрах функций API они передаются как обычные 32-разрядные числа, поэтому и применяется ByVal.
    В DLL можно размещать дополнительный код расширяющий возможности приложения. Функции для работы с COM портами, так же можно разместить в DLL, тогда этими функциями могут пользоваться приложения, написанные на VB/VB.NET, C# или Delphi. По типу приложения с кругами напишем приложение, в котором в DLL разместим код для работы с COM портами, а интерфейсную часть с VB6. Начнем с построения DLL. Проект находится в папке COMPort/ComPortDll. В случае с выводом кругов DLL была построена с помощью мастера Visual Studio. В данном случае были созданы пустые файлы ComDll.h; ComDll.cpp; ComDll.def в которые был помещен код. В заголовочном файле с расширением *.h в языке C/C++ помещают прототипы функций структур и переменных. В языке C#, который является С-подобным заголовочные файлы отсутствуют. В файле с расширением *.cpp кодируются тела функций, а в файле с расширением *.def помещаются имена функций которые может “увидеть” Visual Basic. Данный пример приведен для демонстрации концепции клиент/сервер, когда работа с портами отделена от интерфейсной части, написанной на VB6. Обратите внимание на код из файла ComDll.cpp:

	MyDCD.BaudRate = 9600;
	MyDCD.ByteSize = 8;
	MyDCD.Parity = NOPARITY; 
	MyDCD.StopBits = ONESTOPBIT;

    В данном случае формат данных, который мы рассматривали выше будет передаваться со скоростью 9600 бит/сек, данные будут состоять из 8 бит, не будет стоп-бита, проверки на четность не будет. Клиентское приложение написанное на VB6 находится в папке COMPort/UARTVB. Напомню, что для проверки работы COM порта необходимо соединить выводы TD и RD. Приложение в работе представлено на рисунке 11.


Рис. 11

    В поле “Пишу” введена фраза “Привет 1234”. Фраза состоит из одиннадцати символов. В коде для события записи к строке прибавляется символ str = str + Chr$(13) перевода строки. В данном случае можно обойтись и без него, но многие терминальные устройства не смогут правильно обработать данные без него. Известно, что Visual Basic “гоняет” строки из формата Unicode в ANSI и обратно (создавая при этом временные буферы). Такое положение вещей может привести к серьезным ошибкам. Приведенная программа клиент/сервер, написана исключительно в методических целях. Для написания коммерческих DLL необходимо досконально изучить последовательные коммуникации. Если для построения DLL вы воспользуетесь инфраструктурой COM (наиболее просто это можно сделать с помощью библиотеки ATL) то получите внутрипроцессный сервер COM. Если этот сервер будет построен по правилам OPC Foundation (независимая организация с штаб-квартирой Флорида, США) то получите OPC сервер – промышленный стандарт программного обеспечения для работы с аппаратурой. Для работы с последовательными коммуникациями в среде Visual Basic 6 не обязательно выносить функции API в Dll или COM серверы (написанные на C/C++). С помощью Declare вы можете, так же как и в C/C++, обращаться к драйверу последовательного порта Windows, но это чревато ошибками. В состав VB6 входит ActiveX элемент - MSComm, который создан корпорацией Microsoft специально для работы с модемами и последовательными коммуникациями. Это элемент инкапсулирует в себе настройку COM портов и умеет правильно обращаться с данными. Программа демонстрирующая работу с элементом ActiveX находится в папке COMPort/ ComPortMSComm. Обратите внимание на строчку в коде примера - Settings = "9600,n,8,1". Эта строка соответствует структуре из Dll. При нажатии на кнопку “Отправка данных” вы посылаете в выходной буфер строку "$002" + Chr$(13). Эти данные должны появится в входном буфере при нажатии кнопки “Прием данных”.